You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3068 lines
70 KiB
3068 lines
70 KiB
/*++
|
|
|
|
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);
|
|
|
|
INITIALIZE_CRITICAL_SECTION(&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;
|
|
|
|
INITIALIZE_CRITICAL_SECTION(&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);
|
|
INITIALIZE_CRITICAL_SECTION(&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;
|
|
}
|