|
|
#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
|