/*++ 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 #pragma hdrstop #include 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; }