windows-nt/Source/XPSP1/NT/ds/netapi/svcimgs/ntrepl/util/queue.c
2020-09-26 16:20:57 +08:00

3069 lines
67 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name:
queue.c
Abstract:
Generic efficient queue package.
Author:
John Vert (jvert) 12-Jan-1996
Revision History:
David Orbits (davidor) 23-Apr-1997
Added command packet routines.
Added interlocked list routines.
Introduction
A striped queue is really just a list of queues that are managed as a single
queue. When a caller request a queue entry, the first entry for the queue at
the head of the list is returned and that queue is moved to the tail of the
list to prevent starvation of the other queues on the list. Callers sleep when
none of the queues have entries. The caller must be ready to accept an entry
from any queue. Striped queues allow a caller to serialize access to a
sub-queue.
Structures
The same structure is used for both the queue and the controlling queue.
A controlling queue plus its component queues are termed a striped queue.
There is no striped queue structure. The struct contains the following:
- critical section for locking
- List head for entries
- Address of the controlling queue
- List head for Full queues
- List head for Empty queues
- List head for Idled queues
- Count of the number of entries on a queue
- Count of the number of entries on all controlled queues
Initializing
A non-striped (regular) queue is created with:
FrsRtlInitializeQueue(Queue, Queue)
A striped queue controlled by ControlQueue and composed of QueueA and QueueB
is created with:
FrsRtlInitializeQueue(ControlQueue, ControlQueue)
FrsRtlInitializeQueue(QueueA, ControlQueue)
FrsRtlInitializeQueue(QueueB, ControlQueue)
Queues can be added and deleted from the stripe at any time.
Idling Queues
The controlling queue for a striped queue maintains a list of Full queues,
Empty queues, and Idled queues. A striped queue allows a caller to serialize
access to a queue by "idling" the queue. No other thread is allowed to pull
an entry from the queue until the caller "UnIdles" the queue:
Entry = FrsRtlRemoveHeadTimeoutIdled(Queue, 0, &IdledQueue)
Process Entry
FrsRtlUnIdledQueue(IdledQueue);
Entries can be inserted to an idled queue and they can be removed with
FrsRtlRemoveQueueEntry().
Non-Striped queues do not support the serializing "idling" feature. The
IdledQueue parameter is ignored.
Inserting Entries
Use the normal queue insertion routines for queues, striped queues, and idled
queues. DO NOT insert into the controlling queue if this is a striped queue.
Removing Entries
Use the normal queue removal routines for queues, striped queues, and idled
queues. Removals from a striped queue will return an entry from any of the
sub-queues except for idled sub-queues. The FrsRtlRemoveQueueEntry() function
will remove an entry from even an idled queue.
Functions
DbgCheckLinkage - Checks all of the linkage in a queue
FrsRtlInitializeQueue - Initializes any queue
FrsRtlDeleteQueue - Cleans up any queue
FrsRtlRundownQueue - Aborts the queue and returns a list of entries
FrsRtlUnIdledQueue - Moves a queue from the idle to one of the active lists
FrsRtlRemoveHeadQueue - Remove the head of the queue
FrsRtlRemoveHeadQueueTimeout - Remove the head of the queue
FrsRtlRemoveHeadQueueTimeoutIdled - Remove the head of the queue
FrsRtlRemoveEntryQueueLock - Remove entry from locked queue
FrsRtlInsertTailQueue - Insert entry into queue at tail
FrsRtlInsertTailQueueLock - Insert entry into locked queue at head
FrsRtlInsertHeadQueue - Insert entry into queue at tail
FrsRtlInsertHeadQueueLock - Insert entry into locked queue at head
FrsRtlWaitForQueueFull - wait for an entry to appear on the queue
Rundown
Calling rundown on the controlling queue is NOT supported. Don't do that
Running down a component queue does not rundown the controlling queue.
The abort event is set in the controlling queue when the last component
queue is rundown.
--*/
#include <ntreppch.h>
#pragma hdrstop
#include <frs.h>
VOID
FrsCompleteSynchronousCmdPkt(
IN PCOMMAND_PACKET CmdPkt,
IN PVOID CompletionArg
);
//
// This is the command packet schedule queue. It is used when you need to
// queue a command packet to be processed in the future.
//
FRS_QUEUE FrsScheduleQueue;
// #define PRINT_QUEUE(_S_, _Q_) PrintQueue(_S_, _Q_)
#define PRINT_QUEUE(_S_, _Q_)
VOID
PrintQueue(
IN ULONG Sev,
IN PFRS_QUEUE Queue
)
/*++
Routine Description:
Print the queue
Arguments:
Sev - dprint severity
Queue - Supplies a pointer to a queue structure to check
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "PrintQueue:"
DWORD Count;
DWORD ControlCount;
BOOL FoundFull;
BOOL FoundEmpty;
BOOL FoundIdled;
PLIST_ENTRY Entry;
PLIST_ENTRY OtherEntry;
PFRS_QUEUE OtherQueue;
PFRS_QUEUE Control;
DPRINT1(0, "***** Print Queue %08x *****\n", Queue);
Control = Queue->Control;
if (Queue == Control) {
DPRINT1(Sev, "\tQueue : %08x\n", Queue);
DPRINT1(Sev, "\tCount : %8d\n", Queue->Count);
DPRINT1(Sev, "\tControlCount: %8d\n", Queue->ControlCount);
DPRINT1(Sev, "\tRundown : %s\n", (Queue->IsRunDown) ? "TRUE" : "FALSE");
DPRINT1(Sev, "\tIdled : %s\n", (Queue->IsIdled) ? "TRUE" : "FALSE");
return;
}
DPRINT2(Sev, "\tControl : %08x for %08x\n", Control, Queue);
DPRINT1(Sev, "\tCount : %8d\n", Control->Count);
DPRINT1(Sev, "\tControlCount: %8d\n", Control->ControlCount);
DPRINT1(Sev, "\tRundown : %s\n", (Control->IsRunDown) ? "TRUE" : "FALSE");
DPRINT1(Sev, "\tIdled : %s\n", (Control->IsIdled) ? "TRUE" : "FALSE");
//
// Full list
//
DPRINT(Sev, "\tFULL\n");
for (Entry = GetListNext(&Control->Full);
Entry != &Control->Full;
Entry = GetListNext(Entry)) {
OtherQueue = CONTAINING_RECORD(Entry, FRS_QUEUE, Full);
if (OtherQueue == Queue) {
DPRINT(Sev, "\t\tTHIS QUEUE\n");
}
DPRINT1(Sev, "\t\tQueue : %08x\n", OtherQueue);
DPRINT1(Sev, "\t\tCount : %8d\n", OtherQueue->Count);
DPRINT1(Sev, "\t\tControlCount: %8d\n", OtherQueue->ControlCount);
DPRINT1(Sev, "\t\tRundown : %s\n", (OtherQueue->IsRunDown) ? "TRUE" : "FALSE");
DPRINT1(Sev, "\t\tIdled : %s\n", (OtherQueue->IsIdled) ? "TRUE" : "FALSE");
}
//
// Empty list
//
DPRINT(Sev, "\tEMPTY\n");
for (Entry = GetListNext(&Control->Empty);
Entry != &Control->Empty;
Entry = GetListNext(Entry)) {
OtherQueue = CONTAINING_RECORD(Entry, FRS_QUEUE, Empty);
if (OtherQueue == Queue) {
DPRINT(Sev, "\t\tTHIS QUEUE\n");
}
DPRINT1(Sev, "\t\tQueue : %08x\n", OtherQueue);
DPRINT1(Sev, "\t\tCount : %8d\n", OtherQueue->Count);
DPRINT1(Sev, "\t\tControlCount: %8d\n", OtherQueue->ControlCount);
DPRINT1(Sev, "\t\tRundown : %s\n", (OtherQueue->IsRunDown) ? "TRUE" : "FALSE");
DPRINT1(Sev, "\t\tIdled : %s\n", (OtherQueue->IsIdled) ? "TRUE" : "FALSE");
}
//
// Idle list
//
DPRINT(Sev, "\tIDLE\n");
for (Entry = GetListNext(&Control->Idled);
Entry != &Control->Idled;
Entry = GetListNext(Entry)) {
OtherQueue = CONTAINING_RECORD(Entry, FRS_QUEUE, Idled);
if (OtherQueue == Queue) {
DPRINT(Sev, "\t\tTHIS QUEUE\n");
}
DPRINT1(Sev, "\t\tQueue : %08x\n", OtherQueue);
DPRINT1(Sev, "\t\tCount : %8d\n", OtherQueue->Count);
DPRINT1(Sev, "\t\tControlCount: %8d\n", OtherQueue->ControlCount);
DPRINT1(Sev, "\t\tRundown : %s\n", (OtherQueue->IsRunDown) ? "TRUE" : "FALSE");
DPRINT1(Sev, "\t\tIdled : %s\n", (OtherQueue->IsIdled) ? "TRUE" : "FALSE");
}
}
BOOL
DbgCheckQueue(
PFRS_QUEUE Queue
)
/*++
Routine Description:
Check the consistency of the queue
Arguments:
Queue - Supplies a pointer to a queue structure to check
Return Value:
TRUE - everything is okay
Assert - assert error
--*/
{
#undef DEBSUB
#define DEBSUB "DbgCheckQueue:"
DWORD Count;
DWORD ControlCount;
BOOL FoundFull;
BOOL FoundEmpty;
BOOL FoundIdled;
PLIST_ENTRY Entry;
PLIST_ENTRY OtherEntry;
PFRS_QUEUE OtherQueue;
PFRS_QUEUE Control;
if (!DebugInfo.Queues) {
return TRUE;
}
FRS_ASSERT(Queue);
Control = Queue->Control;
FRS_ASSERT(Control);
if (Control->IsRunDown) {
FRS_ASSERT(Control->ControlCount == 0);
FRS_ASSERT(Queue->IsRunDown);
FRS_ASSERT(IsListEmpty(&Control->Full));
FRS_ASSERT(IsListEmpty(&Control->Empty));
FRS_ASSERT(IsListEmpty(&Control->Idled));
}
if (Queue->IsRunDown) {
FRS_ASSERT(Queue->Count == 0);
FRS_ASSERT(IsListEmpty(&Queue->Full));
FRS_ASSERT(IsListEmpty(&Queue->Empty));
FRS_ASSERT(IsListEmpty(&Queue->Idled));
}
FRS_ASSERT(!Control->IsIdled);
//
// Check Full list
//
ControlCount = 0;
FoundFull = FALSE;
Entry = &Control->Full;
do {
Entry = GetListNext(Entry);
OtherQueue = CONTAINING_RECORD(Entry, FRS_QUEUE, Full);
if (OtherQueue == Queue) {
FoundFull = TRUE;
}
FRS_ASSERT(Control == OtherQueue ||
(!OtherQueue->IsRunDown && !OtherQueue->IsIdled));
Count = 0;
if (!IsListEmpty(&OtherQueue->ListHead)) {
OtherEntry = GetListNext(&OtherQueue->ListHead);
do {
++Count;
++ControlCount;
OtherEntry = GetListNext(OtherEntry);
} while (OtherEntry != &OtherQueue->ListHead);
}
FRS_ASSERT(Count == OtherQueue->Count);
} while (OtherQueue != Control);
FRS_ASSERT(ControlCount == Control->ControlCount ||
(Control == Queue && Control->ControlCount == 0));
//
// Check Empty list
//
ControlCount = 0;
FoundEmpty = FALSE;
Entry = &Control->Empty;
do {
Entry = GetListNext(Entry);
OtherQueue = CONTAINING_RECORD(Entry, FRS_QUEUE, Empty);
if (OtherQueue == Queue) {
FoundEmpty = TRUE;
}
FRS_ASSERT(Control == OtherQueue ||
(!OtherQueue->IsRunDown && !OtherQueue->IsIdled));
Count = 0;
if (!IsListEmpty(&OtherQueue->ListHead)) {
OtherEntry = GetListNext(&OtherQueue->ListHead);
do {
++Count;
++ControlCount;
OtherEntry = GetListNext(OtherEntry);
} while (OtherEntry != &OtherQueue->ListHead);
}
FRS_ASSERT(Count == OtherQueue->Count);
} while (OtherQueue != Control);
//
// Check Idled list
//
FoundIdled = FALSE;
Entry = &Control->Idled;
do {
Entry = GetListNext(Entry);
OtherQueue = CONTAINING_RECORD(Entry, FRS_QUEUE, Idled);
if (OtherQueue == Queue) {
FoundIdled = TRUE;
}
FRS_ASSERT(Control == OtherQueue || OtherQueue->IsIdled);
Count = 0;
if (!IsListEmpty(&OtherQueue->ListHead)) {
OtherEntry = GetListNext(&OtherQueue->ListHead);
do {
++Count;
OtherEntry = GetListNext(OtherEntry);
} while (OtherEntry != &OtherQueue->ListHead);
}
FRS_ASSERT(Count == OtherQueue->Count);
} while (OtherQueue != Control);
//
// Verify state
//
FRS_ASSERT((Queue->Count && !IsListEmpty(&Queue->ListHead)) ||
(!Queue->Count && IsListEmpty(&Queue->ListHead)));
if (Control == Queue) {
//
// We are our own controlling queue
//
FRS_ASSERT(FoundFull && FoundEmpty && FoundIdled);
} else {
//
// Controlled by a separate queue
//
if (Queue->IsRunDown) {
FRS_ASSERT(!FoundFull && !FoundEmpty && !FoundIdled && !Queue->Count);
} else {
FRS_ASSERT(FoundFull || FoundEmpty || FoundIdled);
}
if (FoundFull) {
FRS_ASSERT(!FoundEmpty && !FoundIdled && Queue->Count);
} else if (FoundEmpty) {
FRS_ASSERT(!FoundFull && !FoundIdled && !Queue->Count);
} else if (FoundIdled) {
FRS_ASSERT(!FoundFull && !FoundEmpty);
}
}
return TRUE;
}
VOID
FrsInitializeQueue(
PFRS_QUEUE Queue,
PFRS_QUEUE Control
)
/*++
Routine Description:
Initializes a queue for use.
Arguments:
Queue - Supplies a pointer to a queue structure to initialize
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsInitializeQueue:"
ZeroMemory(Queue, sizeof(FRS_QUEUE));
InitializeListHead(&Queue->ListHead);
InitializeListHead(&Queue->Full);
InitializeListHead(&Queue->Empty);
InitializeListHead(&Queue->Idled);
InitializeCriticalSection(&Queue->Lock);
Queue->IsRunDown = FALSE;
Queue->IsIdled = FALSE;
Queue->Control = Control;
Queue->InitTime = GetTickCount();
if (Control->IsRunDown) {
Queue->IsRunDown = TRUE;
return;
}
//
// Begin life on the empty queue
//
FrsRtlAcquireQueueLock(Queue);
InsertTailList(&Control->Empty, &Queue->Empty);
FRS_ASSERT(DbgCheckQueue(Queue));
FrsRtlReleaseQueueLock(Queue);
//
// The controlling queue supplies the events so there is no
// need to create extraneous events.
//
if (Queue == Control) {
Queue->Event = FrsCreateEvent(TRUE, FALSE);
Queue->RunDown = FrsCreateEvent(TRUE, FALSE);
}
}
VOID
FrsRtlDeleteQueue(
IN PFRS_QUEUE Queue
)
/*++
Routine Description:
Releases all resources used by a queue.
Arguments:
Queue - supplies the queue to be deleted
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsRtlDeleteQueue:"
PFRS_QUEUE Control;
Control = Queue->Control;
if (Queue == Control) {
FRS_ASSERT(IsListEmpty(&Queue->Full) &&
IsListEmpty(&Queue->Empty) &&
IsListEmpty(&Queue->Idled));
} else {
FRS_ASSERT(IsListEmpty(&Queue->ListHead));
}
EnterCriticalSection(&Control->Lock);
FRS_ASSERT(DbgCheckQueue(Queue));
RemoveEntryListB(&Queue->Full);
RemoveEntryListB(&Queue->Empty);
RemoveEntryListB(&Queue->Idled);
Control->ControlCount -= Queue->Count;
FRS_ASSERT(DbgCheckQueue(Queue));
LeaveCriticalSection(&Control->Lock);
DeleteCriticalSection(&Queue->Lock);
//
// Only the controlling queue has valid handles
//
if (Queue == Control) {
FRS_CLOSE(Queue->Event);
FRS_CLOSE(Queue->RunDown);
}
//
// Zero the memory in order to cause grief for those who
// use a deleted queue.
//
ZeroMemory(Queue, sizeof(FRS_QUEUE));
}
VOID
FrsRtlRunDownQueue(
IN PFRS_QUEUE Queue,
OUT PLIST_ENTRY ListHead
)
/*++
Routine Description:
Runs down a queue that is about to be destroyed. Any threads currently
waiting on the queue are unwaited (FrsRtlRemoveHeadQueue will return NULL)
and the contents of the queue (if any) are returned to the caller for
cleanup.
Arguments:
Queue - supplies the queue to be rundown
ListHead - returns the list of items currently in the queue.
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsRtlRunDownQueue:"
PFRS_QUEUE Control = Queue->Control;
PLIST_ENTRY Entry;
PLIST_ENTRY First;
PLIST_ENTRY Last;
EnterCriticalSection(&Control->Lock);
//
// Running down a controlling queue is not allowed unless they
// are the same queue.
//
if (Control == Queue) {
FRS_ASSERT(IsListEmpty(&Control->Full) &&
IsListEmpty(&Control->Empty) &&
IsListEmpty(&Control->Idled));
} else {
FRS_ASSERT(!IsListEmpty(&Control->Full) ||
!IsListEmpty(&Control->Empty) ||
!IsListEmpty(&Control->Idled) ||
Control->IsRunDown);
}
/*
FRS_ASSERT((Control == Queue &&
IsListEmpty(&Control->Full) &&
IsListEmpty(&Control->Empty) &&
IsListEmpty(&Control->Idled)
)
||
(Control != Queue &&
(!IsListEmpty(&Control->Full) ||
!IsListEmpty(&Control->Empty) ||
!IsListEmpty(&Control->Idled) ||
Control->IsRunDown
)
)
)
*/
FRS_ASSERT(DbgCheckQueue(Queue));
Queue->IsRunDown = TRUE;
//
// return the list of entries
//
if (IsListEmpty(&Queue->ListHead)) {
InitializeListHead(ListHead);
} else {
*ListHead = Queue->ListHead;
ListHead->Flink->Blink = ListHead;
ListHead->Blink->Flink = ListHead;
}
InitializeListHead(&Queue->ListHead);
//
// Don't update counters if the queue is idled
//
if (!Queue->IsIdled) {
Control->ControlCount -= Queue->Count;
if (Control->ControlCount == 0) {
ResetEvent(Control->Event);
}
}
Queue->Count = 0;
RemoveEntryListB(&Queue->Full);
RemoveEntryListB(&Queue->Empty);
RemoveEntryListB(&Queue->Idled);
FRS_ASSERT(DbgCheckQueue(Queue));
//
// Set the aborted event to awaken any threads currently
// blocked on the queue if the controlling queue has no
// more queues.
//
DPRINT2(4, "Rundown for queue - %08x, Control - %08x\n", Queue, Control);
DPRINT1(4, "Control->Full queue %s empty.\n",
IsListEmpty(&Control->Full) ? "is" : "is not");
DPRINT1(4, "Control->Empty queue %s empty.\n",
IsListEmpty(&Control->Empty) ? "is" : "is not");
DPRINT1(4, "Control->Idled queue %s empty.\n",
IsListEmpty(&Control->Idled) ? "is" : "is not");
if (IsListEmpty(&Control->Full) &&
IsListEmpty(&Control->Empty) &&
IsListEmpty(&Control->Idled)) {
Control->IsRunDown = TRUE;
SetEvent(Control->RunDown);
DPRINT(4, "Setting Control->RunDown event.\n");
}
FRS_ASSERT(DbgCheckQueue(Control));
LeaveCriticalSection(&Control->Lock);
}
VOID
FrsRtlCancelQueue(
IN PFRS_QUEUE Queue,
OUT PLIST_ENTRY ListHead
)
/*++
Routine Description:
Returns the entries on Queue for cancelling.
Arguments:
Queue - supplies the queue to be rundown
ListHead - returns the list of items currently in the queue.
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsRtlCancelQueue:"
PFRS_QUEUE Control = Queue->Control;
PLIST_ENTRY Entry;
PLIST_ENTRY First;
PLIST_ENTRY Last;
EnterCriticalSection(&Control->Lock);
FRS_ASSERT(DbgCheckQueue(Queue));
//
// return the list of entries
//
if (IsListEmpty(&Queue->ListHead)) {
InitializeListHead(ListHead);
} else {
*ListHead = Queue->ListHead;
ListHead->Flink->Blink = ListHead;
ListHead->Blink->Flink = ListHead;
}
InitializeListHead(&Queue->ListHead);
//
// Don't update counters if the queue is idled
//
if (!Queue->IsIdled) {
Control->ControlCount -= Queue->Count;
if (Control->ControlCount == 0) {
ResetEvent(Control->Event);
}
}
Queue->Count = 0;
RemoveEntryListB(&Queue->Full);
RemoveEntryListB(&Queue->Empty);
RemoveEntryListB(&Queue->Idled);
FRS_ASSERT(DbgCheckQueue(Queue));
FRS_ASSERT(DbgCheckQueue(Control));
LeaveCriticalSection(&Control->Lock);
}
VOID
FrsRtlIdleQueue(
IN PFRS_QUEUE Queue
)
{
#undef DEBSUB
#define DEBSUB "FrsRtlIdleQueue:"
/*++
Routine Description:
Idle a queue
Arguments:
Queue - queue to idle
Return Value:
None.
--*/
PFRS_QUEUE Control = Queue->Control;
//
// Queues that don't have a separate controlling queue can't
// support "idling" themselves
//
if (Control == Queue) {
return;
}
//
// Lock the controlling queue
//
EnterCriticalSection(&Control->Lock);
FrsRtlIdleQueueLock(Queue);
LeaveCriticalSection(&Control->Lock);
}
VOID
FrsRtlIdleQueueLock(
IN PFRS_QUEUE Queue
)
{
#undef DEBSUB
#define DEBSUB "FrsRtlIdleQueueLock:"
/*++
Routine Description:
Idle a queue. Caller has the lock already.
Arguments:
Queue - queue to idle
Return Value:
None.
--*/
PFRS_QUEUE Control = Queue->Control;
//
// Queues that don't have a separate controlling queue can't
// support "idling" themselves
//
if (Control == Queue) {
return;
}
PRINT_QUEUE(5, Queue);
FRS_ASSERT(DbgCheckQueue(Queue));
//
// Stop, this queue has been aborted (rundown)
//
if (Queue->IsRunDown || Queue->IsIdled) {
goto out;
}
if (Queue->Count == 0) {
RemoveEntryListB(&Queue->Empty);
} else {
RemoveEntryListB(&Queue->Full);
}
FRS_ASSERT(IsListEmpty(&Queue->Idled));
InsertTailList(&Control->Idled, &Queue->Idled);
Queue->IsIdled = TRUE;
Control->ControlCount -= Queue->Count;
FRS_ASSERT(DbgCheckQueue(Queue));
if (Control->ControlCount == 0) {
ResetEvent(Control->Event);
}
out:
//
// Done
//
FRS_ASSERT(DbgCheckQueue(Queue));
}
VOID
FrsRtlUnIdledQueue(
IN PFRS_QUEUE IdledQueue
)
{
#undef DEBSUB
#define DEBSUB "FrsRtlUnIdledQueue:"
/*++
Routine Description:
Removes the queue from the "idled" list and puts it back on the
full or empty lists. The controlling queue is updated accordingly.
Arguments:
IdledQueue - Supplies the queue to remove an item from.
Return Value:
None.
--*/
DWORD OldControlCount;
PFRS_QUEUE Control = IdledQueue->Control;
//
// Queues that don't have a separate controlling queue can't
// support "idling" themselves
//
if (Control == IdledQueue) {
return;
}
//
// Lock the controlling queue
//
EnterCriticalSection(&Control->Lock);
FrsRtlUnIdledQueueLock(IdledQueue);
LeaveCriticalSection(&Control->Lock);
}
VOID
FrsRtlUnIdledQueueLock(
IN PFRS_QUEUE IdledQueue
)
{
#undef DEBSUB
#define DEBSUB "FrsRtlUnIdledQueueLock:"
/*++
Routine Description:
Removes the queue from the "idled" list and puts it back on the
full or empty lists. The controlling queue is updated accordingly.
Caller has lock on controlling queue.
Arguments:
IdledQueue - Supplies the queue to remove an item from.
Return Value:
None.
--*/
DWORD OldControlCount;
PFRS_QUEUE Control = IdledQueue->Control;
//
// Queues that don't have a separate controlling queue can't
// support "idling" themselves
//
if (Control == IdledQueue) {
return;
}
PRINT_QUEUE(5, IdledQueue);
FRS_ASSERT(DbgCheckQueue(IdledQueue));
//
// Stop, this queue has been aborted (rundown)
//
if (IdledQueue->IsRunDown) {
goto out;
}
//
// Remove from idled list
//
FRS_ASSERT(IdledQueue->IsIdled);
RemoveEntryListB(&IdledQueue->Idled);
IdledQueue->IsIdled = FALSE;
//
// Put onto full or empty list
//
if (IdledQueue->Count) {
InsertTailList(&Control->Full, &IdledQueue->Full);
} else {
InsertTailList(&Control->Empty, &IdledQueue->Empty);
}
//
// Wakeup sleepers if count is now > 0
//
OldControlCount = Control->ControlCount;
Control->ControlCount += IdledQueue->Count;
if (Control->ControlCount && OldControlCount == 0) {
SetEvent(Control->Event);
}
//
// Done
//
out:
FRS_ASSERT(DbgCheckQueue(IdledQueue));
}
PLIST_ENTRY
FrsRtlRemoveHeadQueueTimeoutIdled(
IN PFRS_QUEUE Queue,
IN DWORD dwMilliseconds,
OUT PFRS_QUEUE *IdledQueue
)
/*++
Routine Description:
Removes the item at the head of the queue. If the queue is empty,
blocks until an item is inserted into the queue.
Arguments:
Queue - Supplies the queue to remove an item from.
Timeout - Supplies a timeout value that specifies the relative
time, in milliseconds, over which the wait is to be completed.
IdledQueue - If non-NULL then on return this will be the address
of the queue from which the entry was retrieved. Or NULL if
the returned entry is NULL. No other thread will be allowed
to pull an entry from the returned queue until that queue is
released with FrsRtlUnIdledQueue(*IdledQueue).
Return Value:
Pointer to list entry removed from the head of the queue.
NULL if the wait times out or the queue is run down. If this
routine returns NULL, GetLastError will return either
ERROR_INVALID_HANDLE (if the queue has been rundown) or
WAIT_TIMEOUT (to indicate a timeout has occurred)
IdledQueue - If non-NULL then on return this will be the address
of the queue from which the entry was retrieved. Or NULL if
the returned entry is NULL. No other thread will be allowed
to pull an entry from the returned queue until that queue is
released with FrsRtlUnIdledQueue(*IdledQueue).
--*/
{
#undef DEBSUB
#define DEBSUB "FrsRtlRemoveHeadQueueTimeoutIdled:"
DWORD Status;
PLIST_ENTRY Entry;
HANDLE WaitArray[2];
PFRS_QUEUE Control = Queue->Control;
//
// No idled queue at this time
//
if (IdledQueue) {
*IdledQueue = NULL;
}
Retry:
if (Control->ControlCount == 0) {
//
// Block until something is inserted on the queue
//
WaitArray[0] = Control->RunDown;
WaitArray[1] = Control->Event;
Status = WaitForMultipleObjects(2, WaitArray, FALSE, dwMilliseconds);
if (Status == 0) {
//
// The queue has been rundown, return NULL immediately.
//
SetLastError(ERROR_INVALID_HANDLE);
return(NULL);
} else if (Status == WAIT_TIMEOUT) {
SetLastError(WAIT_TIMEOUT);
return(NULL);
}
FRS_ASSERT(Status == 1);
}
//
// Lock the queue and try to remove something
//
EnterCriticalSection(&Control->Lock);
PRINT_QUEUE(5, Queue);
if (Control->ControlCount == 0) {
//
// Somebody got here before we did, drop the lock and retry
//
LeaveCriticalSection(&Control->Lock);
goto Retry;
}
FRS_ASSERT(DbgCheckQueue(Queue));
Entry = GetListNext(&Control->Full);
RemoveEntryListB(Entry);
Queue = CONTAINING_RECORD(Entry, FRS_QUEUE, Full);
Entry = RemoveHeadList(&Queue->ListHead);
//
// update counters
//
--Queue->Count;
--Control->ControlCount;
//
// A separate controlling queue is required for idling
//
if (IdledQueue && Queue != Control) {
//
// Idle the queue
//
FRS_ASSERT(IsListEmpty(&Queue->Idled));
FRS_ASSERT(!Queue->IsIdled);
InsertTailList(&Control->Idled, &Queue->Idled);
Queue->IsIdled = TRUE;
Control->ControlCount -= Queue->Count;
*IdledQueue = Queue;
} else if (Queue->Count) {
//
// Queue still has entries
//
InsertTailList(&Control->Full, &Queue->Full);
} else {
//
// Queue is empty
//
InsertTailList(&Control->Empty, &Queue->Empty);
}
//
// Queues are empty (or idled)
//
if (Control->ControlCount == 0) {
ResetEvent(Control->Event);
}
PRINT_QUEUE(5, Queue);
FRS_ASSERT(DbgCheckQueue(Queue));
LeaveCriticalSection(&Control->Lock);
return(Entry);
}
PLIST_ENTRY
FrsRtlRemoveHeadQueue(
IN PFRS_QUEUE Queue
)
/*++
Routine Description:
Removes the item at the head of the queue. If the queue is empty,
blocks until an item is inserted into the queue.
Arguments:
Queue - Supplies the queue to remove an item from.
Return Value:
Pointer to list entry removed from the head of the queue.
--*/
{
return(FrsRtlRemoveHeadQueueTimeoutIdled(Queue, INFINITE, NULL));
}
PLIST_ENTRY
FrsRtlRemoveHeadQueueTimeout(
IN PFRS_QUEUE Queue,
IN DWORD dwMilliseconds
)
/*++
Routine Description:
Removes the item at the head of the queue. If the queue is empty,
blocks until an item is inserted into the queue.
Arguments:
Queue - Supplies the queue to remove an item from.
Timeout - Supplies a timeout value that specifies the relative
time, in milliseconds, over which the wait is to be completed.
Return Value:
Pointer to list entry removed from the head of the queue.
NULL if the wait times out or the queue is run down. If this
routine returns NULL, GetLastError will return either
ERROR_INVALID_HANDLE (if the queue has been rundown) or
WAIT_TIMEOUT (to indicate a timeout has occurred)
--*/
{
return(FrsRtlRemoveHeadQueueTimeoutIdled(Queue, dwMilliseconds, NULL));
}
VOID
FrsRtlRemoveEntryQueue(
IN PFRS_QUEUE Queue,
IN PLIST_ENTRY Entry
)
/*++
Routine Description:
Removes the entry from the queue. The entry is assumed to be on the
queue since we derement the queue count.
Arguments:
Queue - Supplies the queue to remove an item from.
Entry - pointer to the entry to remove.
Return Value:
none.
--*/
{
FrsRtlAcquireQueueLock(Queue);
FrsRtlRemoveEntryQueueLock(Queue, Entry);
FrsRtlReleaseQueueLock(Queue);
}
VOID
FrsRtlRemoveEntryQueueLock(
IN PFRS_QUEUE Queue,
IN PLIST_ENTRY Entry
)
/*++
Routine Description:
Removes the entry from the queue. The entry is assumed to be on the
queue since we derement the queue count. We also assume the caller
has acquired the queue lock since this was needed to scan the queue
in the first place to find the entry in question.
The LOCK suffix means the caller has already acquired the lock.
Arguments:
Queue - Supplies the queue to remove an item from.
Entry - pointer to the entry to remove.
Return Value:
none.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsRtlRemoveEntryQueueLock:"
PFRS_QUEUE Control = Queue->Control;
FRS_ASSERT(Queue->Count != 0);
FRS_ASSERT(!IsListEmpty(&Queue->ListHead));
PRINT_QUEUE(5, Queue);
FRS_ASSERT(DbgCheckQueue(Queue));
RemoveEntryListB(Entry);
//
// If the queue is idled then just update the count
//
--Queue->Count;
if (!Queue->IsIdled) {
//
// Queue is empty; remove from full list
//
if (Queue->Count == 0) {
RemoveEntryListB(&Queue->Full);
InsertTailList(&Control->Empty, &Queue->Empty);
}
//
// Control queue is empty
//
if (--Control->ControlCount == 0) {
ResetEvent(Control->Event);
}
}
PRINT_QUEUE(5, Queue);
FRS_ASSERT(DbgCheckQueue(Queue));
return;
}
DWORD
FrsRtlInsertTailQueue(
IN PFRS_QUEUE Queue,
IN PLIST_ENTRY Item
)
/*++
Routine Description:
Inserts a new entry on the tail of the queue.
Arguments:
Queue - Supplies the queue to add the entry to.
Item - Supplies the entry to be added to the queue.
Return Value:
ERROR_SUCCESS and the item is queueed. Otherwise, the item
is not queued.
--*/
{
DWORD Status;
FrsRtlAcquireQueueLock(Queue);
Status = FrsRtlInsertTailQueueLock(Queue, Item);
FrsRtlReleaseQueueLock(Queue);
return Status;
}
DWORD
FrsRtlInsertTailQueueLock(
IN PFRS_QUEUE Queue,
IN PLIST_ENTRY Item
)
/*++
Routine Description:
Inserts a new entry on the tail of the queue. Caller already has the
queue lock.
Arguments:
Queue - Supplies the queue to add the entry to.
Item - Supplies the entry to be added to the queue.
Return Value:
ERROR_SUCCESS and the item is queueed. Otherwise, the item
is not queued.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsRtlInsertTailQueueLock:"
PFRS_QUEUE Control = Queue->Control;
PRINT_QUEUE(5, Queue);
FRS_ASSERT(DbgCheckQueue(Queue));
if (Queue->IsRunDown) {
return ERROR_ACCESS_DENIED;
}
InsertTailList(&Queue->ListHead, Item);
//
// If the queue is idled then just update the count
//
if (Queue->IsIdled) {
++Queue->Count;
} else {
//
// Queue is transitioning from empty to full
//
if (++Queue->Count == 1) {
RemoveEntryListB(&Queue->Empty);
InsertTailList(&Control->Full, &Queue->Full);
}
//
// Controlling queue is transitioning from empty to full
//
if (++Control->ControlCount == 1) {
SetEvent(Control->Event);
}
}
PRINT_QUEUE(5, Queue);
FRS_ASSERT(DbgCheckQueue(Queue));
return ERROR_SUCCESS;
}
DWORD
FrsRtlInsertHeadQueue(
IN PFRS_QUEUE Queue,
IN PLIST_ENTRY Item
)
/*++
Routine Description:
Inserts a new entry on the tail of the queue.
Arguments:
Queue - Supplies the queue to add the entry to.
Item - Supplies the entry to be added to the queue.
Return Value:
ERROR_SUCCESS and the item is queueed. Otherwise, the item
is not queued.
--*/
{
DWORD Status;
FrsRtlAcquireQueueLock(Queue);
Status = FrsRtlInsertHeadQueueLock(Queue, Item);
FrsRtlReleaseQueueLock(Queue);
return Status;
}
DWORD
FrsRtlInsertHeadQueueLock(
IN PFRS_QUEUE Queue,
IN PLIST_ENTRY Item
)
/*++
Routine Description:
Inserts a new entry on the head of the queue.
Caller already has the queue lock.
Arguments:
Queue - Supplies the queue to add the entry to.
Item - Supplies the entry to be added to the queue.
Return Value:
ERROR_SUCCESS and the item is queueed. Otherwise, the item
is not queued.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsRtlInsertHeadQueueLock:"
PFRS_QUEUE Control = Queue->Control;
PRINT_QUEUE(5, Queue);
FRS_ASSERT(DbgCheckQueue(Queue));
if (Queue->IsRunDown) {
return ERROR_ACCESS_DENIED;
}
InsertHeadList(&Queue->ListHead, Item);
//
// If the queue is idled then just update the count
//
if (Queue->IsIdled) {
++Queue->Count;
} else {
//
// Queue is transitioning from empty to full
//
if (++Queue->Count == 1) {
RemoveEntryListB(&Queue->Empty);
InsertTailList(&Control->Full, &Queue->Full);
}
//
// Controlling queue is transitioning from empty to full
//
if (++Control->ControlCount == 1) {
SetEvent(Control->Event);
}
}
PRINT_QUEUE(5, Queue);
FRS_ASSERT(DbgCheckQueue(Queue));
return ERROR_SUCCESS;
}
DWORD
FrsRtlWaitForQueueFull(
IN PFRS_QUEUE Queue,
IN DWORD dwMilliseconds
)
/*++
Routine Description:
Waits until the queue is non-empty. Returns immediately if queue is
non-empty else wait on insert or timeout.
Arguments:
Queue - Supplies the queue to wait on.
Timeout - Supplies a timeout value that specifies the relative
time, in milliseconds, over which the wait is to be completed.
Return Value:
Win32 Status:
ERROR_SUCCESS if queue is now non-empty.
ERROR_INVALID_HANDLE if the queue has been rundown.
WAIT_TIMEOUT to indicate a timeout has occurred.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsRtlWaitForQueueFull:"
DWORD Status;
HANDLE WaitArray[2];
PFRS_QUEUE Control = Queue->Control;
Retry:
if (Control->ControlCount == 0) {
//
// Block until something is inserted on the queue
//
WaitArray[0] = Control->RunDown;
WaitArray[1] = Control->Event;
Status = WaitForMultipleObjects(2, WaitArray, FALSE, dwMilliseconds);
if (Status == 0) {
//
// The queue has been rundown, return immediately.
//
return(ERROR_INVALID_HANDLE);
}
if (Status == WAIT_TIMEOUT) {
return(WAIT_TIMEOUT);
}
FRS_ASSERT(Status == 1);
}
//
// Lock the queue and check again.
//
EnterCriticalSection(&Control->Lock);
if (Control->ControlCount == 0) {
//
// Somebody got here before we did, drop the lock and retry
//
LeaveCriticalSection(&Control->Lock);
goto Retry;
}
LeaveCriticalSection(&Control->Lock);
return(ERROR_SUCCESS);
}
VOID
FrsSubmitCommand(
IN PCOMMAND_PACKET CmdPkt,
IN BOOL Headwise
)
/*++
Routine Description:
Insert the command packet on the command's target queue.
If the time delay parameter is non-zero the command is instead
queued to the scheduler thread to initiate at the specified time.
FrsCompleteCommand(Status) is called if the packet could not be
queued.
Arguments:
CmdPkt
Headwise - Queue at the head (high priority)
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsSubmitCommand:"
DWORD WStatus;
//
// Queue to the target
//
if (Headwise) {
WStatus = FrsRtlInsertHeadQueue(CmdPkt->TargetQueue, &CmdPkt->ListEntry);
} else {
WStatus = FrsRtlInsertTailQueue(CmdPkt->TargetQueue, &CmdPkt->ListEntry);
}
if (!WIN_SUCCESS(WStatus)) {
FrsCompleteCommand(CmdPkt, WStatus);
}
}
ULONG
FrsSubmitCommandAndWait(
IN PCOMMAND_PACKET Cmd,
IN BOOL Headwise,
IN ULONG Timeout
)
/*++
Routine Description:
Create or Reset the event, Submit the command and wait for the return.
Arguments:
Cmd - command packet to queue
Timeout - Wait Timeout
Headwise - if True, insert to head.
Return Value:
Win32 status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsSubmitCommandAndWait:"
DWORD WStatus;
//
// Set the synchronous flag in the command packet.
//
FrsSetCommandSynchronous(Cmd);
if (!HANDLE_IS_VALID(Cmd->WaitEvent)){
Cmd->WaitEvent = FrsCreateEvent(TRUE, FALSE);
} else {
ResetEvent(Cmd->WaitEvent);
}
//
// Save the callers completion routine and replace it with a function
// that signals the event. It does not delete the packet so we can
// return the command status to the caller.
//
Cmd->SavedCompletionRoutine = Cmd->CompletionRoutine;
Cmd->CompletionRoutine = FrsCompleteSynchronousCmdPkt;
//
// Queue the command and create a thread if needed.
//
FrsSubmitCommand(Cmd, Headwise);
//
// Wait for the command to complete.
//
WStatus = WaitForSingleObject(Cmd->WaitEvent, Timeout);
CHECK_WAIT_ERRORS(3, WStatus, 1, ACTION_RETURN);
//
// Return the command error status.
//
WStatus = Cmd->ErrorStatus;
//
// Restore and call the caller's completion routine. This may free the
// the packet. We don't call FrsCompleteCommand() here because it was
// already called when the server finished the packet and there is no
// point in setting the wait event twice.
//
Cmd->CompletionRoutine = Cmd->SavedCompletionRoutine;
FRS_ASSERT(Cmd->CompletionRoutine != NULL);
(Cmd->CompletionRoutine)(Cmd, Cmd->CompletionArg);
return WStatus;
}
#define HEADWISE TRUE
VOID
FrsUnSubmitCommand(
IN PCOMMAND_PACKET Cmd
)
/*++
Routine Description:
Put the entry back on the head of the queue.
Arguments:
Cmd
Return Value:
None.
--*/
{
FrsSubmitCommand(Cmd, HEADWISE);
}
VOID
FrsCompleteCommand(
IN PCOMMAND_PACKET CmdPkt,
IN DWORD ErrorStatus
)
/*++
Routine Description:
Retire the command packet based on what the original requestor specified
in the packet. The ErrorStatus is returned in the packet.
The completion routine is called for clean up and propagation.
Arguments:
CmdPkt -- A ptr to the command packet.
ErrorStatus -- Status to store in returned command packet.
Return Value:
None.
--*/
{
//
// Set the error status and call the completion routine
//
CmdPkt->ErrorStatus = ErrorStatus;
FRS_ASSERT(CmdPkt->CompletionRoutine != NULL);
(CmdPkt->CompletionRoutine)(CmdPkt, CmdPkt->CompletionArg);
}
VOID
FrsInitializeCommandServer(
IN PCOMMAND_SERVER Cs,
IN DWORD MaxThreads,
IN PWCHAR Name,
IN DWORD (*Main)(PVOID)
)
/*++
Routine Description:
Initialize a command server
Arguments:
Cs - command server
MaxThreads - Max # of threads to kick off
Name - Printable name for thread
Main - Thread starts here
Return Value:
None.
--*/
{
ZeroMemory(Cs, sizeof(COMMAND_SERVER));
FrsInitializeQueue(&Cs->Control, &Cs->Control);
FrsInitializeQueue(&Cs->Queue, &Cs->Control);
Cs->Main = Main;
Cs->Name = Name;
Cs->MaxThreads = MaxThreads;
Cs->Idle = FrsCreateEvent(TRUE, TRUE);
}
VOID
FrsDeleteCommandServer(
IN PCOMMAND_SERVER Cs
)
/*++
Routine Description:
Undo the work of FrsInitializeCommandServer(). This function
assumes the queue and its control queue are inactive (whatever
that means). Queues and command servers are normally only
deleted at the very end of MainFrsShutDown() when all other threads
have exited and the RPC servers aren't listening for new requests.
The caller is responsible for handling all of the other queues
that may be being controlled by the control queue in the command
server struct, Cs.
Arguments:
Cs - command server
Return Value:
None.
--*/
{
if (Cs) {
FrsRtlDeleteQueue(&Cs->Queue);
FrsRtlDeleteQueue(&Cs->Control);
ZeroMemory(Cs, sizeof(COMMAND_SERVER));
}
}
PCOMMAND_PACKET
FrsAllocCommand(
IN PFRS_QUEUE TargetQueue,
IN USHORT Command
)
/*++
Routine Description:
Allocate a command packet and initialize the most common fields.
Arguments:
TargetQueue
Command
Return Value:
Address of allocated, initialized COMMAND_PACKET. Call
FrsCompleteCommand() when done.
--*/
{
PCOMMAND_PACKET Cmd;
Cmd = FrsAllocType(COMMAND_PACKET_TYPE);
Cmd->TargetQueue = TargetQueue;
FrsSetCompletionRoutine(Cmd, FrsFreeCommand, NULL);
Cmd->Command = Command;
return Cmd;
}
PCOMMAND_PACKET
FrsAllocCommandEx(
IN PFRS_QUEUE TargetQueue,
IN USHORT Command,
IN ULONG Size
)
/*++
Routine Description:
Allocate a command packet with some extra space
and initialize the most common fields.
Arguments:
TargetQueue
Command
Return Value:
Address of allocated, initialized COMMAND_PACKET. Call
FrsCompleteCommand() when done.
--*/
{
PCOMMAND_PACKET Cmd;
Cmd = FrsAllocTypeSize(COMMAND_PACKET_TYPE, Size);
Cmd->TargetQueue = TargetQueue;
Cmd->CompletionRoutine = FrsFreeCommand;
Cmd->Command = Command;
return Cmd;
}
VOID
FrsFreeCommand(
IN PCOMMAND_PACKET Cmd,
IN PVOID CompletionArg
)
/*++
Routine Description:
Free a command packet
Arguments:
Cmd - command packet allocated with FrsAllocCommand().
Return Value:
NULL
--*/
{
ULONG WStatus;
if (((Cmd->Flags & CMD_PKT_FLAGS_SYNC) != 0) &&
(HANDLE_IS_VALID(Cmd->WaitEvent))){
//
// Close the event handle. The command complete function should have
// already set the event.
//
if (!CloseHandle(Cmd->WaitEvent)) {
WStatus = GetLastError();
DPRINT_WS(0, "ERROR: Close event handle failed", WStatus);
// Don't free the packet if the close handle failed.
return;
}
Cmd->WaitEvent = NULL;
}
FrsFreeType(Cmd);
}
VOID
FrsExitCommandServer(
IN PCOMMAND_SERVER Cs,
IN PFRS_THREAD FrsThread
)
/*++
Routine Description:
Exit the calling command server thread.
Arguments:
Cs - command server
Thread - calling thread
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsExitCommandServer:"
PFRS_QUEUE Queue = &Cs->Queue;
//
// If there is work to be done
//
FrsRtlAcquireQueueLock(Queue);
--Cs->FrsThreads;
if (FrsRtlCountQueue(Queue) && Cs->Waiters == 0 && Cs->FrsThreads == 0) {
//
// and no one to do it; don't exit
//
++Cs->FrsThreads;
FrsRtlReleaseQueueLock(Queue);
return;
}
//
// Set the idle event if all threads are waiting, there are no entries
// on the queue, and there are no idled queues
//
if (Cs->Waiters == Cs->FrsThreads) {
if (FrsRtlCountQueue(&Cs->Queue) == 0) {
if (FrsRtlNoIdledQueues(&Cs->Queue)) {
SetEvent(Cs->Idle);
}
}
}
FrsRtlReleaseQueueLock(Queue);
//
// The thread command server (ThQs) will "wait" on this thread's exit
// and drop the reference on its thread struct.
//
ThSupSubmitThreadExitCleanup(FrsThread);
//
// Exit
//
ExitThread(ERROR_SUCCESS);
}
#define TAILWISE FALSE
VOID
FrsSubmitCommandServer(
IN PCOMMAND_SERVER Cs,
IN PCOMMAND_PACKET Cmd
)
/*++
Routine Description:
If needed, create a thread for the command queue
Arguments:
Cs - command server
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsSubmitCommandServer:"
//
// Enqueue the command and make sure there are threads running
//
FRS_ASSERT(Cmd && Cmd->TargetQueue && Cs &&
Cmd->TargetQueue->Control == &Cs->Control);
FrsSubmitCommand(Cmd, TAILWISE);
FrsKickCommandServer(Cs);
}
ULONG
FrsSubmitCommandServerAndWait(
IN PCOMMAND_SERVER Cs,
IN PCOMMAND_PACKET Cmd,
IN ULONG Timeout
)
/*++
Routine Description:
Create or Reset the event, Submit the command and wait for the return.
If needed, create a thread for the command queue.
Arguments:
Cs - command server
Cmd - command packet to queue
Timeout - Wait Timeout
Return Value:
Win32 status
--*/
{
#undef DEBSUB
#define DEBSUB "FrsSubmitCommandServerAndWait:"
DWORD WStatus;
//
// Enqueue the command and make sure there are threads running
//
FRS_ASSERT(Cmd && Cmd->TargetQueue && Cs &&
Cmd->TargetQueue->Control == &Cs->Control);
//
// Set the synchronous flag in the command packet.
//
FrsSetCommandSynchronous(Cmd);
if (!HANDLE_IS_VALID(Cmd->WaitEvent)){
Cmd->WaitEvent = FrsCreateEvent(TRUE, FALSE);
} else {
ResetEvent(Cmd->WaitEvent);
}
//
// Save the callers completion routine and replace it with a function
// that signals the event. It does not delete the packet so we can
// return the command status to the caller.
//
Cmd->SavedCompletionRoutine = Cmd->CompletionRoutine;
Cmd->CompletionRoutine = FrsCompleteSynchronousCmdPkt;
//
// Queue the command and create a thread if needed.
//
FrsSubmitCommand(Cmd, TAILWISE);
FrsKickCommandServer(Cs);
//
// Wait for the command to complete.
//
WStatus = WaitForSingleObject(Cmd->WaitEvent, Timeout);
CHECK_WAIT_ERRORS(3, WStatus, 1, ACTION_RETURN);
//
// Return the command error status.
//
WStatus = Cmd->ErrorStatus;
//
// Restore and call the caller's completion routine. This may free the
// the packet. We don't call FrsCompleteCommand() here because it was
// already called when the server finished the packet and there is no
// point in setting the wait event twice.
//
Cmd->CompletionRoutine = Cmd->SavedCompletionRoutine;
FRS_ASSERT(Cmd->CompletionRoutine != NULL);
(Cmd->CompletionRoutine)(Cmd, Cmd->CompletionArg);
return WStatus;
}
#define THREAD_CREATE_RETRY (10 * 1000) // 10 seconds
VOID
FrsKickCommandServer(
IN PCOMMAND_SERVER Cs
)
/*++
Routine Description:
If needed, create a thread for the command queue
Arguments:
Cs - command server
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsKickCommandServer:"
PFRS_QUEUE Queue = &Cs->Queue;
//
// Kick off more threads if no one is waiting for this command
// and the number of threads serving this command queue is less
// than the maximum.
//
// If the thread cannot be created and there are no threads
// processing the command queue then put this command on the
// delayed queue and try again later
//
FrsRtlAcquireQueueLock(Queue);
//
// There are entries on the queue
//
if (FrsRtlCountQueue(Queue)) {
//
// But there are no threads to process the entries
//
if (Cs->Waiters == 0 && Cs->FrsThreads < Cs->MaxThreads) {
//
// First thread; reset idle
//
if (Cs->FrsThreads == 0) {
ResetEvent(Cs->Idle);
}
if (ThSupCreateThread(Cs->Name, Cs, Cs->Main, ThSupExitThreadNOP)) {
//
// Created a new thread
//
++Cs->FrsThreads;
} else if (Cs->FrsThreads == 0) {
//
// Thread could not be created and there are no other
// threads to process this entry. Put it on the delayed
// queue and try again in a few seconds.
//
FrsDelCsSubmitKick(Cs, &Cs->Queue, THREAD_CREATE_RETRY);
}
}
}
FrsRtlReleaseQueueLock(Queue);
}
PCOMMAND_PACKET
FrsGetCommandIdled(
IN PFRS_QUEUE Queue,
IN DWORD MilliSeconds,
IN PFRS_QUEUE *IdledQueue
)
/*++
Routine Description:
Get the next command from the queue; idling the queue if requested.
Arguments:
Queue
MilliSeconds
IdledQueue
Return Value:
COMMAND_PACKET or NULL.
If non-NULL, IdledQueue is set
--*/
{
PLIST_ENTRY Entry;
Entry = FrsRtlRemoveHeadQueueTimeoutIdled(Queue, MilliSeconds, IdledQueue);
if (Entry == NULL) {
return NULL;
}
//
// Return the command packet
//
return CONTAINING_RECORD(Entry, COMMAND_PACKET, ListEntry);
}
PCOMMAND_PACKET
FrsGetCommand(
IN PFRS_QUEUE Queue,
IN DWORD MilliSeconds
)
/*++
Routine Description:
Get the next command from the queue.
Arguments:
Queue
MilliSeconds
Return Value:
COMMAND_PACKET or NULL.
--*/
{
return FrsGetCommandIdled(Queue, MilliSeconds, NULL);
}
PCOMMAND_PACKET
FrsGetCommandServerTimeoutIdled(
IN PCOMMAND_SERVER Cs,
IN ULONG Timeout,
OUT PFRS_QUEUE *IdledQueue,
OUT PBOOL IsRunDown
)
/*++
Routine Description:
Get the next command from the queue for the command server.
If nothing appears on the queue in the specified time, then
return NULL and set IsRunDown.
Arguments:
Cs - command server
Timeout
IdledQueue - Idled queue
IsRunDown
Return Value:
COMMAND_PACKET or NULL. If NULL, IsRunDown indicates whether
the NULL was caused by a rundown queue or a simple timeout.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsGetCommandServerTimeoutIdled:"
PCOMMAND_PACKET Cmd;
//
// Pull off the next entry (wait at most 5 minutes)
//
FrsRtlAcquireQueueLock(&Cs->Queue);
++Cs->Waiters;
//
// Set the idle event if all threads are waiting, there are no entries
// on the queue, and there are no idled queues
//
if (Cs->Waiters == Cs->FrsThreads) {
if (FrsRtlCountQueue(&Cs->Queue) == 0) {
if (FrsRtlNoIdledQueues(&Cs->Queue)) {
SetEvent(Cs->Idle);
}
}
}
FrsRtlReleaseQueueLock(&Cs->Queue);
//
// Get the next command
//
Cmd = FrsGetCommandIdled(&Cs->Control, Timeout, IdledQueue);
FrsRtlAcquireQueueLock(&Cs->Queue);
//
// Reset the Idle event if there is any chance it might have been set
//
if (Cs->Waiters == Cs->FrsThreads) {
ResetEvent(Cs->Idle);
}
--Cs->Waiters;
if (IsRunDown) {
*IsRunDown = Cs->Queue.IsRunDown;
}
FrsRtlReleaseQueueLock(&Cs->Queue);
return Cmd;
}
#define COMMAND_SERVER_TIMEOUT (5 * 60 * 1000) // 5 minutes
DWORD FrsCommandServerTimeout = COMMAND_SERVER_TIMEOUT;
PCOMMAND_PACKET
FrsGetCommandServerIdled(
IN PCOMMAND_SERVER Cs,
IN PFRS_QUEUE *IdledQueue
)
/*++
Routine Description:
Get the next command from the queue. If nothing appears on the queue
for 5 minutes, return NULL. The caller will exit. Idle the queue.
Arguments:
Cs - command server
IdledQueue - Idled queue
Return Value:
COMMAND_PACKET or NULL. Caller should exit on NULL.
If non-NULL, IdledQueue is set
--*/
{
return FrsGetCommandServerTimeoutIdled(Cs,
FrsCommandServerTimeout,
IdledQueue,
NULL);
}
PCOMMAND_PACKET
FrsGetCommandServerTimeout(
IN PCOMMAND_SERVER Cs,
IN ULONG Timeout,
OUT PBOOL IsRunDown
)
/*++
Routine Description:
Get the next command from the queue. If nothing appears on the queue
in the specified timeout, return NULL and an indication of the
queue's rundown status.
Arguments:
Cs - command server
Timeout
IsRunDown
Return Value:
COMMAND_PACKET or NULL. IsRunDown is only valid if COMMAND_PACKET
is NULL. Use IsRunDown to check if the NULL return is because of
a rundown'ed queue or simply a timeout.
--*/
{
return FrsGetCommandServerTimeoutIdled(Cs, Timeout, NULL, IsRunDown);
}
DWORD
FrsWaitForCommandServer(
IN PCOMMAND_SERVER Cs,
IN DWORD MilliSeconds
)
/*++
Routine Description:
Wait until all of the threads are idle, there are no entries on the
queue, and there are no idled queues.
Arguments:
Cs - command server
MilliSeconds - Timeout
Return Value:
Status from WaitForSingleObject()
--*/
{
return WaitForSingleObject(Cs->Idle, MilliSeconds);
}
PCOMMAND_PACKET
FrsGetCommandServer(
IN PCOMMAND_SERVER Cs
)
/*++
Routine Description:
Get the next command from the queue. If nothing appears on the queue
for 5 minutes, return NULL. The caller will exit.
Arguments:
Cs - command server
Return Value:
COMMAND_PACKET or NULL. Caller should exit on NULL.
--*/
{
//
// Pull off the next entry (wait at most 5 minutes)
//
return FrsGetCommandServerIdled(Cs, NULL);
}
VOID
FrsRunDownCommand(
IN PFRS_QUEUE Queue
)
/*++
Routine Description:
Rundown a queue of command packets
Arguments:
Queue - queue to rundown
Return Value:
None.
--*/
{
LIST_ENTRY RunDown;
PLIST_ENTRY Entry;
PCOMMAND_PACKET Cmd;
if (!Queue) {
return;
}
//
// RunDown the queue and retrieve the current entries
//
FrsRtlRunDownQueue(Queue, &RunDown);
//
// Free up the commands
//
while (!IsListEmpty(&RunDown)) {
Entry = RemoveHeadList(&RunDown);
Cmd = CONTAINING_RECORD(Entry, COMMAND_PACKET, ListEntry);
FrsCompleteCommand(Cmd, ERROR_ACCESS_DENIED);
}
}
VOID
FrsRunDownCommandServer(
IN PCOMMAND_SERVER Cs,
IN PFRS_QUEUE Queue
)
/*++
Routine Description:
Rundown a queue of a command server
Arguments:
Cs - command server
Queue - queue to abort
Return Value:
None.
--*/
{
FrsRunDownCommand(Queue);
}
VOID
FrsCancelCommandServer(
IN PCOMMAND_SERVER Cs,
IN PFRS_QUEUE Queue
)
/*++
Routine Description:
Cancels the current commands on Queue.
Arguments:
Cs - command server
Queue - queue to abort
Return Value:
None.
--*/
{
LIST_ENTRY Cancel;
PLIST_ENTRY Entry;
PCOMMAND_PACKET Cmd;
//
// RunDown the queue and retrieve the current entries
//
FrsRtlCancelQueue(Queue, &Cancel);
//
// Free up the commands
//
while (!IsListEmpty(&Cancel)) {
Entry = RemoveHeadList(&Cancel);
Cmd = CONTAINING_RECORD(Entry, COMMAND_PACKET, ListEntry);
FrsCompleteCommand(Cmd, ERROR_CANCELLED);
}
}
VOID
FrsCompleteRequestCount(
IN PCOMMAND_PACKET CmdPkt,
IN PFRS_REQUEST_COUNT RequestCount
)
/*++
Routine Description:
This is an Frs Command packet completion routine that takes
an FRS_REQUEST_COUNT struct. It decrements the count and signals
the event when the count goes to zero. The ErrorStatus is
merged into the Status field of the request count struct.
It then frees the command packet.
Arguments:
CmdPkt -- A ptr to the command packet.
RequestCount - Supplies a pointer to a RequestCount structure to initialize
Return Value:
None.
--*/
{
//
// Decrement count and signal waiter. merge error status from packet
// into RequestCount->Status.
//
FrsDecrementRequestCount(RequestCount, CmdPkt->ErrorStatus);
FrsSetCompletionRoutine(CmdPkt, FrsFreeCommand, NULL);
FrsCompleteCommand(CmdPkt, CmdPkt->ErrorStatus);
}
VOID
FrsCompleteRequestCountKeepPkt(
IN PCOMMAND_PACKET CmdPkt,
IN PFRS_REQUEST_COUNT RequestCount
)
/*++
Routine Description:
This is an Frs Command packet completion routine that takes
an FRS_REQUEST_COUNT struct. It decrements the count and signals
the event when the count goes to zero. The ErrorStatus is
merged into the Status field of the request count struct.
It does not free the command packet so the caller can retreive results
or reuse it.
Arguments:
CmdPkt -- A ptr to the command packet.
RequestCount - Supplies a pointer to a RequestCount structure to initialize
Return Value:
None.
--*/
{
// Decrement count and signal waiter. merge error status from packet
// into RequestCount->Status.
//
FrsDecrementRequestCount(RequestCount, CmdPkt->ErrorStatus);
}
VOID
FrsCompleteKeepPkt(
IN PCOMMAND_PACKET CmdPkt,
IN PVOID CompletionArg
)
/*++
Routine Description:
This is an Frs Command packet completion routine that
leaves the CmdPkt alone so the caller can reuse it.
Arguments:
CmdPkt -- A ptr to the command packet.
CompletionArg - Unused.
Return Value:
None.
--*/
{
return;
}
VOID
FrsCompleteSynchronousCmdPkt(
IN PCOMMAND_PACKET CmdPkt,
IN PVOID CompletionArg
)
/*++
Routine Description:
This is an Frs Command packet completion routine that
Signals the Wait Event for a synchronous cmd request.
It leaves the CmdPkt alone so the caller can reuse it.
Arguments:
CmdPkt -- A ptr to the command packet.
CompletionArg - Unused.
Return Value:
None.
--*/
{
FRS_ASSERT(HANDLE_IS_VALID(CmdPkt->WaitEvent));
SetEvent(CmdPkt->WaitEvent);
//
// A ctx switch to the waiter could occur at this point. The waiter could
// free the packet. So no further refs to the packet are allowed.
//
return;
}
VOID
FrsInitializeRequestCount(
IN PFRS_REQUEST_COUNT RequestCount
)
/*++
Routine Description:
Initializes a RequestCount for use.
Arguments:
RequestCount - Supplies a pointer to a RequestCount structure to initialize
Return Value:
ERROR_SUCCESS if successful
Win32 error code otherwise.
--*/
{
ULONG Status;
RequestCount->Count = 0;
RequestCount->Status = 0;
InitializeCriticalSection(&RequestCount->Lock);
RequestCount->Event = FrsCreateEvent(TRUE, FALSE);
}
VOID
FrsDeleteRequestCount(
IN PFRS_REQUEST_COUNT RequestCount
)
/*++
Routine Description:
Releases resources used by a RequestCount.
Arguments:
RequestCount - Supplies a pointer to a RequestCount structure to delete
Return Value:
None.
--*/
{
ULONG WStatus;
if (RequestCount != NULL) {
if (HANDLE_IS_VALID(RequestCount->Event)) {
if (!CloseHandle(RequestCount->Event)) {
WStatus = GetLastError();
DPRINT_WS(0, "ERROR: Close event handle failed", WStatus);
DeleteCriticalSection(&RequestCount->Lock);
return;
}
DeleteCriticalSection(&RequestCount->Lock);
}
//
// Zero memory to catch errors.
//
ZeroMemory(RequestCount, sizeof(FRS_REQUEST_COUNT));
}
}
ULONG
FrsWaitOnRequestCount(
IN PFRS_REQUEST_COUNT RequestCount,
IN ULONG Timeout
)
{
DWORD WStatus;
Retry:
if (RequestCount->Count > 0) {
WStatus = WaitForSingleObject(RequestCount->Event, Timeout);
CHECK_WAIT_ERRORS(3, WStatus, 1, ACTION_RETURN);
}
//
// Lock the queue and check again.
//
EnterCriticalSection(&RequestCount->Lock);
if (RequestCount->Count > 0) {
//
// Somebody got here before we did, drop the lock and retry
//
LeaveCriticalSection(&RequestCount->Lock);
goto Retry;
}
LeaveCriticalSection(&RequestCount->Lock);
return(ERROR_SUCCESS);
}
DWORD
FrsRtlInitializeList(
PFRS_LIST List
)
/*++
Routine Description:
Initializes an interlocked list for use.
Arguments:
List - Supplies a pointer to an FRS_LIST structure to initialize
Return Value:
ERROR_SUCCESS if successful
--*/
{
DWORD Status;
InitializeListHead(&List->ListHead);
InitializeCriticalSection(&List->Lock);
List->Count = 0;
List->ControlCount = 0;
List->Control = List;
return(ERROR_SUCCESS);
}
VOID
FrsRtlDeleteList(
PFRS_LIST List
)
/*++
Routine Description:
Releases all resources used by an interlocked list.
Arguments:
List - supplies the List to be deleted
Return Value:
None.
--*/
{
DeleteCriticalSection(&List->Lock);
//
// Zero the memory in order to cause grief to people who try
// and use a deleted list.
//
ZeroMemory(List, sizeof(FRS_LIST));
}
PLIST_ENTRY
FrsRtlRemoveHeadList(
IN PFRS_LIST List
)
/*++
Routine Description:
Removes the item at the head of the interlocked list.
Arguments:
List - Supplies the list to remove an item from.
Return Value:
Pointer to list entry removed from the head of the list.
NULL if the list is empty.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsRtlRemoveHeadList:"
PLIST_ENTRY Entry;
PFRS_LIST Control = List->Control;
if (List->ControlCount == 0) {
return NULL;
}
//
// Lock the list and try to remove something
//
EnterCriticalSection(&Control->Lock);
if (Control->ControlCount == 0) {
//
// Somebody got here before we did, drop the lock and return null
//
LeaveCriticalSection(&Control->Lock);
return NULL;
}
FRS_ASSERT(!IsListEmpty(&List->ListHead));
Entry = RemoveHeadList(&List->ListHead);
//
// Decrement count.
//
List->Count--;
Control->ControlCount--;
LeaveCriticalSection(&Control->Lock);
return(Entry);
}
VOID
FrsRtlInsertHeadList(
IN PFRS_LIST List,
IN PLIST_ENTRY Entry
)
/*++
Routine Description:
Inserts the item at the head of the interlocked list.
Arguments:
List - Supplies the list to insert the item on.
Entry - The entry to insert.
Return Value:
None.
--*/
{
PFRS_LIST Control = List->Control;
//
// Lock the list and insert at head.
//
EnterCriticalSection(&Control->Lock);
FrsRtlInsertHeadListLock(List, Entry);
LeaveCriticalSection(&Control->Lock);
return;
}
PLIST_ENTRY
FrsRtlRemoveTailList(
IN PFRS_LIST List
)
/*++
Routine Description:
Removes the item at the tail of the interlocked list.
Arguments:
List - Supplies the list to remove an item from.
Return Value:
Pointer to list entry removed from the tail of the list.
NULL if the list is empty.
--*/
{
#undef DEBSUB
#define DEBSUB "FrsRtlRemoveTailList:"
PLIST_ENTRY Entry;
PFRS_LIST Control = List->Control;
if (Control->ControlCount == 0) {
return NULL;
}
//
// Lock the list and try to remove something
//
EnterCriticalSection(&Control->Lock);
if (Control->ControlCount == 0) {
//
// Somebody got here before we did, drop the lock and return null
//
LeaveCriticalSection(&Control->Lock);
return NULL;
}
FRS_ASSERT(!IsListEmpty(&List->ListHead));
Entry = RemoveTailList(&List->ListHead);
//
// Decrement count.
//
List->Count--;
Control->ControlCount--;
LeaveCriticalSection(&Control->Lock);
return(Entry);
}
VOID
FrsRtlInsertTailList(
IN PFRS_LIST List,
IN PLIST_ENTRY Entry
)
/*++
Routine Description:
Inserts the item at the tail of the interlocked list.
Arguments:
List - Supplies the list to insert the item on.
Entry - The entry to insert.
Return Value:
None.
--*/
{
PFRS_LIST Control = List->Control;
//
// Lock the list and insert at tail.
//
EnterCriticalSection(&Control->Lock);
FrsRtlInsertTailListLock(List, Entry);
LeaveCriticalSection(&Control->Lock);
return;
}
VOID
FrsRtlRemoveEntryList(
IN PFRS_LIST List,
IN PLIST_ENTRY Entry
)
/*++
Routine Description:
Removes the entry from the interlocked list. The entry must be on the
given list since we use the lock in the FRS_LIST to synchronize access.
Arguments:
List - Supplies the list to remove an item from.
Entry - The entry to remove.
Return Value:
None.
--*/
{
PFRS_LIST Control = List->Control;
//
// Lock the list and try to remove entry
//
EnterCriticalSection(&Control->Lock);
FrsRtlRemoveEntryListLock(List, Entry);
LeaveCriticalSection(&Control->Lock);
return;
}
VOID
FrsRtlRemoveEntryListLock(
IN PFRS_LIST List,
IN PLIST_ENTRY Entry
)
/*++
Routine Description:
Removes the entry from the interlocked list. The entry must be on the
given list.
The caller already has the list lock.
Arguments:
List - Supplies the list to remove an item from.
Entry - The entry to remove.
Return Value:
None.
--*/
{
PFRS_LIST Control = List->Control;
//
// List better not be empty.
//
FRS_ASSERT(!IsListEmpty(&List->ListHead));
RemoveEntryListB(Entry);
//
// Decrement count.
//
List->Count--;
Control->ControlCount--;
return;
}
VOID
FrsRtlInsertHeadListLock(
IN PFRS_LIST List,
IN PLIST_ENTRY Entry
)
/*++
Routine Description:
Inserts the item at the head of the interlocked list.
The caller has acquired the lock.
Arguments:
List - Supplies the list to insert the item on.
Entry - The entry to insert.
Return Value:
None.
--*/
{
PFRS_LIST Control = List->Control;
InsertHeadList(&List->ListHead, Entry);
List->Count++;
Control->ControlCount++;
return;
}
VOID
FrsRtlInsertTailListLock(
IN PFRS_LIST List,
IN PLIST_ENTRY Entry
)
/*++
Routine Description:
Inserts the item at the tail of the interlocked list.
The caller has acquired the lock.
Arguments:
List - Supplies the list to insert the item on.
Entry - The entry to insert.
Return Value:
None.
--*/
{
PFRS_LIST Control = List->Control;
InsertTailList(&List->ListHead, Entry);
List->Count++;
Control->ControlCount++;
return;
}