#include "wt.h"
#include "wtproto.h"
#include "workerinc.h"

//----------------------------------------------------------------------------------//
//                                         WorkerFunction_ProcessClientNotifyWTEvent                                    //
//      the client has locks on wte_CS, so that no one can add or delete                                //
//  if you need to add timers, you have to take lock on wte_CSTimer                                     //
//----------------------------------------------------------------------------------//
VOID
WorkerFunction_ProcessClientNotifyWTEvent (
        IN      PVOID pContext
    )
{
        PWAIT_THREAD_ENTRY      pwte;
        BOOL                            bLockTimer;


        pwte = (PWAIT_THREAD_ENTRY) pContext;
        TRACE0(ENTER, "Entering WorkerFunction_ProcessClientNotifyWTEvent:");

        switch (pwte->wte_ChangeType) {

                case CHANGE_TYPE_ADD :
                        RegisterClientEventsTimers(pwte);
                        break;

                case CHANGE_TYPE_DELETE :
                        DeRegisterClientEventsTimers(pwte);
                        break;

                case CHANGE_TYPE_BIND_FUNCTION_ADD :
                        RegisterClientEventBinding(pwte);
                        break;

                case CHANGE_TYPE_BIND_FUNCTION_DELETE :
                        DeRegisterClientEventBinding(pwte);
                        break;
        }

        SET_EVENT(pwte->wte_WTNotifyClientEvent, "wte_WTNotifyClientEvent",
                                "WorkerFunction_ProcessClientNotifyWTEvent");
        TRACE0(LEAVE, "Leaving WorkerFunction_ProcessClientNotifyWTEvent:");
        return;

} //end WorkerFunction_ProcessClientNotifyWTEvent


//----------------------------------------------------------------------------------//
//                                       WorkerFunction_ProcessWorkQueueTimer                                                   //
//----------------------------------------------------------------------------------//
VOID
WorkerFunction_ProcessWorkQueueTimer (
        IN      PVOID pContext
    )
{

        TRACE0(ENTER, "Entering WorkerFunction_ProcessWorkQueueTimer:");


        // Work queue has not been served within specified
    // timeout
    while (1) {
            // Make a local copy of the count
        LONG   count = ThreadCount;
            // Make sure we havn't exceded the limit
        if (count>=MAX_WORKER_THREADS)
            break;
        else {
            // Try to increment the value
                    // use another local variable
                    // because of MIPS optimizer bug
            LONG    newCount = count+1;
            if (InterlockedCompareExchange (&ThreadCount,
                                            newCount, count)==count) {
                HANDLE  hThread;
                DWORD   tid;
                    // Create new thread if increment succeded
                hThread = CreateThread (NULL, 0, WorkerThread, NULL, 0, &tid);
                if (hThread!=NULL) {
                    CloseHandle (hThread);
                }
                else    // Restore the value if thread creation
                        // failed
                    InterlockedDecrement (&ThreadCount);
                break;
            }
            // else repeat the loop if ThreadCount was modified
            // while we were checking
        }
    }

        TRACE0(LEAVE, "Leaving WorkerFunction_ProcessWorkQueueTimer:");
    return;

} //end WorkerFunction_ProcessWorkQueueTimer


//----------------------------------------------------------------------------------//
//                                       WorkerFunction_ProcessAlertableThreadSemaphore                                 //
//----------------------------------------------------------------------------------//
VOID
WorkerFunction_ProcessAlertableThreadSemaphore (
        IN      PVOID pContext
    )
{
        WorkItem    *workitem;


        TRACE0(ENTER, "Entering WorkerFunction_ProcessAlertableThreadSemaphore:");

        EnterCriticalSection(&AlertableWorkQueueLock);
    ASSERT (!IsListEmpty (&AlertableWorkQueue));
    workitem = (WorkItem *) RemoveHeadList (&AlertableWorkQueue) ;
    LeaveCriticalSection(&AlertableWorkQueueLock);

    (workitem->WI_Function) (workitem->WI_Context);
    HeapFree (AlertableWorkerHeap, 0, workitem);

        TRACE0(LEAVE, "Leaving WorkerFunction_ProcessAlertableThreadSemaphore:");
    return;
}

//++-------------------------------------------------------------------------------*//
//                                       WorkerFunction_ProcessWaitableTimer                                                            //
// Process event: waitable timer fired                                                                                          //
// takes lock on wte_CSTimer and releases it in the end                                                         //
//----------------------------------------------------------------------------------//
VOID
WorkerFunction_ProcessWaitableTimer(
    IN  PVOID pContext
    )
{
        PWAIT_THREAD_ENTRY      pwte;
        LONGLONG                        liCurrentTime;
        PLIST_ENTRY                     ple, pHead, pleCurrent;
        PWT_TIMER_ENTRY         pte;
        BOOL                            bActiveTimers;

        TRACE0(ENTER, "Entering WorkerFunction_ProcessWaitableTimer:");


        pwte = (PWAIT_THREAD_ENTRY) pContext;


        // get lock on server threads timer
        ENTER_CRITICAL_SECTION(&pwte->wte_CSTimer, "wte_CSTimer", "WorkerFunction_ProcessWaitableTimer");

        NtQuerySystemTime((LARGE_INTEGER*) &liCurrentTime);


        // ordered in increasing time, with inactive timers at the end
        pHead = &pwte->wteL_ClientTimerEntries;
        for (ple=pHead->Flink;  ple!=pHead;  ) {
                pte = CONTAINING_RECORD(ple, WT_TIMER_ENTRY, te_ServerLinks);

                if (pte->te_Status == TIMER_INACTIVE)           // inactive timers at end of list
                        break;

                if (IS_TIMER_INFINITE(pte->te_Timeout)) { //should have been inactive
                        ple = ple->Flink;
                        continue;
                }

                if (pte->te_Timeout<=liCurrentTime) {
                        //
                        // set timer status to inactive and insert at end of timer queue
                        //
                        pte->te_Status = TIMER_INACTIVE;
                        SET_TIMER_INFINITE(pte->te_Timeout);

                        pleCurrent = ple;
                        ple = ple->Flink;
                        RemoveEntryList(pleCurrent);
                        InsertTailList(pHead, pleCurrent);


                        // run the function in current thread or dispatch to worker thread
                        //
                        if (pte->te_RunInServer) {
                                (pte->te_Function)(pte->te_Context);
                        }
                        else {
                                QueueWorkItem( pte->te_Function, pte->te_Context, FALSE ); // do not run in alertable thread
                        }
                }

                else {
                        break;
                }

        }

        //
        // search for active timers with timeout which is not infinite
        //

        if (IsListEmpty(pHead))
                bActiveTimers = FALSE;
        else {
                ple = pHead->Flink;

                pte = CONTAINING_RECORD(ple, WT_TIMER_ENTRY, te_ServerLinks);

                bActiveTimers = (pte->te_Status==TIMER_INACTIVE) ? FALSE : TRUE;
        }

        //
        // if active timers present, then set waitableTimer
        //
        if (bActiveTimers) {

                // set next timeout value for the wait server
                pwte->wte_Timeout = pte->te_Timeout;
                TRACE2(TIMER, "SetWaitableTimer set to <%lu:%lu> after being fired",
                                        TIMER_HIGH(pte->te_Timeout), TIMER_LOW(pte->te_Timeout));

                SetWaitableTimer(pwte->wte_Timer, (LARGE_INTEGER*)&pte->te_Timeout, 0, NULL, NULL, FALSE);
        }

        // no active timer in queue. do not set the waitable timer
        else {
                SET_TIMER_INFINITE(pwte->wte_Timeout);
        }

#if DBG2
        DebugPrintWaitWorkerThreads(DEBUGPRINT_FILTER_EVENTS);
#endif
        LEAVE_CRITICAL_SECTION(&pwte->wte_CSTimer, "wte_CSTimer", "WorkerFunction_ProcessWaitableTimer");

        TRACE0(LEAVE, "Leaving WorkerFunction_ProcessWaitableTimer:");
        return;

} //end WorkerFunction_ProcessWaitableTimer


//---------------------------------------------------------------------------------*//
//                                      CreateServerEventsAndTimer                                                                      //
// Create the events for a server and the timer(called by server only)                          //
// assumes lock on server and server timer structure                                                            //
// THIS FUNCTION SHOULD NOT HAVE ANY CALLS TO API FUNCTIONS                                                     //
//----------------------------------------------------------------------------------//
DWORD
CreateServerEventsAndTimer (
        IN      PWAIT_THREAD_ENTRY      pwte
        )
{
        PWT_EVENT_ENTRY peeWaitableTimer, peeClientNotifyWTEvent,
                                        peeAlertableThreadSemaphore, peeWorkQueueTimer;


        // waitable timer (set to infinity time)
        peeWaitableTimer = CreateWaitEvent(
                                                                pwte->wte_Timer,
                                                                NULL, FALSE, FALSE, NULL,
                                                                TRUE,   // highPriority
                                                                (WORKERFUNCTION) WorkerFunction_ProcessWaitableTimer,
                                                                (PVOID)pwte,    // context
                                                                0,              // context size=0, as just a pointer value is being passed
                                                                TRUE    // run in server context
                                                           );

        if (!peeWaitableTimer)
            return ERROR_NOT_ENOUGH_MEMORY;

        peeWaitableTimer->ee_EventId = 1;
        RegisterClientEventLocal(peeWaitableTimer, pwte);

        // ClientNotifyWTEvent
        peeClientNotifyWTEvent = CreateWaitEvent(
                                                                        pwte->wte_ClientNotifyWTEvent,
                                                                        NULL, FALSE, FALSE, NULL,
                                                                        TRUE,   // highPriority
                                                                        (WORKERFUNCTION) WorkerFunction_ProcessClientNotifyWTEvent,
                                                                        (PVOID)pwte,    // context
                                                                        0,              // context size=0, as just a pointer value is being passed
                                                                        TRUE    // run in server context
                                                                   );
        if (!peeClientNotifyWTEvent )
            return ERROR_NOT_ENOUGH_MEMORY;

        peeClientNotifyWTEvent->ee_EventId = 2;
        RegisterClientEventLocal(peeClientNotifyWTEvent, pwte);


        // AlertableThreadSemaphore
        peeAlertableThreadSemaphore = CreateWaitEvent(
                                                                                AlertableThreadSemaphore,
                                                                                NULL, FALSE, FALSE, NULL,
                                                                                FALSE,  // Priority=low
                                                                                (WORKERFUNCTION) WorkerFunction_ProcessAlertableThreadSemaphore,
                                                                                NULL,   // context
                                                                                0,              // context size=0, as just a pointer value is being passed
                                                                                TRUE    // run in server context
                                                                           );
        peeAlertableThreadSemaphore->ee_EventId = 3;
        RegisterClientEventLocal(peeAlertableThreadSemaphore, pwte);


        // WorkQueueTimer
        peeWorkQueueTimer = CreateWaitEvent(
                                                                WorkQueueTimer,
                                                                NULL, FALSE, FALSE, NULL,
                                                                FALSE,  // Priority=low
                                                                (WORKERFUNCTION) WorkerFunction_ProcessWorkQueueTimer,
                                                                NULL,   // context
                                                                0,              // context size=0, as just a pointer value is being passed
                                                                TRUE    // run in server context
                                                           );
        peeWorkQueueTimer->ee_EventId = 4;
        RegisterClientEventLocal(peeWorkQueueTimer, pwte);


        return NO_ERROR;

} //end CreateServerEventsAndTimer

//---------------------------------------------------------------------------------*//
//                                      InitializeWaitGlobal                                                                                    //
// initialize the global data structure for all wait threads                                            //
//----------------------------------------------------------------------------------//
DWORD
InitializeWaitGlobal(
        )
{
        BOOL    bErr;
        DWORD   dwErr = NO_ERROR;



        ZeroMemory(&WTG, sizeof(WTG));

        WTG.g_Initialized = 0x12345678;


        //
        // initialize tracing and logging
        //
        WTG.g_TraceId = TraceRegister("WAIT_THREAD");
        //todo:set the logging level
        WTG.g_LogLevel = WT_LOGGING_ERROR;
        WTG.g_LogHandle = RouterLogRegister("WAIT_THREAD");

        TRACE0(ENTER, "Entering InitializeWaitGlobal()");



        //
        // initialize global structure
        //
        bErr = FALSE;
        do { // error breakout loop

                //
                // create a private heap for Wait-Thread
                //

                WTG.g_Heap = AlertableWorkerHeap;                               // created in worker.c
                if (WTG.g_Heap==NULL)
                        WTG.g_Heap = HeapCreate(0, 0, 0);

                if (WTG.g_Heap == NULL) {

                        dwErr = GetLastError();
                        TRACE1(
                        ANY, "error %d creating Wait-Thread global heap", dwErr
                        );
                        LOGERR0(HEAP_CREATE_FAILED, dwErr);

                        bErr = FALSE;
                        break;
                }



                // initialize list for wait thread entries
                //
                InitializeListHead(&WTG.gL_WaitThreadEntries);



                // initialize critical section
                //
                try {
                InitializeCriticalSection(&WTG.g_CS);
                }
                except (EXCEPTION_EXECUTE_HANDLER) {
                dwErr = GetExceptionCode();
                TRACE1(
                         ANY, "exception %d initializing global critical section",
                         dwErr
                         );
                LOGERR0(INIT_CRITSEC_FAILED, dwErr);

                        bErr = TRUE;
                break;
                }


        } while (FALSE);

        TRACE1(LEAVE, "leaving InitializeWaitGlobal: %d", dwErr);

        if (bErr)
                return dwErr;
        else
                return NO_ERROR;

} //end InitializeWaitGlobal


//---------------------------------------------------------------------------------*//
//                                      DeInitializeWaitGlobal                                                                                  //
//deinitializes the global structure for wait-thread                                                            //
//todo: free the server entries and the event/timers etc associated with them           //
//----------------------------------------------------------------------------------//

DWORD
DeInitializeWaitGlobal(
        )
{
        PLIST_ENTRY                     ple, pHead;
        PWAIT_THREAD_ENTRY      pwte;


        ENTER_CRITICAL_SECTION(&WTG.g_CS, "g_CS", "DeInitializeWaitGlobal");

        if (WTG.g_Initialized==0) {
                LEAVE_CRITICAL_SECTION(&WTG.g_CS, "g_CS", "DeInitializeWaitGlobal");
                TRACE0(LEAVE, "leaving DeInitializeWaitGlobal:Pending");
                return ERROR_CAN_NOT_COMPLETE;
        }
        else
                WTG.g_Initialized = 0;


        //
        // if waitThreadEntries exist, then for each server entry mark each event/binding
        // as deleted and return pending
        //
        if (!IsListEmpty(&WTG.gL_WaitThreadEntries)) {

                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, "deleting wte_CS", "DeInitializeWaitGlobal");
                        pwte->wte_Status = WT_STATUS_DELETED;
                }

                //todo should I also mark each event and timer as deleted

                TRACE0(LEAVE, "leaving DeInitializeWaitGlobal:Pending");

                return ERROR_CAN_NOT_COMPLETE;
        }

        DeInitializeWaitGlobalComplete();

        return NO_ERROR;

} //end DeInitializeWaitGlobal

//----------------------DeInitializeWaitGlobalComplete-----------------------------//
DWORD
DeInitializeWaitGlobalComplete (
        )
{

        TRACE0(LEAVE, "leaving DeInitializeWaitGlobal");

        // for each server entry mark each event/binding as deleted
        // and return pending

        TraceDeregister(WTG.g_TraceId);
        RouterLogDeregister(WTG.g_LogHandle);


        // delete critical section
        try{
        DeleteCriticalSection(&WTG.g_CS);
        }
        except (EXCEPTION_EXECUTE_HANDLER) {
        }


        // destroy heap
        /*if (WTG.g_Heap != NULL) {
        HeapDestroy(WTG.g_Heap);
        }*/

        return NO_ERROR;

} //end DeInitializeWaitGlobal


//++-------------------------------------------------------------------------------*//
//                                      InsertWaitThreadEntry                                                                                   //
// insert the new wait server thread into a list of increasing ServerIds                        //
// initializes the serverId                                                                                                                     //
// assumes: it has lock on WTG.g_cs, and pwte->wte_CS, and pwte->wte_CSTimer            //
// NO CALLS TO APIS SHOULD BE MADE HERE                                                                                         //
//----------------------------------------------------------------------------------//
DWORD
InsertWaitThreadEntry (
        IN      PWAIT_THREAD_ENTRY      pwteInsert
        )
{
        PLIST_ENTRY                     ple, pHead;
        PWAIT_THREAD_ENTRY      pwte;
        DWORD                           dwServerId;


        //
        // find the lowest unallocated SeverId and insert the new server in the ordered list
        //

        dwServerId = 1;
        pHead = &WTG.gL_WaitThreadEntries;


        for (ple=pHead->Flink;  ple!=pHead;  ple=ple->Flink,dwServerId++) {

                pwte = CONTAINING_RECORD(ple, WAIT_THREAD_ENTRY, wte_Links);

                if (dwServerId==pwte->wte_ServerId) {
                        continue;
                }
                else
                        break;
        }


        // insert in list
        if (dwServerId==1) { // not required. but kept for easy understanding
                InsertHeadList(&WTG.gL_WaitThreadEntries, &pwteInsert->wte_Links);
        }
        else {
                InsertTailList(ple, &pwteInsert->wte_Links);
        }

        // set serverId
        pwteInsert->wte_ServerId = dwServerId;


        pwteInsert->wte_Status = WT_STATUS_REGISTERED;

        return NO_ERROR;

} //end InsertWaitThreadEntry


//---------------------------------------------------------------------------------*//
//                                      RemoveWaitThreadEntry                                                                                   //
// removes a wait server thread from the list of servers                                                        //
// assumes: it has lock on WTG.g_cs     and wte_CS                                                                              //
//----------------------------------------------------------------------------------//
DWORD
DeleteWaitThreadEntry (
        IN      PWAIT_THREAD_ENTRY      pwte
        )
{
        // set status to deleted
        pwte->wte_Status = WT_STATUS_DELETED;


        // if RefCount == 0, then remove it from the list and free it

        if (pwte->wte_RefCount==0) {

                // free the wait thread entry
                RemoveEntryList(&pwte->wte_Links);
                FreeWaitThreadEntry(pwte);

                // deinitialize global structure if it is also marked for delete and its
                // WaitThreadEntry list is empty
                if ((WTG.g_Initialized==WT_STATUS_DELETED)
                        &&(IsListEmpty(&WTG.gL_WaitThreadEntries)))
                {
                        DeInitializeWaitGlobalComplete();
                }

        }
        return NO_ERROR;
}


//---------------------------------------------------------------------------------*//
//                                      CreateWaitThreadEntry                                                                                   //
// creates a wait thread entry and initializes it                                                                       //
// no locks required                                                                                                                            //
//----------------------------------------------------------------------------------//
DWORD
CreateWaitThreadEntry (
        IN      DWORD                           dwThreadId,
        OUT     PWAIT_THREAD_ENTRY      *ppwte
        )
{
        PWAIT_THREAD_ENTRY      pwte;
        DWORD                           dwErr = NO_ERROR;
        BOOL                            bErr = TRUE;



        TRACE0(ENTER, "Entering CreateWaitThreadEntry");

        //
        // allocate wait-thread-entry entry
        //
        *ppwte = pwte = WT_MALLOC(sizeof(WAIT_THREAD_ENTRY));
        if (pwte == NULL) {

                dwErr = GetLastError();
                TRACE2(
                ANY, "error %d allocating %d bytes for wait-thread-entry",
                dwErr, sizeof(WAIT_THREAD_ENTRY)
                );
                LOGERR0(HEAP_ALLOC_FAILED, dwErr);

        return dwErr;
        }

        ZeroMemory(pwte, sizeof(WAIT_THREAD_ENTRY));


        //
        // initialize global structure
        //

        do { // error breakout loop


                // critical section
                try {
                InitializeCriticalSection(&pwte->wte_CS);
                }
                except (EXCEPTION_EXECUTE_HANDLER) {
                dwErr = GetExceptionCode();
                TRACE1(
                         ANY, "exception %d initializing global critical section",
                         dwErr
                         );
                LOGERR0(INIT_CRITSEC_FAILED, dwErr);

                break;
                }



                // current clients
                //server id is set when it is inserted into global list
                pwte->wte_ThreadId = dwThreadId;                                                //ServerId is assigned during insertion of wte
                pwte->wte_NumClients = 0;


                // list for events/timers
                InitializeListHead(&pwte->wteL_ClientEventEntries);
                InitializeListHead(&pwte->wteL_ClientTimerEntries);



                // create waitable timer
                pwte->wte_Timer = CreateWaitableTimer(NULL, FALSE, NULL);

                if (pwte->wte_Timer == NULL) {
                        dwErr = GetLastError();
                        TRACE1(
                         ANY, "error creating waitable timer",
                         dwErr
                         );
                LOGERR0(CREATE_WAITABLE_TIMER_FAILED, dwErr);

                break;
                }


                SET_TIMER_INFINITE(pwte->wte_Timeout);                  //set timeout to infinity


                // critical section for timers
                try {
                InitializeCriticalSection(&pwte->wte_CSTimer);
                }
                except (EXCEPTION_EXECUTE_HANDLER) {
                dwErr = GetExceptionCode();
                TRACE1(
                         ANY, "exception %d initializing critical section for timer",
                         dwErr
                         );
                LOGERR0(INIT_CRITSEC_FAILED, dwErr);

                break;
                }


                // array for WaitForMultipleObjects
                pwte->wte_LowIndex = 0;
                pwte->wte_NumTotalEvents = 0;
                pwte->wte_NumActiveEvents = 0;
                pwte->wte_NumHighPriorityEvents = 0;
                pwte->wte_NumActiveHighPriorityEvents = 0;

                //
                // adding/deleting events/timers
                //

                // create event: Client notifies WT: wake up WT to add/delete events/timers
                pwte->wte_ClientNotifyWTEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

                if (pwte->wte_ClientNotifyWTEvent == NULL) {
                        dwErr = GetLastError();
                        TRACE1(START, "error %d creating event Client-notify-WT", dwErr);
                        LOGERR0(CREATE_EVENT_FAILED, dwErr);

                        break;
                }


                // create event: WT notifies Client: the work requested has been done
                pwte->wte_WTNotifyClientEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

                if (pwte->wte_WTNotifyClientEvent == NULL) {
                        dwErr = GetLastError();
                        TRACE1(START, "error:%d creating event WT-notify-Client", dwErr);
                        LOGERR0(CREATE_EVENT_FAILED, dwErr);

                        break;
                }



                // variables used to add/delete events/timers

                pwte->wte_ChangeType = CHANGE_TYPE_NONE;
                pwte->wte_Status = 0;
                pwte->wte_RefCount = 0;
                pwte->wtePL_EventsToChange = NULL;
                pwte->wtePL_TimersToChange = NULL;


                bErr = FALSE;

        } while (FALSE);



        TRACE1(LEAVE, "Leaving CreateWaitThreadEntry: %d", dwErr);

        if (bErr) {
                FreeWaitThreadEntry(pwte);
                return dwErr;
        }
        else
                return NO_ERROR;

}//end CreateWaitThreadEntry


//---------------------------------------------------------------------------------*//
//                                      FreeWaitThreadEntry                                                                                         //
//----------------------------------------------------------------------------------//
DWORD
FreeWaitThreadEntry (
        IN      PWAIT_THREAD_ENTRY      pwte
        )
{
        DWORD           dwErr = NO_ERROR;



        TRACE0(ENTER, "Entering FreeWaitThreadEntry");


        DeleteCriticalSection(&pwte->wte_CS);



        // delete waitable timer
        if (pwte->wte_Timer!=NULL)
                CloseHandle(pwte->wte_Timer);


        DeleteCriticalSection(&pwte->wte_CSTimer);

        // delete event: Client notifies WT
        if (pwte->wte_ClientNotifyWTEvent!=NULL)
                CloseHandle(&pwte->wte_ClientNotifyWTEvent);


        // delete event: WT notifies Client
        if (pwte->wte_WTNotifyClientEvent!=NULL)
                CloseHandle(pwte->wte_WTNotifyClientEvent);


        //
        // free wait-thread-entry record
        //
        WT_FREE(pwte);


        // deinitialize global structure if it is also marked for delete and its
        // WaitThreadEntry list is empty
        if ((WTG.g_Initialized==WT_STATUS_DELETED)
                &&(IsListEmpty(&WTG.gL_WaitThreadEntries)))
        {
                DeInitializeWaitGlobalComplete();
        }


        TRACE1(LEAVE, "Leaving FreeWaitThreadEntry: %d", dwErr);

        return NO_ERROR;

} //end FreeWaitThreadEntry




//----------------------------------------------------------------------------------//
//                                      GetWaitThread                                                                                           //
// returns a wait thread which has the required number of free events                           //
// assumes lock on g_CS.                                                                                                                        //
// if NumTotalEvents is low enough, do interlocked increment.                                           //
//----------------------------------------------------------------------------------//
PWAIT_THREAD_ENTRY
GetWaitThread (
        IN      DWORD   dwNumEventsToAdd,  //==0, if only timers are being added
        IN  DWORD       dwNumTimersToadd
        )
{
        PLIST_ENTRY             ple, pHead;
        PWAIT_THREAD_ENTRY      pwte;
        BOOL                            bFound;
        DWORD                           dwIncrRefCount = dwNumEventsToAdd + dwNumTimersToadd;


        pHead = &WTG.gL_WaitThreadEntries;

        //
        // Locate a wait-thread with required events free.
        //
        bFound = FALSE;
        for (ple=pHead->Flink;  ple!=pHead;  ple=ple->Flink) {
                pwte = CONTAINING_RECORD(ple, WAIT_THREAD_ENTRY, wte_Links);

                // cannot allocate this server because it is not active
                if ((pwte->wte_Status != WT_STATUS_REGISTERED)
                        && (pwte->wte_Status != WT_STATUS_ACTIVE)
                   )
                {
                        ETRACE0("could not allocate this wte as it is not registered");
                        continue;
                }

                if (NUM_CLIENT_EVENTS_FREE(pwte) >= dwNumEventsToAdd) {

                        // increment RefCount so that it cannot be deleted
                        // interlocked operation as it can be decremented outside g_CS and inside only wte_CS
                        InterlockedExchangeAdd((PLONG)&pwte->wte_RefCount, dwIncrRefCount);

                        InterlockedExchangeAdd((PLONG)&pwte->wte_NumTotalEvents,
                                                                        (LONG)dwNumEventsToAdd);
                        bFound = TRUE;
                        break;
                }
        }

        if (bFound) {

                //shift the wte to the end so that all server threads are balanced
                RemoveEntryList(ple);
                InsertTailList(pHead, ple);

                return pwte;
        }
        else {
                return NULL;
        }
}


//----------------------------------------------------------------------------------//
//                                      RegisterClientEventLocal                                                                                //
// Register the wait event locally                                                                                                      //
// event being registered by the server itself:                                                                         //
// assumes lock on server                                                                                                                       //
// somewhat similar to AddClientEvent                                               //
//----------------------------------------------------------------------------------//
DWORD
RegisterClientEventLocal (
        IN      PWT_EVENT_ENTRY         pee,
        IN      PWAIT_THREAD_ENTRY      pwte
        )
{
        // insert wait event in list of event entries //dont have to do interlocked here
        pwte->wte_RefCount ++;
        pwte->wte_NumTotalEvents ++;


        InsertInEventsList(pee, pwte);


        // insert wait event in array of event entries
        if (pee->ee_bInitialState == FALSE) {
                InsertInEventsArray(pee, pwte);
        }
        else {
                pee->ee_Status = WT_STATUS_INACTIVE + WT_STATUS_FIRED;  // but arrayIndex ==-1
        }


        return NO_ERROR;
}


//----------------------------------------------------------------------------------//
//                                       InsertInEventsList                                                                                     //
// assumes lock on server                                                                                                                       //
// Insert the event in the list and increment the counters                                                      //
// NumTotalEvents has been increased during allocation of the server                            //
//----------------------------------------------------------------------------------//
VOID
InsertInEventsList (
        IN      PWT_EVENT_ENTRY         pee,
        IN      PWAIT_THREAD_ENTRY      pwte
        )
{

        InsertHeadList(&pwte->wteL_ClientEventEntries, &pee->ee_ServerLinks);

        //
        // set fields of event entry
        //
        pee->ee_ServerId = pwte->wte_ServerId;
        pee->eeP_wte = pwte;
        pee->ee_Status = WT_STATUS_REGISTERED;

        //
        // change fields of wait thread entry
        //

        //pwte->wte_RefCount++; //RefCount incremented during allocation
        //pwte->wte_NumTotalEvents ++; //incremented during allocation
        if (pee->ee_bHighPriority) {
                pwte->wte_NumHighPriorityEvents ++;
        }

        return;
}

//----------------------------------------------------------------------------------//
//                                       DeleteFromEventsList                                                                                   //
// NumTotalEvents/Refcount is interlockedDecremented                                                            //
//----------------------------------------------------------------------------------//
VOID
DeleteFromEventsList (
        IN      PWT_EVENT_ENTRY         pee,
        IN      PWAIT_THREAD_ENTRY      pwte
        )
{

        // remove entry from list
        RemoveEntryList(&pee->ee_ServerLinks);

        //
        // set fields of event entry
        //
        pee->ee_Status = WT_STATUS_DELETED;

        //
        // change fields of wait thread entry
        //

        // pwte->wte_NumTotalEvents ++; interlocked incremented during allocation
        if (pee->ee_bHighPriority) {
                pwte->wte_NumHighPriorityEvents --;
        }

        InterlockedDecrement(&pwte->wte_NumTotalEvents);
        // decremented in DeleteClientEventComplete if ee_RefCount is 0
        //InterlockedDecrement(&pwte->wte_RefCount);

        return;
}

//----------------------------------------------------------------------------------//
//                                      DeleteFromEventsArray                                                                                   //
// pee: status is not set to inactive, but array pointer is set to -1                           //
// pwte: decrement active counters                                                                                                      //
//----------------------------------------------------------------------------------//
VOID
DeleteFromEventsArray (
        IN      PWT_EVENT_ENTRY         pee,
        IN      PWAIT_THREAD_ENTRY      pwte
        )
{
        INT             iIndex;
        DWORD   dwCount;

        iIndex = pee->ee_ArrayIndex;
        dwCount = pwte->wte_NumActiveEvents + pwte->wte_LowIndex - iIndex - 1;

        // shift right part towards left if its size is smaller
        if (dwCount <= pwte->wte_NumActiveEvents/2) {
                EventsArray_MoveOverlap (
                                        pwte,
                                        iIndex,  //dstn
                                        iIndex+1,       //src
                                        dwCount //count
                                    );
        }
        // 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 ++;
        }


        // set fields of event entry
        //
        pee->ee_ArrayIndex = -1;

        // change fields of wait thread entry
        //
        pwte->wte_NumActiveEvents --;
        if (pee->ee_bHighPriority) {
                pwte->wte_NumActiveHighPriorityEvents--;
        }
}//end DeleteFromEventsArray



//----------------------------------------------------------------------------------//
//                                      InsertInEventsArray                                                                                             //
// assumes lock on server:wte_CS        :todo is the lock required                                              //
// Insert the event in the events array and the map array       (no checks are performed)//
// pee: status set to active, set index to array position                                                       //
// pwte: increment active counters                                                                                                      //
//----------------------------------------------------------------------------------//
VOID
InsertInEventsArray (
        IN      PWT_EVENT_ENTRY         pee,
        IN      PWAIT_THREAD_ENTRY      pwte
        )
{
        INT             iIndex;


        // if the array is filled to the extreme right, then shift all events to the left end
        if (EA_OVERFLOW(pwte, 1))
                        EventsArray_CopyLeftEnd(pwte);


        //
        // get index where it has to be inserted
        //
        if (pee->ee_bHighPriority) {

                // the highPriority event has to be moved to the place right of the righmost HighPriority event
                iIndex = EA_INDEX_LOW_LOW_PRIORITY_EVENT(pwte);
                //copy the 1st low priority event to end+1;
                if (EA_EXISTS_LOW_PRIORITY_EVENT(pwte)) {
                        EventsArray_Move(pwte,
                                                         EA_INDEX_HIGH_LOW_PRIORITY_EVENT(pwte)+1,  //dstn
                                                         iIndex,        //src
                                                         1              //count
                                                        );
                }
        }
        else {          // low priority event: insert in the end
                iIndex = EA_INDEX_HIGH_LOW_PRIORITY_EVENT(pwte)+1;

        }


        // insert the event
        EventsArray_InsertEvent(pee, pwte, iIndex);


        // set fields of event entry
        //
        pee->ee_Status = WT_STATUS_ACTIVE;
        pee->ee_ArrayIndex = iIndex;


        // change fields of wait thread entry
        //
        pwte->wte_NumActiveEvents ++;
        if (pee->ee_bHighPriority) {
                pwte->wte_NumActiveHighPriorityEvents++;
        }
}//end InsertInEventsArray



//----------------------------------------------------------------------------------//
//                                      InactivateEvent                                                                                                 //
// Remove the event from the arrays and set the inactive flag                                           //                                                                                      //
// Used with bManual reset                                                                                                                      //
//----------------------------------------------------------------------------------//
VOID
InactivateEvent (
        IN PWT_EVENT_ENTRY      pee
        )
{

        DWORD                           dwIndex;
        PWAIT_THREAD_ENTRY      pwte;


        dwIndex = pee->ee_ArrayIndex;
        pwte = pee->eeP_wte;

        // if event is not at the right end of the array, then events on its right have to be shifted
        if (dwIndex != EA_INDEX_HIGH_LOW_PRIORITY_EVENT(pwte)) {
                EventsArray_MoveOverlap(pwte, dwIndex, dwIndex+1,
                                                (pwte->wte_NumActiveEvents + pwte->wte_LowIndex - dwIndex -1)
                                                );
        }


        //
        // change fields in event entry to make it inactive
        //
        pee->ee_ArrayIndex = -1;
        pee->ee_Status = (pee->ee_Status&WT_STATUS_FIRED) + WT_STATUS_INACTIVE;

        //
        // change fields in wait thread entry
        //
        pwte->wte_NumActiveEvents --;
        if (pee->ee_bHighPriority) {
                pwte->wte_NumActiveHighPriorityEvents --;
        }
        return;
}




//----------------------------------------------------------------------------------//
//                                              EventsArray_CopyLeftEnd                                                                         //
// copy all the events to the left end of the array                                                                     //
// sets the wte_LowIndex value                                                                                                          //
//----------------------------------------------------------------------------------//
VOID
EventsArray_CopyLeftEnd (
        IN      PWAIT_THREAD_ENTRY      pwte
        )
{

        EventsArray_Move(pwte,
                                         0,                                             //dstn
                                         pwte->wte_LowIndex,            //src
                                         pwte->wte_NumActiveEvents      //count
                                        );

        //
        // change fields of wait thread entry
        //
        pwte->wte_LowIndex = 0;

        return;
}

//----------------------------------------------------------------------------------//
//                                              EventsArray_Move                                                                                        //
// copy dwCount events from the srcIndex to dstnIndex (no overlap)                                      //
//----------------------------------------------------------------------------------//
VOID
EventsArray_Move (
        IN      PWAIT_THREAD_ENTRY      pwte,
        IN      DWORD   dwDstnIndex,
        IN      DWORD   dwSrcIndex,
        IN      DWORD   dwCount
        )
{
        PWT_EVENT_ENTRY pee;
        DWORD   i;

        if (dwCount==0)
                return;

        CopyMemory( &pwte->wteA_Events[dwDstnIndex],
                                &pwte->wteA_Events[dwSrcIndex],
                                sizeof(HANDLE) * dwCount
                          );

        CopyMemory( &pwte->wteA_EventMapper[dwDstnIndex],
                                &pwte->wteA_EventMapper[dwSrcIndex],
                                sizeof(PWT_EVENT_ENTRY) * dwCount
                          );

        for (i=0;  i<dwCount;  i++) {
                pee = pwte->wteA_EventMapper[dwDstnIndex + i];
                pee->ee_ArrayIndex = dwDstnIndex + i;
        }
        return;
}

//----------------------------------------------------------------------------------//
//                                              EventsArray_MoveOverlap                                                                         //
// copy dwCount events from the srcIndex to dstnIndex (with overlap)                            //
//----------------------------------------------------------------------------------//
VOID
EventsArray_MoveOverlap (
        IN      PWAIT_THREAD_ENTRY      pwte,
        IN      DWORD   dwDstnIndex,
        IN      DWORD   dwSrcIndex,
        IN      DWORD   dwCount
        )
{
        PWT_EVENT_ENTRY pee;
        DWORD   i;

        if (dwCount==0)
                return;

        MoveMemory( &pwte->wteA_Events[dwDstnIndex],
                                &pwte->wteA_Events[dwSrcIndex],
                                sizeof(HANDLE) * dwCount
                          );

        MoveMemory( &pwte->wteA_EventMapper[dwDstnIndex],
                                &pwte->wteA_EventMapper[dwSrcIndex],
                                sizeof(PWT_EVENT_ENTRY) * dwCount
                          );

        for (i=0;  i<dwCount;  i++) {
                pee = pwte->wteA_EventMapper[dwDstnIndex + i];
                pee->ee_ArrayIndex = dwDstnIndex + i;
        }
        return;
}


//----------------------------------------------------------------------------------//
//                                      EventsArray_InsertEvent                                                                                 //
// Insert the event in the events array and the map array                                                       //
//----------------------------------------------------------------------------------//
VOID
EventsArray_InsertEvent (
        IN      PWT_EVENT_ENTRY         pee,
        IN      PWAIT_THREAD_ENTRY      pwte,
        IN      INT                                     iIndex
        )
{
        // insert event in events array
        pwte->wteA_Events[iIndex] = pee->ee_Event;

        // insert pointer in map array
        pwte->wteA_EventMapper[iIndex] = pee;

        return;
}



//---------------------------------------------------------------------------------*//
//                                      GetListLength                                                                                                   //
// returns the length of the list                                                                                                       //
// returns 0 if the list contains the header only                                                                       //
//----------------------------------------------------------------------------------//
INT
GetListLength (
        IN      PLIST_ENTRY     pHead
        )
{
        PLIST_ENTRY             ple;
        DWORD                   dwCount=0;

        if (pHead==NULL)
                return -1;

        for (ple=pHead->Flink;  ple!=pHead;  ple=ple->Flink) {

                dwCount++;
        }

        return dwCount;

}

VOID
PrintEvent (
        PWT_EVENT_ENTRY pee,
        DWORD                   level
        )
{
        PLIST_ENTRY             pHead, ple;
        PWT_WORK_ITEM   pwi;

        if (pee->ee_Event==NULL)
                printf("        Event is NULL\n");

        printf("--      -------------------------------------------------------------------------\n");

        printf("--      <%2d><ee_bManualReset:%2d>   <ee_bInitialState:%2d>   <ee_Status:%2d>  <ee_bHighPriority:%2d>\n",
                                pee->ee_EventId, pee->ee_bManualReset, pee->ee_bInitialState, pee->ee_Status,
                                pee->ee_bHighPriority);

        printf("--      <ee_bSignalSingle:%2d>      <ee_bOwnerSelf:%2d>       <ee_ArrayIndex:%2d> <ee_ServerId:4%d>\n",
                                pee->ee_bSignalSingle, pee->ee_bOwnerSelf, pee->ee_ArrayIndex, pee->ee_ServerId);
        printf("--      <ee_RefCount:%d\n",     pee->ee_RefCount);
        pHead = &pee->eeL_wi;
        printf("--      LIST OF EVENT BINDINGS\n");
        for (ple=pHead->Flink;  ple!=pHead;  ple=ple->Flink) {
                pwi = CONTAINING_RECORD(ple, WT_WORK_ITEM, wi_ServerLinks);
                printf("--              <wi_ContextSz:%lu>      <wi_RunInServer:%d>      <wiP_ee:%lu>\n",
                                pwi->wi_ContextSz,  pwi->wi_RunInServer, (UINT_PTR)(PVOID)pwi->wiP_ee);
        }

        return;
}



VOID
PrintTimer (
        PWT_TIMER_ENTRY         pte,
        DWORD                           level
        )
{

        LARGE_INTEGER   li;
        TRACE0(WAIT_TIMER,"__      ________________________________________________________________________");
        TRACE4(WAIT_TIMER, "__      <TimerId:%2d><Timeout:%lu:%lu>   <te_ContextSz:%lu>",
                                        pte->te_TimerId, TIMER_HIGH(pte->te_Timeout), TIMER_LOW(pte->te_Timeout), pte->te_ContextSz);
        TRACE3(WAIT_TIMER, "__      <te_RunInServer:%1d>  <te_Status:%1d>   <te_ServerId:%d>",
                                        pte->te_RunInServer, pte->te_Status, pte->te_ServerId);

        return;
}


//----------------------------------------------------------------------------------//
//          PrintWaitThreadEntry                                                    //
// Prints the events and timers registered with this server.                        //
//----------------------------------------------------------------------------------//
VOID
PrintWaitThreadEntry (
        PWAIT_THREAD_ENTRY      pwte,
        DWORD                           level
        )
{
        PLIST_ENTRY                     ple, pHead;
        PWT_EVENT_ENTRY         pee;
        PWT_TIMER_ENTRY         pte;
        DWORD                           dwHigh, dwLow;
        LARGE_INTEGER           li;
        BOOL                            bPrint;
        DWORD                           i;


        TRACE0(WAIT_TIMER, "\n");
        TRACE0(WAIT_TIMER, "=================================================================================");
        TRACE3(WAIT_TIMER, "== <wte_ServerId:%2lu>        <wte_ThreadId:%2lu> <wte_NumClients:%2lu>",
                                pwte->wte_ServerId, pwte->wte_ThreadId, pwte->wte_NumClients);
        TRACE2(WAIT_TIMER, "== <wte_Status:%2lu>  <wte_RefCount:%2lu>", pwte->wte_Status, pwte->wte_RefCount);


        //
        // print list of events registered
        //
        if (!(level&DEBUGPRINT_FILTER_EVENTS)) {

                TRACE0(WAIT_TIMER, "-- ");
                TRACE0(WAIT_TIMER, "---------------------------------------------------------------------------------");
                TRACE2(WAIT_TIMER,"-- <wte_LowIndex:%2lu>         <wte_NumTotalEvents:%2lu>",
                                        pwte->wte_LowIndex, pwte->wte_NumTotalEvents);
                TRACE2(WAIT_TIMER, "-- <wte_NumActiveEvents:%2lu>  <wte_NumHighPriorityEvents:%2lu>",
                                        pwte->wte_NumActiveEvents, pwte->wte_NumHighPriorityEvents);
                TRACE1(WAIT_TIMER, "-- <wte_NumActiveHighPriorityEvents:%2lu>", pwte->wte_NumActiveHighPriorityEvents);


                if (level&0x2) {//dont print the initial 4 reserved events
                        bPrint = FALSE;
                        i = 0;
                }
                else
                        bPrint = TRUE;

                TRACE0(WAIT_TIMER, "--");
                pHead = &pwte->wteL_ClientEventEntries;
                for (ple=pHead->Blink;  ple!=pHead;  ple=ple->Blink) {
                        if (!bPrint) {
                                if (++i==4)
                                        bPrint=TRUE;
                                continue;
                        }

                        pee = CONTAINING_RECORD(ple, WT_EVENT_ENTRY, ee_ServerLinks);
                        PrintEvent(pee, level);
                }

                for (i=0;  i<=10; i++) {
                        if (pwte->wteA_EventMapper[i]==NULL)
                                TRACE0(WAIT_TIMER, "--");
                        else
                                TRACE2(WAIT_TIMER, "<%d:%d>", (pwte->wteA_EventMapper[i])->ee_EventId,
                                                        (pwte->wteA_EventMapper[i])->ee_ArrayIndex);
                }

        }


        //
        // print list of timers registered
        //
        if (!(level&DEBUGPRINT_FILTER_TIMERS)) {
                li = *(LARGE_INTEGER*)(PVOID)&pwte->wte_Timeout;
                dwHigh = li.HighPart;
                dwLow = li.LowPart;

                TRACE0(WAIT_TIMER, "--");
                TRACE0(WAIT_TIMER,"_________________________________________________________________________________");
                TRACE2(WAIT_TIMER, "__ <wte_Timeout.high:%lu> <wte_Timeout.low:%lu>", dwHigh, dwLow);
                pHead = &pwte->wteL_ClientTimerEntries;
                for (ple=pHead->Flink;  ple!=pHead;  ple=ple->Flink) {
                        pte = CONTAINING_RECORD(ple, WT_TIMER_ENTRY, te_ServerLinks);
                        PrintTimer(pte, level);
                }
        }

        return;

} //PrintWaitThreadEntry