/*========================================================================== * * Copyright (C) 2000-2002 Microsoft Corporation. All Rights Reserved. * * File: dnnbqueue.cpp * Content: DirectPlay implementations of OS NBQueue functions * * History: * Date By Reason * ==== == ====== * 04/24/2000 davec Created nbqueue.c * 10/31/2001 vanceo Converted for use in DPlay source * ***************************************************************************/ #include "dncmni.h" // Until this gets ported, we won't use the NBQueue functions if WINCE is // defined. // Also, for DPNBUILD_ONLYONETHREAD builds we want to use the fallback code // because the critical sections get compiled away and we're left with a simple // queue. //============================================================================= #if ((defined(WINCE)) || (defined(DPNBUILD_ONLYONETHREAD))) //============================================================================= // // For now, the Windows CE NBQueue is just a critical section protected list. // On DPNBUILD_ONLYONETHREAD builds, we use the same structure because // the critical section will be compiled away. // typedef struct _DNNBQUEUE_HEADER { DNSLIST_HEADER * pSlistHeadFreeNodes; // pointer to Slist containing free nodes, the user must add 1 DNNBQUEUE_BLOCK for every item to be in the queue + 1 extra DNNBQUEUE_BLOCK * pHead; DNNBQUEUE_BLOCK * pTail; #ifndef DPNBUILD_ONLYONETHREAD DNCRITICAL_SECTION csLock; #endif // !DPNBUILD_ONLYONETHREAD } DNNBQUEUE_HEADER, *PDNNBQUEUE_HEADER; #undef DPF_MODNAME #define DPF_MODNAME "DNInitializeNBQueueHead" //============================================================================= // DNInitializeNBQueueHead //----------------------------------------------------------------------------- // // Description: This function creates and initializes a non-blocking queue // header. The specified SList must contain at least one pre- // allocated DNNBQUEUE_BLOCK. // // Arguments: // DNSLIST_HEADER * pSlistHeadFreeNodes - Pointer to list with free nodes. // // Returns: Pointer to queue header memory if successful, NULL if failed. //============================================================================= PVOID WINAPI DNInitializeNBQueueHead(DNSLIST_HEADER * const pSlistHeadFreeNodes) { DNNBQUEUE_HEADER * pQueueHeader; DNASSERT(pSlistHeadFreeNodes != NULL); pQueueHeader = (DNNBQUEUE_HEADER*) DNMalloc(sizeof(DNNBQUEUE_HEADER)); if (pQueueHeader != NULL) { pQueueHeader->pSlistHeadFreeNodes = pSlistHeadFreeNodes; pQueueHeader->pHead = NULL; pQueueHeader->pTail = NULL; if (! DNInitializeCriticalSection(&pQueueHeader->csLock)) { DNFree(pQueueHeader); pQueueHeader = NULL; } else { DebugSetCriticalSectionRecursionCount(&pQueueHeader->csLock, 0); } } return pQueueHeader; } // DNInitializeNBQueueHead #undef DPF_MODNAME #define DPF_MODNAME "DNDeinitializeNBQueueHead" //============================================================================= // DNDeinitializeNBQueueHead //----------------------------------------------------------------------------- // // Description: This function cleans up a previously initialized non- // blocking queue header. // // Arguments: // PVOID pvQueueHeader - Pointer to queue header. // // Returns: None. //============================================================================= void WINAPI DNDeinitializeNBQueueHead(PVOID const pvQueueHeader) { DNNBQUEUE_HEADER * pQueueHeader; DNASSERT(pvQueueHeader != NULL); pQueueHeader = (DNNBQUEUE_HEADER*) pvQueueHeader; DNASSERT(pQueueHeader->pHead == NULL); DNASSERT(pQueueHeader->pTail == NULL); DNDeleteCriticalSection(&pQueueHeader->csLock); DNFree(pQueueHeader); pQueueHeader = NULL; } // DNDeinitializeNBQueueHead #undef DPF_MODNAME #define DPF_MODNAME "DNInsertTailNBQueue" //============================================================================= // DNInsertTailNBQueue //----------------------------------------------------------------------------- // // Description: This function inserts the specified value at the tail of the // specified non-blocking queue. // // Arguments: // PVOID pvQueueHeader - Pointer to queue header. // ULONG64 Value - Value to insert. // // Returns: None. //============================================================================= void WINAPI DNInsertTailNBQueue(PVOID const pvQueueHeader, const ULONG64 Value) { DNNBQUEUE_HEADER * pQueueHeader; DNNBQUEUE_BLOCK * pQueueNode; DNASSERT(pvQueueHeader != NULL); pQueueHeader = (DNNBQUEUE_HEADER*) pvQueueHeader; DNASSERT(Value != 0); // // Retrieve a queue node from the SLIST owned by the specified non-blocking // queue. If this fails, we will assert or crash. // DBG_CASSERT(sizeof(DNNBQUEUE_BLOCK) >= sizeof(DNSLIST_ENTRY)); pQueueNode = (DNNBQUEUE_BLOCK*) DNInterlockedPopEntrySList(pQueueHeader->pSlistHeadFreeNodes); DNASSERT(pQueueNode != NULL); pQueueNode->Next = NULL; pQueueNode->Data = Value; DNEnterCriticalSection(&pQueueHeader->csLock); if (pQueueHeader->pTail == NULL) { DNASSERT(pQueueHeader->pHead == NULL); pQueueHeader->pHead = pQueueNode; } else { DNASSERT(pQueueHeader->pTail->Next == NULL); pQueueHeader->pTail->Next = (ULONG64) pQueueNode; } pQueueHeader->pTail = pQueueNode; DNLeaveCriticalSection(&pQueueHeader->csLock); } // DNInsertTailNBQueue #undef DPF_MODNAME #define DPF_MODNAME "DNRemoveHeadNBQueue" //============================================================================= // DNRemoveHeadNBQueue //----------------------------------------------------------------------------- // // Description: This function removes a queue entry from the head of the // specified non-blocking queue and returns its value. // // Arguments: // PVOID pvQueueHeader - Pointer to queue header. // // Returns: First value retrieved, or 0 if none. //============================================================================= ULONG64 WINAPI DNRemoveHeadNBQueue(PVOID const pvQueueHeader) { DNNBQUEUE_HEADER * pQueueHeader; ULONG64 ReturnValue; DNNBQUEUE_BLOCK * pNode; DNASSERT(pvQueueHeader != NULL); pQueueHeader = (DNNBQUEUE_HEADER*) pvQueueHeader; DNEnterCriticalSection(&pQueueHeader->csLock); pNode = pQueueHeader->pHead; if (pNode != NULL) { DNASSERT(pQueueHeader->pTail != NULL); pQueueHeader->pHead = (DNNBQUEUE_BLOCK*) pNode->Next; if (pQueueHeader->pHead == NULL) { DNASSERT(pQueueHeader->pTail == pNode); pQueueHeader->pTail = NULL; } DNLeaveCriticalSection(&pQueueHeader->csLock); ReturnValue = pNode->Data; // // Return the node that was removed for the list by inserting the node in // the associated SLIST. // DNInterlockedPushEntrySList(pQueueHeader->pSlistHeadFreeNodes, (DNSLIST_ENTRY*) pNode); } else { DNASSERT(pQueueHeader->pTail == NULL); DNLeaveCriticalSection(&pQueueHeader->csLock); ReturnValue = 0; } return ReturnValue; } // DNRemoveHeadNBQueue #undef DPF_MODNAME #define DPF_MODNAME "DNIsNBQueueEmpty" //============================================================================= // DNIsNBQueueEmpty //----------------------------------------------------------------------------- // // Description: This function returns TRUE if the queue contains no items at // this instant, FALSE if there are items. // // Arguments: // PVOID pvQueueHeader - Pointer to queue header. // // Returns: TRUE if queue is empty, FALSE otherwise. //============================================================================= BOOL WINAPI DNIsNBQueueEmpty(PVOID const pvQueueHeader) { DNNBQUEUE_HEADER * pQueueHeader; BOOL fReturn; DNASSERT(pvQueueHeader != NULL); pQueueHeader = (DNNBQUEUE_HEADER*) pvQueueHeader; DNEnterCriticalSection(&pQueueHeader->csLock); fReturn = (pQueueHeader->pHead == NULL) ? TRUE : FALSE; DNLeaveCriticalSection(&pQueueHeader->csLock); return fReturn; } // DNIsNBQueueEmpty #undef DPF_MODNAME #define DPF_MODNAME "DNAppendListNBQueue" //============================================================================= // DNAppendListNBQueue //----------------------------------------------------------------------------- // // Description: This function appends a queue of items to the tail of the // specified non-blocking queue. The queue of items to be added // must be linked in the form of an SLIST, where the actual // ULONG64 value to be queued is the DNSLIST_ENTRY pointer minus // iValueOffset. // // Arguments: // PVOID pvQueueHeader - Pointer to queue header. // DNSLIST_ENTRY * pSlistEntryAppend - Pointer to first item to append. // INT_PTR iValueOffset - How far DNSLIST_ENTRY field is offset // // Returns: None. //============================================================================= void WINAPI DNAppendListNBQueue(PVOID const pvQueueHeader, DNSLIST_ENTRY * const pSlistEntryAppend, INT_PTR iValueOffset) { DNNBQUEUE_HEADER * pQueueHeader; DNSLIST_ENTRY * pCurrent; DNNBQUEUE_BLOCK * pFirstQueueNode; DNNBQUEUE_BLOCK * pLastQueueNode; DNNBQUEUE_BLOCK * pCurrentQueueNode; DNASSERT(pvQueueHeader != NULL); pQueueHeader = (DNNBQUEUE_HEADER*) pvQueueHeader; DNASSERT(pSlistEntryAppend != NULL); // // Retrieve queue nodes for each value to add from the SLIST owned by the // specified non-blocking queue. If this fails, we will assert or crash. // pFirstQueueNode = NULL; pCurrent = pSlistEntryAppend; do { DBG_CASSERT(sizeof(DNNBQUEUE_BLOCK) >= sizeof(DNSLIST_ENTRY)); pCurrentQueueNode = (DNNBQUEUE_BLOCK*) DNInterlockedPopEntrySList(pQueueHeader->pSlistHeadFreeNodes); DNASSERT(pCurrentQueueNode != NULL); // // Initialize the queue node next pointer and value. // pCurrentQueueNode->Next = NULL; pCurrentQueueNode->Data = (ULONG64) (pCurrent - iValueOffset); // // Link the item as appropriate. // if (pFirstQueueNode == NULL) { pFirstQueueNode = pCurrentQueueNode; pLastQueueNode = pCurrentQueueNode; } else { pLastQueueNode->Next = (ULONG64) pCurrentQueueNode; pLastQueueNode = pCurrentQueueNode; } pCurrent = pCurrent->Next; } while (pCurrent != NULL); // // Lock the queue and append the list. // DNEnterCriticalSection(&pQueueHeader->csLock); if (pQueueHeader->pTail == NULL) { DNASSERT(pQueueHeader->pHead == NULL); pQueueHeader->pHead = pFirstQueueNode; } else { DNASSERT(pQueueHeader->pTail->Next == NULL); pQueueHeader->pTail->Next = (ULONG64) pFirstQueueNode; } pQueueHeader->pTail = pLastQueueNode; DNLeaveCriticalSection(&pQueueHeader->csLock); } // DNAppendListNBQueue //============================================================================= #else // ! WINCE and ! DPNBUILD_ONLYONETHREAD //============================================================================= // Forward declare the generic node structure. typedef struct _DNNBQUEUE_NODE DNNBQUEUE_NODE, *PDNNBQUEUE_NODE; // // Define inline functions to pack and unpack pointers in the platform // specific non-blocking queue pointer structure, as well as // InterlockedCompareExchange64. // //----------------------------------------------------------------------------- #if defined(_AMD64_) //----------------------------------------------------------------------------- typedef union _DNNBQUEUE_POINTER { struct { LONG64 Node : 48; LONG64 Count : 16; }; LONG64 Data; } DNNBQUEUE_POINTER, * PDNNBQUEUE_POINTER; __inline VOID PackNBQPointer(IN PDNNBQUEUE_POINTER Entry, IN PDNNBQUEUE_NODE Node) { Entry->Node = (LONG64)Node; return; } __inline PDNNBQUEUE_NODE UnpackNBQPointer(IN PDNNBQUEUE_POINTER Entry) { return (PDNNBQUEUE_NODE)((LONG64)(Entry->Node)); } // // For whatever reason we need to redirect through an inline, the compiler doesn't // like the casting when calling it directly through a macro. // inline LONG64 _DNInterlockedCompareExchange64(volatile PVOID * Destination, PVOID Exchange, PVOID Comperand) { return reinterpret_cast(InterlockedCompareExchangePointer(Destination, Exchange, Comperand)); } #define DNInterlockedCompareExchange64(Destination, Exchange, Comperand) \ _DNInterlockedCompareExchange64((volatile PVOID*) (Destination), reinterpret_cast(Exchange), reinterpret_cast(Comperand)) //----------------------------------------------------------------------------- #elif defined(_IA64_) //----------------------------------------------------------------------------- typedef union _DNNBQUEUE_POINTER { struct { LONG64 Node : 45; LONG64 Region : 3; LONG64 Count : 16; }; LONG64 Data; } DNNBQUEUE_POINTER, *PDNNBQUEUE_POINTER; __inline VOID PackNBQPointer(IN PDNNBQUEUE_POINTER Entry, IN PDNNBQUEUE_NODE Node) { Entry->Node = (LONG64)Node; Entry->Region = (LONG64)Node >> 61; return; } __inline PDNNBQUEUE_NODE UnpackNBQPointer(IN PDNNBQUEUE_POINTER Entry) { LONG64 Value; Value = Entry->Node & 0x1fffffffffffffff; Value |= Entry->Region << 61; return (PDNNBQUEUE_NODE)(Value); } // // For whatever reason we need to redirect through an inline, the compiler doesn't // like the casting when calling it directly through a macro. // inline LONG64 _DNInterlockedCompareExchange64(volatile PVOID * Destination, PVOID Exchange, PVOID Comperand) { return reinterpret_cast(InterlockedCompareExchangePointer(Destination, Exchange, Comperand)); } #define DNInterlockedCompareExchange64(Destination, Exchange, Comperand) \ _DNInterlockedCompareExchange64((volatile PVOID*) (Destination), reinterpret_cast(Exchange), reinterpret_cast(Comperand)) //----------------------------------------------------------------------------- #elif defined(_X86_) //----------------------------------------------------------------------------- typedef union _DNNBQUEUE_POINTER { struct { LONG Count; LONG Node; }; LONG64 Data; } DNNBQUEUE_POINTER, *PDNNBQUEUE_POINTER; __inline VOID PackNBQPointer(IN PDNNBQUEUE_POINTER Entry, IN PDNNBQUEUE_NODE Node) { Entry->Node = (LONG)Node; return; } __inline PDNNBQUEUE_NODE UnpackNBQPointer(IN PDNNBQUEUE_POINTER Entry) { return (PDNNBQUEUE_NODE)(Entry->Node); } #define DNInterlockedCompareExchange64(Destination, Exchange, Comperand) \ xInterlockedCompareExchange64(Destination, &(Exchange), &(Comperand)) __declspec(naked) LONG64 __fastcall xInterlockedCompareExchange64(IN OUT LONG64 volatile * Destination, IN PLONG64 Exchange, IN PLONG64 Comperand) { __asm { // Save nonvolatile registers and read the exchange and comperand values. push ebx ; save nonvolatile registers push ebp ; mov ebp, ecx ; set destination address mov ebx, [edx] ; get exchange value mov ecx, [edx] + 4 ; mov edx, [esp] + 12 ; get comperand address mov eax, [edx] ; get comperand value mov edx, [edx] + 4 ; lock cmpxchg8b qword ptr [ebp] ; compare and exchange // Restore nonvolatile registers and return result in edx:eax. pop ebp ; restore nonvolatile registers pop ebx ; ret 4 } } //----------------------------------------------------------------------------- #else //----------------------------------------------------------------------------- #error "no target architecture" //----------------------------------------------------------------------------- #endif //----------------------------------------------------------------------------- struct _DNNBQUEUE_NODE { DNNBQUEUE_POINTER Next; ULONG64 Value; }; typedef struct _DNNBQUEUE_HEADER { DNSLIST_HEADER * pSlistHeadFreeNodes; // pointer to Slist containing free nodes, the user must add 1 DNNBQUEUE_BLOCK for every item to be in the queue + 1 extra DNNBQUEUE_POINTER Head; DNNBQUEUE_POINTER Tail; } DNNBQUEUE_HEADER, *PDNNBQUEUE_HEADER; /* //============================================================================= // Globals //============================================================================= #if ((defined(DBG)) && (defined(_X86_))) DNCRITICAL_SECTION g_csValidation; DWORD g_dwEntries; #endif // DBG and _X86_ */ #undef DPF_MODNAME #define DPF_MODNAME "DNInitializeNBQueueHead" //============================================================================= // DNInitializeNBQueueHead //----------------------------------------------------------------------------- // // Description: This function creates and initializes a non-blocking queue // header. The specified SList must contain at least one pre- // allocated DNNBQUEUE_BLOCK. // // Arguments: // DNSLIST_HEADER * pSlistHeadFreeNodes - Pointer to list with free nodes. // // Returns: Pointer to queue header memory if successful, NULL if failed. //============================================================================= PVOID WINAPI DNInitializeNBQueueHead(DNSLIST_HEADER * const pSlistHeadFreeNodes) { DNNBQUEUE_HEADER * pQueueHeader; DNNBQUEUE_NODE * pQueueNode; DNASSERT(pSlistHeadFreeNodes != NULL); pQueueHeader = (DNNBQUEUE_HEADER*) DNMalloc(sizeof(DNNBQUEUE_HEADER)); if (pQueueHeader != NULL) { pQueueHeader->pSlistHeadFreeNodes = pSlistHeadFreeNodes; pQueueNode = (DNNBQUEUE_NODE*) DNInterlockedPopEntrySList(pQueueHeader->pSlistHeadFreeNodes); DNASSERT(pQueueNode != NULL); // // Initialize the initial root node's next pointer and value. // pQueueNode->Next.Data = 0; pQueueNode->Value = 0; // // Initialize the head and tail pointers in the queue header. // PackNBQPointer(&pQueueHeader->Head, pQueueNode); pQueueHeader->Head.Count = 0; PackNBQPointer(&pQueueHeader->Tail, pQueueNode); pQueueHeader->Tail.Count = 0; /* #if ((defined(DBG)) && (defined(_X86_))) DNInitializeCriticalSection(&g_csValidation); g_dwEntries = 1; #endif // DBG and _X86_ */ } return pQueueHeader; } // DNInitializeNBQueueHead #undef DPF_MODNAME #define DPF_MODNAME "DNDeinitializeNBQueueHead" //============================================================================= // DNDeinitializeNBQueueHead //----------------------------------------------------------------------------- // // Description: This function cleans up a previously initialized non- // blocking queue header. // // Arguments: // PVOID pvQueueHeader - Pointer to queue header. // // Returns: None. //============================================================================= void WINAPI DNDeinitializeNBQueueHead(PVOID const pvQueueHeader) { DNNBQUEUE_HEADER * pQueueHeader; DNNBQUEUE_NODE * pQueueNode; #ifdef DBG DNNBQUEUE_NODE * pQueueNodeCompare; #endif // DBG DNASSERT(pvQueueHeader != NULL); pQueueHeader = (DNNBQUEUE_HEADER*) pvQueueHeader; // // There should be just the root node left. // pQueueNode = UnpackNBQPointer(&pQueueHeader->Head); #ifdef DBG DNASSERT(pQueueNode != NULL); pQueueNodeCompare = UnpackNBQPointer(&pQueueHeader->Tail); DNASSERT(pQueueNode == pQueueNodeCompare); #endif // DBG // // Return the node that was removed for the list by // inserting the node in the associated SLIST. // DNInterlockedPushEntrySList(pQueueHeader->pSlistHeadFreeNodes, (DNSLIST_ENTRY*) pQueueNode); DNFree(pQueueHeader); pQueueHeader = NULL; } // DNDeinitializeNBQueueHead #undef DPF_MODNAME #define DPF_MODNAME "DNInsertTailNBQueue" //============================================================================= // DNInsertTailNBQueue //----------------------------------------------------------------------------- // // Description: This function inserts the specified value at the tail of the // specified non-blocking queue. // // Arguments: // PVOID pvQueueHeader - Pointer to queue header. // ULONG64 Value - Value to insert. // // Returns: None. //============================================================================= void WINAPI DNInsertTailNBQueue(PVOID const pvQueueHeader, const ULONG64 Value) { DNNBQUEUE_HEADER * pQueueHeader; DNNBQUEUE_POINTER Insert; DNNBQUEUE_POINTER Next; DNNBQUEUE_NODE * pNextNode; DNNBQUEUE_NODE * pQueueNode; DNNBQUEUE_POINTER Tail; DNNBQUEUE_NODE * pTailNode; DNASSERT(pvQueueHeader != NULL); pQueueHeader = (DNNBQUEUE_HEADER*) pvQueueHeader; DNASSERT(Value != 0); // // Retrieve a queue node from the SLIST owned by the specified non-blocking // queue. If this fails, we will assert or crash. // DBG_CASSERT(sizeof(DNNBQUEUE_NODE) >= sizeof(DNSLIST_ENTRY)); pQueueNode = (DNNBQUEUE_NODE*) DNInterlockedPopEntrySList(pQueueHeader->pSlistHeadFreeNodes); DNASSERT(pQueueNode != NULL); // // Initialize the queue node next pointer and value. // pQueueNode->Next.Data = 0; pQueueNode->Value = Value; // // The following loop is executed until the specified entry can be safely // inserted at the tail of the specified non-blocking queue. // do { // // Read the tail queue pointer and the next queue pointer of the tail // queue pointer making sure the two pointers are coherent. // Tail.Data = *((volatile LONG64 *)(&pQueueHeader->Tail.Data)); pTailNode = UnpackNBQPointer(&Tail); Next.Data = *((volatile LONG64 *)(&pTailNode->Next.Data)); pQueueNode->Next.Count = Tail.Count + 1; if (Tail.Data == *((volatile LONG64 *)(&pQueueHeader->Tail.Data))) { // // If the tail is pointing to the last node in the list, then // attempt to insert the new node at the end of the list. // Otherwise, the tail is not pointing to the last node in the list // and an attempt is made to move the tail pointer to the next // node. // pNextNode = UnpackNBQPointer(&Next); if (pNextNode == NULL) { PackNBQPointer(&Insert, pQueueNode); Insert.Count = Next.Count + 1; if (DNInterlockedCompareExchange64(&pTailNode->Next.Data, Insert.Data, Next.Data) == Next.Data) { break; } } else { PackNBQPointer(&Insert, pNextNode); Insert.Count = Tail.Count + 1; DNInterlockedCompareExchange64(&pQueueHeader->Tail.Data, Insert.Data, Tail.Data); } } } while (TRUE); // // Attempt to move the tail to the new tail node. // PackNBQPointer(&Insert, pQueueNode); Insert.Count = Tail.Count + 1; DNInterlockedCompareExchange64(&pQueueHeader->Tail.Data, Insert.Data, Tail.Data); } // DNInsertTailNBQueue #undef DPF_MODNAME #define DPF_MODNAME "DNRemoveHeadNBQueue" //============================================================================= // DNRemoveHeadNBQueue //----------------------------------------------------------------------------- // // Description: This function removes a queue entry from the head of the // specified non-blocking queue and returns its value. // // Arguments: // PVOID pvQueueHeader - Pointer to queue header. // // Returns: First value retrieved, or 0 if none. //============================================================================= ULONG64 WINAPI DNRemoveHeadNBQueue(PVOID const pvQueueHeader) { DNNBQUEUE_HEADER * pQueueHeader; ULONG64 ReturnValue; DNNBQUEUE_POINTER Head; PDNNBQUEUE_NODE pHeadNode; DNNBQUEUE_POINTER Insert; DNNBQUEUE_POINTER Next; PDNNBQUEUE_NODE pNextNode; DNNBQUEUE_POINTER Tail; PDNNBQUEUE_NODE pTailNode; DNASSERT(pvQueueHeader != NULL); pQueueHeader = (DNNBQUEUE_HEADER*) pvQueueHeader; // // The following loop is executed until an entry can be removed from // the specified non-blocking queue or until it can be determined that // the queue is empty. // do { // // Read the head queue pointer, the tail queue pointer, and the // next queue pointer of the head queue pointer making sure the // three pointers are coherent. // Head.Data = *((volatile LONG64 *)(&pQueueHeader->Head.Data)); Tail.Data = *((volatile LONG64 *)(&pQueueHeader->Tail.Data)); pHeadNode = UnpackNBQPointer(&Head); Next.Data = *((volatile LONG64 *)(&pHeadNode->Next.Data)); if (Head.Data == *((volatile LONG64 *)(&pQueueHeader->Head.Data))) { // // If the queue header node is equal to the queue tail node, // then either the queue is empty or the tail pointer is falling // behind. Otherwise, there is an entry in the queue that can // be removed. // pNextNode = UnpackNBQPointer(&Next); pTailNode = UnpackNBQPointer(&Tail); if (pHeadNode == pTailNode) { // // If the next node of head pointer is NULL, then the queue // is empty. Otherwise, attempt to move the tail forward. // if (pNextNode == NULL) { ReturnValue = 0; break; } else { PackNBQPointer(&Insert, pNextNode); Insert.Count = Tail.Count + 1; DNInterlockedCompareExchange64(&pQueueHeader->Tail.Data, Insert.Data, Tail.Data); } } else { // // There is an entry in the queue that can be removed. // ReturnValue = pNextNode->Value; PackNBQPointer(&Insert, pNextNode); Insert.Count = Head.Count + 1; if (DNInterlockedCompareExchange64(&pQueueHeader->Head.Data, Insert.Data, Head.Data) == Head.Data) { // // Return the node that was removed for the list by // inserting the node in the associated SLIST. // DNInterlockedPushEntrySList(pQueueHeader->pSlistHeadFreeNodes, (DNSLIST_ENTRY*) pHeadNode); break; } } } } while (TRUE); return ReturnValue; } // DNRemoveHeadNBQueue #undef DPF_MODNAME #define DPF_MODNAME "DNIsNBQueueEmpty" //============================================================================= // DNIsNBQueueEmpty //----------------------------------------------------------------------------- // // Description: This function returns TRUE if the queue contains no items at // this instant, FALSE if there are items. // // Arguments: // PVOID pvQueueHeader - Pointer to queue header. // // Returns: TRUE if queue is empty, FALSE otherwise. //============================================================================= BOOL WINAPI DNIsNBQueueEmpty(PVOID const pvQueueHeader) { DNNBQUEUE_HEADER * pQueueHeader; BOOL fReturn; DNNBQUEUE_POINTER Head; PDNNBQUEUE_NODE pHeadNode; DNNBQUEUE_POINTER Insert; DNNBQUEUE_POINTER Next; PDNNBQUEUE_NODE pNextNode; DNNBQUEUE_POINTER Tail; PDNNBQUEUE_NODE pTailNode; DNASSERT(pvQueueHeader != NULL); pQueueHeader = (DNNBQUEUE_HEADER*) pvQueueHeader; // // The following loop is executed until it can be determined that the queue // is empty or contains at least one item. // do { // // Read the head queue pointer, the tail queue pointer, and the // next queue pointer of the head queue pointer making sure the // three pointers are coherent. // Head.Data = *((volatile LONG64 *)(&pQueueHeader->Head.Data)); Tail.Data = *((volatile LONG64 *)(&pQueueHeader->Tail.Data)); pHeadNode = UnpackNBQPointer(&Head); Next.Data = *((volatile LONG64 *)(&pHeadNode->Next.Data)); if (Head.Data == *((volatile LONG64 *)(&pQueueHeader->Head.Data))) { // // If the queue header node is equal to the queue tail node, // then either the queue is empty or the tail pointer is falling // behind. Otherwise, there is an entry in the queue that can // be removed. // pNextNode = UnpackNBQPointer(&Next); pTailNode = UnpackNBQPointer(&Tail); if (pHeadNode == pTailNode) { // // If the next node of head pointer is NULL, then the queue // is empty. Otherwise, attempt to move the tail forward. // if (pNextNode == NULL) { fReturn = TRUE; break; } else { PackNBQPointer(&Insert, pNextNode); Insert.Count = Tail.Count + 1; DNInterlockedCompareExchange64(&pQueueHeader->Tail.Data, Insert.Data, Tail.Data); } } else { // // There is an entry in the queue. // fReturn = FALSE; break; } } } while (TRUE); return fReturn; } // DNIsNBQueueEmpty #undef DPF_MODNAME #define DPF_MODNAME "DNAppendListNBQueue" //============================================================================= // DNAppendListNBQueue //----------------------------------------------------------------------------- // // Description: This function appends a queue of items to the tail of the // specified non-blocking queue. The queue of items to be added // must be linked in the form of an SLIST, where the actual // ULONG64 value to be queued is the DNSLIST_ENTRY pointer minus // iValueOffset. // // Arguments: // PVOID pvQueueHeader - Pointer to queue header. // DNSLIST_ENTRY * pSlistEntryAppend - Pointer to first item to append. // INT_PTR iValueOffset - How far DNSLIST_ENTRY field is offset // from start of value. // // Returns: None. //============================================================================= void WINAPI DNAppendListNBQueue(PVOID const pvQueueHeader, DNSLIST_ENTRY * const pSlistEntryAppend, INT_PTR iValueOffset) { DNNBQUEUE_HEADER * pQueueHeader; DNSLIST_ENTRY * pCurrent; DNNBQUEUE_POINTER Insert; DNNBQUEUE_POINTER Next; DNNBQUEUE_NODE * pNextNode; DNNBQUEUE_NODE * pFirstQueueNode; DNNBQUEUE_NODE * pLastQueueNode; DNNBQUEUE_NODE * pCurrentQueueNode; DNNBQUEUE_POINTER Tail; DNNBQUEUE_NODE * pTailNode; DNASSERT(pvQueueHeader != NULL); pQueueHeader = (DNNBQUEUE_HEADER*) pvQueueHeader; DNASSERT(pSlistEntryAppend != NULL); // // Retrieve queue nodes for each value to add from the SLIST owned by the // specified non-blocking queue. If this fails, we will assert or crash. // pFirstQueueNode = NULL; pCurrent = pSlistEntryAppend; do { DBG_CASSERT(sizeof(DNNBQUEUE_NODE) >= sizeof(DNSLIST_ENTRY)); pCurrentQueueNode = (DNNBQUEUE_NODE*) DNInterlockedPopEntrySList(pQueueHeader->pSlistHeadFreeNodes); DNASSERT(pCurrentQueueNode != NULL); // // Initialize the queue node next pointer and value. // pCurrentQueueNode->Next.Data = 0; pCurrentQueueNode->Value = (ULONG64) (pCurrent - iValueOffset); // // Link the item as appropriate. // if (pFirstQueueNode == NULL) { pFirstQueueNode = pCurrentQueueNode; pLastQueueNode = pCurrentQueueNode; } else { PackNBQPointer(&pLastQueueNode->Next, pCurrentQueueNode); pLastQueueNode = pCurrentQueueNode; } pCurrent = pCurrent->Next; } while (pCurrent != NULL); // // The following loop is executed until the specified entries can be safely // inserted at the tail of the specified non-blocking queue. // do { // // Read the tail queue pointer and the next queue pointer of the tail // queue pointer making sure the two pointers are coherent. // Tail.Data = *((volatile LONG64 *)(&pQueueHeader->Tail.Data)); pTailNode = UnpackNBQPointer(&Tail); Next.Data = *((volatile LONG64 *)(&pTailNode->Next.Data)); pFirstQueueNode->Next.Count = Tail.Count + 1; if (Tail.Data == *((volatile LONG64 *)(&pQueueHeader->Tail.Data))) { // // If the tail is pointing to the last node in the list, then // attempt to insert the new nodes at the end of the list. // Otherwise, the tail is not pointing to the last node in the list // and an attempt is made to move the tail pointer to the next // node. // pNextNode = UnpackNBQPointer(&Next); if (pNextNode == NULL) { PackNBQPointer(&Insert, pFirstQueueNode); Insert.Count = Next.Count + 1; if (DNInterlockedCompareExchange64(&pTailNode->Next.Data, Insert.Data, Next.Data) == Next.Data) { break; } } else { PackNBQPointer(&Insert, pNextNode); Insert.Count = Tail.Count + 1; DNInterlockedCompareExchange64(&pQueueHeader->Tail.Data, Insert.Data, Tail.Data); } } } while (TRUE); // // Attempt to move the tail to the new tail node. // PackNBQPointer(&Insert, pLastQueueNode); Insert.Count = Tail.Count + 1; DNInterlockedCompareExchange64(&pQueueHeader->Tail.Data, Insert.Data, Tail.Data); } // DNAppendListNBQueue //============================================================================= #endif // ! WINCE and ! DPNBUILD_ONLYONETHREAD //=============================================================================