/*++ Copyright (c) 1990 Microsoft Corporation Module Name: winstmm.c Abstract: This module contains the timer manager functions Functions: WinsTmmInsertEntry TmmThdInitFn WinsTmmInit HandleReq SignalClient WinsTmmDeleteReqs WinsTmmDeallocReq Portability: This module is portable Author: Pradeep Bahl (PradeepB) Mar-1993 Revision History: Modification date Person Description of modification ----------------- ------- ---------------------------- --*/ /* * Includes */ #include #include "wins.h" #include "nms.h" #include "winsevt.h" #include "winsmsc.h" #include "winstmm.h" #include "winsque.h" #include "winsthd.h" /* * Local Macro Declarations */ /* * Local Typedef Declarations */ /* * Global Variable Definitions */ HANDLE WinsTmmHeapHdl; //handle of heap to allocate TMM work items // from /* * Local Variable Definitions */ STATIC CRITICAL_SECTION sTmmReqCntCrtSec; STATIC DWORD sTmmReqCnt = 0; // // This stores the req. id of the request at the top of the queue // (i.e. one for which the timer thread is doing a timed wait) // STATIC DWORD sReqIdOfCurrReq; /* * Local Function Prototype Declarations */ STATIC DWORD TmmThdInitFn( LPVOID pParam ); VOID HandleReq( OUT LPLONG pDeltaTime ); VOID SignalClient( VOID ); /* prototypes for functions local to this module go here */ VOID WinsTmmInsertEntry( PQUE_TMM_REQ_WRK_ITM_T pPassedWrkItm, WINS_CLIENT_E Client_e, QUE_CMD_TYP_E CmdTyp_e, BOOL fResubmit, time_t AbsTime, DWORD TimeInt, PQUE_HD_T pRspQueHd, LPVOID pClientCtx, DWORD MagicNo, PWINSTMM_TIMER_REQ_ACCT_T pSetTimerReqs ) /*++ Routine Description: This function is called to insert a work item into the Timer Manager's request queue (delta queue). It allocates a work item if required (if pWrkItm != NULL) and enqueues it in its proper position in the delta queue of the TMM thread. It then signals the TMM thread if required. Arguments: pWrkItm - work item to queue (if pWrkItm != NULL) Client_e - id. of client that submitted the request CmdTyp_e - type of command (set timer, modify timer, etc) fResubmit - Is it a resubmit AbsTime - absolute time (in secs) at which the client needs to be notified TimeInt - time interval in seconds after which the client needs to be notified pRspQueHd - Que Head of the queue where the notification needs to be queued pClientCtx - Client's context that needs to be put in the work item pSetTimerReqs - For future extensibility Externals Used: None Return Value: None Error Handling: Called by: Side Effects: Comments: None --*/ { PQUE_TMM_REQ_WRK_ITM_T pWrkItm; PQUE_TMM_REQ_WRK_ITM_T pTmp; BOOL fInserted = FALSE; UNREFERENCED_PARAMETER(pSetTimerReqs); if (!pPassedWrkItm) { QueAllocWrkItm( WinsTmmHeapHdl, sizeof(QUE_TMM_REQ_WRK_ITM_T), &pWrkItm ); } else { pWrkItm = pPassedWrkItm; } // // Put a request id (a monotonically increasing number) // EnterCriticalSection(&sTmmReqCntCrtSec); sTmmReqCnt++; LeaveCriticalSection(&sTmmReqCntCrtSec); pWrkItm->ReqId = sTmmReqCnt; // // If work item was allocated or if it is not a resubmit // init the rest of the fields appropriately // pWrkItm->CmdTyp_e = CmdTyp_e; pWrkItm->AbsTime = AbsTime; pWrkItm->TimeInt = TimeInt; pWrkItm->QueTyp_e = QUE_E_WINSTMQ; pWrkItm->RspEvtHdl = pRspQueHd->EvtHdl; pWrkItm->pRspQueHd = pRspQueHd; pWrkItm->pClientCtx = pClientCtx; pWrkItm->Client_e = Client_e; pWrkItm->MagicNo = MagicNo; EnterCriticalSection(&QueWinsTmmQueHd.CrtSec); try { if (IsListEmpty(&QueWinsTmmQueHd.Head)) { InsertTailList(&QueWinsTmmQueHd.Head, &pWrkItm->Head); if (!SetEvent(QueWinsTmmQueHd.EvtHdl)) { WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SIGNAL_TMM_ERR); DBGPRINT0(EXC, "Could not signal Tmm Thd\n"); WINS_RAISE_EXC_M(WINS_EXC_SIGNAL_TMM_ERR); } fInserted = TRUE; } else { // // get the address of the first entry in the queue. // pTmp = (PQUE_TMM_REQ_WRK_ITM_T)QueWinsTmmQueHd.Head.Flink; // // Go over the circular linked list until we hit the head // of the queue // while(pTmp != (PQUE_TMM_REQ_WRK_ITM_T)&QueWinsTmmQueHd.Head) { // // If the list entry has a longer timer than the new entry, // insert the new entry before it // if (pTmp->AbsTime > pWrkItm->AbsTime) { pWrkItm->Head.Flink = &pTmp->Head; pWrkItm->Head.Blink = pTmp->Head.Blink; pTmp->Head.Blink->Flink = &pWrkItm->Head; pTmp->Head.Blink = &pWrkItm->Head; fInserted = TRUE; // // The element has been inserted. Let us break out // of the loop // break; } pTmp = (PQUE_TMM_REQ_WRK_ITM_T)pTmp->Head.Flink; } } // // If the entry was not inserted (i.e. all entries in the queue have // a shorter expiry than our entry), insert the entry at the // end of the list // if (!fInserted) { InsertTailList(&QueWinsTmmQueHd.Head, &pWrkItm->Head); } // // If this is the top most entry in the queue and there is // at least one more entry in the queue, signal the TMM // thread so that it can start a timer for it. If the above // is not true, relax (either the TMM thread has already been // signaled (if this is the only item in the queue) or it does not // need to be signaled (this is not the top-most item in the queue). // if ( (pWrkItm->Head.Blink == &QueWinsTmmQueHd.Head) && (pWrkItm->Head.Flink != &QueWinsTmmQueHd.Head) ) { if (!SetEvent(QueWinsTmmQueHd.EvtHdl)) { WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SIGNAL_TMM_ERR); DBGPRINT0(EXC, "Could not signal Tmm Thd\n"); WINS_RAISE_EXC_M(WINS_EXC_SIGNAL_TMM_ERR); } } } // end of try .. finally { LeaveCriticalSection(&QueWinsTmmQueHd.CrtSec); } return; } VOID WinsTmmInit( VOID ) /*++ Routine Description: This is the function that initializes the timer manager Arguments: None Externals Used: None Return Value: None Error Handling: Called by: Init in nms.c Side Effects: A timer thread is created Comments: None --*/ { STATUS RetStat; // // Initialize the critical section guarding the counter for // Tmm requests // InitializeCriticalSection(&sTmmReqCntCrtSec); /* * Create heap for allocating name challenge work items */ DBGPRINT0(HEAP_CRDL, "WinsTmmInit: Tmm. Chl. heap\n"); WinsTmmHeapHdl = WinsMscHeapCreate( 0, /*to have mutual exclusion */ TMM_INIT_HEAP_SIZE ); InitializeListHead(&QueWinsTmmQueHd.Head); // // // Create the timer thread. This function will // initialize the critical section and the Evt handle passed // to it // RetStat = WinsMscSetUpThd( &QueWinsTmmQueHd, //queue head TmmThdInitFn, //init function NULL, // no param &WinsThdPool.TimerThds[0].ThdHdl, &WinsThdPool.TimerThds[0].ThdId ); if (RetStat == WINS_SUCCESS) { WinsThdPool.TimerThds[0].fTaken = TRUE; WinsThdPool.ThdCount++; //increment the thread count } else { WINS_RAISE_EXC_M(WINS_EXC_FAILURE); } return; } DWORD TmmThdInitFn( LPVOID pParam ) /*++ Routine Description: This is the top-most function of the TMM thread Arguments: pParam - Param to the top most function (not used) Externals Used: None Return Value: Success status codes -- WINS_SUCCESS Error status codes -- WINS_FAILURE Error Handling: Called by: WinsTmmInit Side Effects: Comments: None --*/ { LONG DeltaTime; BOOL fSignaled; UNREFERENCED_PARAMETER(pParam); try { while(TRUE) { WinsMscWaitInfinite(QueWinsTmmQueHd.EvtHdl); while(!IsListEmpty(&QueWinsTmmQueHd.Head)) { HandleReq(&DeltaTime); if (DeltaTime != 0) { // // Do a timed wait until either the timer expires // or the event is signaled. // WinsMscWaitTimed( QueWinsTmmQueHd.EvtHdl, DeltaTime * 1000, //convert to millisecs &fSignaled ); // // If signaled (interrupte from the wait), it means // that there is a more urgent request in the timer // queue. // if (fSignaled) { HandleReq(&DeltaTime); } else //timer expired { SignalClient(); } } else { SignalClient(); } } } } except(EXCEPTION_EXECUTE_HANDLER) { DBGPRINT0(EXC, "TmmThdInitFn: Timer Thread encountered an exception\n"); WINSEVT_LOG_M(GetExceptionCode(), WINS_EVT_TMM_EXC); ExitThread(WINS_FAILURE); } // // We should never reach here // ASSERT(0); return(WINS_FAILURE); } VOID HandleReq( OUT LPLONG pDeltaTime ) /*++ Routine Description: This function is called to handle a timer request. The function is called when the Timer thread is signaled. Arguments: pDeltaTime - Time Interval to wait for before signaling the client Externals Used: None Return Value: None Error Handling: Called by: TmmThdInitFn Side Effects: Comments: None --*/ { time_t AbsTime; time_t CurrTime; QUE_CMD_TYP_E CmdTyp_e; (void)time(&CurrTime); EnterCriticalSection(&QueWinsTmmQueHd.CrtSec); // // If list is empty, return. // if (IsListEmpty(&QueWinsTmmQueHd.Head)) { *pDeltaTime = 0; LeaveCriticalSection(&QueWinsTmmQueHd.CrtSec); return; } // // Retrieve the absolute time corresponding to the // first entry in the timer queue. // AbsTime = ((PQUE_TMM_REQ_WRK_ITM_T) (QueWinsTmmQueHd.Head.Flink))->AbsTime; CmdTyp_e = ((PQUE_TMM_REQ_WRK_ITM_T) (QueWinsTmmQueHd.Head.Flink))->CmdTyp_e; ASSERT(CmdTyp_e == QUE_E_CMD_SET_TIMER); // // Store the request id of the request that we will wait on // in the STATIC // sReqIdOfCurrReq = ((PQUE_TMM_REQ_WRK_ITM_T) (QueWinsTmmQueHd.Head.Flink))->ReqId; LeaveCriticalSection(&QueWinsTmmQueHd.CrtSec); switch(CmdTyp_e) { case(QUE_E_CMD_SET_TIMER): *pDeltaTime = (LONG)(AbsTime - CurrTime); // // It is possible that we might have a small // negative value here, just because of the // time it took to process the request. // if (*pDeltaTime < 0) { *pDeltaTime = 0; } break; case(QUE_E_CMD_MODIFY_TIMER): WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SFT_ERR); DBGPRINT0(ERR, "Not supported yet\n"); WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR); break; case(QUE_E_CMD_CANCEL_TIMER): WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SFT_ERR); DBGPRINT0(ERR, "Not supported yet\n"); WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR); break; default: WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SFT_ERR); DBGPRINT1(EXC, "Wierd: Invalid Request. CmdType is (%d), \n", CmdTyp_e); WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR); break; } return; } VOID SignalClient( VOID ) /*++ Routine Description: This function is called to notify the client that its timer request has expired. Arguments: None Externals Used: None Return Value: None Error Handling: Called by: TmmThdInitFn Side Effects: Comments: None --*/ { PQUE_TMM_REQ_WRK_ITM_T pWrkItm; FUTURES("Optimize to signal all clients whose requests have expired") EnterCriticalSection(&QueWinsTmmQueHd.CrtSec); pWrkItm = (PQUE_TMM_REQ_WRK_ITM_T)RemoveHeadList(&QueWinsTmmQueHd.Head); // // If the top of the queue has a different work item than the // one we were doing a timed wait for, it means that the there has // been a queue purge (see WinsTmmDeleteEntry. Simply return. // if (sReqIdOfCurrReq != pWrkItm->ReqId) { LeaveCriticalSection(&QueWinsTmmQueHd.CrtSec); return; } LeaveCriticalSection(&QueWinsTmmQueHd.CrtSec); pWrkItm->CmdTyp_e = QUE_E_CMD_TIMER_EXPIRED; // // Insert into client's queue // QueInsertWrkItm( &pWrkItm->Head, 0, //not used -- change to an enumrator val pWrkItm->pRspQueHd ); return; } VOID WinsTmmDeleteReqs( WINS_CLIENT_E Client_e ) /*++ Routine Description: This function is called to delete all set timer requests submitted by a client Arguments: Externals Used: None Return Value: None Error Handling: Called by: Reconfig (in rplpull.c) Side Effects: Comments: In the future, enhance this function so that it delete requests based on other criteria --*/ { PQUE_TMM_REQ_WRK_ITM_T pTmp; PQUE_TMM_REQ_WRK_ITM_T pMemToDealloc; BOOL fFirstEntry = FALSE; EnterCriticalSection(&QueWinsTmmQueHd.CrtSec); try { if (!IsListEmpty(&QueWinsTmmQueHd.Head)) { // // get the address of the first entry in the queue. // pTmp = (PQUE_TMM_REQ_WRK_ITM_T)QueWinsTmmQueHd.Head.Flink; // // We loop until we get to the head of the list (the linked // list is a circular list) // while(pTmp != (PQUE_TMM_REQ_WRK_ITM_T)&QueWinsTmmQueHd.Head) { // // If the entry was queued by the client, get rid of // it // if (pTmp->Client_e == Client_e) { // // If this is the first entry in the queue, it // means that the timer thread is doing a // wait on it. Signal it. // if ( !fFirstEntry && (pTmp->Head.Blink == &QueWinsTmmQueHd.Head) ) { fFirstEntry = TRUE; } // if (pTmp->Head.Flink == &QueWinsTmmQueHd.Head) if (fFirstEntry) { if (!SetEvent(QueWinsTmmQueHd.EvtHdl)) { WINSEVT_LOG_M( WINS_FAILURE, WINS_EVT_SIGNAL_TMM_ERR ); DBGPRINT0(EXC, "Could not signal Tmm for canceling a request\n"); WINS_RAISE_EXC_M(WINS_EXC_SIGNAL_TMM_ERR); } } // // unlink the request // pTmp->Head.Flink->Blink = pTmp->Head.Blink; pTmp->Head.Blink->Flink = pTmp->Head.Flink; pMemToDealloc = pTmp; pTmp = (PQUE_TMM_REQ_WRK_ITM_T)pTmp->Head.Flink; // // Dealloc the dequeued work item // WinsTmmDeallocReq(pMemToDealloc); } else { pTmp = (PQUE_TMM_REQ_WRK_ITM_T)pTmp->Head.Flink; } } } } // end of try block except(EXCEPTION_EXECUTE_HANDLER) { DBGPRINT1(EXC, "WinsTmmDeleteReqs: Got exception (%x)\n", (DWORD)GetExceptionCode()); WINSEVT_LOG_M(GetExceptionCode(), WINS_EVT_TMM_EXC); } LeaveCriticalSection(&QueWinsTmmQueHd.CrtSec); return; } __inline VOID WinsTmmDeallocReq( PQUE_TMM_REQ_WRK_ITM_T pWrkItm ) /*++ Routine Description: This function is called to deallocate a timer request work item Arguments: pWrkItm - Work Item Externals Used: WinsTmmHeapHdl Return Value: None Error Handling: Called by: RplPullInit Side Effects: Comments: None --*/ { // // deallocate the work item // QueDeallocWrkItm( WinsTmmHeapHdl, pWrkItm ); return; }