windows-nt/Source/XPSP1/NT/net/rras/common/rtutils/wtapi.c

1826 lines
55 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include "wt.h"
#include "wtproto.h"
#include "workerinc.h"
//////////////////////////////////////////////////////////////////////////////////
//
// create event and register client are separated so that all events/timers can be
// registered as one atomic operation.
// assumption: you should always register.
//
// Locks: the server does not need to take lock when changing the arrays/lists
// arrays and lists can be changed by the wait server thread only after it has
// been signalled
// g_CS, wte_CS, wte_CSTimer -->> order of taking locks
// -- wte_CS always taken by client on behalf of the server:
// used to lock the list of changes till the changes have completed
// (RefCount can decrease now)
// server locks it only when it is initializing itself
// -- wte_CSTimer taken by client when it wants to change a timer which already
// exists or by server when it wants to go throught the timer list after
// the timer is fired.
// note: if wte_CS is taken by server then it may lead to deadlock
// lock taken by client which wakes up server. server wakes up on
// some other event which needs that lock
// note: wte_CSTimer lock is taken by clients and server independently and not
// on behalf of the other. So no possibility of deadlock
// -- g_CS : locked by client for finding which server to use (wte->wte_NumTotalEvents)
// -- g_CS : locked by server to delete itself.
// wte_NumTotalEvents read under g_CS or wte_CS, incremented under g_CS,
// decremented under wte_CS
// so use interlocked operation. you might not have been able to allocate
// a server which just then frees events, but this "inconsistency" is fine
// RULES: never free a shared event when other bindings exist
// //todo: when last event/timer is deleted, delete the wte if deleted flag is set
// 3. all events that have been created have to be registered before deleting them
// //todo: remove above requirement
// you can call deregister functions from within server thread
// YOU CANNOT CALL FUNCTIONS LIKE DEREGISTER//REGISTER from within a server thread.
// Be careful when you queue to alertable thread. an alertable thread cannot be
// client/server at same time.
//
//////////////////////////////////////////////////////////////////////////////////
VOID
APIENTRY
WTFreeEvent (
IN PWT_EVENT_ENTRY peeEvent
)
{
if (peeEvent==NULL)
return;
FreeWaitEvent(peeEvent);
return;
}
VOID
APIENTRY
WTFreeTimer (
IN PWT_TIMER_ENTRY pteTimer
)
{
if (pteTimer==NULL)
return;
FreeWaitTimer(pteTimer);
return;
}
VOID
APIENTRY
WTFree (
PVOID ptr
)
{
WT_FREE(ptr);
return;
}
//------------------------------------------------------------------------------//
// DebugPrintWaitWorkerThreads //
//THE LATEST STATE MAY NOT BE THE ONE YOU EXPECT, eg if you set event and //
//immediately do debugprint, the alertableThread might not have processed it by //
//the time the enumeration is done //
//------------------------------------------------------------------------------//
VOID
APIENTRY
DebugPrintWaitWorkerThreads (
DWORD dwDebugLevel
)
{
PLIST_ENTRY ple, pHead;
PWAIT_THREAD_ENTRY pwte;
DWORD dwErr;
// initialize worker threads if not yet done
if (!ENTER_WAIT_API()) {
dwErr = GetLastError();
return;
}
TRACE_ENTER("Entering DebugPrintWaitWorkerThreads");
ENTER_CRITICAL_SECTION(&WTG.g_CS, "g_CS", "DebugPrintWaitWorkerThreads");
pHead = &WTG.gL_WaitThreadEntries;
for (ple=pHead->Flink; ple!=pHead; ple=ple->Flink) {
pwte = CONTAINING_RECORD(ple, WAIT_THREAD_ENTRY, wte_Links);
ENTER_CRITICAL_SECTION(&pwte->wte_CS, "wte_CS",
"DebugPrintWaitWorkerThreads");
ENTER_CRITICAL_SECTION(&pwte->wte_CSTimer, "wte_CSTimer",
"DebugPrintWaitWorkerThreads");
PrintWaitThreadEntry(pwte, dwDebugLevel);
LEAVE_CRITICAL_SECTION(&pwte->wte_CSTimer,
"wte_CSTimer", "DebugPrintWaitWorkerThreads");
LEAVE_CRITICAL_SECTION(&pwte->wte_CS, "wte_CS",
"DebugPrintWaitWorkerThreads");
}
LEAVE_CRITICAL_SECTION(&WTG.g_CS, "g_CS", "DebugPrintWaitWorkerThreads");
TRACE_LEAVE("Leaving DebugPrintWaitWorkerThreads");
return;
}
//++----------------------------------------------------------------------------//
// AlertableWaitWorkerThread //
//------------------------------------------------------------------------------//
DWORD
APIENTRY
AlertableWaitWorkerThread (
IN LPVOID param
)
{
PWAIT_THREAD_ENTRY pwte;
DWORD dwErr = NO_ERROR;
DWORD dwThreadId = GetCurrentThreadId();
DWORD dwRetVal, dwIndex;
BOOL bBreakout = FALSE;
//DWORD dwFirstThread;
//TRACE0(ENTER, "Entering AlertableWaitWorkerThread: ");
//dwFirstThread = (DWORD) param;
//
// create the structures and add this thread as one of the aletertable
// wait threads.
//
// create and initialize wait thread entry
dwErr = CreateWaitThreadEntry(dwThreadId, &pwte);
// exit if thread could not be initialized
if (dwErr!=NO_ERROR) {
return dwErr;
}
//
// get global lock and insert wait-thread-entry into the list of entries
//
EnterCriticalSection(&WTG.g_CS);
// acquire lock on server thread and timer section before inserting
// into global list
EnterCriticalSection(&pwte->wte_CS);
EnterCriticalSection(&pwte->wte_CSTimer);
InsertWaitThreadEntry(pwte);
//THERE SHOULD BE NO CALLS TO RTUTILS APIS IN THIS FUNCTION TILL HERE WHEN
//INITIALIZING THE FIRST ALERTABLE THREAD
// set event so that rest of rtutils initialization can proceed
/*if (dwFirstThread==0) {
TRACE0(CS, "Initialization of 1st worker thread done");
SetEvent(WTG.g_InitializedEvent);
}
*/
//
// create the initial events and waitable timer
//
dwErr = CreateServerEventsAndTimer(pwte);
LeaveCriticalSection(&WTG.g_CS);
// initialization done: release lock on server thread
LeaveCriticalSection(&pwte->wte_CSTimer);
LeaveCriticalSection(&pwte->wte_CS);
//
// wait for servicing events
//
do {
// locks not required as pwteEvents(array,list) can be changed by server
// thread only
TRACE0(CS, "AlertableThread waiting for events:%d");
dwRetVal = WaitForMultipleObjectsEx(pwte->wte_NumActiveEvents,
&pwte->wteA_Events[pwte->wte_LowIndex],
FALSE, INFINITE, TRUE);
TRACE0(CS, "AlertableThread woken by signalled event");
switch (dwRetVal) {
case WAIT_IO_COMPLETION:
{
// Handled IO completion
break;
}
case 0xFFFFFFFF:
{
// Error, we must have closed the semaphore handle
bBreakout = TRUE;
break;
}
default:
{
PLIST_ENTRY ple, pHead;
PWT_WORK_ITEM pwi;
PWT_EVENT_ENTRY pee;
DWORD dwCount;
//
// wait returned an error
//
if (dwRetVal > (WAIT_OBJECT_0+pwte->wte_NumActiveEvents-1))
{
bBreakout = TRUE;
break;
}
//
// service the different events
//
// get pointer to event entry
dwIndex = dwRetVal - WAIT_OBJECT_0 + pwte->wte_LowIndex;
pee = pwte->wteA_EventMapper[dwIndex];
// move event to end of array, and shift the left part rightwards, or
// the right part leftwards (only if the event is not a high priority event)
if (!pee->ee_bHighPriority) {
if (EA_OVERFLOW(pwte, 1)) {
EventsArray_CopyLeftEnd(pwte);
dwIndex = dwRetVal - WAIT_OBJECT_0 + pwte->wte_LowIndex;
}
//if righmost entry, then dont have to shift
if (!(dwIndex==EA_INDEX_HIGH_LOW_PRIORITY_EVENT(pwte))) {
// move the signalled event to the right end
EventsArray_Move(pwte,
EA_INDEX_HIGH_LOW_PRIORITY_EVENT(pwte)+1, //dstn
dwIndex, //src
1 //count
);
dwCount = pwte->wte_NumActiveEvents + pwte->wte_LowIndex
- dwIndex - 1;
// shift right part towards left if its size is smaller
if (dwCount <= pwte->wte_NumActiveEvents/2) {
EventsArray_MoveOverlap (
pwte,
dwIndex, //dstn
dwIndex+1, //src
dwCount+1 //count:
//+1 as the entry has been moved to the right end
);
}
// shift left part towards right
else {
EventsArray_MoveOverlap (
pwte,
pwte->wte_LowIndex+1, //dstn
pwte->wte_LowIndex, //src
pwte->wte_NumActiveEvents - dwCount -1 //count
);
pwte->wte_LowIndex ++;
}
}
}
// for each work item allocate context if not run in server context
// so all functions can be run only in server or worker context.
// else there will be errors.
pHead = &pee->eeL_wi;
for (ple=pHead->Flink; ple!=pHead; ) {
pwi = CONTAINING_RECORD(ple, WT_WORK_ITEM, wi_ServerLinks);
if (pwi->wi_Function==NULL) {
continue;
}
ple = ple->Flink; // this allows deregistering of current binding
// Run the work item in the present context or queue it to worker thread
DispatchWorkItem(pwi);
}
//
// if it is manual reset then remove from array, and shift it from
// active to inactive
//
if (pee->ee_bManualReset) {
pee->ee_Status = WT_STATUS_FIRED;
InactivateEvent(pee);
}
break;
} //end case:default
} //end switch
} while ((WorkersInitialized==WORKERS_INITIALIZED) &&(bBreakout!=TRUE));
TRACE0(LEAVE, "Leaving AlertableWaitWorkerThread:");
return 0;
} //end AlertableWaitWorkerThread
//------------------------------------------------------------------------------//
// UpdateWaitTimer //
// a timer can be inactivated by setting the update timeout to 0 //
// relative time can be sent by sending the complement: time = -time //
//------------------------------------------------------------------------------//
DWORD
APIENTRY
UpdateWaitTimer (
IN PWT_TIMER_ENTRY pte,
IN LONGLONG *pTime
)
{
PWAIT_THREAD_ENTRY pwte;
PLIST_ENTRY ple, pHead;
PWT_TIMER_ENTRY pteCur;
BOOL bDecrease;
LONGLONG newTime = *pTime;
LONGLONG oldTime;
LONGLONG currentTime;
// get the waitThreadEntry
pwte = pte->teP_wte;
oldTime = pte->te_Timeout;
pte->te_Timeout = newTime;
//convert relative time to absolute time
if (newTime<0) {
newTime = -newTime;
NtQuerySystemTime((LARGE_INTEGER*) &currentTime);
newTime += currentTime;
}
// if wait thread is set to deleted, then return error
if (pwte->wte_Status == WT_STATUS_DELETED) {
return ERROR_CAN_NOT_COMPLETE;
}
ENTER_CRITICAL_SECTION(&pwte->wte_CSTimer, "wte_CSTimer", "UpdateWaitTimer");
// the timer should have been deleted
if (pte->te_Status==WT_STATUS_DELETED) {
TRACE0(TIMER, "cannot update timer whose status is set to deleted");
LEAVE_CRITICAL_SECTION(&pwte->wte_CSTimer, "wte_CSTimer", "UpdateWaitTimer");
return ERROR_CAN_NOT_COMPLETE;
}
// the timer is to be disabled and inserted at end of timers list
if (IS_TIMER_INFINITE(newTime)) {
TRACE0(TIMER, "Called UpdateWaitTimer to disable a timer");
SET_TIMER_INFINITE(pte->te_Timeout);
pte->te_Status = TIMER_INACTIVE;
RemoveEntryList(&pte->te_ServerLinks);
InsertTailList(&pwte->wteL_ClientTimerEntries, &pte->te_ServerLinks);
}
else {
TRACE4(TIMER, "Called UpdateWaitTimer to update timer from <%lu:%lu> to <%lu:%lu>",
TIMER_HIGH(oldTime), TIMER_LOW(oldTime),
TIMER_HIGH(newTime), TIMER_LOW(newTime));
bDecrease = (newTime<oldTime) || (IS_TIMER_INFINITE(oldTime));
pte->te_Status = TIMER_ACTIVE;
pHead = &pwte->wteL_ClientTimerEntries;
ple = bDecrease? (pte->te_ServerLinks).Blink: (pte->te_ServerLinks).Flink;
RemoveEntryList(&pte->te_ServerLinks);
if (bDecrease) {
for ( ; ple!=pHead; ple=ple->Blink) {
pteCur = CONTAINING_RECORD(ple, WT_TIMER_ENTRY, te_ServerLinks);
if (pteCur->te_Status==TIMER_INACTIVE)
continue;
if (pteCur->te_Timeout>newTime)
continue;
else
break;
}
InsertHeadList(ple, &pte->te_ServerLinks);
}
else {
for ( ; ple!=pHead; ple=ple->Flink) {
pteCur = CONTAINING_RECORD(ple, WT_TIMER_ENTRY, te_ServerLinks);
if (IS_TIMER_INFINITE(pteCur->te_Timeout)
|| (pteCur->te_Status==TIMER_INACTIVE)
|| (pteCur->te_Timeout>newTime))
{
break;
}
}
InsertTailList(ple, &pte->te_ServerLinks);
}
}
//
// see if the WaitableTimer timeout has to be changed
//
pteCur = CONTAINING_RECORD(pwte->wteL_ClientTimerEntries.Flink, WT_TIMER_ENTRY, te_ServerLinks);
if (pteCur->te_Status == TIMER_INACTIVE) {
TRACE0(TIMER, "CancelWaitableTimer called");
SET_TIMER_INFINITE(pwte->wte_Timeout);
CancelWaitableTimer(pwte->wte_Timer);
}
else if (pteCur->te_Timeout!=pwte->wte_Timeout) {
TRACE2(TIMER, "SetWaitableTimer called <%lu:%lu>",
TIMER_HIGH(pteCur->te_Timeout), TIMER_LOW(pteCur->te_Timeout));
pwte->wte_Timeout = pteCur->te_Timeout;
SetWaitableTimer(pwte->wte_Timer, (LARGE_INTEGER*)&pwte->wte_Timeout, 0, NULL, NULL, FALSE);
}
LEAVE_CRITICAL_SECTION(&pwte->wte_CSTimer, "wte_CSTimer", "UpdateWaitTimer");
return NO_ERROR;
}
//++--------------------------------------------------------------------------------//
// DispatchWorkItem //
//----------------------------------------------------------------------------------//
DWORD
DispatchWorkItem (
IN PWT_WORK_ITEM pwi
)
{
PVOID pContext;
DWORD dwErr = NO_ERROR;
// if it has to run in server context
if (pwi->wi_RunInServer) {
(pwi->wi_Function)(pwi->wi_Context);
}
// else queue to worker thread
else {
QueueWorkItem(pwi->wi_Function, pwi->wi_Context, FALSE);
}
return NO_ERROR;
}
//++----------------------------------------------------------------------------//
// RegisterWaitEventsTimers //
// Register the client with the wait thread //
// The client acquires lock on wte_CS on behalf of the server. If timers have to//
// change then the server has to lock the timers in RegisterClientEventTimers//
//------------------------------------------------------------------------------//
DWORD
APIENTRY
RegisterWaitEventsTimers (
IN PLIST_ENTRY pLEventsToAdd,
IN PLIST_ENTRY pLTimersToAdd
)
{
PWAIT_THREAD_ENTRY pwte;
INT iNumEventsToAdd, iNumTimersToAdd;
DWORD dwErr;
//ChangeClientEventsTimersAux(1, pwte); // add
// you cannot register without creating it first!!
// initialize worker threads if not yet done
if (!ENTER_WAIT_API()) {
dwErr = GetLastError();
return (dwErr == NO_ERROR ? ERROR_CAN_NOT_COMPLETE : dwErr);
}
TRACE0(ENTER, "Entering : RegisterWaitEventsTimers");
// get global lock: g_CS
ENTER_CRITICAL_SECTION(&WTG.g_CS, "g_CS", "RegisterWaitEventsTimers");
//
// locate the wait thread server with iNumEventsToAdd free events
//
// if iNumEventsToAdd ptr !=0 but list length is 1, means no list header
iNumEventsToAdd = GetListLength(pLEventsToAdd);
if (iNumEventsToAdd<0)
iNumEventsToAdd = 0;
if ((pLEventsToAdd!=NULL) && (iNumEventsToAdd==0))
iNumEventsToAdd = 1;
iNumTimersToAdd = GetListLength(pLTimersToAdd);
if (iNumTimersToAdd<0)
iNumTimersToAdd = 0;
if ((pLTimersToAdd!=NULL) && (iNumTimersToAdd==0))
iNumTimersToAdd = 1;
// iNumEventsToAdd may be 0, if timers only have to be added
// getWaiThread increments the refCount of the server so that it cannot be deleted
// wte_NumTotalEvents so that space is reserved
pwte = GetWaitThread(iNumEventsToAdd, iNumTimersToAdd);
if (pwte==NULL) {
TRACE0(WT, "could not allocate wait thread");
LEAVE_CRITICAL_SECTION(&WTG.g_CS, "g_CS", "RegisterWaitEventsTimers");
return ERROR_WAIT_THREAD_UNAVAILABLE;
}
// lock server: wte_CS
ENTER_CRITICAL_SECTION(&pwte->wte_CS, "wte_CS", "RegisterWaitEventsTimers");
// release global lock: g_CS
LEAVE_CRITICAL_SECTION(&WTG.g_CS, "g_CS", "RegisterWaitEventsTimers");
//
// events/timers to be added
//
pwte->wte_ChangeType = CHANGE_TYPE_ADD;
pwte->wtePL_EventsToChange = pLEventsToAdd;
pwte->wtePL_TimersToChange = pLTimersToAdd;
//
// Wake up the server so that it can enter the events and timers of the client
//
SET_EVENT(pwte->wte_ClientNotifyWTEvent, "wte_ClientNotifyWTEvent", "RegisterWaitEventsTimers");
// Wait till wait server notifies on completion
//
WAIT_FOR_SINGLE_OBJECT(pwte->wte_WTNotifyClientEvent, INFINITE,
"wte_WTNotifyClientEvent", "RegisterWaitEventsTimers");
//release server lock: wte_CS
LEAVE_CRITICAL_SECTION(&pwte->wte_CS, "wte_CS", "RegisterWaitEventsTimers");
TRACE0(LEAVE, "Leaving: RegisterWaitEventsTimers");
return NO_ERROR;
} //end RegisterWaitEventsTimers
//++----------------------------------------------------------------------------//
// RegisterClientEventsTimers:aux //
// Add the client events and timers //
// The client has the lock on the server and has already increased the refCount //
//------------------------------------------------------------------------------//
DWORD
RegisterClientEventsTimers (
IN PWAIT_THREAD_ENTRY pwte
)
{
ChangeClientEventsTimersAux(1, pwte, pwte->wtePL_EventsToChange,
pwte->wtePL_TimersToChange); // add
return NO_ERROR;
}
//++----------------------------------------------------------------------------//
// ChangeClientEventsTimersAux:aux //
// Add/delete the client events and timers //
// The client has the lock on the server and has already increased the refCount //
// (add) //
//------------------------------------------------------------------------------//
DWORD
ChangeClientEventsTimersAux (
IN BOOL bChangeTypeAdd,
IN PWAIT_THREAD_ENTRY pwte,
IN PLIST_ENTRY pLEvents,
IN PLIST_ENTRY pLTimers
)
{
PLIST_ENTRY ple;
PWT_EVENT_ENTRY pee;
PWT_TIMER_ENTRY pte;
//
// process each event to be changed(added/deleted)
//
if (pLEvents != NULL) {
//
// list of events
//
if (!IsListEmpty(pLEvents)) {
for (ple=pLEvents->Flink; ple!=pLEvents; ) {
pee = CONTAINING_RECORD(ple, WT_EVENT_ENTRY, ee_Links);
ple = ple->Flink;
if (bChangeTypeAdd)
AddClientEvent(pee, pwte);
else
DeleteClientEvent(pee, pwte);
}
}
//
// single event
else { // list empty but not null, so pointer to only one event
pee = CONTAINING_RECORD(pLEvents, WT_EVENT_ENTRY, ee_Links);
if (bChangeTypeAdd)
AddClientEvent(pee, pwte);
else
DeleteClientEvent(pee, pwte);
}
}
//
// process each timer to be added/deleted
//
if (pLTimers != NULL) {
ENTER_CRITICAL_SECTION(&pwte->wte_CSTimer, "wte_CSTimer",
"ChangeClientEventsTimersAux");
//
// list of timers
//
if (!IsListEmpty(pLTimers)) {
for (ple=pLTimers->Flink; ple!=pLTimers;) {
pte = CONTAINING_RECORD(ple, WT_TIMER_ENTRY, te_Links);
ple = ple->Flink;
if (bChangeTypeAdd)
AddClientTimer(pte, pwte);
else
DeleteClientTimer(pte, pwte);
}
}
//
//single timer
else { // list empty but not null, so pointer to only one timer
pte = CONTAINING_RECORD(pLTimers, WT_TIMER_ENTRY, te_Links);
if (bChangeTypeAdd)
AddClientTimer(pte, pwte);
else
DeleteClientTimer(pte, pwte);
}
LEAVE_CRITICAL_SECTION(&pwte->wte_CSTimer, "wte_CSTimer",
"ChangeClientEventsTimersAux");
}
return NO_ERROR;
}
//++----------------------------------------------------------------------------//
// AddClientEvent //
// calling client has locked the server wte_CS: and increased refCount with server //
// somewhat similar to RegisterClientEventLocal //
//------------------------------------------------------------------------------//
DWORD
AddClientEvent (
IN PWT_EVENT_ENTRY pee,
IN PWAIT_THREAD_ENTRY pwte
)
{
// the event might have been deleted by some other thread (such things should
// never happen)
if (pee->ee_Status==WT_STATUS_DELETED) {
//todo check above
// delete the event if RefCount is 0
if (pee->ee_RefCount==0) {
FreeWaitEvent(pee);
}
// decrement refcount as it was increased by client adding the events
InterlockedDecrement(&pwte->wte_NumTotalEvents);
InterlockedDecrement(&pwte->wte_RefCount);
return ERROR_WT_EVENT_ALREADY_DELETED;
}
// insert the client event into the list of client events
InsertInEventsList(pee, pwte);
// insert the client into the MapArray and EventsArray
if (pee->ee_bInitialState == FALSE) {
InsertInEventsArray(pee, pwte);
}
//fired so not inserted in array
else {
pee->ee_Status = WT_STATUS_INACTIVE + WT_STATUS_FIRED;
}
return NO_ERROR;
}
//++----------------------------------------------------------------------------//
// AddClientTimer //
// The client has the lock on the server //
// refcount has already been changed during registration //
//------------------------------------------------------------------------------//
DWORD
AddClientTimer (
PWT_TIMER_ENTRY pte,
PWAIT_THREAD_ENTRY pwte
)
{
// change fields of timerEntry
pte->te_Status = TIMER_INACTIVE;
pte->te_ServerId = pwte->wte_ServerId;
pte->teP_wte = pwte;
//insert inactive timers at end of list
InsertTailList(&pwte->wteL_ClientTimerEntries, &pte->te_ServerLinks);
return NO_ERROR;
}
//++----------------------------------------------------------------------------//
// DeleteClientTimer //
// The client has the lock on the server //
//------------------------------------------------------------------------------//
DWORD
DeleteClientTimer (
PWT_TIMER_ENTRY pte,
PWAIT_THREAD_ENTRY pwte
)
{
// change fields of waitThreadEntry
InterlockedDecrement(&pwte->wte_RefCount);
// if deleted flag set and refcount is 0, delete the wait entry
if ((pwte->wte_Status == WT_STATUS_DELETED)
&& (pwte->wte_RefCount==0)
)
{
FreeWaitThreadEntry(pwte);
}
//remove the timer from timer list
RemoveEntryList(&pte->te_ServerLinks);
FreeWaitTimer(pte);
//
// see if the WaitableTimer timeout has to be changed
//
// if list is empty then cancel the timer
if (IsListEmpty(&pwte->wteL_ClientTimerEntries)) {
if (!IS_TIMER_INFINITE(pwte->wte_Timeout))
CancelWaitableTimer(pwte->wte_Timer);
SET_TIMER_INFINITE(pwte->wte_Timeout);
}
else {
PWT_TIMER_ENTRY pteCur;
pteCur = CONTAINING_RECORD(pwte->wteL_ClientTimerEntries.Flink,
WT_TIMER_ENTRY, te_ServerLinks);
if (pteCur->te_Status == TIMER_INACTIVE) {
TRACE0(TIMER, "CancelWaitableTimer called in DeleteClientTimer");
if (!IS_TIMER_INFINITE(pwte->wte_Timeout))
CancelWaitableTimer(pwte->wte_Timer);
SET_TIMER_INFINITE(pwte->wte_Timeout);
}
else if (pteCur->te_Timeout!=pwte->wte_Timeout) {
TRACE2(TIMER, "SetWaitableTimer called in DeleteClientTimer<%lu:%lu>",
TIMER_HIGH(pteCur->te_Timeout),
TIMER_LOW(pteCur->te_Timeout));
pwte->wte_Timeout = pteCur->te_Timeout;
SetWaitableTimer(pwte->wte_Timer, (LARGE_INTEGER*)&pwte->wte_Timeout,
0, NULL, NULL, FALSE);
}
}
return NO_ERROR;
}
//++---------------------------------------------------------------------------*//
// CreateWaitEventBinding //
// creates, initializes and returns a work_item for waitEvent //
// note WT_EVENT_BINDING==WT_WORK_ITEM //
// increments refCount for the EventEntry so that it cannot be deleted //
//------------------------------------------------------------------------------//
PWT_EVENT_BINDING
APIENTRY
CreateWaitEventBinding (
IN PWT_EVENT_ENTRY pee,
IN WORKERFUNCTION pFunction,
IN PVOID pContext,
IN DWORD dwContextSz,
IN BOOL bRunInServerContext
)
{
DWORD dwErr = NO_ERROR;
PWT_WORK_ITEM pWorkItem;
PWAIT_THREAD_ENTRY pwte;
BOOL bErr = TRUE;
// initialize worker threads if not yet done
if (!ENTER_WAIT_API()) {
dwErr = GetLastError();
return NULL;
}
TRACE0(ENTER, "CreateWaitEventBinding:");
do { //breakout loop
//
// allocate work item
//
pWorkItem = WT_MALLOC(sizeof(WT_WORK_ITEM));
if (pWorkItem==NULL) {
dwErr = GetLastError();
TRACE2(
ANY, "error %d allocating %d bytes for work item",
dwErr, sizeof(WT_WORK_ITEM)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
//
// initialize work item
//
pWorkItem->wi_Function = pFunction;
pWorkItem->wi_Context = pContext;
pWorkItem->wi_ContextSz = dwContextSz;
pWorkItem->wi_RunInServer = bRunInServerContext;
pWorkItem->wiP_ee = pee;
InitializeListHead(&pWorkItem->wi_ServerLinks);
InitializeListHead(&pWorkItem->wi_Links);
pwte = pee->eeP_wte;
if ((pee->ee_Status==0) || (pee->ee_Status==WT_STATUS_DELETED)) {
break;
}
ENTER_CRITICAL_SECTION(&pwte->wte_CS, "wte_CS", "CreateWaitEventBinding");
if ((pee->ee_Status==0) || (pee->ee_Status==WT_STATUS_DELETED)) {
LEAVE_CRITICAL_SECTION(&pwte->wte_CS, "wte_CS",
"CreateWaitEventBinding");
break;
}
LEAVE_CRITICAL_SECTION(&pwte->wte_CS, "wte_CS", "CreateWaitEventBinding");
bErr = FALSE;
} while (FALSE); //breakout loop
if (bErr) {
TRACE1(LEAVE, "Could not CreateWaitEventBinding: %d", dwErr);
FreeWaitEventBinding(pWorkItem);
return NULL;
}
else {
TRACE1(LEAVE, "Leaving CreateWaitEventBinding: %d", NO_ERROR);
return pWorkItem;
}
} //end CreateWaitEventBinding
//++----------------------------------------------------------------------------//
// FreeWaitEventBinding //
// Called by DeleteWaitEventBinding //
// Free an eventBinding entry which has been deregistered(deleted) //
//------------------------------------------------------------------------------//
VOID
FreeWaitEventBinding (
IN PWT_WORK_ITEM pwiWorkItem
)
{
DWORD dwErr;
TRACE0(ENTER, "Entering FreeWaitEventBinding: ");
if (pwiWorkItem==NULL)
return ;
// free context
/*if (pwiWorkItem->wi_ContextSz>0)
WT_FREE_NOT_NULL(pwiWorkItem->wi_Context);*/
// free wt_eventBinding structure
WT_FREE(pwiWorkItem);
TRACE0(LEAVE, "Leaving FreeWaitEventBinding:");
return;
}
//++--------------------------------------------------------------------------------//
// ChangeWaitEventBindingAux //
// called by (De)RegisterWaitEventBinding API //
//----------------------------------------------------------------------------------//
DWORD
ChangeWaitEventBindingAux (
IN BOOL bChangeTypeAdd,
IN PWT_EVENT_BINDING pwiWorkItem
)
{
DWORD dwErr = NO_ERROR;
PWAIT_THREAD_ENTRY pwte;
PWT_EVENT_ENTRY pee;
// get pointer to event entry
pee = pwiWorkItem->wiP_ee;
if (pee==NULL) {
return ERROR_CAN_NOT_COMPLETE; //this error should not occur
}
// get pointer to wait-thread-entry
pwte = pee->eeP_wte;
if (pwte==NULL) { //this error should not occur
return ERROR_CAN_NOT_COMPLETE;
}
//
// lock wait server: insert the binding into change list and wake up server
//
ENTER_CRITICAL_SECTION(&pwte->wte_CS, "wte_CS", "ChangeWaitEventBindingAux");
if (bChangeTypeAdd) {
if ((pee->ee_Status==0) || (pee->ee_Status==WT_STATUS_DELETED)) {
LEAVE_CRITICAL_SECTION(&pwte->wte_CS, "wte_CS",
"CreateWaitEventBindingAux");
return ERROR_CAN_NOT_COMPLETE;
}
pwte->wte_ChangeType = CHANGE_TYPE_BIND_FUNCTION_ADD;
}
else {
pwte->wte_ChangeType = CHANGE_TYPE_BIND_FUNCTION_DELETE;
}
//insert in wte list of wait server thread
pwte->wteP_BindingToChange = pwiWorkItem;
//
// Wake up the server so that it can enter the events and timers of the client
//
SET_EVENT(pwte->wte_ClientNotifyWTEvent, "wte_ClientNotifyWTEvent",
"ChangeWaitEventBindingAux");
// Wait till wait server notifies on completion
//
WAIT_FOR_SINGLE_OBJECT(pwte->wte_WTNotifyClientEvent, INFINITE, "wte_WTNotifyClientEvent", "ChangeWaitEventBindingAux");
//release server lock: wte_CS
LEAVE_CRITICAL_SECTION(&pwte->wte_CS, "wte_CS", "ChangeWaitEventBindingAux");
return NO_ERROR;
} //end ChangeWaitEventBindingAux
//++-----------------------------------------------------------------------------//
// RegisterClientEventBinding:aux //
// Server adds the event binding to the event //
//-------------------------------------------------------------------------------//
DWORD
RegisterClientEventBinding (
IN PWAIT_THREAD_ENTRY pwte
)
{
return ChangeClientEventBindingAux(1, pwte, pwte->wteP_BindingToChange); //server add event binding
}
//++----------------------------------------------------------------------------//
// DeRegisterClientEventBinding:aux //
// Server adds the event binding to the event //
//------------------------------------------------------------------------------//
DWORD
DeRegisterClientEventBinding (
IN PWAIT_THREAD_ENTRY pwte
)
{
return ChangeClientEventBindingAux(0, pwte, pwte->wteP_BindingToChange); //server delete event binding
}
//++----------------------------------------------------------------------------//
// ChangeClientEventBindingAux //
// client has lock on the server. RefCount of event increased when binding created//
//------------------------------------------------------------------------------//
DWORD
ChangeClientEventBindingAux (
IN BOOL bChangeTypeAdd,
IN PWAIT_THREAD_ENTRY pwte,
IN PWT_WORK_ITEM pwi
)
{
PWT_EVENT_ENTRY pee;
// client has verified that the event entry has not been deleted
if (pwi==NULL) // binding cannot be null
return ERROR_CAN_NOT_COMPLETE;
pee = pwi->wiP_ee;
if (bChangeTypeAdd) {
pee->ee_RefCount++;
InsertHeadList(&pee->eeL_wi, &pwi->wi_ServerLinks);
// if count==0 and initial state is active and
if (pee->ee_Status & WT_STATUS_FIRED) {
// queue the work item
DispatchWorkItem(pwi);
}
}
// deleting eventBinding
else {
pee->ee_RefCount --;
RemoveEntryList(&pwi->wi_ServerLinks);
FreeWaitEventBinding(pwi);
// if ee has deleted flag set and RefCount is 0, then complete its deletion
if ( (pee->ee_RefCount==0) && (pee->ee_Status==WT_STATUS_DELETED) ) {
DeleteClientEventComplete(pee, pwte);
}
}
return NO_ERROR;
}
//++----------------------------------------------------------------------------//
// RegisterWaitEventBinding //
//------------------------------------------------------------------------------//
DWORD
APIENTRY
RegisterWaitEventBinding (
IN PWT_EVENT_BINDING pwiWorkItem
)
{
DWORD dwErr = NO_ERROR;
// initialize worker threads if not yet done
if (!ENTER_WAIT_API()) {
dwErr = GetLastError();
return (dwErr == NO_ERROR ? ERROR_CAN_NOT_COMPLETE : dwErr);
}
TRACE0(ENTER, "Entering RegisterWaitEventFunction:");
// call the auxiliary function to add the entry into the server structure and wake it
// the aux function checks if it is running in server context
ChangeWaitEventBindingAux(1,//add
pwiWorkItem
);
TRACE1(LEAVE, "Leaving BindFunctionToWaitEvent: %d", NO_ERROR);
return NO_ERROR;
} //end RegisterWaitEventBinding
//++----------------------------------------------------------------------------//
// DeRegisterWaitEventBindingSelf //
//------------------------------------------------------------------------------//
DWORD
APIENTRY
DeRegisterWaitEventBindingSelf (
IN PWT_EVENT_BINDING pwiWorkItem
)
{
PWAIT_THREAD_ENTRY pwte;
DWORD dwError = NO_ERROR;
PWT_EVENT_ENTRY pee;
TRACE0(ENTER, "Entering DeRegisterWaitEventBindingSelf:");
if (pwiWorkItem==NULL)
return NO_ERROR;
// get pointer to event entry
pee = pwiWorkItem->wiP_ee;
if (pee==NULL) {
return ERROR_CAN_NOT_COMPLETE; //this error should not occur
}
// get pointer to wait-thread-entry
pwte = pee->eeP_wte;
if (pwte==NULL) { //this error should not occur
return ERROR_CAN_NOT_COMPLETE;
}
dwError = ChangeClientEventBindingAux(0, pwte, pwiWorkItem);
TRACE1(LEAVE, "Leaving DeRegisterWaitEventBindingSelf: %d", dwError);
return dwError;
}
//++----------------------------------------------------------------------------//
// DeRegisterWaitEventBinding //
//------------------------------------------------------------------------------//
DWORD
APIENTRY
DeRegisterWaitEventBinding (
IN PWT_EVENT_BINDING pwiWorkItem
)
{
DWORD dwErr = NO_ERROR;
TRACE0(ENTER, "Entering DeRegisterWaitEventBinding:");
// call the auxiliary function to add the entry into the server structure and wake it
dwErr = ChangeWaitEventBindingAux(0,//Delete
pwiWorkItem
);
TRACE1(LEAVE, "Leaving DeRegisterWaitEventBinding: %d", NO_ERROR);
return dwErr;
} //end DeRegisterWaitEventBinding
//++---------------------------------------------------------------------------*//
// CreateWaitTimer //
// creates, initializes and returns a wait timer //
//------------------------------------------------------------------------------//
PWT_TIMER_ENTRY
APIENTRY
CreateWaitTimer (
IN WORKERFUNCTION pFunction,
IN PVOID pContext,
IN DWORD dwContextSz,
IN BOOL bRunInServerContext
)
{
PWT_TIMER_ENTRY pte;
DWORD dwErr;
DWORD bErr = TRUE;
// initialize worker threads if not yet done
if (!ENTER_WAIT_API()) {
dwErr = GetLastError();
return (NULL);
}
TRACE0(ENTER, "Entering CreateWaitTimer: ");
do { // breakout loop
//
// allocate wait-timer structure
//
pte = WT_MALLOC(sizeof(WT_TIMER_ENTRY));
if (pte==NULL) {
dwErr = GetLastError();
TRACE2(
ANY, "error %d allocating %d bytes for wait-timer structure",
dwErr, sizeof(WT_TIMER_ENTRY)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
//
// initialize fields of the waitTimer
//
SET_TIMER_INFINITE(pte->te_Timeout);
pte->te_Function = pFunction;
pte->te_Context = pContext;
pte->te_ContextSz = dwContextSz;
pte->te_RunInServer = bRunInServerContext;
pte->te_Status = 0;
//serverId, teP_wte and ServerLinks are set when it is registered
InitializeListHead(&pte->te_ServerLinks);
InitializeListHead(&pte->te_Links);
pte->te_Flag = FALSE;
bErr = FALSE;
} while (FALSE); //end breakout loop
if (bErr) {
FreeWaitTimer(pte);
TRACE0(LEAVE, "Leaving CreateWaitTimer: Not Created");
return NULL;
}
else {
TRACE0(LEAVE, "Leaving CreateWaitTimer: Created");
return pte;
}
} //end createWaitTimer
//++---------------------------------------------------------------------------*//
// FreeWaitTimer //
// frees memory/handles associated with the waitTimer //
//------------------------------------------------------------------------------//
VOID
FreeWaitTimer (
IN PWT_TIMER_ENTRY pte
)
{
TRACE0(ENTER, "Entering FreeWaitTimer: ");
if (pte==NULL)
return;
//free context
//WT_FREE_NOT_NULL(pte->te_Context);
//free timer structure
WT_FREE(pte);
TRACE0(LEAVE, "Leaving FreeWaitTimer:");
return;
} //end FreeWaitTimer
//++---------------------------------------------------------------------------*//
// CreateWaitEvent //
// no locks required //
// creates, initializes and returns a wait event //
//------------------------------------------------------------------------------//
PWT_EVENT_ENTRY
APIENTRY
CreateWaitEvent (
IN HANDLE pEvent, // handle to event if already created
IN LPSECURITY_ATTRIBUTES lpEventAttributes, // pointer to security attributes
IN BOOL bManualReset,
IN BOOL bInitialState,
IN LPCTSTR lpName, // pointer to event-object name
IN BOOL bHighPriority, // create high priority event
IN WORKERFUNCTION pFunction, // if null, means will be set by other clients
IN PVOID pContext, // can be null
IN DWORD dwContextSz, // size of context: used for allocating context to functions
// context size can be 0, without context being null
IN BOOL bRunInServerContext // run in server thread or get dispatched to worker thread
)
{
PWT_EVENT_ENTRY peeEvent;
PWT_WORK_ITEM pWorkItem;
DWORD dwErr;
BOOL bErr = TRUE;
// initialize worker threads if not yet done
if (!ENTER_WAIT_API()) {
dwErr = GetLastError();
return (NULL);
}
TRACE0(ENTER, "Entering CreateWaitEvent: ");
do { // breakout loop
//
// allocate wt_event structure
//
peeEvent = WT_MALLOC(sizeof(WT_EVENT_ENTRY));
if (peeEvent==NULL) {
dwErr = GetLastError();
TRACE2(
ANY, "error %d allocating %d bytes for wait-thread-entry",
dwErr, sizeof(WT_EVENT_ENTRY)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
//
// create event
//
if (pEvent!=NULL) {
peeEvent->ee_Event = pEvent;
peeEvent->ee_bDeleteEvent = FALSE;
}
else {
peeEvent->ee_bDeleteEvent = TRUE;
peeEvent->ee_Event = CreateEvent(lpEventAttributes, bManualReset,
bInitialState, lpName);
if (peeEvent->ee_Event==NULL) {
WT_FREE(peeEvent);
peeEvent = NULL;
dwErr = GetLastError();
TRACE1(
ANY, "error %d creating event",
dwErr
);
LOGERR0(CREATE_EVENT_FAILED, dwErr);
break;
}
}
//
// initialize fields of wt_event entry
//
peeEvent->ee_bManualReset = bManualReset;
peeEvent->ee_bInitialState = bInitialState;
peeEvent->ee_Status = 0; // created but not registered
peeEvent->ee_bHighPriority = bHighPriority;
// initialize list of work items
InitializeListHead(&peeEvent->eeL_wi);
// create work item
//
if (pFunction==NULL) {
// no work item set by this client
}
else {
pWorkItem = WT_MALLOC(sizeof(WT_WORK_ITEM));
if (pWorkItem==NULL) {
dwErr = GetLastError();
TRACE2(
ANY, "error %d allocating %d bytes for work item",
dwErr, sizeof(WT_WORK_ITEM)
);
LOGERR0(HEAP_ALLOC_FAILED, dwErr);
break;
}
pWorkItem->wi_Function = pFunction;
pWorkItem->wi_Context = pContext;
pWorkItem->wi_ContextSz = dwContextSz;
pWorkItem->wi_RunInServer = bRunInServerContext;
pWorkItem->wiP_ee = peeEvent; //not required to be set in this case
InitializeListHead(&pWorkItem->wi_ServerLinks);
InsertHeadList(&peeEvent->eeL_wi, &pWorkItem->wi_ServerLinks);
InitializeListHead(&pWorkItem->wi_Links);
}
peeEvent->ee_bSignalSingle = FALSE; // signal everyone
if (pFunction!=NULL)
peeEvent->ee_bOwnerSelf = TRUE; // currently all events can be shared
else
peeEvent->ee_bOwnerSelf = FALSE;
peeEvent->ee_ArrayIndex = -1; // points to index in events array, when event is active
//peeEvent->ee_ServerId = 0; // server id is set when it is inserted into wait server
peeEvent->eeP_wte = NULL;
InitializeListHead(&peeEvent->ee_ServerLinks);
InitializeListHead(&peeEvent->ee_Links);
peeEvent->ee_RefCount = 0; // note: refcount can be 0 and still there can be 1 workItem
peeEvent->ee_bFlag = FALSE;
bErr = FALSE;
} while (FALSE); //end breakout loop
if (bErr) {
FreeWaitEvent(peeEvent);
TRACE0(LEAVE, "Leaving CreateWaitEvent: Not Created");
return NULL;
}
else {
TRACE0(LEAVE, "Leaving CreateWaitEvent: Created");
return peeEvent;
}
} //end createWaitEvent
//++----------------------------------------------------------------------------//
// DeleteClientEvent //
// Free an event entry which has been deregistered(deleted) //
// frees all fields of the wait event without touching others //
// interlockedDecrement(pwte->wte_NumTotalEvents) //
//------------------------------------------------------------------------------//
DWORD
DeleteClientEvent (
IN PWT_EVENT_ENTRY pee,
IN PWAIT_THREAD_ENTRY pwte
)
{
DWORD dwErr;
// can be called when the event has to be deleted or when the last binding is
// being deleted and the delete flag is set.
// if event is active Delete from events array
if ((pee->ee_Status==WT_STATUS_ACTIVE)&&(pee->ee_ArrayIndex!=-1)) {
DeleteFromEventsArray(pee, pwte);
pee->ee_Status = WT_STATUS_INACTIVE; // not req
}
// does interlocked decrement and sets status to deleted
DeleteFromEventsList(pee, pwte);
// decrement the RefCount with the waitThreadEntry
// free memory and handles assocaited with the wait event entry
if (pee->ee_RefCount==0) {
DeleteClientEventComplete(pee, pwte);
}
// else DeleteClientEventComplete will be called when last binding is deleted
return NO_ERROR;
} //end DeleteWaitEvent
// complete the deletion of the client eventEntry which could not be completed
// due to existing bindings
VOID
DeleteClientEventComplete (
IN PWT_EVENT_ENTRY pee,
IN PWAIT_THREAD_ENTRY pwte
)
{
pwte->wte_RefCount--;
//todo: do i have to check if wte_refcount==0 and set to deleted
FreeWaitEvent(pee);
return;
}
//++--------------------------------------------------------------------------------//
// FreeWaitEvent //
// Called by DeleteWaitEvent //
// Free an event entry which has been deregistered(deleted) //
//----------------------------------------------------------------------------------//
VOID
FreeWaitEvent (
IN PWT_EVENT_ENTRY peeEvent
)
{
PLIST_ENTRY pHead, ple;
PWT_WORK_ITEM pwi;
DWORD dwErr;
DWORD dwNumWorkItems=0;
TRACE0(ENTER, "Entering FreeWaitEvent: ");
if (peeEvent==NULL)
return;
if (peeEvent->ee_bDeleteEvent)
CloseHandle(peeEvent->ee_Event);
// there should be no work items or should have been created as part of this entry
dwNumWorkItems = GetListLength(&peeEvent->eeL_wi);
ASSERT((peeEvent->ee_RefCount==0)
&& ((dwNumWorkItems==0)
|| ((dwNumWorkItems==1)&&(peeEvent->ee_bOwnerSelf))
)
);
// free all work items and their context
// actually there cannot be more than 1 such item at most
pHead = &peeEvent->eeL_wi;
for (ple=pHead->Flink; ple!=pHead; ) {
pwi = CONTAINING_RECORD(ple, WT_WORK_ITEM, wi_ServerLinks);
if ((pwi->wi_Context!=NULL) && (pwi->wi_ContextSz>0))
;
//WT_FREE(pwi->wi_Context);
ple = ple->Flink;
WT_FREE(pwi);
}
// free wt_event structure
WT_FREE(peeEvent);
TRACE0(LEAVE, "Leaving FreeWaitEvent:");
return;
}
//++--------------------------------------------------------------------------------//
// DeRegisterClientsEventsTimers //
// Server DeRegister the client with the wait thread //
//----------------------------------------------------------------------------------//
DWORD
DeRegisterClientEventsTimers (
IN PWAIT_THREAD_ENTRY pwte
)
{
DWORD dwErr;
dwErr = ChangeClientEventsTimersAux(0, pwte, pwte->wtePL_EventsToChange,
pwte->wtePL_TimersToChange); //delete event
return dwErr;
}
//++--------------------------------------------------------------------------------//
// DeRegisterWaitEventsTimersSelf //
// DeRegister the client from within its server //
// Either send in NULL, a proper list, of a single node. dont send a empty header!! //
//----------------------------------------------------------------------------------//
DWORD
APIENTRY
DeRegisterWaitEventsTimersSelf (
IN PLIST_ENTRY pLEvents,
IN PLIST_ENTRY pLTimers
)
{
PWAIT_THREAD_ENTRY pwte;
DWORD dwError;
if ((pLEvents==NULL)&&(pLTimers==NULL))
return NO_ERROR;
TRACE_ENTER("DeRegisterWaitEventsTimersSelf");
// get pwte
if (pLEvents!=NULL) {
PWT_EVENT_ENTRY pee;
// the below will work even if there is only one record
pee = CONTAINING_RECORD(pLEvents->Flink, WT_EVENT_ENTRY, ee_Links);
pwte = pee->eeP_wte;
}
else {
PWT_TIMER_ENTRY pte;
if (pLTimers==NULL)
return NO_ERROR;
pte = CONTAINING_RECORD(pLTimers->Flink, WT_TIMER_ENTRY, te_Links);
pwte = pte->teP_wte;
}
dwError = ChangeClientEventsTimersAux ( 0, //delete
pwte, pLEvents, pLTimers);
TRACE_LEAVE("DeRegisterWaitEventsTimersSelf");
return dwError;
}
//++--------------------------------------------------------------------------------//
// DeRegisterWaitEventsTimers //
// DeRegister the client with the wait thread //
// Either send in NULL, a proper list, of a single node. dont send a empty header!! //
//----------------------------------------------------------------------------------//
DWORD
APIENTRY
DeRegisterWaitEventsTimers (
IN PLIST_ENTRY pLEvents,
IN PLIST_ENTRY pLTimers
)
{
PWAIT_THREAD_ENTRY pwte;
DWORD dwErr = NO_ERROR;
if ((pLEvents==NULL)&&(pLTimers==NULL))
return NO_ERROR;
TRACE0(ENTER, "Entering : DeRegisterWaitEventsTimers");
// get pwte
if (pLEvents!=NULL) {
PWT_EVENT_ENTRY pee;
// the below will work even if there is only one record
pee = CONTAINING_RECORD(pLEvents->Flink, WT_EVENT_ENTRY, ee_Links);
pwte = pee->eeP_wte;
}
else {
PWT_TIMER_ENTRY pte;
if (pLTimers==NULL)
return NO_ERROR;
pte = CONTAINING_RECORD(pLTimers->Flink, WT_TIMER_ENTRY, te_Links);
pwte = pte->teP_wte;
}
// lock server: wte_CS
ENTER_CRITICAL_SECTION(&pwte->wte_CS, "wte_CS", "DeRegisterWaitEventsTimers");
//
// events/timers to be deleted
//
pwte->wte_ChangeType = CHANGE_TYPE_DELETE;
pwte->wtePL_EventsToChange = pLEvents;
pwte->wtePL_TimersToChange = pLTimers;
// Wake up the server so that it can delete the events and timers of the client
SET_EVENT(pwte->wte_ClientNotifyWTEvent, "wte_ClientNotifyWTEvent", "DeRegisterWaitEventsTimers");
WAIT_FOR_SINGLE_OBJECT(pwte->wte_WTNotifyClientEvent, INFINITE, "wte_WTNotifyClientEvent", "DeRegisterWaitEventsTimers");
LEAVE_CRITICAL_SECTION(&pwte->wte_CS, "wte_CS", "DeRegisterWaitEventsTimers");
TRACE0(LEAVE, "Leaving: DeRegisterWaitEventsTimers");
return NO_ERROR;
} //end DeRegisterWaitEventsTimers
#pragma hdrstop