Leaked source code of windows server 2003
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.

1272 lines
39 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: fifoqimp.h
  5. //
  6. // Description: Implementation for Fifo Queue template
  7. //
  8. // Author: mikeswa
  9. //
  10. // Copyright (C) 1997 Microsoft Corporation
  11. //
  12. //-----------------------------------------------------------------------------
  13. #include <fifoq.h>
  14. #define FIFOQ_ASSERT_QUEUE
  15. //#define SHARELOCK_TRY_BROKEN
  16. //some constants used
  17. const DWORD FIFOQ_QUEUE_PAGE_SIZE = 127; //number of entires per page
  18. const DWORD FIFOQ_QUEUE_MAX_FREE_PAGES = 200; //maximum # of free pages kept
  19. //$$REVIEW: It might be nice to pick a size that is page size friendly
  20. // Current of objects is
  21. // sizeof(PVOID) + sizeof(PVOID)*FIFOQ_QUEUE_PAGE_SIZE = 512 bytes
  22. //---[ CFifoQueuePage ]--------------------------------------------------------
  23. //
  24. //
  25. // Hungarian: fqp, pfqp
  26. //
  27. // Single page of a FIFO queue. Most operations are handled within the actual
  28. // CFifoQueue class. FQPAGE is a typedef for this template class within
  29. // the scope of the CFifoQueue class
  30. //-----------------------------------------------------------------------------
  31. template<class PQDATA>
  32. class CFifoQueuePage
  33. {
  34. public:
  35. friend class CFifoQueue<PQDATA>;
  36. CFifoQueuePage() {Recycle();};
  37. protected:
  38. inline void Recycle();
  39. inline bool FIsOutOfBounds(IN PQDATA *ppqdata);
  40. CFifoQueuePage<PQDATA> *m_pfqpNext; //Next page in linked list
  41. CFifoQueuePage<PQDATA> *m_pfqpPrev; //previous page in linked list
  42. PQDATA m_rgpqdata[FIFOQ_QUEUE_PAGE_SIZE];
  43. #ifdef FIFOQ_ASSERT_QUEUE
  44. //# of entries on this page that have been removed out of order
  45. //- Used in assertion routines
  46. DWORD m_cHoles;
  47. #endif //FIFOQ_ASSERT_QUEUE
  48. };
  49. //---[ CFifoQueuePage::Recycle ]-----------------------------------------------
  50. //
  51. //
  52. // Description:
  53. // Performs initialization of a page. Called when a page is created as
  54. // well as when it is retrieved from the free list
  55. // Parameters:
  56. // -
  57. // Returns:
  58. // -
  59. //
  60. //-----------------------------------------------------------------------------
  61. template<class PQDATA>
  62. void CFifoQueuePage<PQDATA>::Recycle()
  63. {
  64. m_pfqpNext = NULL;
  65. m_pfqpPrev = NULL;
  66. #ifdef FIFOQ_ASSERT_QUEUE
  67. m_cHoles = 0;
  68. #endif //FIFOQ_ASSERT_QUEUE
  69. }
  70. //---[ CFifoQueuePage::FIsOutOfBounds ]----------------------------------------
  71. //
  72. //
  73. // Description:
  74. // Tests to see if a PQDATA ptr is within range of this page
  75. // Parameters:
  76. // IN ppqdata - PQDATA ptr to test
  77. // Returns:
  78. // TRUE if in bounds
  79. // FALSE if ptr is out of bounds
  80. //-----------------------------------------------------------------------------
  81. template<class PQDATA>
  82. bool CFifoQueuePage<PQDATA>::FIsOutOfBounds(PQDATA *ppqdata)
  83. {
  84. return ((ppqdata < m_rgpqdata) ||
  85. ((m_rgpqdata + (FIFOQ_QUEUE_PAGE_SIZE-1)) < ppqdata));
  86. }
  87. #ifdef DEBUG
  88. #ifdef FIFOQ_ASSERT_QUEUE
  89. //---[ CFifoQueue::AssertQueueFn() ]-------------------------------------------
  90. //
  91. //
  92. // Description:
  93. // Perform some rather involved validation of the queue. Including:
  94. // - Check Head and Tail page to make sure they conform various
  95. // retrictions of our data structure
  96. // - Check count to make sure it reflects data
  97. // At some point we may wish to add further checking (ie walking the linked
  98. // list in both directions to validate it).
  99. // Parameters:
  100. // fHaveLocks - set to true if the caller has both the head and tail locked
  101. // Default value is FALSE.
  102. // Returns:
  103. // -
  104. //
  105. //-----------------------------------------------------------------------------
  106. template <class PQDATA>
  107. void CFifoQueue<PQDATA>::AssertQueueFn(BOOL fHaveLocks)
  108. {
  109. TraceFunctEnterEx((LPARAM) this, "CFifoQueue::AssertQueue");
  110. FQPAGE *pfqpTmp = NULL; //used to count entries
  111. DWORD cEntries = 0; //what we think count should be
  112. _ASSERT(FIFOQ_SIG == m_dwSignature);
  113. //include text in assert, to have it appear in dialog box (if applicable)
  114. if (!fHaveLocks)
  115. {
  116. m_slHead.ShareLock();
  117. m_slTail.ShareLock();
  118. }
  119. if ((m_pfqpHead != NULL) && (NULL != m_pfqpHead->m_pfqpPrev))
  120. {
  121. //If Head is not NULL, it should not have a pervious page
  122. DebugTrace((LPARAM) this, "Queue Assert: Head's Previous ptr is non-NULL");
  123. Assert(0 && "Queue Assert: Head's Previous is non-NULL");
  124. }
  125. if ((m_pfqpTail != NULL) && (NULL != m_pfqpTail->m_pfqpNext))
  126. {
  127. //If Tail is not NULL, it should not have a next page
  128. DebugTrace((LPARAM) this, "Queue Assert: Tail's Next ptr is non-NULL");
  129. Assert(0 && "Queue Assert: Tail's Next is non-NULL");
  130. }
  131. if ((m_pfqpHead != NULL) && (m_pfqpTail != NULL))
  132. {
  133. Assert(m_ppqdataTail);
  134. Assert(m_ppqdataHead);
  135. if (m_pfqpHead != m_pfqpTail)
  136. {
  137. // If Tail and Head are non-NULL and not equal to each other, then they
  138. // must have non-NULL Prev and Next ptrs (respectively).
  139. if (NULL == m_pfqpTail->m_pfqpPrev)
  140. {
  141. DebugTrace((LPARAM) this, "Queue Assert: Tail's Prev ptr is NULL, Head != Tail");
  142. Assert(0 && "Queue Assert: Tail's Prev ptr is NULL, Head != Tail");
  143. }
  144. if (NULL == m_pfqpHead->m_pfqpNext)
  145. {
  146. DebugTrace((LPARAM) this, "Queue Assert: Head's Next ptr is NULL, Head != Tail");
  147. Assert(0 && "Queue Assert: Head's Next ptr is NULL, Head != Tail");
  148. }
  149. //Check count when Head and Tail differ
  150. pfqpTmp = m_pfqpTail->m_pfqpPrev;
  151. while (NULL != pfqpTmp)
  152. {
  153. cEntries += FIFOQ_QUEUE_PAGE_SIZE - pfqpTmp->m_cHoles;
  154. pfqpTmp = pfqpTmp->m_pfqpPrev;
  155. }
  156. cEntries += (DWORD)(m_ppqdataTail - m_pfqpTail->m_rgpqdata); //tail page
  157. cEntries -= m_pfqpTail->m_cHoles;
  158. cEntries -= (DWORD)(m_ppqdataHead - m_pfqpHead->m_rgpqdata); //head page
  159. if (cEntries != m_cQueueEntries)
  160. {
  161. DebugTrace((LPARAM) this, "Queue Assert: Count is %d when it should be %d",
  162. m_cQueueEntries, cEntries);
  163. Assert(0 && "Queue Assert: Entry Count is inaccurate");
  164. }
  165. }
  166. else //Head and Tail are same
  167. {
  168. Assert(m_pfqpHead == m_pfqpTail);
  169. cEntries = (DWORD)(m_ppqdataTail - m_ppqdataHead) - m_pfqpTail->m_cHoles;
  170. if (cEntries != m_cQueueEntries)
  171. {
  172. DebugTrace((LPARAM) this, "Queue Assert: Count is %d when it should be %d",
  173. m_cQueueEntries, cEntries);
  174. Assert(0 && "Queue Assert: Entry Count is inaccurate");
  175. }
  176. }
  177. }
  178. else if ((m_pfqpHead != NULL) && (m_pfqpTail == NULL))
  179. {
  180. //If Tail is NULL, then Head should be as well
  181. DebugTrace((LPARAM) this, "Queue Assert: Tail is NULL while Head is non-NULL");
  182. Assert(0 && "Queue Assert: Tail is NULL while Head is non-NULL");
  183. }
  184. else if (m_pfqpTail != NULL)
  185. {
  186. Assert(m_pfqpHead == NULL); //should fall out of if/else
  187. if (NULL == m_pfqpTail->m_pfqpPrev)
  188. {
  189. //count is easy here :)
  190. if (m_cQueueEntries != (size_t) (m_ppqdataTail - m_pfqpTail->m_rgpqdata))
  191. {
  192. DebugTrace((LPARAM) this, "Queue Assert: Count is %d when it should be %d",
  193. m_cQueueEntries, (m_ppqdataTail - m_pfqpTail->m_rgpqdata));
  194. Assert(0 && "Queue Assert: Entry Count is inaccurate");
  195. }
  196. }
  197. else //there is more than 1 page, but head is still NULL
  198. {
  199. pfqpTmp = m_pfqpTail->m_pfqpPrev;
  200. while (NULL != pfqpTmp)
  201. {
  202. cEntries += FIFOQ_QUEUE_PAGE_SIZE - pfqpTmp->m_cHoles;
  203. pfqpTmp = pfqpTmp->m_pfqpPrev;
  204. }
  205. cEntries += (DWORD)(m_ppqdataTail - m_pfqpTail->m_rgpqdata) - m_pfqpTail->m_cHoles;
  206. if (cEntries != m_cQueueEntries)
  207. {
  208. DebugTrace((LPARAM) this, "Queue Assert: Count is %d when it should be %d",
  209. m_cQueueEntries, cEntries);
  210. Assert(0 && "Queue Assert: Entry Count is inaccurate");
  211. }
  212. }
  213. }
  214. else //both head and tail are NULL
  215. {
  216. Assert((m_pfqpHead == NULL) && (m_pfqpTail == NULL)); //falls out of if/else
  217. if (m_cQueueEntries != 0)
  218. {
  219. //If both Head and Tail are NULL, them m_cQueueEntries == 0
  220. DebugTrace((LPARAM) this,
  221. "Queue Assert: Entry Counter is %d when queue should be empty",
  222. m_cQueueEntries);
  223. Assert(0 && "Queue Assert: Entry Counter is non-zero when queue should be empty");
  224. }
  225. }
  226. if (!fHaveLocks) //we aquired the locks in this function
  227. {
  228. m_slTail.ShareUnlock();
  229. m_slHead.ShareUnlock();
  230. }
  231. TraceFunctLeave();
  232. }
  233. #define AssertQueue() AssertQueueFn(FALSE)
  234. #define AssertQueueHaveLocks() AssertQueueFn(TRUE)
  235. #else //FIFOQ_ASSERT_QUEUE
  236. #define AssertQueue()
  237. #define AssertQueueHaveLocks()
  238. #endif //FIFOQ_ASSERT_QUEUE
  239. #else //not DEBUG
  240. #define AssertQueue()
  241. #define AssertQueueHaveLocks()
  242. #endif //DEBUG
  243. //---[ CFifoQueue Static Variables ]-------------------------------------------
  244. template <class PQDATA>
  245. volatile CFifoQueuePage<PQDATA> *CFifoQueue<PQDATA>::s_pfqpFree = NULL;
  246. template <class PQDATA>
  247. DWORD CFifoQueue<PQDATA>::s_cFreePages = 0;
  248. template <class PQDATA>
  249. DWORD CFifoQueue<PQDATA>::s_cFifoQueueObj = 0;
  250. template <class PQDATA>
  251. DWORD CFifoQueue<PQDATA>::s_cStaticRefs = 0;
  252. template <class PQDATA>
  253. CRITICAL_SECTION CFifoQueue<PQDATA>::s_csAlloc;
  254. #ifdef DEBUG
  255. template <class PQDATA>
  256. DWORD CFifoQueue<PQDATA>::s_cAllocated = 0;
  257. template <class PQDATA>
  258. DWORD CFifoQueue<PQDATA>::s_cDeleted = 0;
  259. template <class PQDATA>
  260. DWORD CFifoQueue<PQDATA>::s_cFreeAllocated = 0;
  261. template <class PQDATA>
  262. DWORD CFifoQueue<PQDATA>::s_cFreeDeleted = 0;
  263. #endif //DEBUG
  264. //---[ CFifoQueue::CFifoQueue ]------------------------------------------------
  265. //
  266. //
  267. // Description: CFifoQueue constructor
  268. //
  269. // Parameters: -
  270. //
  271. // Returns: -
  272. //
  273. //
  274. //-----------------------------------------------------------------------------
  275. template <class PQDATA>
  276. CFifoQueue<PQDATA>::CFifoQueue<PQDATA>()
  277. {
  278. TraceFunctEnterEx((LPARAM) this, "CFifoQueue::CFifoQueue");
  279. m_dwSignature = FIFOQ_SIG;
  280. m_cQueueEntries = 0; //set count of entries to 0
  281. m_pfqpHead = NULL; //Initialize page pointers
  282. m_pfqpTail = NULL;
  283. m_pfqpCursor = NULL;
  284. m_ppqdataHead = NULL; //Initialize data pointers
  285. m_ppqdataTail = NULL;
  286. m_ppqdataCursor = NULL;
  287. InterlockedIncrement((PLONG) &s_cFifoQueueObj);
  288. TraceFunctLeave();
  289. }
  290. //---[ CFifoQueue::~CFifoQueue ]------------------------------------------------
  291. //
  292. //
  293. // Description: CFifoQueue destructor
  294. //
  295. // Parameters: -
  296. //
  297. // Returns: -
  298. //
  299. //
  300. //-----------------------------------------------------------------------------
  301. template <class PQDATA>
  302. CFifoQueue<PQDATA>::~CFifoQueue<PQDATA>()
  303. {
  304. TraceFunctEnterEx((LPARAM) this, "CFifoQueue::~CFifoQueue");
  305. FQPAGE *pfqpTmp = NULL;
  306. if (m_cQueueEntries != 0)
  307. {
  308. PQDATA pqdata = NULL;
  309. int iLeft = m_cQueueEntries;
  310. for (int i = iLeft; i > 0; i--)
  311. {
  312. if (FAILED(HrDequeue(&pqdata)))
  313. break;
  314. Assert(NULL != pqdata);
  315. pqdata->Release();
  316. }
  317. }
  318. while (m_pfqpHead)
  319. {
  320. //If last dequeue could not delete page, then make sure pages
  321. //are freed
  322. pfqpTmp = m_pfqpHead->m_pfqpNext;
  323. FreeQueuePage(m_pfqpHead);
  324. m_pfqpHead = pfqpTmp;
  325. }
  326. InterlockedDecrement((PLONG) &s_cFifoQueueObj);
  327. TraceFunctLeave();
  328. }
  329. //---[ CFifoQueue::StaticInit() ]--------------------------------------------
  330. //
  331. //
  332. // Description: Initialization routines for CFifoQueue. This
  333. // is excplcitly single threaded. The limitations are:
  334. // - Only one thread in this function
  335. // - You cannot use any queues until this has completed
  336. //
  337. // Parameters: -
  338. //
  339. // Returns: -
  340. //
  341. //
  342. //-----------------------------------------------------------------------------
  343. template <class PQDATA>
  344. void CFifoQueue<PQDATA>::StaticInit()
  345. {
  346. TraceFunctEnter("CFifoQueue::HrStaticInit()");
  347. DWORD cRefs = 0;
  348. //
  349. // Add a static ref for each call to this
  350. //
  351. cRefs = InterlockedIncrement((PLONG) &s_cStaticRefs);
  352. if (1 == cRefs)
  353. {
  354. InitializeCriticalSection(&s_csAlloc);
  355. }
  356. //
  357. // Catch unsafe callers
  358. //
  359. _ASSERT(cRefs == s_cStaticRefs);
  360. TraceFunctLeave();
  361. }
  362. //---[ CFifoQueue::StaticDeinit() ]------------------------------------------
  363. //
  364. //
  365. // Description: Deinitialization routines for CFifoQueue
  366. //
  367. // Parameters: -
  368. //
  369. // Returns:
  370. // -
  371. //
  372. //-----------------------------------------------------------------------------
  373. template <class PQDATA>
  374. void CFifoQueue<PQDATA>::StaticDeinit()
  375. {
  376. TraceFunctEnter("CFifoQueue::HrStaticDeinit()");
  377. LONG lRefs = 0;
  378. lRefs = InterlockedDecrement((PLONG) &s_cStaticRefs);
  379. DWORD cLost = 0;
  380. DEBUG_DO_IT(cLost = s_cAllocated - s_cDeleted - s_cFreePages);
  381. if (lRefs == 0)
  382. {
  383. if (0 != cLost)
  384. ErrorTrace((LPARAM) NULL, "ERROR: CFifoQueue Deinit with %d Lost Pages", cLost);
  385. //This assert will catch if the any queue pages were allocated but not freed
  386. _ASSERT(!cLost && "We are leaking some queue pages");
  387. //There should be no other threads calling into this
  388. //note quite true, there are still outstanding refs at the time
  389. FQPAGE *pfqpCur = (FQPAGE *) s_pfqpFree;
  390. while (NULL != pfqpCur)
  391. {
  392. s_pfqpFree = pfqpCur->m_pfqpNext;
  393. delete pfqpCur;
  394. pfqpCur = (FQPAGE *) s_pfqpFree;
  395. s_cFreePages--;
  396. //It is possible to stop all server instances without
  397. //unloading the DLL. The cLost Assert will fire on the next
  398. //shutdown if we don't increment the deleted counter as well...
  399. //even though we aren't leaking any pages.
  400. DEBUG_DO_IT(s_cDeleted++);
  401. }
  402. //This assert catches if there are any free pages left after we walk the list
  403. Assert(s_cFreePages == 0);
  404. DeleteCriticalSection(&s_csAlloc);
  405. }
  406. TraceFunctLeave();
  407. }
  408. //---[ CFifoQueue::HrEnqueue ]-------------------------------------------------
  409. //
  410. //
  411. // Description: Enqueue a new item to the tail of the queue
  412. //
  413. // Parameters:
  414. // IN PQDATA pqdata Data to enqueue
  415. // Returns:
  416. // S_OK on success
  417. // E_OUTOFMEMORY if unable to allocate page
  418. //-----------------------------------------------------------------------------
  419. template <class PQDATA>
  420. HRESULT CFifoQueue<PQDATA>::HrEnqueue(IN PQDATA pqdata)
  421. {
  422. TraceFunctEnterEx((LPARAM) this, "CFifoQueue::HrEnqueue");
  423. HRESULT hr = S_OK;
  424. FQPAGE *pfqpNew = NULL; //newly allocated page
  425. AssertQueue();
  426. Assert(pqdata);
  427. pqdata->AddRef();
  428. m_slTail.ExclusiveLock();
  429. if ((m_pfqpTail == NULL) || //Queue is empty or needs a new queue page
  430. (m_pfqpTail->FIsOutOfBounds(m_ppqdataTail)))
  431. {
  432. //assert that tail is NULL or 1 past end of previous tail page
  433. Assert((m_ppqdataTail == NULL) || (m_ppqdataTail == (m_pfqpTail->m_rgpqdata + FIFOQ_QUEUE_PAGE_SIZE)));
  434. Assert((m_cQueueEntries == 0) || (m_pfqpTail != NULL));
  435. hr = HrAllocQueuePage(&pfqpNew);
  436. if (FAILED(hr))
  437. goto Exit;
  438. Assert(pfqpNew);
  439. if (NULL != m_pfqpTail) //Update Next & prev ptr if not first page
  440. {
  441. Assert(NULL == m_pfqpTail->m_pfqpNext);
  442. m_pfqpTail->m_pfqpNext = pfqpNew;
  443. pfqpNew->m_pfqpPrev = m_pfqpTail;
  444. }
  445. #ifndef SHARELOCK_TRY_BROKEN
  446. else {
  447. if (m_slHead.TryExclusiveLock())
  448. {
  449. //can update head stuff with impunity
  450. m_pfqpHead = pfqpNew;
  451. m_ppqdataHead = pfqpNew->m_rgpqdata;
  452. m_slHead.ExclusiveUnlock();
  453. }
  454. //else requeue or MapFn has lock
  455. }
  456. #endif //SHARELOCK_TRY_BROKEN
  457. m_pfqpTail = pfqpNew;
  458. m_ppqdataTail = pfqpNew->m_rgpqdata;
  459. }
  460. Assert(!m_pfqpTail->FIsOutOfBounds(m_ppqdataTail));
  461. Assert(m_ppqdataTail);
  462. *m_ppqdataTail = pqdata;
  463. m_ppqdataTail++;
  464. //increment count
  465. InterlockedIncrement((PLONG) &m_cQueueEntries);
  466. m_slTail.ExclusiveUnlock();
  467. Exit:
  468. AssertQueue();
  469. if (FAILED(hr))
  470. pqdata->Release();
  471. TraceFunctLeave();
  472. return hr;
  473. }
  474. //---[ CFifoQueue::HrDequeue ]-------------------------------------------------
  475. //
  476. //
  477. // Description: Dequeue an item from the queue
  478. //
  479. // Parameters:
  480. // OUT PQDATA *ppqdata Data dequeued
  481. //
  482. // Returns:
  483. // S_OK on success
  484. // AQUEUE_E_QUEUE_EMPTY if the queue is empty
  485. // E_NOTIMPL if fPrimary is FALSE (for now)
  486. //
  487. //-----------------------------------------------------------------------------
  488. template <class PQDATA>
  489. HRESULT CFifoQueue<PQDATA>::HrDequeue(OUT PQDATA *ppqdata)
  490. {
  491. TraceFunctEnterEx((LPARAM) this, "CFifoQueue::HrDequeue");
  492. HRESULT hr = S_OK;
  493. AssertQueue();
  494. Assert(ppqdata);
  495. if (m_cQueueEntries == 0)
  496. {
  497. hr = AQUEUE_E_QUEUE_EMPTY;
  498. goto Exit;
  499. }
  500. m_slHead.ExclusiveLock();
  501. hr = HrAdjustHead();
  502. if (FAILED(hr))
  503. {
  504. m_slHead.ExclusiveUnlock();
  505. goto Exit;
  506. }
  507. *ppqdata = *m_ppqdataHead;
  508. *m_ppqdataHead = NULL;
  509. InterlockedDecrement((PLONG) &m_cQueueEntries);
  510. m_ppqdataHead++; //If it crosses page boundary, then HrAdjustQueue
  511. //will fix it on next dequeue
  512. #ifndef SHARELOCK_TRY_BROKEN
  513. //Deal with brand new way of deleting last page
  514. if ((m_cQueueEntries == 0) && (m_slTail.TryExclusiveLock()))
  515. {
  516. //If we cannot access tail ptr, the enqueue in progress and
  517. //we should not delete the page they are enqueueing on
  518. if (m_cQueueEntries == 0) //gotta be thread safe
  519. {
  520. Assert(m_pfqpHead == m_pfqpTail);
  521. m_pfqpTail = NULL;
  522. m_ppqdataTail = NULL;
  523. m_slTail.ExclusiveUnlock();
  524. m_ppqdataHead = NULL;
  525. FreeQueuePage(m_pfqpHead);
  526. m_pfqpHead = NULL;
  527. }
  528. else
  529. m_slTail.ExclusiveUnlock();
  530. }
  531. #endif //SHARELOCK_TRY_BROKEN
  532. m_slHead.ExclusiveUnlock();
  533. Exit:
  534. AssertQueue();
  535. if (FAILED(hr))
  536. *ppqdata = NULL;
  537. #ifdef DEBUG
  538. else
  539. Assert(NULL != *ppqdata);
  540. #endif //DEBUG
  541. TraceFunctLeave();
  542. return hr;
  543. }
  544. //---[ CFifoQueue::HrRequeue ]--------------------------------------------------
  545. //
  546. //
  547. // Description:
  548. // Requeues a message to the head of the queue (like an enqueue that occurs
  549. // at the head.
  550. //
  551. // Parameters:
  552. // IN PQDATA pqdata data to be enqueued
  553. // Returns:
  554. // S_OK on success
  555. // E_OUTOFMEMORY if an allocation error occurs
  556. //
  557. //-----------------------------------------------------------------------------
  558. template <class PQDATA>
  559. HRESULT CFifoQueue<PQDATA>::HrRequeue(IN PQDATA pqdata)
  560. {
  561. TraceFunctEnterEx((LPARAM) this, "CFifoQueue::HrRequeue");
  562. HRESULT hr = S_OK;
  563. PQDATA *ppqdataNewHead = NULL;
  564. BOOL fHeadLocked = FALSE;
  565. AssertQueue();
  566. Assert(pqdata);
  567. pqdata->AddRef();
  568. m_slHead.ExclusiveLock();
  569. fHeadLocked = TRUE;
  570. ppqdataNewHead = m_ppqdataHead - 1;
  571. //There are 2 cases to worry about here
  572. // CASE 0: Head page is NULL - Either Queue is empty, or head has not
  573. // been updated yet... may be changed into CASE 2 if queue non-empty
  574. // CASE 1: Head page is valid and decremented Headptr is on a Head page
  575. // In this case, the data can be requeued. Having m_slHead will make
  576. // sure that the Head Page is not deleted from underneath us
  577. // CASE 2: New Head ptr is invalid. We need to allocate a new page to
  578. // put requeued data on.
  579. if (NULL == m_pfqpHead)
  580. {
  581. //CASE 0
  582. hr = HrAdjustHead();
  583. if (FAILED(hr))
  584. {
  585. if (AQUEUE_E_QUEUE_EMPTY == hr)
  586. {
  587. //Queue is empty... just enqueue
  588. //But first, release head lock so enqueue has can allocate
  589. //first page etc.... Otherwise, we would guarantee failure
  590. //of enqueue TryExlusiveLock and for HrAdjustHead to do the
  591. //work next time.
  592. m_slHead.ExclusiveUnlock();
  593. fHeadLocked = FALSE;
  594. hr = HrEnqueue(pqdata);
  595. if (SUCCEEDED(hr))
  596. pqdata->Release();
  597. }
  598. goto Exit;
  599. }
  600. //else will fall through to case 2
  601. }
  602. if ((m_pfqpHead != NULL) && !m_pfqpHead->FIsOutOfBounds(ppqdataNewHead))
  603. {
  604. //CASE 1
  605. *ppqdataNewHead = pqdata;
  606. m_ppqdataHead = ppqdataNewHead;
  607. }
  608. else
  609. {
  610. //CASE 2
  611. FQPAGE *pfqpNew = NULL;
  612. hr = HrAllocQueuePage(&pfqpNew);
  613. if (FAILED(hr))
  614. goto Exit;
  615. //make sure next points to the head page
  616. pfqpNew->m_pfqpNext = m_pfqpHead;
  617. //prev needs to point to the new page
  618. if (m_pfqpHead)
  619. m_pfqpHead->m_pfqpPrev = pfqpNew;
  620. m_pfqpHead = pfqpNew;
  621. //write the data & update local copy of head
  622. m_ppqdataHead = &(pfqpNew->m_rgpqdata[FIFOQ_QUEUE_PAGE_SIZE-1]);
  623. *m_ppqdataHead = pqdata;
  624. }
  625. InterlockedIncrement((PLONG) &m_cQueueEntries);
  626. Exit:
  627. if (fHeadLocked)
  628. m_slHead.ExclusiveUnlock();
  629. AssertQueue();
  630. if (FAILED(hr))
  631. pqdata->Release();
  632. TraceFunctLeave();
  633. return hr;
  634. }
  635. //---[ CFifoQueue::HrPeek ]-----------------------------------------------------
  636. //
  637. //
  638. // Description:
  639. // Peeks at the head data on the queue.
  640. // Parameters:
  641. // OUT PQDATA *ppqdata returned data
  642. // Returns:
  643. // S_OK on success
  644. // AQUEUE_E_QUEUE_EMPTY if the queue has no data in it
  645. // possibly E_FAIL or E_OUTOFMEMORY if one of the supporting functions fail
  646. //-----------------------------------------------------------------------------
  647. template <class PQDATA>
  648. HRESULT CFifoQueue<PQDATA>::HrPeek(OUT PQDATA *ppqdata)
  649. {
  650. TraceFunctEnterEx((LPARAM) this, "CFifoQueue::HrPeek");
  651. HRESULT hr = S_OK;
  652. AssertQueue();
  653. Assert(ppqdata);
  654. if (m_cQueueEntries == 0)
  655. {
  656. hr = AQUEUE_E_QUEUE_EMPTY;
  657. goto Exit;
  658. }
  659. m_slHead.ExclusiveLock();
  660. hr = HrAdjustHead();
  661. if (FAILED(hr))
  662. goto Exit;
  663. *ppqdata = *m_ppqdataHead;
  664. (*ppqdata)->AddRef();
  665. Exit:
  666. m_slHead.ExclusiveUnlock();
  667. AssertQueue();
  668. TraceFunctLeave();
  669. return hr;
  670. }
  671. //---[ CFifoQueue::HrMapFn ]---------------------------------
  672. //
  673. //
  674. // Description:
  675. // Advances a secondary cursor until supplied function returns FALSE
  676. // Parameters:
  677. // IN pFunc - must be a function with the following prototype:
  678. //
  679. // HRESULT pvFunc(
  680. // IN PQDATA pqdata, //ptr to data on queue
  681. // IN PVOID pvContext,
  682. // OUT BOOL *pfContinue, //TRUE if we should continue
  683. // OUT BOOL *pfDelete); //TRUE if item should be deleted
  684. // pvFunc must NOT release pqdata.. if it is no longer valid, it should
  685. // return TRUE in pfDelete, and the calling code will remove it from
  686. // the queue and release it.
  687. //
  688. // OUT pcItems - count of queue items removed from queue
  689. //
  690. // Returns:
  691. // S_OK on success
  692. // E_INVALIDARG if pvFunc is not valid
  693. //-----------------------------------------------------------------------------
  694. template <class PQDATA>
  695. HRESULT CFifoQueue<PQDATA>::HrMapFn(
  696. IN MAPFNAPI pFunc,
  697. IN PVOID pvContext,
  698. OUT DWORD *pcItems)
  699. {
  700. //$$TODO: Test the context handle feature
  701. TraceFunctEnterEx((LPARAM) this, "CFifoQueue::HrMapFn");
  702. HRESULT hr = S_OK;
  703. FQPAGE *pfqpCurrent = NULL; //The current page we are looking at
  704. FQPAGE *pfqpTmp = NULL;
  705. PQDATA *ppqdataCurrent = NULL; //The current queue data we are looking at
  706. PQDATA *ppqdataLastValid = NULL; //The last non-NULL queue data
  707. DWORD cItems = 0;
  708. BOOL fPageInUse = FALSE;
  709. BOOL fContinue = FALSE;
  710. BOOL fDelete = FALSE;
  711. BOOL fLocked = FALSE;
  712. //Variables that make it easier to debug this function
  713. PQDATA *ppqdataOldTail = NULL;
  714. PQDATA *ppqdataOldHead = NULL;
  715. if (NULL != pcItems)
  716. *pcItems = 0;
  717. if (NULL == pFunc) //$$REVIEW - more validation than this?
  718. {
  719. hr = E_INVALIDARG;
  720. goto Exit;
  721. }
  722. if (0 == m_cQueueEntries) //don't even bother if nothing is in the queue
  723. goto Exit;
  724. m_slHead.ExclusiveLock();
  725. m_slTail.ExclusiveLock();
  726. fLocked = TRUE;
  727. DebugTrace((LPARAM) this, "MapFn Has Exclusive Locks");
  728. //make sure that head pointer is adjusted properly
  729. hr = HrAdjustHead();
  730. if (FAILED(hr))
  731. {
  732. _ASSERT((AQUEUE_E_QUEUE_EMPTY == hr) && "HrAdjustHead failed without AQUEUE_E_QUEUE_EMPTY!!!!");
  733. hr = S_OK;
  734. }
  735. AssertQueueHaveLocks();
  736. pfqpCurrent = m_pfqpHead; //start at head and work backwards
  737. ppqdataCurrent = m_ppqdataHead;
  738. _ASSERT(pfqpCurrent || !m_cQueueEntries);
  739. while (NULL != pfqpCurrent)
  740. {
  741. DEBUG_DO_IT(ppqdataOldTail = m_ppqdataTail);
  742. DEBUG_DO_IT(ppqdataOldHead = m_ppqdataHead);
  743. if (m_cQueueEntries == 0)
  744. {
  745. Assert(m_pfqpHead == m_pfqpTail);
  746. Assert(m_pfqpHead == pfqpCurrent);
  747. m_pfqpHead = NULL;
  748. m_pfqpTail = NULL;
  749. m_ppqdataHead = NULL;
  750. m_ppqdataTail = NULL;
  751. FreeQueuePage(pfqpCurrent);
  752. pfqpCurrent = NULL;
  753. goto Exit;
  754. }
  755. if (pfqpCurrent->FIsOutOfBounds(ppqdataCurrent) ||
  756. ((m_pfqpTail == pfqpCurrent) && (ppqdataCurrent >= m_ppqdataTail)))
  757. {
  758. //We are ready to set pfqpCurrent to point to the next page.. may need to
  759. //free the old page.
  760. if (fPageInUse)
  761. {
  762. //don't delete the page if there is still something on there
  763. pfqpCurrent = pfqpCurrent->m_pfqpNext;
  764. }
  765. else
  766. {
  767. pfqpTmp = pfqpCurrent->m_pfqpNext;
  768. if (NULL != pfqpTmp)
  769. pfqpTmp->m_pfqpPrev = pfqpCurrent->m_pfqpPrev;
  770. else
  771. {
  772. Assert(pfqpCurrent == m_pfqpTail); //It must be the tail
  773. //point the tail to the next page
  774. m_pfqpTail = m_pfqpTail->m_pfqpPrev;
  775. m_ppqdataTail = ppqdataLastValid + 1;
  776. //If last page was not deleted, then the last valid ptr should be on it
  777. Assert((NULL == m_pfqpTail) || !m_pfqpTail->FIsOutOfBounds(ppqdataLastValid));
  778. #ifdef FIFOQ_ASSERT_QUEUE
  779. //fixup Hole count
  780. //will not touch count if Tail ptr is after end of tail page
  781. for (PQDATA *ppqdataTmp = m_ppqdataTail;
  782. ppqdataTmp < m_pfqpTail->m_rgpqdata + FIFOQ_QUEUE_PAGE_SIZE;
  783. ppqdataTmp++)
  784. {
  785. if (NULL == *ppqdataTmp)
  786. m_pfqpTail->m_cHoles--;
  787. }
  788. #endif //FIFOQ_ASSERT_QUEUE
  789. ppqdataLastValid = NULL;
  790. }
  791. if (NULL != pfqpCurrent->m_pfqpPrev)
  792. {
  793. Assert(pfqpCurrent->m_pfqpPrev->m_pfqpNext == pfqpCurrent);
  794. pfqpCurrent->m_pfqpPrev->m_pfqpNext = pfqpTmp;
  795. }
  796. else
  797. {
  798. //if it does not have a prev pointer is should be the head
  799. Assert(pfqpCurrent == m_pfqpHead);
  800. Assert(NULL == pfqpCurrent->m_pfqpPrev);
  801. if (m_pfqpTail == m_pfqpHead) //be sure to make tail valid
  802. {
  803. Assert(0); //the 1st if/else now handles this
  804. }
  805. m_pfqpHead = pfqpTmp;
  806. m_ppqdataHead = m_pfqpHead->m_rgpqdata;
  807. }
  808. AssertQueueHaveLocks();//try to see what has happened before freeing
  809. FreeQueuePage(pfqpCurrent);
  810. pfqpCurrent = pfqpTmp;
  811. if (NULL != m_pfqpHead) {
  812. Assert(NULL == m_pfqpHead->m_pfqpPrev);
  813. }
  814. AssertQueueHaveLocks();
  815. }
  816. if (NULL == pfqpCurrent)
  817. break;
  818. ppqdataCurrent = pfqpCurrent->m_rgpqdata;
  819. fPageInUse = FALSE;
  820. }
  821. Assert(ppqdataCurrent); //the above should guarantee this
  822. if (NULL != *ppqdataCurrent)
  823. {
  824. hr = pFunc(*ppqdataCurrent, pvContext, &fContinue, &fDelete);
  825. if (FAILED(hr))
  826. goto Exit;
  827. if (fDelete)
  828. {
  829. InterlockedDecrement((PLONG) &m_cQueueEntries);
  830. (*ppqdataCurrent)->Release();
  831. *ppqdataCurrent = NULL;
  832. #ifdef FIFOQ_ASSERT_QUEUE
  833. pfqpCurrent->m_cHoles++; //adjust Hole counter for assertions
  834. #endif //FIFOQ_ASSERT_QUEUE
  835. cItems++;
  836. }
  837. else
  838. {
  839. fPageInUse = TRUE;
  840. ppqdataLastValid = ppqdataCurrent;
  841. }
  842. if (!fContinue)
  843. break;
  844. }
  845. ppqdataCurrent++;
  846. }
  847. Exit:
  848. if (fLocked)
  849. {
  850. AssertQueueHaveLocks();
  851. m_slTail.ExclusiveUnlock();
  852. m_slHead.ExclusiveUnlock();
  853. }
  854. else
  855. {
  856. AssertQueue();
  857. }
  858. if (NULL != pcItems)
  859. *pcItems = cItems;
  860. TraceFunctLeave();
  861. return hr;
  862. }
  863. //---[ CFifoQueue::HrAdjustHead ]----------------------------------------------
  864. //
  865. //
  866. // Description:
  867. // Adjust Head ptr and Head Page ptr if necessary for pending dequeue or
  868. // peek. To keep operations thread-safe, you MUST have the head lock
  869. //
  870. // This function is used because there are many operations that may leave
  871. // the head page/ptr in an inconsistant state, but very few that actually
  872. // need them to be consistant. Rather than running the risk of missing
  873. // a case where the head ptr is inconsistant, we call this function when
  874. // we need them to be consistant
  875. //
  876. // Head page and head ptr may be updated as a side-effect
  877. // Parameters:
  878. // -
  879. // Returns:
  880. // S_OK on success
  881. // AQUEUE_E_QUEUE_EMPTY if the queue is empty (or becomes empty)
  882. //
  883. //-----------------------------------------------------------------------------
  884. template <class PQDATA>
  885. HRESULT CFifoQueue<PQDATA>::HrAdjustHead()
  886. {
  887. TraceFunctEnterEx((LPARAM) this, "CFifoQueue::HrAdjustHead");
  888. HRESULT hr = S_OK;
  889. //AssertQueue(); // the locks we are using are not re-entrant.
  890. //Make sure that something hasn't been dequeued from underneath us
  891. //at least from our perception of the ptrs
  892. if (m_cQueueEntries == 0)
  893. {
  894. hr = AQUEUE_E_QUEUE_EMPTY;
  895. goto Exit;
  896. }
  897. while (TRUE) //handle holes in queue (marked as NULL ptrs)
  898. {
  899. //now find an appropriate value for the head ptr
  900. // Case 0: if Head Page is NULL, then find first page by searching from
  901. // Tail page. This case happens when the queue is truely empty
  902. // or first enqueue could not get Tail lock.
  903. // Case 1: if Head data pointer is NULL, or invalid and not just
  904. // past end of head page, then set it to first thing on
  905. // head page.
  906. //
  907. // $$REVIEW - I don't think there are any cases that
  908. // can cause (and not case 0). I will put an assert in to
  909. // make sure this is truely the case.
  910. // Case 2: if just past end of page, attempt to update head page,
  911. // and set to first thing on new head page. This means
  912. // that the last item on that page has been dequeued.
  913. // Case 3: Within current head page boundaries, keep it as is.
  914. // This is the 90% case that happens most often during
  915. // normal operation.
  916. if (NULL == m_pfqpHead)
  917. {
  918. //case 0
  919. DebugTrace((LPARAM) this, "Searching list for Head page");
  920. m_pfqpHead = m_pfqpTail;
  921. if (NULL == m_pfqpHead) //there IS nothing in the queue
  922. {
  923. Assert(0 == m_cQueueEntries);
  924. hr = AQUEUE_E_QUEUE_EMPTY;
  925. goto Exit;
  926. }
  927. while (NULL != m_pfqpHead->m_pfqpPrev) //get to first page
  928. {
  929. m_pfqpHead = m_pfqpHead->m_pfqpPrev;
  930. }
  931. m_ppqdataHead = m_pfqpHead->m_rgpqdata;
  932. }
  933. _ASSERT(m_pfqpHead); //otherwise should have returned AQUEUE_E_QUEUE_EMPTY
  934. if ((m_ppqdataHead == NULL) ||
  935. (m_pfqpHead->FIsOutOfBounds(m_ppqdataHead) &&
  936. (m_ppqdataHead != (&m_pfqpHead->m_rgpqdata[FIFOQ_QUEUE_PAGE_SIZE]))))
  937. {
  938. //case 1
  939. m_ppqdataHead = m_pfqpHead->m_rgpqdata;
  940. _ASSERT(0 && "Non-fatal assert... get mikeswa to take a look at this case");
  941. }
  942. else if (m_ppqdataHead == (&m_pfqpHead->m_rgpqdata[FIFOQ_QUEUE_PAGE_SIZE]))
  943. {
  944. //case 2
  945. DebugTrace((LPARAM) this, "Deleting page 0x%08X", m_pfqpHead);
  946. //set new head page
  947. FQPAGE *pfqpOld = m_pfqpHead;
  948. m_pfqpHead = m_pfqpHead->m_pfqpNext;
  949. Assert(m_pfqpHead->m_pfqpPrev == pfqpOld);
  950. Assert(m_pfqpHead); //There must be a next head if not empty
  951. m_pfqpHead->m_pfqpPrev = NULL;
  952. m_ppqdataHead = m_pfqpHead->m_rgpqdata;
  953. FreeQueuePage(pfqpOld);
  954. _ASSERT(m_pfqpHead && (NULL == m_pfqpHead->m_pfqpPrev));
  955. }
  956. if (NULL != *m_ppqdataHead)
  957. break;
  958. else
  959. {
  960. //Case 3
  961. m_ppqdataHead++;
  962. #ifdef FIFOQ_ASSERT_QUEUE
  963. Assert(m_pfqpHead->m_cHoles >= 1);
  964. Assert(m_pfqpHead->m_cHoles <= FIFOQ_QUEUE_PAGE_SIZE);
  965. m_pfqpHead->m_cHoles--;
  966. #endif //FIFOQ_ASSERT_QUEUE
  967. }
  968. }
  969. Exit:
  970. TraceFunctLeave();
  971. return hr;
  972. }
  973. //---[ CFifoQueue::HrAllocQueuePage ]------------------------------------------
  974. //
  975. //
  976. // Description: Allocates a queue page
  977. //
  978. // Parameters:
  979. // OUT FQPAGE **ppfqp newly allocated page
  980. // Returns:
  981. // S_OK on success
  982. // E_OUTOFMEMORY on failure
  983. //-----------------------------------------------------------------------------
  984. template <class PQDATA>
  985. HRESULT CFifoQueue<PQDATA>::HrAllocQueuePage(FQPAGE **ppfqp)
  986. {
  987. TraceFunctEnterEx((LPARAM) s_cFreePages, "CFifoQueue::HrAllocQueuePage");
  988. HRESULT hr = S_OK;
  989. FQPAGE *pfqpNew = NULL;
  990. FQPAGE *pfqpNext = NULL;
  991. FQPAGE *pfqpCheck = NULL;
  992. Assert(ppfqp);
  993. *ppfqp = NULL;
  994. if (s_cFreePages)
  995. {
  996. //
  997. // Grab critical section before looking at head of the free list
  998. //
  999. EnterCriticalSection(&s_csAlloc);
  1000. pfqpNew = (FQPAGE *) s_pfqpFree;
  1001. if (NULL != pfqpNew)
  1002. {
  1003. pfqpNext = pfqpNew->m_pfqpNext;
  1004. s_pfqpFree = pfqpNext;
  1005. *ppfqp = pfqpNew;
  1006. }
  1007. //
  1008. // Release the critical section now that we are done with the free list
  1009. //
  1010. LeaveCriticalSection(&s_csAlloc);
  1011. //
  1012. // If our allocation was successfull, bail and return the new page
  1013. //
  1014. if (*ppfqp)
  1015. {
  1016. InterlockedDecrement((PLONG) &s_cFreePages);
  1017. #ifdef DEBUG
  1018. InterlockedIncrement((PLONG) &s_cFreeAllocated);
  1019. #endif //DEBUG
  1020. pfqpNew->Recycle();
  1021. goto Exit;
  1022. }
  1023. }
  1024. *ppfqp = new FQPAGE();
  1025. if (*ppfqp == NULL)
  1026. {
  1027. hr = E_OUTOFMEMORY;
  1028. goto Exit;
  1029. }
  1030. #ifdef DEBUG
  1031. InterlockedIncrement((PLONG) &s_cAllocated);
  1032. #endif //DEBUG
  1033. Exit:
  1034. TraceFunctLeave();
  1035. return hr;
  1036. }
  1037. //---[ CFifoQueue::FreeQueuePage ]------------------------------------------------------------
  1038. //
  1039. //
  1040. // Description: Free's a queue page, by putting it on the free list.
  1041. //
  1042. // Parameters:
  1043. // FQPAGE *pfqp page to free
  1044. // Returns:
  1045. // -
  1046. //-----------------------------------------------------------------------------
  1047. template <class PQDATA>
  1048. void CFifoQueue<PQDATA>::FreeQueuePage(FQPAGE *pfqp)
  1049. {
  1050. TraceFunctEnterEx((LPARAM) s_cFreePages, "CFifoQueue::FreeQueuePage");
  1051. Assert(pfqp);
  1052. Assert(pfqp != s_pfqpFree); //check against pushing same thing twice in a row
  1053. FQPAGE *pfqpCheck = NULL;
  1054. FQPAGE *pfqpFree = NULL;
  1055. if (s_cFreePages < FIFOQ_QUEUE_MAX_FREE_PAGES)
  1056. {
  1057. //
  1058. // Grab critical section before looking at head of the free list
  1059. //
  1060. EnterCriticalSection(&s_csAlloc);
  1061. //
  1062. // Update the free list
  1063. //
  1064. pfqpFree = (FQPAGE *) s_pfqpFree;
  1065. pfqp->m_pfqpNext = pfqpFree;
  1066. s_pfqpFree = pfqp;
  1067. //
  1068. // Release Critical section now that we have updated the freelist
  1069. //
  1070. LeaveCriticalSection(&s_csAlloc);
  1071. InterlockedIncrement((PLONG) &s_cFreePages);
  1072. #ifdef DEBUG
  1073. InterlockedIncrement((PLONG) &s_cFreeDeleted);
  1074. #endif //DEBUG
  1075. }
  1076. else
  1077. {
  1078. delete pfqp;
  1079. #ifdef DEBUG
  1080. InterlockedIncrement((PLONG) &s_cDeleted);
  1081. #endif //DEBUG
  1082. }
  1083. TraceFunctLeave();
  1084. }
  1085. //---[ HrClearQueueMapFn ]-----------------------------------------------------
  1086. //
  1087. //
  1088. // Description:
  1089. // Example default function to use with HrMapFn... will always return TRUE
  1090. // to continue and delete the current queued data
  1091. // Parameters:
  1092. // IN PQDATA pqdata, //ptr to data on queue
  1093. // IN PVOID pvContext - ignored
  1094. // OUT BOOL *pfContinue, //TRUE if we should continue
  1095. // OUT BOOL *pfDelete); //TRUE if item should be deleted
  1096. // Returns:
  1097. // S_OK
  1098. //
  1099. //-----------------------------------------------------------------------------
  1100. template <class PQDATA>
  1101. HRESULT HrClearQueueMapFn(IN PQDATA pqdata, IN PVOID pvContext, OUT BOOL *pfContinue, OUT BOOL *pfDelete)
  1102. {
  1103. Assert(pfContinue);
  1104. Assert(pfDelete);
  1105. HRESULT hr = S_OK;
  1106. *pfContinue = TRUE;
  1107. *pfDelete = TRUE;
  1108. return hr;
  1109. }