1826 lines
55 KiB
C
1826 lines
55 KiB
C
|
|
#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*) ¤tTime);
|
|
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
|
|
|