528 lines
9.6 KiB
C
528 lines
9.6 KiB
C
//****************************************************************************
|
|
//
|
|
// Module: Unimdm
|
|
// File: timer.c
|
|
//
|
|
// Copyright (c) 1992-1996, Microsoft Corporation, all rights reserved
|
|
//
|
|
// Revision History
|
|
//
|
|
//
|
|
//
|
|
//
|
|
// Description:
|
|
//
|
|
//****************************************************************************
|
|
|
|
#include "unimdm.h"
|
|
#include "umdmspi.h"
|
|
|
|
#include "timer.h"
|
|
|
|
typedef struct _TIMER_CONTROL {
|
|
|
|
CRITICAL_SECTION Lock;
|
|
|
|
PUNIMODEM_TIMER TimerToBeSet;
|
|
|
|
HANDLE Event;
|
|
|
|
HANDLE ThreadHandle;
|
|
|
|
DWORD ThreadID;
|
|
|
|
BOOL TimerEnd;
|
|
|
|
HANDLE TimerThreadRunningEvent;
|
|
|
|
CRITICAL_SECTION CancelCriticalSection;
|
|
|
|
} TIMER_CONTROL, *PTIMER_CONTROL;
|
|
|
|
VOID WINAPI
|
|
TimerThreadProc(
|
|
PTIMER_CONTROL TimerControl
|
|
);
|
|
|
|
VOID WINAPI
|
|
TimerApcRoutine(
|
|
PUNIMODEM_TIMER ThisTimer,
|
|
DWORD LowTime,
|
|
DWORD HighTime
|
|
);
|
|
|
|
|
|
TIMER_CONTROL TimerControlBlock;
|
|
|
|
LONG WINAPI
|
|
InitializeTimerThread(
|
|
VOID
|
|
)
|
|
|
|
{
|
|
LONG Result;
|
|
PTIMER_CONTROL TimerControl;
|
|
|
|
TimerControl=&TimerControlBlock;
|
|
|
|
TimerControl->TimerEnd=FALSE;
|
|
|
|
TimerControl->TimerToBeSet=NULL;
|
|
|
|
InitializeCriticalSection(
|
|
&TimerControl->Lock
|
|
);
|
|
|
|
|
|
InitializeCriticalSection(
|
|
&TimerControl->CancelCriticalSection
|
|
);
|
|
|
|
TimerControl->Event=CreateEvent(
|
|
NULL,
|
|
FALSE, // autoreset
|
|
FALSE, // reset
|
|
NULL
|
|
);
|
|
|
|
|
|
if (TimerControl->Event == NULL) {
|
|
|
|
return GetLastError();
|
|
|
|
}
|
|
|
|
TimerControl->TimerThreadRunningEvent=CreateEvent(
|
|
NULL,
|
|
TRUE, // man reset
|
|
FALSE, // reset
|
|
NULL
|
|
);
|
|
|
|
if (TimerControl->TimerThreadRunningEvent == NULL) {
|
|
|
|
Result=GetLastError();
|
|
|
|
CloseHandle(TimerControl->Event);
|
|
|
|
return Result;
|
|
}
|
|
|
|
TimerControl->ThreadHandle=CreateThread(
|
|
NULL,
|
|
0,
|
|
TimerThreadProc,
|
|
TimerControl,
|
|
0,
|
|
&TimerControl->ThreadID
|
|
);
|
|
|
|
if (TimerControl->ThreadHandle == NULL) {
|
|
|
|
Result=GetLastError();
|
|
|
|
CloseHandle(TimerControl->TimerThreadRunningEvent);
|
|
|
|
CloseHandle(TimerControl->Event);
|
|
|
|
return Result;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
VOID WINAPI
|
|
TimerThreadProc(
|
|
PTIMER_CONTROL TimerControl
|
|
)
|
|
|
|
{
|
|
|
|
while (!TimerControl->TimerEnd) {
|
|
|
|
//
|
|
// if canceling, block here until the cancel code is done
|
|
//
|
|
EnterCriticalSection(
|
|
&TimerControl->CancelCriticalSection
|
|
);
|
|
|
|
D_TRACE(McxDpf(888,"TimerThreadProc: Past cancel spinlock");)
|
|
|
|
//
|
|
// done running for now
|
|
//
|
|
ResetEvent(
|
|
TimerControl->TimerThreadRunningEvent
|
|
);
|
|
|
|
//
|
|
// release it now, since the cancel routine has done what it needed to
|
|
//
|
|
LeaveCriticalSection(
|
|
&TimerControl->CancelCriticalSection
|
|
);
|
|
|
|
D_TRACE(McxDpf(888,"TimerThreadProc: Thread waiting");)
|
|
|
|
//
|
|
// wait for APC's, or our event to be signaled
|
|
//
|
|
WaitForSingleObjectEx(
|
|
TimerControl->Event,
|
|
INFINITE,
|
|
TRUE
|
|
);
|
|
|
|
D_TRACE(McxDpf(888,"TimerThreadProc: thread running");)
|
|
|
|
//
|
|
// set this so the cancel code can tell when the thread non alertable
|
|
//
|
|
SetEvent(
|
|
TimerControl->TimerThreadRunningEvent
|
|
);
|
|
|
|
|
|
|
|
EnterCriticalSection(
|
|
&TimerControl->Lock
|
|
);
|
|
|
|
while (TimerControl->TimerToBeSet != NULL) {
|
|
|
|
PUNIMODEM_TIMER NewTimer;
|
|
|
|
NewTimer=TimerControl->TimerToBeSet;
|
|
|
|
TimerControl->TimerToBeSet=NewTimer->Next;
|
|
|
|
D_TRACE(McxDpf(888,"TimerThreadProc: Setting new timer");)
|
|
|
|
SetWaitableTimer(
|
|
NewTimer->TimerHandle,
|
|
&NewTimer->DueTime,
|
|
0,
|
|
TimerApcRoutine,
|
|
NewTimer,
|
|
FALSE
|
|
);
|
|
|
|
}
|
|
|
|
LeaveCriticalSection(
|
|
&TimerControl->Lock
|
|
);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
VOID WINAPI
|
|
TimerApcRoutine(
|
|
PUNIMODEM_TIMER TimerObject,
|
|
DWORD LowTime,
|
|
DWORD HighTime
|
|
)
|
|
|
|
{
|
|
|
|
TIMER_CALLBACK *Callback;
|
|
HANDLE Context1;
|
|
HANDLE Context2;
|
|
|
|
|
|
EnterCriticalSection(
|
|
&TimerObject->CriticalSection
|
|
);
|
|
|
|
Callback=TimerObject->CallbackProc;
|
|
Context1=TimerObject->Context1;
|
|
Context2=TimerObject->Context2;
|
|
|
|
|
|
TimerObject->CallbackProc=NULL;
|
|
TimerObject->Context1=NULL;
|
|
TimerObject->Context2=NULL;
|
|
|
|
LeaveCriticalSection(
|
|
&TimerObject->CriticalSection
|
|
);
|
|
|
|
|
|
(*Callback)(
|
|
Context1,
|
|
Context2
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
HANDLE WINAPI
|
|
CreateUnimodemTimer(
|
|
VOID
|
|
)
|
|
|
|
{
|
|
|
|
PUNIMODEM_TIMER TimerObject;
|
|
|
|
TimerObject=LocalAlloc(LPTR,sizeof(UNIMODEM_TIMER));
|
|
|
|
if (TimerObject == NULL) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
TimerObject->Next=NULL;
|
|
|
|
TimerObject->CallbackProc=NULL;
|
|
TimerObject->Context1=NULL;
|
|
TimerObject->Context2=NULL;
|
|
|
|
InitializeCriticalSection(
|
|
&TimerObject->CriticalSection
|
|
);
|
|
|
|
|
|
TimerObject->TimerHandle=CreateWaitableTimer(
|
|
NULL,
|
|
TRUE,
|
|
NULL
|
|
);
|
|
|
|
if (TimerObject->TimerHandle == NULL) {
|
|
|
|
LocalFree(TimerObject);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return (HANDLE)TimerObject;
|
|
|
|
}
|
|
|
|
VOID WINAPI
|
|
FreeUnimodemTimer(
|
|
HANDLE TimerHandle
|
|
)
|
|
|
|
{
|
|
PUNIMODEM_TIMER TimerObject=(PUNIMODEM_TIMER) TimerHandle;
|
|
|
|
CancelUnimodemTimer(
|
|
TimerObject
|
|
);
|
|
|
|
|
|
CloseHandle(
|
|
TimerObject->TimerHandle
|
|
);
|
|
|
|
LocalFree(TimerObject);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
VOID WINAPI
|
|
SetUnimodemTimer(
|
|
HANDLE TimerHandle,
|
|
DWORD Duration,
|
|
TIMER_CALLBACK CallbackFunc,
|
|
HANDLE Context1,
|
|
HANDLE Context2
|
|
)
|
|
|
|
{
|
|
|
|
PUNIMODEM_TIMER TimerObject=(PUNIMODEM_TIMER) TimerHandle;
|
|
|
|
EnterCriticalSection(
|
|
&TimerControlBlock.Lock
|
|
);
|
|
|
|
EnterCriticalSection(
|
|
&TimerObject->CriticalSection
|
|
);
|
|
|
|
D_TRACE(McxDpf(888,"SetUnimodemTimer: ");)
|
|
|
|
TimerObject->Next=NULL;
|
|
|
|
TimerObject->CallbackProc=CallbackFunc;
|
|
TimerObject->Context1=Context1;
|
|
TimerObject->Context2=Context2;
|
|
|
|
|
|
TimerObject->DueTime=Int32x32To64(Duration,-10000);
|
|
|
|
|
|
TimerObject->Next=TimerControlBlock.TimerToBeSet;
|
|
|
|
TimerControlBlock.TimerToBeSet=TimerObject;
|
|
|
|
SetEvent(
|
|
TimerControlBlock.Event
|
|
);
|
|
|
|
D_TRACE(McxDpf(888,"SetUnimodemTimer: Done");)
|
|
|
|
LeaveCriticalSection(
|
|
&TimerObject->CriticalSection
|
|
);
|
|
|
|
LeaveCriticalSection(
|
|
&TimerControlBlock.Lock
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
BOOL WINAPI
|
|
CancelUnimodemTimer(
|
|
HANDLE TimerHandle
|
|
)
|
|
|
|
{
|
|
|
|
PUNIMODEM_TIMER TimerObject=(PUNIMODEM_TIMER) TimerHandle;
|
|
|
|
PUNIMODEM_TIMER Current;
|
|
PUNIMODEM_TIMER Prev;
|
|
BOOL ReturnValue=TRUE;
|
|
|
|
D_TRACE(McxDpf(888,"CancelUnimodemTimer: ");)
|
|
//
|
|
// enter the cancel critical section, so the timer thread will block
|
|
//
|
|
EnterCriticalSection(
|
|
&TimerControlBlock.CancelCriticalSection
|
|
);
|
|
|
|
D_TRACE(McxDpf(888,"CancelUnimodemTimer: Got cancel lock");)
|
|
//
|
|
// Signal the event, so the timer thread will run and block on the criical section
|
|
//
|
|
SetEvent(
|
|
TimerControlBlock.Event
|
|
);
|
|
|
|
D_TRACE(McxDpf(888,"CancelUnimodemTimer: Waiting for thread to run");)
|
|
//
|
|
// now wait for the thread to actaully run so we know it is not alerted
|
|
//
|
|
WaitForSingleObject(
|
|
TimerControlBlock.TimerThreadRunningEvent,
|
|
INFINITE
|
|
);
|
|
|
|
|
|
EnterCriticalSection(
|
|
&TimerControlBlock.Lock
|
|
);
|
|
|
|
EnterCriticalSection(
|
|
&TimerObject->CriticalSection
|
|
);
|
|
|
|
|
|
Prev=NULL;
|
|
|
|
Current=TimerControlBlock.TimerToBeSet;
|
|
|
|
//
|
|
// see if it waiting to be set
|
|
//
|
|
while (Current != NULL) {
|
|
|
|
if (Current == TimerObject) {
|
|
//
|
|
// found it
|
|
//
|
|
if (Current == TimerControlBlock.TimerToBeSet) {
|
|
|
|
TimerControlBlock.TimerToBeSet=Current->Next;
|
|
|
|
} else {
|
|
|
|
Prev->Next=Current->Next;
|
|
}
|
|
|
|
TimerObject->Next=NULL;
|
|
TimerObject->CallbackProc=NULL;
|
|
|
|
D_TRACE(McxDpf(888,"CancelUnimodemTimer: timer not set yet");)
|
|
|
|
goto Done;
|
|
|
|
}
|
|
|
|
Prev=Current;
|
|
Current=Current->Next;
|
|
}
|
|
|
|
//
|
|
// not on list
|
|
//
|
|
if (TimerObject->CallbackProc != NULL) {
|
|
//
|
|
// hasn't run yet, so kill it
|
|
//
|
|
D_TRACE(McxDpf(888,"CancelUnimodemTimer: Canceling pending timer");)
|
|
|
|
CancelWaitableTimer(
|
|
TimerObject->TimerHandle
|
|
);
|
|
|
|
TimerObject->Next=NULL;
|
|
TimerObject->CallbackProc=NULL;
|
|
|
|
} else {
|
|
//
|
|
// didn't get the timer, it has run
|
|
//
|
|
D_TRACE(McxDpf(888,"CancelUnimodemTimer: Missed timer");)
|
|
|
|
ReturnValue=FALSE;
|
|
}
|
|
|
|
Done:
|
|
|
|
LeaveCriticalSection(
|
|
&TimerObject->CriticalSection
|
|
);
|
|
|
|
|
|
LeaveCriticalSection(
|
|
&TimerControlBlock.Lock
|
|
);
|
|
|
|
|
|
//
|
|
// Done canceling, let the thread go
|
|
//
|
|
LeaveCriticalSection(
|
|
&TimerControlBlock.CancelCriticalSection
|
|
);
|
|
|
|
D_TRACE(McxDpf(888,"CancelUnimodemTimer: done canceling");)
|
|
|
|
return ReturnValue;
|
|
|
|
}
|