windows-nt/Source/XPSP1/NT/net/wins/server/msc/winstmm.c
2020-09-26 16:20:57 +08:00

815 lines
21 KiB
C

/*++
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 <time.h>
#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;
}