3069 lines
67 KiB
C
3069 lines
67 KiB
C
/*++
|
||
|
||
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;
|
||
}
|