Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

505 lines
12 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. nbqueue.c
  5. Abstract:
  6. This module implements non-blocking fifo queue.
  7. Author:
  8. David N. Cutler (davec) 24-Apr-2000
  9. Environment:
  10. Kernel mode only.
  11. Revision History:
  12. --*/
  13. #include "exp.h"
  14. //
  15. // Define queue pointer structure - this is platform target specific.
  16. //
  17. #if defined(_AMD64_)
  18. typedef union _NBQUEUE_POINTER {
  19. struct {
  20. LONG64 Node : 48;
  21. LONG64 Count : 16;
  22. };
  23. LONG64 Data;
  24. } NBQUEUE_POINTER, *PNBQUEUE_POINTER;
  25. #elif defined(_X86_)
  26. typedef union _NBQUEUE_POINTER {
  27. struct {
  28. LONG Count;
  29. LONG Node;
  30. };
  31. LONG64 Data;
  32. } NBQUEUE_POINTER, *PNBQUEUE_POINTER;
  33. #elif defined(_IA64_)
  34. typedef union _NBQUEUE_POINTER {
  35. struct {
  36. LONG64 Node : 45;
  37. LONG64 Region : 3;
  38. LONG64 Count : 16;
  39. };
  40. LONG64 Data;
  41. } NBQUEUE_POINTER, *PNBQUEUE_POINTER;
  42. #else
  43. #error "no target architecture"
  44. #endif
  45. //
  46. // Define queue node struture.
  47. //
  48. typedef struct _NBQUEUE_NODE {
  49. NBQUEUE_POINTER Next;
  50. ULONG64 Value;
  51. } NBQUEUE_NODE, *PNBQUEUE_NODE;
  52. //
  53. // Define inline functions to pack and unpack pointers in the platform
  54. // specific non-blocking queue pointer structure.
  55. //
  56. #if defined(_AMD64_)
  57. __inline
  58. VOID
  59. PackNBQPointer (
  60. IN PNBQUEUE_POINTER Entry,
  61. IN PNBQUEUE_NODE Node
  62. )
  63. {
  64. Entry->Node = (LONG64)Node;
  65. return;
  66. }
  67. __inline
  68. PNBQUEUE_NODE
  69. UnpackNBQPointer (
  70. IN PNBQUEUE_POINTER Entry
  71. )
  72. {
  73. return (PVOID)((LONG64)(Entry->Node));
  74. }
  75. #elif defined(_X86_)
  76. __inline
  77. VOID
  78. PackNBQPointer (
  79. IN PNBQUEUE_POINTER Entry,
  80. IN PNBQUEUE_NODE Node
  81. )
  82. {
  83. Entry->Node = (LONG)Node;
  84. return;
  85. }
  86. __inline
  87. PNBQUEUE_NODE
  88. UnpackNBQPointer (
  89. IN PNBQUEUE_POINTER Entry
  90. )
  91. {
  92. return (PVOID)(Entry->Node);
  93. }
  94. #elif defined(_IA64_)
  95. __inline
  96. VOID
  97. PackNBQPointer (
  98. IN PNBQUEUE_POINTER Entry,
  99. IN PNBQUEUE_NODE Node
  100. )
  101. {
  102. Entry->Node = (LONG64)Node;
  103. Entry->Region = (LONG64)Node >> 61;
  104. return;
  105. }
  106. __inline
  107. PNBQUEUE_NODE
  108. UnpackNBQPointer (
  109. IN PNBQUEUE_POINTER Entry
  110. )
  111. {
  112. LONG64 Value;
  113. Value = Entry->Node & 0x1fffffffffffffff;
  114. Value |= Entry->Region << 61;
  115. return (PVOID)(Value);
  116. }
  117. #else
  118. #error "no target architecture"
  119. #endif
  120. //
  121. // Define queue descriptor structure.
  122. //
  123. typedef struct _NBQUEUE_HEADER {
  124. NBQUEUE_POINTER Head;
  125. NBQUEUE_POINTER Tail;
  126. PSLIST_HEADER SlistHead;
  127. } NBQUEUE_HEADER, *PNBQUEUE_HEADER;
  128. #pragma alloc_text(PAGE, ExInitializeNBQueueHead)
  129. PVOID
  130. ExInitializeNBQueueHead (
  131. IN PSLIST_HEADER SlistHead
  132. )
  133. /*++
  134. Routine Description:
  135. This function initializes a non-blocking queue header.
  136. N.B. It is assumed that the specified SLIST has been populated with
  137. non-blocking queue nodes prior to calling this routine.
  138. Arguments:
  139. SlistHead - Supplies a pointer to an SLIST header.
  140. Return Value:
  141. If the non-blocking queue is successfully initialized, then the
  142. address of the queue header is returned as the function value.
  143. Otherwise, NULL is returned as the function value.
  144. --*/
  145. {
  146. PNBQUEUE_HEADER QueueHead;
  147. PNBQUEUE_NODE QueueNode;
  148. //
  149. // Attempt to allocate the queue header. If the allocation fails, then
  150. // return NULL.
  151. //
  152. QueueHead = (PNBQUEUE_HEADER)ExAllocatePoolWithTag(NonPagedPool,
  153. sizeof(NBQUEUE_HEADER),
  154. 'hqBN');
  155. if (QueueHead == NULL) {
  156. return NULL;
  157. }
  158. //
  159. // Attempt to allocate a queue node from the specified SLIST. If a node
  160. // can be allocated, then initialize the non-blocking queue header and
  161. // return the address of the queue header. Otherwise, free the queue
  162. // header and return NULL.
  163. //
  164. QueueHead->SlistHead = SlistHead;
  165. QueueNode = (PNBQUEUE_NODE)InterlockedPopEntrySList(QueueHead->SlistHead);
  166. if (QueueNode != NULL) {
  167. //
  168. // Initialize the queue node next pointer and value.
  169. //
  170. QueueNode->Next.Data = 0;
  171. QueueNode->Value = 0;
  172. //
  173. // Initialize the head and tail pointers in the queue header.
  174. //
  175. PackNBQPointer(&QueueHead->Head, QueueNode);
  176. QueueHead->Head.Count = 0;
  177. PackNBQPointer(&QueueHead->Tail, QueueNode);
  178. QueueHead->Tail.Count = 0;
  179. return QueueHead;
  180. } else {
  181. ExFreePool(QueueHead);
  182. return NULL;
  183. }
  184. }
  185. BOOLEAN
  186. ExInsertTailNBQueue (
  187. IN PVOID Header,
  188. IN ULONG64 Value
  189. )
  190. /*++
  191. Routine Description:
  192. This function inserts the specific data value at the tail of the
  193. specified non-blocking queue.
  194. Arguments:
  195. Header - Supplies an opaque pointer to a non-blocking queue header.
  196. Value - Supplies a pointer to an opaque data value.
  197. Return Value:
  198. If the specified opaque data value is successfully inserted at the tail
  199. of the specified non-blocking queue, then a value of TRUE is returned as
  200. the function value. Otherwise, a value of FALSE is returned.
  201. N.B. FALSE is returned if a queue node cannot be allocated from the
  202. associated SLIST.
  203. --*/
  204. {
  205. NBQUEUE_POINTER Insert;
  206. NBQUEUE_POINTER Next;
  207. PNBQUEUE_NODE NextNode;
  208. PNBQUEUE_HEADER QueueHead;
  209. PNBQUEUE_NODE QueueNode;
  210. NBQUEUE_POINTER Tail;
  211. PNBQUEUE_NODE TailNode;
  212. //
  213. // Attempt to allocate a queue node from the SLIST associated with
  214. // the specified non-blocking queue. If a node can be allocated, then
  215. // the node is inserted at the tail of the specified non-blocking
  216. // queue, and TRUE is returned as the function value. Otherwise, FALSE
  217. // is returned.
  218. //
  219. QueueHead = (PNBQUEUE_HEADER)Header;
  220. QueueNode = (PNBQUEUE_NODE)InterlockedPopEntrySList(QueueHead->SlistHead);
  221. if (QueueNode != NULL) {
  222. //
  223. // Initialize the queue node next pointer and value.
  224. //
  225. QueueNode->Next.Data = 0;
  226. QueueNode->Value = Value;
  227. //
  228. // The following loop is executed until the specified entry can
  229. // be safely inserted at the tail of the specified non-blocking
  230. // queue.
  231. //
  232. do {
  233. //
  234. // Read the tail queue pointer and the next queue pointer of
  235. // the tail queue pointer making sure the two pointers are
  236. // coherent.
  237. //
  238. Tail.Data = *((volatile LONG64 *)(&QueueHead->Tail.Data));
  239. TailNode = UnpackNBQPointer(&Tail);
  240. Next.Data = *((volatile LONG64 *)(&TailNode->Next.Data));
  241. QueueNode->Next.Count = Tail.Count + 1;
  242. if (Tail.Data == *((volatile LONG64 *)(&QueueHead->Tail.Data))) {
  243. //
  244. // If the tail is pointing to the last node in the list,
  245. // then attempt to insert the new node at the end of the
  246. // list. Otherwise, the tail is not pointing to the last
  247. // node in the list and an attempt is made to move the
  248. // tail pointer to the next node.
  249. //
  250. NextNode = UnpackNBQPointer(&Next);
  251. if (NextNode == NULL) {
  252. PackNBQPointer(&Insert, QueueNode);
  253. Insert.Count = Next.Count + 1;
  254. if (InterlockedCompareExchange64(&TailNode->Next.Data,
  255. Insert.Data,
  256. Next.Data) == Next.Data) {
  257. break;
  258. }
  259. } else {
  260. PackNBQPointer(&Insert, NextNode);
  261. Insert.Count = Tail.Count + 1;
  262. InterlockedCompareExchange64(&QueueHead->Tail.Data,
  263. Insert.Data,
  264. Tail.Data);
  265. }
  266. }
  267. } while (TRUE);
  268. //
  269. // Attempt to move the tail to the new tail node.
  270. //
  271. PackNBQPointer(&Insert, QueueNode);
  272. Insert.Count = Tail.Count + 1;
  273. InterlockedCompareExchange64(&QueueHead->Tail.Data,
  274. Insert.Data,
  275. Tail.Data);
  276. return TRUE;
  277. } else {
  278. return FALSE;
  279. }
  280. }
  281. BOOLEAN
  282. ExRemoveHeadNBQueue (
  283. IN PVOID Header,
  284. OUT PULONG64 Value
  285. )
  286. /*++
  287. Routine Description:
  288. This function removes a queue entry from the head of the specified
  289. non-blocking queue and returns the associated data value.
  290. Arguments:
  291. Header - Supplies an opaque pointer to a non-blocking queue header.
  292. Value - Supplies a pointer to a variable that receives the queue
  293. element value.
  294. Return Value:
  295. If an entry is removed from the specified non-blocking queue, then
  296. TRUE is returned as the function value. Otherwise, FALSE is returned.
  297. --*/
  298. {
  299. NBQUEUE_POINTER Head;
  300. PNBQUEUE_NODE HeadNode;
  301. NBQUEUE_POINTER Insert;
  302. NBQUEUE_POINTER Next;
  303. PNBQUEUE_NODE NextNode;
  304. PNBQUEUE_HEADER QueueHead;
  305. NBQUEUE_POINTER Tail;
  306. PNBQUEUE_NODE TailNode;
  307. //
  308. // The following loop is executed until an entry can be removed from
  309. // the specified non-blocking queue or until it can be determined that
  310. // the queue is empty.
  311. //
  312. QueueHead = (PNBQUEUE_HEADER)Header;
  313. do {
  314. //
  315. // Read the head queue pointer, the tail queue pointer, and the
  316. // next queue pointer of the head queue pointer making sure the
  317. // three pointers are coherent.
  318. //
  319. Head.Data = *((volatile LONG64 *)(&QueueHead->Head.Data));
  320. Tail.Data = *((volatile LONG64 *)(&QueueHead->Tail.Data));
  321. HeadNode = UnpackNBQPointer(&Head);
  322. Next.Data = *((volatile LONG64 *)(&HeadNode->Next.Data));
  323. if (Head.Data == *((volatile LONG64 *)(&QueueHead->Head.Data))) {
  324. //
  325. // If the queue header node is equal to the queue tail node,
  326. // then either the queue is empty or the tail pointer is falling
  327. // behind. Otherwise, there is an entry in the queue that can
  328. // be removed.
  329. //
  330. NextNode = UnpackNBQPointer(&Next);
  331. TailNode = UnpackNBQPointer(&Tail);
  332. if (HeadNode == TailNode) {
  333. //
  334. // If the next node of head pointer is NULL, then the queue
  335. // is empty. Otherwise, attempt to move the tail forward.
  336. //
  337. if (NextNode == NULL) {
  338. return FALSE;
  339. } else {
  340. PackNBQPointer(&Insert, NextNode);
  341. Insert.Count = Tail.Count + 1;
  342. InterlockedCompareExchange64(&QueueHead->Tail.Data,
  343. Insert.Data,
  344. Tail.Data);
  345. }
  346. } else {
  347. //
  348. // There is an entry in the queue that can be removed.
  349. //
  350. *Value = NextNode->Value;
  351. PackNBQPointer(&Insert, NextNode);
  352. Insert.Count = Head.Count + 1;
  353. if (InterlockedCompareExchange64(&QueueHead->Head.Data,
  354. Insert.Data,
  355. Head.Data) == Head.Data) {
  356. break;
  357. }
  358. }
  359. }
  360. } while (TRUE);
  361. //
  362. // Free the node that was removed for the list by inserting the node
  363. // in the associated SLIST.
  364. //
  365. InterlockedPushEntrySList(QueueHead->SlistHead,
  366. (PSINGLE_LIST_ENTRY)HeadNode);
  367. return TRUE;
  368. }