windows-nt/Source/XPSP1/NT/base/subsys/posix/client/dlltimer.c
2020-09-26 16:20:57 +08:00

244 lines
3.9 KiB
C

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
dlltimer.c
Abstract:
This module implements client side stubs for timer related APIs
Author:
Mark Lucovsky (markl) 08-Aug-1989
Revision History:
--*/
#include "psxdll.h"
#include <signal.h>
ULONG MagicMultiplier = 10000000;
VOID
SecondsToTime (
OUT PLARGE_INTEGER Time,
IN ULONG Seconds
)
/*++
Routine Description:
This function converts from seconds to an equivalent
relative time value in units of 100ns intervals.
Arguments:
Time - Returns the equivalant relative time value.
Seconds - Supplies the time in seconds.
Return Value:
None.
--*/
{
Time->QuadPart = (LONGLONG)Seconds * (LONGLONG)MagicMultiplier;
Time->QuadPart = -Time->QuadPart;
}
ULONG
TimeToSeconds (
IN PLARGE_INTEGER Time
)
/*++
Routine Description:
This function converts from absolute time in units of 100ns
intervals to seconds.
Arguments:
Time - Supplies an absolute time in units of 100ns intervals.
Return Value:
The value of time in seconds.
--*/
{
LARGE_INTEGER Seconds;
#if 0
Seconds = RtlExtendedMagicDivide(
*Time,
MagicDivisor,
MagicShiftCount
);
#else
ULONG R; // remainder
Seconds = RtlExtendedLargeIntegerDivide(
*Time, // Dividend
MagicMultiplier, // Divisor
&R
);
#endif
return Seconds.LowPart;
}
unsigned int __cdecl
alarm(unsigned int seconds)
{
PSX_API_MSG m;
PPSX_ALARM_MSG args;
NTSTATUS st;
unsigned int PreviousSeconds;
args = &m.u.Alarm;
PSX_FORMAT_API_MSG(m, PsxAlarmApi, sizeof(*args));
if (0 == seconds) {
args->CancelAlarm = TRUE;
} else {
SecondsToTime(&args->Seconds, seconds);
args->CancelAlarm = FALSE;
}
args->PreviousSeconds.LowPart = 0;
args->PreviousSeconds.HighPart = 0;
st = NtRequestWaitReplyPort(PsxPortHandle, (PPORT_MESSAGE)&m,
(PPORT_MESSAGE)&m);
#ifdef PSX_MORE_ERRORS
ASSERT(NT_SUCCESS(st));
#endif
PreviousSeconds = TimeToSeconds(&args->PreviousSeconds);
if (args->PreviousSeconds.LowPart != 0 && PreviousSeconds == 0)
PreviousSeconds = 1;
return PreviousSeconds;
}
static void
__cdecl
sigalrm(int signo)
{
//
// XXX.mjb: do we need to do anything here?
//
}
unsigned int
__cdecl
sleep(unsigned int seconds)
{
unsigned int prev, t;
PVOID ohandler;
sigset_t set, oset;
if (seconds == 0) {
getpid(); // encourage context switch
return 0;
}
sigemptyset(&set);
sigaddset(&set, SIGALRM);
sigprocmask(SIG_UNBLOCK, &set, &oset);
prev = alarm(0);
if (0 != prev && prev < seconds) {
//
// There was already an alarm scheduled, and it would
// have gone off before the sleep should have been done.
// We sleep for the shorter amount of time, and return
// with no alarm scheduled.
//
sigset_t s;
// block SIGALRM until we're ready for it.
sigemptyset(&s);
sigaddset(&s, SIGALRM);
sigprocmask(SIG_BLOCK, &s, NULL);
(void)alarm(prev); // restore previous alarm
s = oset;
sigdelset(&s, SIGALRM);
sigsuspend(&s);
sigprocmask(SIG_SETMASK, &oset, NULL);
return seconds - prev;
}
ohandler = signal(SIGALRM, sigalrm);
{
sigset_t s;
#if 1
// block SIGALARM until we're ready for it.
sigemptyset(&s);
sigaddset(&s, SIGALRM);
sigprocmask(SIG_BLOCK, &s, NULL);
#endif
(void)alarm(seconds);
#if 1
s = oset;
sigdelset(&s, SIGALRM);
sigsuspend(&s);
#else
pause();
#endif
}
t = alarm(0);
signal(SIGALRM, ohandler);
if (0 != prev) {
//
// There was a previous alarm scheduled, and we re-schedule
// it to make it look like we hadn't fiddled with it.
//
if (prev - seconds == 0) {
//
// the previously-scheduled alarm would have gone
// off at the same time the sleep was supposed to
// return. We want to make sure two alarms are
// actually delivered, so we add a second to the
// previous alarm time.
//
prev++;
}
(void)alarm(prev - seconds);
}
sigprocmask(SIG_SETMASK, &oset, NULL);
return t;
}