/*++ Copyright (c) 1996 Microsoft Corporation Module Name: queue.c Abstract: Generic efficient queue package. Author: John Vert (jvert) 12-Jan-1996 Revision History: --*/ #include "clusrtlp.h" DWORD ClRtlInitializeQueue( PCL_QUEUE Queue ) /*++ 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. --*/ { DWORD Status; InitializeListHead(&Queue->ListHead); InitializeCriticalSection(&Queue->Lock); Queue->Count = 0; Queue->Event = CreateEvent(NULL, TRUE, FALSE, NULL); if (Queue->Event == NULL) { Status = GetLastError(); DeleteCriticalSection(&Queue->Lock); } else { Queue->Abort = CreateEvent(NULL, TRUE, FALSE, NULL); if (Queue->Abort == NULL) { Status = GetLastError(); CloseHandle(Queue->Event); DeleteCriticalSection(&Queue->Lock); } else { Status = ERROR_SUCCESS; } } return(Status); } VOID ClRtlDeleteQueue( IN PCL_QUEUE Queue ) /*++ Routine Description: Releases all resources used by a queue. Arguments: Queue - supplies the queue to be deleted Return Value: None. --*/ { DeleteCriticalSection(&Queue->Lock); CloseHandle(Queue->Event); CloseHandle(Queue->Abort); // // Zero the memory in order to cause grief to people who try // and use a deleted queue. // ZeroMemory(Queue, sizeof(CL_QUEUE)); } VOID ClRtlRundownQueue( IN PCL_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 (ClRtlRemoveHeadQueue 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. --*/ { EnterCriticalSection(&Queue->Lock); // // Set the aborted event to awaken any threads currently // blocked on the queue. // SetEvent(Queue->Abort); // // Move the contents of the list into the passed in listhead // if (IsListEmpty(&Queue->ListHead)) { InitializeListHead(ListHead); } else { *ListHead = Queue->ListHead; ListHead->Flink->Blink = ListHead; ListHead->Blink->Flink = ListHead; } Queue->ListHead.Flink = Queue->ListHead.Blink = NULL; Queue->Count = 0; LeaveCriticalSection(&Queue->Lock); } PLIST_ENTRY ClRtlRemoveHeadQueue( IN PCL_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(ClRtlRemoveHeadQueueTimeout(Queue, INFINITE,NULL,NULL)); } PLIST_ENTRY ClRtlRemoveHeadQueueTimeout( IN PCL_QUEUE Queue, IN DWORD dwMilliseconds, IN CLRTL_CHECK_HEAD_QUEUE_CALLBACK pfnCallback, IN OUT PVOID pvContext ) /*++ 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. Callback - Checks to see whether we should return the event if we find one. This simulates a peek. Context - Caller-defined data to be passed in with callback. Return Value: Pointer to list entry removed from the head of the queue. NULL if the wait times out, the queue is run down, or the name exceeds the buffer length. If this routine returns NULL, GetLastError will return ERROR_INVALID_HANDLE (if the queue has been rundown), WAIT_TIMEOUT (to indicate a timeout has occurred) --*/ { DWORD Status; PLIST_ENTRY Entry; BOOL Empty; HANDLE WaitArray[2]; Retry: if (Queue->Count == 0) { // // Block until something is inserted on the queue // WaitArray[0] = Queue->Abort; WaitArray[1] = Queue->Event; Status = WaitForMultipleObjects(2, WaitArray, FALSE, dwMilliseconds); if ((Status == WAIT_OBJECT_0) || (Status == WAIT_FAILED)) { // // The queue has been rundown, return NULL immediately. // SetLastError(ERROR_INVALID_HANDLE); return(NULL); } else if (Status == WAIT_TIMEOUT) { SetLastError(WAIT_TIMEOUT); return(NULL); } CL_ASSERT(Status == 1); } // // Lock the queue and try to remove something // EnterCriticalSection(&Queue->Lock); if (Queue->Count == 0) { // // Somebody got here before we did, drop the lock and retry // LeaveCriticalSection(&Queue->Lock); goto Retry; } CL_ASSERT(!IsListEmpty(&Queue->ListHead)); if ( NULL != pfnCallback ) { // // We've got a callback function - if it returns ERROR_SUCCESS then dequeue. // Otherwise return NULL and SetLastError to whatever error code the callback returns. // Entry = (&Queue->ListHead)->Flink; Status = (*pfnCallback)( Entry, pvContext ); if ( ERROR_SUCCESS == Status ) { // // The entry is appropriate to pass back. // Entry = RemoveHeadList(&Queue->ListHead); } else { Entry = NULL; } } else { Entry = RemoveHeadList(&Queue->ListHead); Status = ERROR_SUCCESS; } // // Only decrement the count if we removed the event // if ( NULL != Entry ) { // // Decrement count and check for empty list. // if (--Queue->Count == 0) { // // The queue has transitioned from full to empty, // reset the event. // CL_ASSERT(IsListEmpty(&Queue->ListHead)); ResetEvent(Queue->Event); } } LeaveCriticalSection(&Queue->Lock); SetLastError( Status ); return(Entry); } VOID ClRtlInsertTailQueue( IN PCL_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: None. --*/ { EnterCriticalSection(&Queue->Lock); InsertTailList(&Queue->ListHead, Item); if (++Queue->Count == 1) { // // The queue has transitioned from empty to full, set // the event to awaken any waiters. // SetEvent(Queue->Event); } LeaveCriticalSection(&Queue->Lock); }