mirror of https://github.com/lianthony/NT4.0
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
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);
|
|
}
|