#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 = (newTimete_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