474 lines
8 KiB
C++
474 lines
8 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;
|
|||
|
}
|
|||
|
|