474 lines
8.5 KiB
C++
474 lines
8.5 KiB
C++
//+-------------------------------------------------------------------------
|
||
//
|
||
// Microsoft Windows
|
||
//
|
||
// Copyright (C) Microsoft Corporation, 1994 - 1999
|
||
//
|
||
// File: delaytab.cxx
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
/*++
|
||
|
||
Module Name:
|
||
|
||
delaytab.cxx
|
||
|
||
Abstract:
|
||
|
||
interface for DELAYED_ACTION_TABLE, which asynchronously calls
|
||
functions after a specified delay.
|
||
|
||
Author:
|
||
|
||
Jeff Roberts (jroberts) 2-Nov-1994
|
||
|
||
Revision History:
|
||
|
||
2-Nov-1994 jroberts
|
||
|
||
Created this module.
|
||
|
||
--*/
|
||
|
||
#include <precomp.hxx>
|
||
#include "delaytab.hxx"
|
||
|
||
#include "rpcuuid.hxx"
|
||
#include "sdict.hxx"
|
||
#include "binding.hxx"
|
||
#include "handle.hxx"
|
||
#include "rpcssp.h"
|
||
#include "secclnt.hxx"
|
||
#include "hndlsvr.hxx"
|
||
|
||
|
||
|
||
inline unsigned long
|
||
CurrentTimeInMsec(
|
||
void
|
||
)
|
||
{
|
||
return GetTickCount();
|
||
}
|
||
|
||
|
||
BOOL
|
||
DelayedActionThread(
|
||
LPVOID Parms
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is a thread proc for the delayed call table.
|
||
|
||
Arguments:
|
||
|
||
Parms - address of table
|
||
|
||
Return Value:
|
||
|
||
FALSE - thread should be returned to the the cache.
|
||
|
||
--*/
|
||
{
|
||
DELAYED_ACTION_TABLE * pTable = (DELAYED_ACTION_TABLE *) Parms;
|
||
|
||
pTable->ThreadProc();
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
void
|
||
DELAYED_ACTION_TABLE::ThreadProc()
|
||
{
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This thread takes requests off the delayed-action list and processes them.
|
||
After 15 seconds of inactivity, this thread terminates.
|
||
|
||
Arguments:
|
||
|
||
none
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
--*/
|
||
|
||
BOOL EmptyList = FALSE;
|
||
long CurrentTime;
|
||
long WaitTime;
|
||
|
||
DELAYED_ACTION_NODE Copy;
|
||
DELAYED_ACTION_NODE * pNode;
|
||
|
||
//
|
||
// Disabling the event priority boost has the benefit of allowing a thread
|
||
// who posts a request to continue processing afterwards. This should
|
||
// improve locality of reference, since this thread will not preempt
|
||
// the poster.
|
||
//
|
||
if (FALSE == SetThreadPriorityBoost(GetCurrentThread(), TRUE))
|
||
{
|
||
#ifdef DEBUGRPC
|
||
DbgPrint("RPC DG: SetThreadPriorityBoost failed with %lu\n", GetLastError());
|
||
#endif
|
||
}
|
||
|
||
do
|
||
{
|
||
DELAYED_ACTION_FN pFn;
|
||
void * pData;
|
||
|
||
Mutex.Request();
|
||
|
||
#ifdef DEBUGRPC
|
||
{
|
||
unsigned ObservedCount = 0;
|
||
DELAYED_ACTION_NODE * Scan = ActiveList.Next;
|
||
while (Scan != &ActiveList)
|
||
{
|
||
++ObservedCount;
|
||
Scan = Scan->Next;
|
||
}
|
||
|
||
if (ObservedCount != NodeCount)
|
||
{
|
||
PrintToDebugger("RPC DG: delay thread sees %lu nodes but there should be %lu\n", ObservedCount, NodeCount);
|
||
RpcpBreakPoint();
|
||
}
|
||
}
|
||
#endif
|
||
|
||
pNode = ActiveList.Next;
|
||
if (pNode != &ActiveList)
|
||
{
|
||
ASSERT(pNode->TriggerTime != DELAYED_ACTION_NODE::DA_NodeFree);
|
||
|
||
EmptyList = FALSE;
|
||
|
||
CurrentTime = CurrentTimeInMsec();
|
||
if (pNode->TriggerTime - CurrentTime > 0)
|
||
{
|
||
WaitTime = pNode->TriggerTime - CurrentTime;
|
||
}
|
||
else
|
||
{
|
||
WaitTime = 0;
|
||
|
||
pFn = pNode->Fn;
|
||
pData = pNode->Data;
|
||
|
||
Cancel(pNode);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
CurrentTime = CurrentTimeInMsec();
|
||
|
||
if (!EmptyList)
|
||
{
|
||
WaitTime = 15000UL;
|
||
EmptyList = TRUE;
|
||
}
|
||
else
|
||
{
|
||
ThreadActive = FALSE;
|
||
Mutex.Clear();
|
||
break;
|
||
}
|
||
}
|
||
|
||
ThreadWakeupTime = CurrentTime + WaitTime;
|
||
|
||
Mutex.Clear();
|
||
|
||
if (WaitTime)
|
||
{
|
||
ThreadEvent.Wait(WaitTime);
|
||
}
|
||
else
|
||
{
|
||
(*pFn)(pData);
|
||
}
|
||
}
|
||
while ( !fExitThread );
|
||
}
|
||
|
||
|
||
DELAYED_ACTION_TABLE::DELAYED_ACTION_TABLE(
|
||
RPC_STATUS * pStatus
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
constructor for the delayed-action table. Initially no thread is created.
|
||
|
||
Arguments:
|
||
|
||
pStatus - if an error occurs, this will be filled in
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
--*/
|
||
|
||
: Mutex(pStatus),
|
||
ThreadEvent(pStatus, 0),
|
||
ActiveList(0, 0),
|
||
fExitThread(0),
|
||
ThreadActive(FALSE)
|
||
{
|
||
fConstructorFinished = FALSE;
|
||
|
||
if (*pStatus != RPC_S_OK)
|
||
{
|
||
return;
|
||
}
|
||
|
||
ActiveList.Next = &ActiveList;
|
||
ActiveList.Prev = &ActiveList;
|
||
|
||
#ifdef DEBUGRPC
|
||
|
||
LastAdded = 0;
|
||
LastRemoved = 0;
|
||
NodeCount = 0;
|
||
|
||
#endif
|
||
|
||
fConstructorFinished = TRUE;
|
||
}
|
||
|
||
|
||
DELAYED_ACTION_TABLE::~DELAYED_ACTION_TABLE(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Dsestructor for the delayed-action table. It tells the associated thread
|
||
to terminate, and waits until that happens.
|
||
|
||
Arguments:
|
||
|
||
none
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
--*/
|
||
|
||
{
|
||
if (FALSE == fConstructorFinished)
|
||
{
|
||
return;
|
||
}
|
||
|
||
DELAYED_ACTION_NODE * pNode;
|
||
|
||
fExitThread = 1;
|
||
ThreadEvent.Raise();
|
||
|
||
while (ActiveList.Next != &ActiveList)
|
||
{
|
||
Sleep(500);
|
||
}
|
||
}
|
||
|
||
|
||
RPC_STATUS
|
||
DELAYED_ACTION_TABLE::Add(
|
||
DELAYED_ACTION_NODE * pNode,
|
||
unsigned Delay,
|
||
BOOL ForceUpdate
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Adds a node to the table with the specified delay. The action taken if
|
||
the node is already in the list depends upon <ForceUpdate>: if FALSE,
|
||
the old trigger time is kept; if TRUE, the new time is used and the node
|
||
is moved in the list appropriately.
|
||
|
||
Arguments:
|
||
|
||
pNode - the node
|
||
|
||
Delay - delay time in milliseconds
|
||
|
||
ForceUpdate - only used when pNode is already in the list (see text above)
|
||
|
||
Return Value:
|
||
|
||
RPC_S_OK, or an error
|
||
|
||
--*/
|
||
|
||
{
|
||
if (!ForceUpdate && pNode->IsActive())
|
||
{
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
CLAIM_MUTEX Lock(Mutex);
|
||
|
||
if (pNode->IsActive())
|
||
{
|
||
Cancel(pNode);
|
||
}
|
||
|
||
//
|
||
// Add the node to the active list.
|
||
//
|
||
DELAYED_ACTION_NODE * pScan;
|
||
|
||
pScan = ActiveList.Next;
|
||
pNode->TriggerTime = Delay + CurrentTimeInMsec();
|
||
|
||
long TriggerTime = pNode->TriggerTime;
|
||
|
||
while (pScan != &ActiveList && pScan->TriggerTime - TriggerTime < 0)
|
||
{
|
||
ASSERT(pScan->IsActive());
|
||
ASSERT(pScan != pNode);
|
||
|
||
pScan = pScan->Next;
|
||
}
|
||
|
||
pNode->Next = pScan;
|
||
pNode->Prev = pScan->Prev;
|
||
|
||
pNode->Next->Prev = pNode;
|
||
pNode->Prev->Next = pNode;
|
||
|
||
#ifdef DEBUGRPC
|
||
++NodeCount;
|
||
LastAdded = pNode;
|
||
#endif
|
||
|
||
if (!ThreadActive)
|
||
{
|
||
fExitThread = FALSE;
|
||
|
||
RPC_STATUS Status = GlobalRpcServer->CreateThread(DelayedActionThread, this);
|
||
if (Status)
|
||
{
|
||
return Status;
|
||
}
|
||
|
||
ThreadActive = TRUE;
|
||
}
|
||
else if (ActiveList.Next == pNode && TriggerTime - ThreadWakeupTime < 0)
|
||
{
|
||
ThreadEvent.Raise();
|
||
}
|
||
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
|
||
BOOL
|
||
DELAYED_ACTION_TABLE::SearchForNode(
|
||
DELAYED_ACTION_NODE * pNode
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Finds the node in the table.
|
||
|
||
Arguments:
|
||
|
||
|
||
|
||
Return Value:
|
||
|
||
TRUE if pNode is in the table
|
||
FALSE if not
|
||
|
||
--*/
|
||
|
||
{
|
||
CLAIM_MUTEX Lock(Mutex);
|
||
|
||
//
|
||
// Search for node in active list.
|
||
//
|
||
DELAYED_ACTION_NODE * pScan;
|
||
|
||
pScan = ActiveList.Next;
|
||
while (pScan != &ActiveList && pScan != pNode)
|
||
{
|
||
pScan = pScan->Next;
|
||
}
|
||
|
||
if (pScan)
|
||
{
|
||
return TRUE;
|
||
}
|
||
else
|
||
{
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
void
|
||
DELAYED_ACTION_TABLE::QueueLength(
|
||
unsigned * pTotalCount,
|
||
unsigned * pOverdueCount
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determines the number of active entries and the number of entries
|
||
that are late.
|
||
|
||
Arguments:
|
||
|
||
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
--*/
|
||
|
||
{
|
||
CLAIM_MUTEX Lock(Mutex);
|
||
|
||
DELAYED_ACTION_NODE * pScan = ActiveList.Next;
|
||
unsigned Count = 0;
|
||
long CurrentTime = GetTickCount();
|
||
|
||
while (pScan != &ActiveList && CurrentTime - pScan->TriggerTime > 0)
|
||
{
|
||
++Count;
|
||
pScan = pScan->Next;
|
||
}
|
||
|
||
*pOverdueCount = Count;
|
||
|
||
while (pScan != &ActiveList)
|
||
{
|
||
++Count;
|
||
pScan = pScan->Next;
|
||
}
|
||
|
||
*pTotalCount = Count;
|
||
}
|
||
|