355 lines
7.2 KiB
C
355 lines
7.2 KiB
C
|
/*++
|
|||
|
|
|||
|
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);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|