Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1101 lines
28 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
dlltimer.c
Abstract:
This module implements the OS/2 V2.0 Time and Timer API Calls
Author:
Steve Wood (stevewo) 20-Sep-1989 (Adapted from URTL\alloc.c)
Revision History:
Yaron Shamir 12-17-91 Implemented the timers completely different
and correctly (without using APC routines which break 16b
semaphores.
Yaron Shamir 12-19-91 Implemented DosTimerAsync. Use Recycled timer
handles.
Ofer Porat 10-28-92 Added inter-thread synchronization to timer
management. Corrected semaphore validity check. Slightly
improved timer accuracy. Corrected timer thread cleanup.
Michael Jarus 4-1-93. DosGetDateTime retrives its info from the Global
info segment. Only the first time after DosSetDateTime it gets the
info from the system.
--*/
#define INCL_OS2V20_TIMERS
#define INCL_OS2V20_ERRORS
#define INCL_OS2V20_SEMAPHORES
#include "os2dll.h"
#include "os2dll16.h"
#include "os2win.h"
#include <stdio.h>
APIRET DosSemClear( HSEM handle );
PVOID Od2LookupSem( HSEM hsem );
extern ULONG Od2GlobalInfoSeg;
typedef struct _TIME_SEM {
ULONG TimerSlot;
HSEM Sem;
ULONG ulTime;
ULONG ulSysTime;
BOOLEAN RepeatingTimer;
} TIME_SEM, *PTIME_SEM;
#define TIMER_FREE (HANDLE) 0L // indicates a free timer slot
#define TIMER_GRABBED (HANDLE) -1L // temproarily holds the slot until it's filled
#define OD2_MAX_TIMERS 100 // maximal number of timers available to process
#define STK_SIZE 0x1000 // timer thread stack size
#define OP_CANCEL 0 // some operation codes...
#define OP_DELETE 1
#define OP_UDELETE 2
typedef struct _Od2_TIMERS {
ULONG nexttimer; //
// indicates the highest used timer slot.
// if nexttimer == OD2_MAX_TIMER then slots
// must be recycled from free slots inside the array.
//
ULONG used; //
// indicates how many slots are actually in use.
//
HANDLE Timers[OD2_MAX_TIMERS];
} OD2_TIMERS;
static OD2_TIMERS Od2Timers = {0};
// these are used to sync access to Od2Timers
static RTL_CRITICAL_SECTION ProtectOd2Timers;
static PRTL_CRITICAL_SECTION pProtectOd2Timers = NULL;
BOOL Od2DosSetDateTimeDone; // after SteDateTime, reads info from system
// Initialization routine for the Critical Section used to sync threads
// Must be called during process startup.
NTSTATUS
Od2InitializeTimers(VOID)
{
NTSTATUS Status;
Status = RtlInitializeCriticalSection(&ProtectOd2Timers);
if (NT_SUCCESS(Status)) {
pProtectOd2Timers = &ProtectOd2Timers;
}
return(Status);
}
APIRET
DosGetDateTime(
OUT PDATETIME DateTime
)
{
APIRET RetCode;
LARGE_INTEGER SystemTime;
LARGE_INTEGER LocalTime;
TIME_FIELDS NtDateTime;
SYSTEM_TIMEOFDAY_INFORMATION SystemInformation;
SHORT TimeZone;
GINFOSEG *pGlobalInfo = (GINFOSEG *) Od2GlobalInfoSeg;
if (!Od2DosSetDateTimeDone)
{
try
{
DateTime->year = pGlobalInfo->year;
DateTime->month = pGlobalInfo->month;
DateTime->day = pGlobalInfo->day;
DateTime->weekday = pGlobalInfo->weekday;
DateTime->hours = pGlobalInfo->hour;
DateTime->minutes = pGlobalInfo->minutes;
DateTime->seconds = pGlobalInfo->seconds;
DateTime->hundredths = pGlobalInfo->hundredths;
DateTime->weekday = pGlobalInfo->weekday ;
DateTime->timezone = pGlobalInfo->timezone;
} except( EXCEPTION_EXECUTE_HANDLER )
{
Od2ExitGP();
}
} else
{
Od2DosSetDateTimeDone = FALSE;
RetCode = Or2GetDateTimeInfo(
&SystemTime,
&LocalTime,
&NtDateTime,
(PVOID)&SystemInformation,
&TimeZone
);
if (RetCode)
{
#if DBG
IF_OD2_DEBUG ( TIMERS )
{
KdPrint(("DosGetDateTime: Or2GetDateTimeInfo rc %lu\n",
RetCode));
}
#endif
return(RetCode);
}
try
{
DateTime->year = NtDateTime.Year;
DateTime->month = (UCHAR)(NtDateTime.Month);
DateTime->day = (UCHAR)(NtDateTime.Day);
DateTime->weekday = (UCHAR)(NtDateTime.Weekday);
DateTime->hours = (UCHAR)(NtDateTime.Hour);
DateTime->minutes = (UCHAR)(NtDateTime.Minute);
DateTime->seconds = (UCHAR)(NtDateTime.Second);
DateTime->hundredths = (UCHAR)(NtDateTime.Milliseconds / 10);
DateTime->weekday = (UCHAR)NtDateTime.Weekday;
DateTime->timezone = TimeZone;
} except( EXCEPTION_EXECUTE_HANDLER )
{
Od2ExitGP();
}
}
#if DBG
IF_OD2_DEBUG ( TIMERS )
{
KdPrint(("DosGetDateTime: Time %u:%u:%u.%u, Day %u:%u:%u, TimeZone %i, WeekDay %u\n",
DateTime->hours, DateTime->minutes, DateTime->seconds,
DateTime->hundredths,
DateTime->month, DateTime->day, DateTime->year,
DateTime->timezone, DateTime->weekday));
}
#endif
return( NO_ERROR );
}
// This routine sets the time zone given the bias
NTSTATUS
Od2SetTimeZoneFromBias(
IN LONG Bias,
OUT PRTL_TIME_ZONE_INFORMATION pOldTz OPTIONAL
)
{
NTSTATUS Status;
RTL_TIME_ZONE_INFORMATION OldTz;
RTL_TIME_ZONE_INFORMATION NewTz;
WCHAR Sign;
Status = RtlQueryTimeZoneInformation(&OldTz);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint(("Od2SetTimeZoneFromBias: RtlQueryTimeZoneInformation rc %lx\n", Status));
}
#endif
return(Status);
}
if (ARGUMENT_PRESENT(pOldTz)) {
RtlMoveMemory(pOldTz, &OldTz, sizeof(OldTz));
}
if (Bias == -1 || OldTz.Bias == Bias) {
return(STATUS_SUCCESS);
}
RtlZeroMemory(&NewTz, sizeof(NewTz));
NewTz.Bias = Bias;
if (Bias <= 0) {
Sign = L'+';
Bias = -Bias;
} else {
Sign = L'-';
}
_snwprintf(NewTz.StandardName, 32, L"GMT %c %lu:%02lu", Sign, Bias/60, Bias%60);
RtlMoveMemory(NewTz.DaylightName, NewTz.StandardName, 32);
Status = RtlSetTimeZoneInformation(&NewTz);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint(("Od2SetTimeZoneFromBias: RtlSetTimeZoneInformation rc %lx\n", Status));
}
#endif
return(Status);
}
// refresh system time
Status = NtSetSystemTime(NULL,NULL);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint(("Od2SetTimeZoneFromBias: NtSetSystemTime(NULL,NULL) rc %lx\n", Status));
}
#endif
RtlSetTimeZoneInformation(&OldTz); // restore old info
NtSetSystemTime(NULL,NULL);
return(Status);
}
return(STATUS_SUCCESS);
}
APIRET
DosSetDateTime(
IN PDATETIME DateTime
)
{
NTSTATUS Status, Status2;
LARGE_INTEGER SystemTime, LocalTime;
TIME_FIELDS NtDateTime;
RTL_TIME_ZONE_INFORMATION OldTz;
LONG ZoneTime;
HANDLE TokenHandle;
//
// The following structure is derived from TOKEN_PRIVILEGES
//
struct {
ULONG PrivilegeCount;
LUID_AND_ATTRIBUTES Privileges[1];
} Priv;
#if DBG
IF_OD2_DEBUG ( TIMERS )
{
KdPrint(("DosSetDateTime: Time %u:%u:%u.%u, Day %u:%u:%u, TimeZone %i, WeekDay %u\n",
DateTime->hours, DateTime->minutes, DateTime->seconds,
DateTime->hundredths,
DateTime->month, DateTime->day, DateTime->year,
DateTime->timezone, DateTime->weekday));
}
#endif
try {
NtDateTime.Year = DateTime->year;
NtDateTime.Month = DateTime->month;
NtDateTime.Day = DateTime->day;
NtDateTime.Hour = DateTime->hours;
NtDateTime.Minute = DateTime->minutes;
NtDateTime.Second = DateTime->seconds;
NtDateTime.Milliseconds = (CSHORT)(DateTime->hundredths * 10);
NtDateTime.Weekday = DateTime->weekday;
ZoneTime = (LONG)DateTime->timezone;
}
except( EXCEPTION_EXECUTE_HANDLER ) {
Od2ExitGP();
}
if (( DateTime->year < 1980 ) || ( DateTime->year > 2079 ))
{
#if DBG
IF_OD2_DEBUG ( TIMERS )
{
KdPrint(("DosSetDateTime: year %u out of range\n",
DateTime->year));
}
#endif
return (ERROR_TS_DATETIME );
}
if (( ZoneTime < -12*60 ) || ( ZoneTime > 12*60 ))
{
#if DBG
IF_OD2_DEBUG ( TIMERS )
{
KdPrint(("DosSetDateTime: timezone %d out of range\n",
DateTime->timezone));
}
#endif
return (ERROR_TS_DATETIME );
}
if (!RtlTimeFieldsToTime( &NtDateTime, &LocalTime ))
{
#if DBG
IF_OD2_DEBUG ( TIMERS )
{
KdPrint(("DosSetDateTime: RtlTimeFieldsToTime failed\n"));
}
#endif
return (ERROR_TS_DATETIME );
}
// First set the time zone correctly
Status = Od2SetTimeZoneFromBias(ZoneTime, &OldTz);
if (!NT_SUCCESS( Status ))
{
#if DBG
IF_OD2_DEBUG ( TIMERS )
{
KdPrint(("DosSetDateTime: Od2SetTimeZoneFromBias rc %lx\n",
Status));
}
#endif
if (Status == STATUS_INVALID_PARAMETER)
{
return (ERROR_TS_DATETIME );
} else
{
return (Or2MapNtStatusToOs2Error(Status, ERROR_TS_DATETIME ));
}
}
//
// Convert Local time to UTC
//
Status = RtlLocalTimeToSystemTime ( &LocalTime, &SystemTime);
if (!NT_SUCCESS( Status ))
{
#if DBG
IF_OD2_DEBUG ( TIMERS )
{
KdPrint(("DosSetDateTime: RtlLocalTileToSystemTime rc %lx\n",
Status));
}
#endif
RtlSetTimeZoneInformation(&OldTz); // restore old timezone
NtSetSystemTime(NULL,NULL);
if (Status == STATUS_INVALID_PARAMETER)
{
return (ERROR_TS_DATETIME );
} else
{
return (Or2MapNtStatusToOs2Error(Status, ERROR_TS_DATETIME ));
}
}
Status = NtSetSystemTime( &SystemTime, NULL);
if (Status == STATUS_PRIVILEGE_NOT_HELD) {
//
// The process does not have the privilege.
// However, the user may be admin, so we try to grab the privilege.
//
do { // 1-time loop so we can break on error
Status2 = NtOpenProcessToken(NtCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES,
&TokenHandle);
if (!NT_SUCCESS(Status2)) {
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint(("DosSetDateTime: NtOpenProcessToken rc %lx\n",
Status2));
}
#endif
break;
}
Priv.PrivilegeCount = 1L;
Priv.Privileges[0].Luid.LowPart = SE_SYSTEMTIME_PRIVILEGE;
Priv.Privileges[0].Luid.HighPart = 0;
Priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
Status2 = NtAdjustPrivilegesToken(TokenHandle,
FALSE,
(PTOKEN_PRIVILEGES) &Priv,
0,
NULL,
0);
NtClose(TokenHandle);
if (Status2 == STATUS_NOT_ALL_ASSIGNED || !NT_SUCCESS(Status2)) {
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint(("DosSetDateTime: NtAdjustTokenPrivileges rc %lx\n",
Status2));
}
#endif
break;
}
Status = NtSetSystemTime( &SystemTime, NULL);
} while (FALSE);
}
if (!NT_SUCCESS( Status )) {
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint(("DosSetDateTime: NtSetSystemTime rc %lx\n",
Status));
}
#endif
RtlSetTimeZoneInformation(&OldTz); // restore old timezone
NtSetSystemTime(NULL,NULL);
if (Status == STATUS_INVALID_PARAMETER)
{
return (ERROR_TS_DATETIME );
} else
{
return (Or2MapNtStatusToOs2Error(Status, ERROR_TS_DATETIME ));
}
}
Od2DosSetDateTimeDone = TRUE; // Next time get time directly from
// system, instead from GInfoSeg
return(NO_ERROR);
}
APIRET
DosSleep(
IN ULONG MilliSeconds
)
{
NTSTATUS Status;
LARGE_INTEGER DelayIntervalValue;
PLARGE_INTEGER DelayInterval;
LARGE_INTEGER StartTimeStamp;
DelayInterval = Od2CaptureTimeout( MilliSeconds, &DelayIntervalValue );
if (DelayInterval == NULL) {
DelayIntervalValue.LowPart = 0x0;
DelayIntervalValue.HighPart = 0x80000000;
DelayInterval = &DelayIntervalValue;
}
DosSleep_retry:
Od2StartTimeout(&StartTimeStamp);
Status = NtDelayExecution( TRUE, DelayInterval );
if ((Status == STATUS_SUCCESS) ||
(Status == STATUS_TIMEOUT)) {
return( NO_ERROR );
}
else
if (Status == STATUS_ALERTED) {
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint(("DosSleep - Error in NtDelayExecution STATUS_ALERTED\n"));
}
#endif
return( ERROR_TS_WAKEUP );
}
else
if (Status == STATUS_USER_APC) {
#if DBG
DbgPrint("[%d,%d] WARNING !!! DosSleep was broken by APC\n",
Od2Process->Pib.ProcessId,
Od2CurrentThreadId()
);
#endif
if (Od2ContinueTimeout(&StartTimeStamp, DelayInterval) == STATUS_SUCCESS) {
goto DosSleep_retry;
}
else {
return( NO_ERROR );
}
}
else {
#if DBG
KdPrint(("DosSleep - Error in NtDelayExecution Status %lx\n", Status));
#endif
return( Or2MapStatus( Status ) );
}
}
APIRET
Od2AllocateTimerSlot(
OUT PULONG TimerSlot
)
{
ULONG FreeSlot;
if (pProtectOd2Timers == NULL ||
!NT_SUCCESS(RtlEnterCriticalSection(pProtectOd2Timers))) {
return(ERROR_TS_NOTIMER);
}
if (Od2Timers.used == OD2_MAX_TIMERS) {
//
// out of timers
//
RtlLeaveCriticalSection(pProtectOd2Timers);
return(ERROR_TS_NOTIMER);
}
if (Od2Timers.nexttimer == OD2_MAX_TIMERS) {
//
// crawl thru used timers to find a free one
//
for (FreeSlot = 0; Od2Timers.Timers[FreeSlot] != TIMER_FREE;
FreeSlot++) {
}
} else {
FreeSlot = Od2Timers.nexttimer++;
}
Od2Timers.used++;
Od2Timers.Timers[FreeSlot] = TIMER_GRABBED;
*TimerSlot = FreeSlot;
RtlLeaveCriticalSection(pProtectOd2Timers);
return(NO_ERROR);
}
APIRET
Od2DeallocateTimerSlot(
IN ULONG TimerSlot,
IN ULONG Op, // OP_CANCEL - cancel a grabbed slot
// OP_DELETE - delete an existing slot
// OP_UDELETE - validate slot number and delete
OUT PHANDLE pHandle OPTIONAL // optionally returns slot value
)
{
HANDLE Handle;
if (Op == OP_UDELETE && (TimerSlot <0 || TimerSlot >= OD2_MAX_TIMERS)) {
return(ERROR_TS_HANDLE);
}
if (pProtectOd2Timers == NULL ||
!NT_SUCCESS(RtlEnterCriticalSection(pProtectOd2Timers))) {
return(ERROR_TS_HANDLE);
}
try {
Handle = Od2Timers.Timers[TimerSlot];
switch ((ULONG) Handle) {
case (ULONG) TIMER_FREE:
return(ERROR_TS_HANDLE);
case (ULONG) TIMER_GRABBED:
if (Op != OP_CANCEL) {
return(ERROR_TS_HANDLE);
}
break;
default:
if (Op == OP_CANCEL) {
return(ERROR_TS_HANDLE);
}
}
if (ARGUMENT_PRESENT(pHandle)) {
*pHandle = Handle;
}
Od2Timers.used--;
Od2Timers.Timers[TimerSlot] = TIMER_FREE;
if (TimerSlot == Od2Timers.nexttimer - 1) {
for (;TimerSlot != (ULONG) -1 &&
Od2Timers.Timers[TimerSlot] == TIMER_FREE; TimerSlot--) {
}
Od2Timers.nexttimer = TimerSlot + 1;
}
return(NO_ERROR);
} finally {
RtlLeaveCriticalSection(pProtectOd2Timers);
}
}
/* This function is omitted since it's only used for a certain synchronization
action below, and this sync action has been removed. See the comment
in DosAsyncTimer for an explanation why.
APIRET
Od2WriteTimerSlot(
IN ULONG TimerSlot, // assumes validity
IN HANDLE Handle
)
{
if (pProtectOd2Timers == NULL ||
!NT_SUCCESS(RtlEnterCriticalSection(pProtectOd2Timers))) {
return(ERROR_TS_HANDLE);
}
Od2Timers.Timers[TimerSlot] = Handle;
RtlLeaveCriticalSection(pProtectOd2Timers);
return(NO_ERROR);
}
*/
VOID
Od2TimerThread(
PVOID param
)
{
APIRET rc;
NTSTATUS Status;
HANDLE Handle;
ULONG Compensation;
PLARGE_INTEGER DelayInterval, RepeatDelayInterval;
LARGE_INTEGER DelayIntervalValue, RepeatDelayIntervalValue;
PTIME_SEM pTimeSem = (PTIME_SEM)(param);
GINFOSEG *pGlobalInfo;
pGlobalInfo = (GINFOSEG *) Od2GlobalInfoSeg;
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint(("Entering Timer Thread: TimerSlot %lu Time %d, Sem %lx\n",
pTimeSem->TimerSlot, pTimeSem->ulTime, pTimeSem->Sem));
KdPrint(("pTimeSem->SysTime = %d\n", pTimeSem->ulSysTime));
KdPrint(("Current Boot Time = %d\n", pGlobalInfo->msecs));
}
#endif
if (NtTestAlert() == STATUS_ALERTED) {
goto FinishOff;
}
//
// First calculate the on-going delay time (no offset)
//
if (pTimeSem->RepeatingTimer) {
RepeatDelayInterval = Od2CaptureTimeout(
pTimeSem->ulTime,
&RepeatDelayIntervalValue);
if (RepeatDelayInterval == NULL) {
RepeatDelayIntervalValue.LowPart = 0x0;
RepeatDelayIntervalValue.HighPart = 0x80000000;
RepeatDelayInterval = &RepeatDelayIntervalValue;
}
}
//
// Now compensate for time passed between DosAsync/StartTimer and
// this moment
//
if (pTimeSem->ulTime == SEM_INDEFINITE_WAIT) {
DelayIntervalValue.LowPart = 0x0;
DelayIntervalValue.HighPart = 0x80000000;
DelayInterval = &DelayIntervalValue;
} else {
Compensation = pGlobalInfo->msecs - pTimeSem->ulSysTime;
if (pTimeSem->ulTime <= Compensation) {
goto NoFirstDelay;
}
DelayInterval = Od2CaptureTimeout(
pTimeSem->ulTime - Compensation,
&DelayIntervalValue );
}
Status = NtDelayExecution(TRUE, DelayInterval);
if (Status == STATUS_ALERTED) {
goto FinishOff;
}
NoFirstDelay:
#if DBG
IF_OD2_DEBUG ( APIS ) {
KdPrint(("[Od2TimerThread] DosSemClear\n"));
}
#endif
rc = DosSemClear (pTimeSem->Sem);
if (rc != NO_ERROR) {
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint (("Od2TimerThread: error at DosSemClear %d, Sem %lx\n",
rc, pTimeSem->Sem));
}
#endif
}
if (!pTimeSem->RepeatingTimer) {
//
// one time timer - exit
//
if (Od2DeallocateTimerSlot(pTimeSem->TimerSlot, OP_DELETE, &Handle)
== NO_ERROR) {
CloseHandle(Handle);
}
goto FinishOff;
}
for (;;) {
Status = NtDelayExecution(TRUE, RepeatDelayInterval);
if (Status == STATUS_ALERTED) {
goto FinishOff;
}
#if DBG
IF_OD2_DEBUG ( APIS ) {
KdPrint(("[Od2TimerThread] DosSemClear\n"));
}
#endif
rc = DosSemClear (pTimeSem->Sem);
if (rc != NO_ERROR) {
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint (("Od2TimerThread: error at DosSemClear %d, Sem %lx\n",
rc, pTimeSem->Sem));
}
#endif
}
}
FinishOff:
RtlFreeHeap(Od2Heap, 0, pTimeSem);
ExitThread(STATUS_SUCCESS);
}
APIRET
DosAsyncTimer(
IN ULONG MilliSeconds,
IN HEV EventSem,
OUT PHTIMER TimerHandle
)
{
GINFOSEG *pGlobalInfo;
ULONG TimeAnchor;
PTIME_SEM pTimeSem;
APIRET RetCode;
NTSTATUS Status=0;
HANDLE ThreadHandle;
ULONG FreeSlot;
ULONG Tid;
PUSHORT pTimerHandle = (PUSHORT)TimerHandle;
pGlobalInfo = (GINFOSEG *) Od2GlobalInfoSeg;
TimeAnchor = pGlobalInfo->msecs;
//
// Adjust the minimum delay to at least one processor clock tick (for compatibility with OS/2)
//
if (MilliSeconds < (ULONG) pGlobalInfo->cusecTimerInterval / 10L) {
MilliSeconds = (ULONG) pGlobalInfo->cusecTimerInterval / 10L;
}
//
// Validate Semaphore Handle
//
if (Od2LookupSem(EventSem) == NULL) {
Od2ExitGP();
}
if ((RetCode = Od2AllocateTimerSlot(&FreeSlot)) != NO_ERROR) {
return(RetCode);
}
try {
*pTimerHandle = (USHORT) (FreeSlot + 1);
}
except( EXCEPTION_EXECUTE_HANDLER ) {
Od2ExitGP();
}
//
// Found a timer slot - value in FreeSlot
// now get the real work done
//
pTimeSem = RtlAllocateHeap( Od2Heap, 0, sizeof (TIME_SEM));
if (pTimeSem == NULL)
{
Od2DeallocateTimerSlot(FreeSlot, OP_CANCEL, NULL);
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint (("DosAsyncTimer: can't allocate heap for TIME_SEM\n"));
}
#endif
return(ERROR_NOT_ENOUGH_MEMORY);
}
pTimeSem->ulSysTime = TimeAnchor;
pTimeSem->ulTime = MilliSeconds;
pTimeSem->TimerSlot = FreeSlot;
pTimeSem->Sem = (HSEM) EventSem;
pTimeSem->RepeatingTimer = FALSE;
//
// Create A thread dedicated to clear the semaphore periodically
// We stop it by alerting the thread
//
ThreadHandle = CreateThread(NULL,
STK_SIZE,
(PFNTHREAD) Od2TimerThread,
pTimeSem,
CREATE_SUSPENDED,
&Tid);
if (ThreadHandle == 0) {
RtlFreeHeap(Od2Heap, 0, pTimeSem);
Od2DeallocateTimerSlot(FreeSlot, OP_CANCEL, NULL);
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint (("DosAsyncTimer: can't Create timer thread %lx\n", Status));
}
#endif
return ERROR_TS_NOTIMER;
}
// The following command should theoretically be synchronized by
// calling Od2WriteTimerSlot(FreeSlot, ThreadHandle);
// However, the only case where the non-synced version can interfere
// is when the assembly-level store instruction is split on a
// multi-processor system. Therefore, in the interest of efficiency
// it's implemented without sync. (A similar situation occurs in
// DosStartTimer below).
Od2Timers.Timers[FreeSlot] = ThreadHandle;
Status = ResumeThread(ThreadHandle);
if (Status == -1) {
RtlFreeHeap(Od2Heap, 0, pTimeSem);
TerminateThread(ThreadHandle, STATUS_SUCCESS);
WaitForSingleObject(ThreadHandle, (ULONG) SEM_INDEFINITE_WAIT);
CloseHandle(ThreadHandle);
Od2DeallocateTimerSlot(FreeSlot, OP_DELETE, NULL);
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint (("DosAsyncTimer: can't resume timer thread %lx\n", Status));
}
#endif
return ERROR_TS_NOTIMER;
}
return(NO_ERROR);
}
APIRET
DosStartTimer(
IN ULONG MilliSeconds,
IN HEV EventSem,
OUT PHTIMER TimerHandle
)
{
ULONG TimeAnchor;
PTIME_SEM pTimeSem;
APIRET RetCode;
NTSTATUS Status=0;
HANDLE ThreadHandle;
ULONG FreeSlot;
ULONG Tid;
PUSHORT pTimerHandle = (PUSHORT)TimerHandle;
GINFOSEG *pGlobalInfo;
pGlobalInfo = (GINFOSEG *) Od2GlobalInfoSeg;
TimeAnchor = pGlobalInfo->msecs;
//
// Adjust the minimum delay to at least one processor clock tick (for compatibility with OS/2)
//
if (MilliSeconds < (ULONG) pGlobalInfo->cusecTimerInterval / 10L) {
MilliSeconds = (ULONG) pGlobalInfo->cusecTimerInterval / 10L;
}
//
// Validate Semaphore Handle
//
if (Od2LookupSem(EventSem) == NULL) {
Od2ExitGP();
}
if ((RetCode = Od2AllocateTimerSlot(&FreeSlot)) != NO_ERROR) {
return(RetCode);
}
try {
*pTimerHandle = (USHORT) (FreeSlot + 1);
}
except( EXCEPTION_EXECUTE_HANDLER ) {
Od2ExitGP();
}
//
// Found a timer slot - value in FreeSlot
// now get the real work done
//
pTimeSem = RtlAllocateHeap( Od2Heap, 0, sizeof (TIME_SEM));
if (pTimeSem == NULL)
{
Od2DeallocateTimerSlot(FreeSlot, OP_CANCEL, NULL);
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint (("DosStartTimer: can't allocate heap for TIME_SEM\n"));
}
#endif
return(ERROR_NOT_ENOUGH_MEMORY);
}
pTimeSem->ulSysTime = TimeAnchor;
pTimeSem->ulTime = MilliSeconds;
pTimeSem->TimerSlot = FreeSlot;
pTimeSem->Sem = (HSEM) EventSem;
pTimeSem->RepeatingTimer = TRUE;
//
// Create A thread dedicated to clear the semaphore periodically
// We stop it by alerting the thread
//
ThreadHandle = CreateThread(NULL,
STK_SIZE,
(PFNTHREAD) Od2TimerThread,
pTimeSem,
CREATE_SUSPENDED,
&Tid);
if (ThreadHandle == 0) {
RtlFreeHeap(Od2Heap, 0, pTimeSem);
Od2DeallocateTimerSlot(FreeSlot, OP_CANCEL, NULL);
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint (("DosStartTimer: can't Create timer thread %lx\n", Status));
}
#endif
return ERROR_TS_NOTIMER;
}
// See the comment in DosAsyncTimer above regarding the synchronization
// of the following instruction.
Od2Timers.Timers[FreeSlot] = ThreadHandle;
Status = ResumeThread(ThreadHandle);
if (Status == -1) {
RtlFreeHeap(Od2Heap, 0, pTimeSem);
TerminateThread(ThreadHandle, STATUS_SUCCESS);
WaitForSingleObject(ThreadHandle, (ULONG) SEM_INDEFINITE_WAIT);
CloseHandle(ThreadHandle);
Od2DeallocateTimerSlot(FreeSlot, OP_DELETE, NULL);
#if DBG
IF_OD2_DEBUG ( TIMERS ) {
KdPrint (("DosStartTimer: can't resume timer thread %lx\n", Status));
}
#endif
return ERROR_TS_NOTIMER;
}
return(NO_ERROR);
}
APIRET
DosStopTimer(
IN HTIMER TimerHandle
)
{
APIRET RetCode;
NTSTATUS Status;
HANDLE ThreadHandle;
if ((RetCode = Od2DeallocateTimerSlot((ULONG) TimerHandle - 1, OP_UDELETE, &ThreadHandle))
!= NO_ERROR) {
return(RetCode);
}
Status = NtAlertThread(ThreadHandle);
if(!NT_SUCCESS(Status)){
#if DBG
KdPrint (("DosStopTimer: can't Alert timer thread %lx\n", Status));
#endif
return(ERROR_TS_HANDLE);
}
WaitForSingleObject(ThreadHandle, INFINITE);
CloseHandle(ThreadHandle);
return(NO_ERROR);
}
VOID
Od2CloseAllTimers(VOID)
{
ULONG Slot;
HANDLE Handle;
PRTL_CRITICAL_SECTION Copy;
if (pProtectOd2Timers == NULL) {
return;
}
RtlEnterCriticalSection(pProtectOd2Timers);
Copy = pProtectOd2Timers;
pProtectOd2Timers = NULL; // invalidate the CritSec
for (Slot = 0; Od2Timers.used > 0; Slot++)
{
Handle = Od2Timers.Timers[Slot];
if (Handle == TIMER_FREE)
continue;
Od2Timers.used--;
if (Handle != TIMER_GRABBED)
{
TerminateThread(Handle, STATUS_SUCCESS);
// Technically, there should be a
// WaitForSingleObject(Handle, (ULONG) SEM_INDEFINITE_WAIT);
// right here in order to completely clean up the thread.
// However, since the process is being killed anyway the wait was
// omitted for a slight speed increase.
CloseHandle(Handle);
}
}
RtlLeaveCriticalSection(Copy);
RtlDeleteCriticalSection(Copy);
}