Team Fortress 2 Source Code as on 22/4/2020
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.

1004 lines
22 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // LIFO from disassembly of Windows API and http://perso.wanadoo.fr/gmem/evenements/jim2002/articles/L17_Fober.pdf
  6. // FIFO from http://perso.wanadoo.fr/gmem/evenements/jim2002/articles/L17_Fober.pdf
  7. //
  8. //=============================================================================
  9. #ifndef TSLIST_H
  10. #define TSLIST_H
  11. #if defined( _WIN32 )
  12. #pragma once
  13. // Suppress this spurious warning:
  14. // warning C4700: uninitialized local variable 'oldHead' used
  15. #pragma warning( push )
  16. #pragma warning( disable : 4700 )
  17. #endif
  18. #if defined( USE_NATIVE_SLIST ) && !defined( _X360 )
  19. #define WIN32_LEAN_AND_MEAN
  20. #include <windows.h>
  21. #endif
  22. #include "tier0/dbg.h"
  23. #include "tier0/threadtools.h"
  24. #include "tier0/memalloc.h"
  25. #include "tier0/memdbgoff.h"
  26. #if defined( _X360 )
  27. #define USE_NATIVE_SLIST
  28. #endif
  29. //-----------------------------------------------------------------------------
  30. #if defined( PLATFORM_64BITS )
  31. #define TSLIST_HEAD_ALIGNMENT 16
  32. #define TSLIST_NODE_ALIGNMENT 16
  33. inline bool ThreadInterlockedAssignIf64x128( volatile int128 *pDest, const int128 &value, const int128 &comperand )
  34. { return ThreadInterlockedAssignIf128( pDest, value, comperand ); }
  35. #else
  36. #define TSLIST_HEAD_ALIGNMENT 8
  37. #define TSLIST_NODE_ALIGNMENT 8
  38. inline bool ThreadInterlockedAssignIf64x128( volatile int64 *pDest, const int64 value, const int64 comperand )
  39. { return ThreadInterlockedAssignIf64( pDest, value, comperand ); }
  40. #endif
  41. #ifdef _MSC_VER
  42. #define TSLIST_HEAD_ALIGN DECL_ALIGN(TSLIST_HEAD_ALIGNMENT)
  43. #define TSLIST_NODE_ALIGN DECL_ALIGN(TSLIST_NODE_ALIGNMENT)
  44. #define TSLIST_HEAD_ALIGN_POST
  45. #define TSLIST_NODE_ALIGN_POST
  46. #elif defined( GNUC )
  47. #define TSLIST_HEAD_ALIGN
  48. #define TSLIST_NODE_ALIGN
  49. #define TSLIST_HEAD_ALIGN_POST DECL_ALIGN(TSLIST_HEAD_ALIGNMENT)
  50. #define TSLIST_NODE_ALIGN_POST DECL_ALIGN(TSLIST_NODE_ALIGNMENT)
  51. #elif defined( _PS3 )
  52. #define TSLIST_HEAD_ALIGNMENT 8
  53. #define TSLIST_NODE_ALIGNMENT 8
  54. #define TSLIST_HEAD_ALIGN ALIGN8
  55. #define TSLIST_NODE_ALIGN ALIGN8
  56. #define TSLIST_HEAD_ALIGN_POST ALIGN8_POST
  57. #define TSLIST_NODE_ALIGN_POST ALIGN8_POST
  58. #else
  59. #error
  60. #endif
  61. //-----------------------------------------------------------------------------
  62. PLATFORM_INTERFACE bool RunTSQueueTests( int nListSize = 10000, int nTests = 1 );
  63. PLATFORM_INTERFACE bool RunTSListTests( int nListSize = 10000, int nTests = 1 );
  64. //-----------------------------------------------------------------------------
  65. // Lock free list.
  66. //-----------------------------------------------------------------------------
  67. //#define USE_NATIVE_SLIST
  68. #ifdef USE_NATIVE_SLIST
  69. typedef SLIST_ENTRY TSLNodeBase_t;
  70. typedef SLIST_HEADER TSLHead_t;
  71. #else
  72. struct TSLIST_NODE_ALIGN TSLNodeBase_t
  73. {
  74. TSLNodeBase_t *Next; // name to match Windows
  75. } TSLIST_NODE_ALIGN_POST;
  76. union TSLIST_HEAD_ALIGN TSLHead_t
  77. {
  78. struct Value_t
  79. {
  80. TSLNodeBase_t *Next;
  81. // <sergiy> Depth must be in the least significant halfword when atomically loading into register,
  82. // to avoid carrying digits from Sequence. Carrying digits from Depth to Sequence is ok,
  83. // because Sequence can be pretty much random. We could operate on both of them separately,
  84. // but it could perhaps (?) lead to problems with store forwarding. I don't know 'cause I didn't
  85. // performance-test or design original code, I'm just making it work on PowerPC.
  86. #ifdef VALVE_BIG_ENDIAN
  87. int16 Sequence;
  88. int16 Depth;
  89. #else
  90. int16 Depth;
  91. int16 Sequence;
  92. #endif
  93. #ifdef PLATFORM_64BITS
  94. int32 Padding;
  95. #endif
  96. } value;
  97. struct Value32_t
  98. {
  99. TSLNodeBase_t *Next_do_not_use_me;
  100. int32 DepthAndSequence;
  101. } value32;
  102. #ifdef PLATFORM_64BITS
  103. int128 value64x128;
  104. #else
  105. int64 value64x128;
  106. #endif
  107. } TSLIST_HEAD_ALIGN_POST;
  108. #endif
  109. //-------------------------------------
  110. class CTSListBase
  111. {
  112. public:
  113. // override new/delete so we can guarantee 8-byte aligned allocs
  114. static void * operator new( size_t size )
  115. {
  116. CTSListBase *pNode = (CTSListBase *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ );
  117. return pNode;
  118. }
  119. static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine )
  120. {
  121. CTSListBase *pNode = (CTSListBase *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, pFileName, nLine );
  122. return pNode;
  123. }
  124. static void operator delete( void *p)
  125. {
  126. MemAlloc_FreeAligned( p );
  127. }
  128. static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine )
  129. {
  130. MemAlloc_FreeAligned( p );
  131. }
  132. private:
  133. // These ain't gonna work
  134. static void * operator new[] ( size_t size );
  135. static void operator delete [] ( void *p);
  136. public:
  137. CTSListBase()
  138. {
  139. if ( ((size_t)&m_Head) % TSLIST_HEAD_ALIGNMENT != 0 )
  140. {
  141. Error( "CTSListBase: Misaligned list\n" );
  142. DebuggerBreak();
  143. }
  144. #ifdef USE_NATIVE_SLIST
  145. InitializeSListHead( &m_Head );
  146. #elif defined(PLATFORM_64BITS)
  147. m_Head.value64x128 = int128_zero();
  148. #else
  149. m_Head.value64x128 = (int64)0;
  150. #endif
  151. }
  152. ~CTSListBase()
  153. {
  154. Detach();
  155. }
  156. TSLNodeBase_t *Push( TSLNodeBase_t *pNode )
  157. {
  158. #ifdef _DEBUG
  159. if ( (size_t)pNode % TSLIST_NODE_ALIGNMENT != 0 )
  160. {
  161. Error( "CTSListBase: Misaligned node\n" );
  162. DebuggerBreak();
  163. }
  164. #endif
  165. #ifdef USE_NATIVE_SLIST
  166. #ifdef _X360
  167. // integrated write-release barrier
  168. return (TSLNodeBase_t *)InterlockedPushEntrySListRelease( &m_Head, pNode );
  169. #else
  170. return (TSLNodeBase_t *)InterlockedPushEntrySList( &m_Head, pNode );
  171. #endif
  172. #else
  173. TSLHead_t oldHead;
  174. TSLHead_t newHead;
  175. #if defined( PLATFORM_PS3 ) || defined( PLATFORM_X360 )
  176. __lwsync(); // write-release barrier
  177. #endif
  178. #ifdef PLATFORM_64BITS
  179. newHead.value.Padding = 0;
  180. #endif
  181. for (;;)
  182. {
  183. oldHead.value64x128 = m_Head.value64x128;
  184. pNode->Next = oldHead.value.Next;
  185. newHead.value.Next = pNode;
  186. newHead.value32.DepthAndSequence = oldHead.value32.DepthAndSequence + 0x10001;
  187. if ( ThreadInterlockedAssignIf64x128( &m_Head.value64x128, newHead.value64x128, oldHead.value64x128 ) )
  188. {
  189. break;
  190. }
  191. ThreadPause();
  192. };
  193. return (TSLNodeBase_t *)oldHead.value.Next;
  194. #endif
  195. }
  196. TSLNodeBase_t *Pop()
  197. {
  198. #ifdef USE_NATIVE_SLIST
  199. #ifdef _X360
  200. // integrated read-acquire barrier
  201. TSLNodeBase_t *pNode = (TSLNodeBase_t *)InterlockedPopEntrySListAcquire( &m_Head );
  202. #else
  203. TSLNodeBase_t *pNode = (TSLNodeBase_t *)InterlockedPopEntrySList( &m_Head );
  204. #endif
  205. return pNode;
  206. #else
  207. TSLHead_t oldHead;
  208. TSLHead_t newHead;
  209. #ifdef PLATFORM_64BITS
  210. newHead.value.Padding = 0;
  211. #endif
  212. for (;;)
  213. {
  214. oldHead.value64x128 = m_Head.value64x128;
  215. if ( !oldHead.value.Next )
  216. return NULL;
  217. newHead.value.Next = oldHead.value.Next->Next;
  218. newHead.value32.DepthAndSequence = oldHead.value32.DepthAndSequence - 1;
  219. if ( ThreadInterlockedAssignIf64x128( &m_Head.value64x128, newHead.value64x128, oldHead.value64x128 ) )
  220. {
  221. #if defined( PLATFORM_PS3 ) || defined( PLATFORM_X360 )
  222. __lwsync(); // read-acquire barrier
  223. #endif
  224. break;
  225. }
  226. ThreadPause();
  227. };
  228. return (TSLNodeBase_t *)oldHead.value.Next;
  229. #endif
  230. }
  231. TSLNodeBase_t *Detach()
  232. {
  233. #ifdef USE_NATIVE_SLIST
  234. TSLNodeBase_t *pBase = (TSLNodeBase_t *)InterlockedFlushSList( &m_Head );
  235. #if defined( _X360 ) || defined( _PS3 )
  236. __lwsync(); // read-acquire barrier
  237. #endif
  238. return pBase;
  239. #else
  240. TSLHead_t oldHead;
  241. TSLHead_t newHead;
  242. #ifdef PLATFORM_64BITS
  243. newHead.value.Padding = 0;
  244. #endif
  245. do
  246. {
  247. ThreadPause();
  248. oldHead.value64x128 = m_Head.value64x128;
  249. if ( !oldHead.value.Next )
  250. return NULL;
  251. newHead.value.Next = NULL;
  252. // <sergiy> the reason for AND'ing it instead of poking a short into memory
  253. // is probably to avoid store forward issues, but I'm not sure because
  254. // I didn't construct this code. In any case, leaving it as is on big-endian
  255. newHead.value32.DepthAndSequence = oldHead.value32.DepthAndSequence & 0xffff0000;
  256. } while( !ThreadInterlockedAssignIf64x128( &m_Head.value64x128, newHead.value64x128, oldHead.value64x128 ) );
  257. return (TSLNodeBase_t *)oldHead.value.Next;
  258. #endif
  259. }
  260. TSLHead_t *AccessUnprotected()
  261. {
  262. return &m_Head;
  263. }
  264. int Count() const
  265. {
  266. #ifdef USE_NATIVE_SLIST
  267. return QueryDepthSList( const_cast<TSLHead_t*>( &m_Head ) );
  268. #else
  269. return m_Head.value.Depth;
  270. #endif
  271. }
  272. private:
  273. TSLHead_t m_Head;
  274. } TSLIST_HEAD_ALIGN_POST;
  275. //-------------------------------------
  276. template <typename T>
  277. class TSLIST_HEAD_ALIGN CTSSimpleList : public CTSListBase
  278. {
  279. public:
  280. void Push( T *pNode )
  281. {
  282. Assert( sizeof(T) >= sizeof(TSLNodeBase_t) );
  283. CTSListBase::Push( (TSLNodeBase_t *)pNode );
  284. }
  285. T *Pop()
  286. {
  287. return (T *)CTSListBase::Pop();
  288. }
  289. } TSLIST_HEAD_ALIGN_POST;
  290. //-------------------------------------
  291. // this is a replacement for CTSList<> and CObjectPool<> that does not
  292. // have a per-item, per-alloc new/delete overhead
  293. // similar to CTSSimpleList except that it allocates it's own pool objects
  294. // and frees them on destruct. Also it does not overlay the TSNodeBase_t memory
  295. // on T's memory
  296. template< class T >
  297. class TSLIST_HEAD_ALIGN CTSPool : public CTSListBase
  298. {
  299. // packs the node and the item (T) into a single struct and pools those
  300. struct TSLIST_NODE_ALIGN simpleTSPoolStruct_t : public TSLNodeBase_t
  301. {
  302. T elem;
  303. } TSLIST_NODE_ALIGN_POST;
  304. public:
  305. ~CTSPool()
  306. {
  307. Purge();
  308. }
  309. void Purge()
  310. {
  311. simpleTSPoolStruct_t *pNode = NULL;
  312. while ( 1 )
  313. {
  314. pNode = (simpleTSPoolStruct_t *)CTSListBase::Pop();
  315. if ( !pNode )
  316. break;
  317. delete pNode;
  318. }
  319. }
  320. void PutObject( T *pInfo )
  321. {
  322. char *pElem = (char *)pInfo;
  323. pElem -= offsetof(simpleTSPoolStruct_t,elem);
  324. simpleTSPoolStruct_t *pNode = (simpleTSPoolStruct_t *)pElem;
  325. CTSListBase::Push( pNode );
  326. }
  327. T *GetObject()
  328. {
  329. simpleTSPoolStruct_t *pNode = (simpleTSPoolStruct_t *)CTSListBase::Pop();
  330. if ( !pNode )
  331. {
  332. pNode = new simpleTSPoolStruct_t;
  333. }
  334. return &pNode->elem;
  335. }
  336. // omg windows sdk - why do you #define GetObject()?
  337. FORCEINLINE T *Get()
  338. {
  339. return GetObject();
  340. }
  341. } TSLIST_HEAD_ALIGN_POST;
  342. //-------------------------------------
  343. template <typename T>
  344. class TSLIST_HEAD_ALIGN CTSList : public CTSListBase
  345. {
  346. public:
  347. struct TSLIST_NODE_ALIGN Node_t : public TSLNodeBase_t
  348. {
  349. Node_t() {}
  350. Node_t( const T &init ) : elem( init ) {}
  351. T elem;
  352. // override new/delete so we can guarantee 8-byte aligned allocs
  353. static void * operator new( size_t size )
  354. {
  355. Node_t *pNode = (Node_t *)MemAlloc_AllocAligned( size, TSLIST_NODE_ALIGNMENT, __FILE__, __LINE__ );
  356. return pNode;
  357. }
  358. // override new/delete so we can guarantee 8-byte aligned allocs
  359. static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine )
  360. {
  361. Node_t *pNode = (Node_t *)MemAlloc_AllocAligned( size, TSLIST_NODE_ALIGNMENT, pFileName, nLine );
  362. return pNode;
  363. }
  364. static void operator delete( void *p)
  365. {
  366. MemAlloc_FreeAligned( p );
  367. }
  368. static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine )
  369. {
  370. MemAlloc_FreeAligned( p );
  371. }
  372. } TSLIST_NODE_ALIGN_POST;
  373. ~CTSList()
  374. {
  375. Purge();
  376. }
  377. void Purge()
  378. {
  379. Node_t *pCurrent = Detach();
  380. Node_t *pNext;
  381. while ( pCurrent )
  382. {
  383. pNext = (Node_t *)pCurrent->Next;
  384. delete pCurrent;
  385. pCurrent = pNext;
  386. }
  387. }
  388. void RemoveAll()
  389. {
  390. Purge();
  391. }
  392. Node_t *Push( Node_t *pNode )
  393. {
  394. return (Node_t *)CTSListBase::Push( pNode );
  395. }
  396. Node_t *Pop()
  397. {
  398. return (Node_t *)CTSListBase::Pop();
  399. }
  400. void PushItem( const T &init )
  401. {
  402. Push( new Node_t( init ) );
  403. }
  404. bool PopItem( T *pResult)
  405. {
  406. Node_t *pNode = Pop();
  407. if ( !pNode )
  408. return false;
  409. *pResult = pNode->elem;
  410. delete pNode;
  411. return true;
  412. }
  413. Node_t *Detach()
  414. {
  415. return (Node_t *)CTSListBase::Detach();
  416. }
  417. } TSLIST_HEAD_ALIGN_POST;
  418. //-------------------------------------
  419. template <typename T>
  420. class TSLIST_HEAD_ALIGN CTSListWithFreeList : public CTSListBase
  421. {
  422. public:
  423. struct TSLIST_NODE_ALIGN Node_t : public TSLNodeBase_t
  424. {
  425. Node_t() {}
  426. Node_t( const T &init ) : elem( init ) {}
  427. T elem;
  428. } TSLIST_NODE_ALIGN_POST;
  429. ~CTSListWithFreeList()
  430. {
  431. Purge();
  432. }
  433. void Purge()
  434. {
  435. Node_t *pCurrent = Detach();
  436. Node_t *pNext;
  437. while ( pCurrent )
  438. {
  439. pNext = (Node_t *)pCurrent->Next;
  440. delete pCurrent;
  441. pCurrent = pNext;
  442. }
  443. pCurrent = (Node_t *)m_FreeList.Detach();
  444. while ( pCurrent )
  445. {
  446. pNext = (Node_t *)pCurrent->Next;
  447. delete pCurrent;
  448. pCurrent = pNext;
  449. }
  450. }
  451. void RemoveAll()
  452. {
  453. Node_t *pCurrent = Detach();
  454. Node_t *pNext;
  455. while ( pCurrent )
  456. {
  457. pNext = (Node_t *)pCurrent->Next;
  458. m_FreeList.Push( pCurrent );
  459. pCurrent = pNext;
  460. }
  461. }
  462. Node_t *Push( Node_t *pNode )
  463. {
  464. return (Node_t *)CTSListBase::Push( pNode );
  465. }
  466. Node_t *Pop()
  467. {
  468. return (Node_t *)CTSListBase::Pop();
  469. }
  470. void PushItem( const T &init )
  471. {
  472. Node_t *pNode = (Node_t *)m_FreeList.Pop();
  473. if ( !pNode )
  474. {
  475. pNode = new Node_t;
  476. }
  477. pNode->elem = init;
  478. Push( pNode );
  479. }
  480. bool PopItem( T *pResult)
  481. {
  482. Node_t *pNode = Pop();
  483. if ( !pNode )
  484. return false;
  485. *pResult = pNode->elem;
  486. m_FreeList.Push( pNode );
  487. return true;
  488. }
  489. Node_t *Detach()
  490. {
  491. return (Node_t *)CTSListBase::Detach();
  492. }
  493. void FreeNode( Node_t *pNode )
  494. {
  495. m_FreeList.Push( pNode );
  496. }
  497. private:
  498. CTSListBase m_FreeList;
  499. } TSLIST_HEAD_ALIGN_POST;
  500. //-----------------------------------------------------------------------------
  501. // Lock free queue
  502. //
  503. // A special consideration: the element type should be simple. This code
  504. // actually dereferences freed nodes as part of pop, but later detects
  505. // that. If the item in the queue is a complex type, only bad things can
  506. // come of that. Also, therefore, if you're using Push/Pop instead of
  507. // push item, be aware that the node memory cannot be freed until
  508. // all threads that might have been popping have completed the pop.
  509. // The PushItem()/PopItem() for handles this by keeping a persistent
  510. // free list. Dont mix Push/PushItem. Note also nodes will be freed at the end,
  511. // and are expected to have been allocated with operator new.
  512. //-----------------------------------------------------------------------------
  513. template <typename T, bool bTestOptimizer = false>
  514. class TSLIST_HEAD_ALIGN CTSQueue
  515. {
  516. public:
  517. // override new/delete so we can guarantee 8-byte aligned allocs
  518. static void * operator new( size_t size )
  519. {
  520. CTSQueue *pNode = (CTSQueue *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ );
  521. return pNode;
  522. }
  523. // override new/delete so we can guarantee 8-byte aligned allocs
  524. static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine )
  525. {
  526. CTSQueue *pNode = (CTSQueue *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, pFileName, nLine );
  527. return pNode;
  528. }
  529. static void operator delete( void *p)
  530. {
  531. MemAlloc_FreeAligned( p );
  532. }
  533. static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine )
  534. {
  535. MemAlloc_FreeAligned( p );
  536. }
  537. private:
  538. // These ain't gonna work
  539. static void * operator new[] ( size_t size ) throw()
  540. {
  541. return NULL;
  542. }
  543. static void operator delete [] ( void *p)
  544. {
  545. }
  546. public:
  547. struct TSLIST_NODE_ALIGN Node_t
  548. {
  549. // override new/delete so we can guarantee 8-byte aligned allocs
  550. static void * operator new( size_t size )
  551. {
  552. Node_t *pNode = (Node_t *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ );
  553. return pNode;
  554. }
  555. static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine )
  556. {
  557. Node_t *pNode = (Node_t *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, pFileName, nLine );
  558. return pNode;
  559. }
  560. static void operator delete( void *p)
  561. {
  562. MemAlloc_FreeAligned( p );
  563. }
  564. static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine )
  565. {
  566. MemAlloc_FreeAligned( p );
  567. }
  568. Node_t() {}
  569. Node_t( const T &init ) : elem( init ) {}
  570. Node_t *pNext;
  571. T elem;
  572. } TSLIST_NODE_ALIGN_POST;
  573. union TSLIST_HEAD_ALIGN NodeLink_t
  574. {
  575. // override new/delete so we can guarantee 8-byte aligned allocs
  576. static void * operator new( size_t size )
  577. {
  578. NodeLink_t *pNode = (NodeLink_t *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ );
  579. return pNode;
  580. }
  581. static void operator delete( void *p)
  582. {
  583. MemAlloc_FreeAligned( p );
  584. }
  585. struct Value_t
  586. {
  587. Node_t *pNode;
  588. intp sequence;
  589. } value;
  590. #ifdef PLATFORM_64BITS
  591. int128 value64x128;
  592. #else
  593. int64 value64x128;
  594. #endif
  595. } TSLIST_HEAD_ALIGN_POST;
  596. CTSQueue()
  597. {
  598. COMPILE_TIME_ASSERT( sizeof(Node_t) >= sizeof(TSLNodeBase_t) );
  599. if ( ((size_t)&m_Head) % TSLIST_HEAD_ALIGNMENT != 0 )
  600. {
  601. Error( "CTSQueue: Misaligned queue\n" );
  602. DebuggerBreak();
  603. }
  604. if ( ((size_t)&m_Tail) % TSLIST_HEAD_ALIGNMENT != 0 )
  605. {
  606. Error( "CTSQueue: Misaligned queue\n" );
  607. DebuggerBreak();
  608. }
  609. m_Count = 0;
  610. m_Head.value.sequence = m_Tail.value.sequence = 0;
  611. m_Head.value.pNode = m_Tail.value.pNode = new Node_t; // list always contains a dummy node
  612. m_Head.value.pNode->pNext = End();
  613. }
  614. ~CTSQueue()
  615. {
  616. Purge();
  617. Assert( m_Count == 0 );
  618. Assert( m_Head.value.pNode == m_Tail.value.pNode );
  619. Assert( m_Head.value.pNode->pNext == End() );
  620. delete m_Head.value.pNode;
  621. }
  622. // Note: Purge, RemoveAll, and Validate are *not* threadsafe
  623. void Purge()
  624. {
  625. if ( IsDebug() )
  626. {
  627. ValidateQueue();
  628. }
  629. Node_t *pNode;
  630. while ( ( pNode = Pop() ) != NULL )
  631. {
  632. delete pNode;
  633. }
  634. while ( ( pNode = (Node_t *)m_FreeNodes.Pop() ) != NULL )
  635. {
  636. delete pNode;
  637. }
  638. Assert( m_Count == 0 );
  639. Assert( m_Head.value.pNode == m_Tail.value.pNode );
  640. Assert( m_Head.value.pNode->pNext == End() );
  641. m_Head.value.sequence = m_Tail.value.sequence = 0;
  642. }
  643. void RemoveAll()
  644. {
  645. if ( IsDebug() )
  646. {
  647. ValidateQueue();
  648. }
  649. Node_t *pNode;
  650. while ( ( pNode = Pop() ) != NULL )
  651. {
  652. m_FreeNodes.Push( (TSLNodeBase_t *)pNode );
  653. }
  654. }
  655. bool ValidateQueue()
  656. {
  657. if ( IsDebug() )
  658. {
  659. bool bResult = true;
  660. int nNodes = 0;
  661. if ( m_Tail.value.pNode->pNext != End() )
  662. {
  663. DebuggerBreakIfDebugging();
  664. bResult = false;
  665. }
  666. if ( m_Count == 0 )
  667. {
  668. if ( m_Head.value.pNode != m_Tail.value.pNode )
  669. {
  670. DebuggerBreakIfDebugging();
  671. bResult = false;
  672. }
  673. }
  674. Node_t *pNode = m_Head.value.pNode;
  675. while ( pNode != End() )
  676. {
  677. nNodes++;
  678. pNode = pNode->pNext;
  679. }
  680. nNodes--;// skip dummy node
  681. if ( nNodes != m_Count )
  682. {
  683. DebuggerBreakIfDebugging();
  684. bResult = false;
  685. }
  686. if ( !bResult )
  687. {
  688. Msg( "Corrupt CTSQueueDetected" );
  689. }
  690. return bResult;
  691. }
  692. else
  693. {
  694. return true;
  695. }
  696. }
  697. void FinishPush( Node_t *pNode, const NodeLink_t &oldTail )
  698. {
  699. NodeLink_t newTail;
  700. newTail.value.pNode = pNode;
  701. newTail.value.sequence = oldTail.value.sequence + 1;
  702. ThreadMemoryBarrier();
  703. InterlockedCompareExchangeNodeLink( &m_Tail, newTail, oldTail );
  704. }
  705. Node_t *Push( Node_t *pNode )
  706. {
  707. #ifdef _DEBUG
  708. if ( (size_t)pNode % TSLIST_NODE_ALIGNMENT != 0 )
  709. {
  710. Error( "CTSListBase: Misaligned node\n" );
  711. DebuggerBreak();
  712. }
  713. #endif
  714. NodeLink_t oldTail;
  715. pNode->pNext = End();
  716. for (;;)
  717. {
  718. oldTail.value.sequence = m_Tail.value.sequence;
  719. oldTail.value.pNode = m_Tail.value.pNode;
  720. if ( InterlockedCompareExchangeNode( &(oldTail.value.pNode->pNext), pNode, End() ) == End() )
  721. {
  722. break;
  723. }
  724. else
  725. {
  726. // Another thread is trying to push, help it along
  727. FinishPush( oldTail.value.pNode->pNext, oldTail );
  728. }
  729. }
  730. FinishPush( pNode, oldTail ); // This can fail if another thread pushed between the sequence and node grabs above. Later pushes or pops corrects
  731. m_Count++;
  732. return oldTail.value.pNode;
  733. }
  734. Node_t *Pop()
  735. {
  736. #define TSQUEUE_BAD_NODE_LINK ( (Node_t *)INT_TO_POINTER( 0xdeadbeef ) )
  737. NodeLink_t * volatile pHead = &m_Head;
  738. NodeLink_t * volatile pTail = &m_Tail;
  739. Node_t * volatile * pHeadNode = &m_Head.value.pNode;
  740. volatile intp * volatile pHeadSequence = &m_Head.value.sequence;
  741. Node_t * volatile * pTailNode = &pTail->value.pNode;
  742. NodeLink_t head;
  743. NodeLink_t newHead;
  744. Node_t *pNext;
  745. intp tailSequence;
  746. T elem;
  747. for (;;)
  748. {
  749. head.value.sequence = *pHeadSequence; // must grab sequence first, which allows condition below to ensure pNext is valid
  750. ThreadMemoryBarrier(); // need a barrier to prevent reordering of these assignments
  751. head.value.pNode = *pHeadNode;
  752. tailSequence = pTail->value.sequence;
  753. pNext = head.value.pNode->pNext;
  754. // Checking pNext only to force optimizer to not reorder the assignment
  755. // to pNext and the compare of the sequence
  756. if ( !pNext || head.value.sequence != *pHeadSequence )
  757. continue;
  758. if ( bTestOptimizer )
  759. {
  760. if ( pNext == TSQUEUE_BAD_NODE_LINK )
  761. {
  762. Msg( "Bad node link detected\n" );
  763. continue;
  764. }
  765. }
  766. if ( head.value.pNode == *pTailNode )
  767. {
  768. if ( pNext == End() )
  769. return NULL;
  770. // Another thread is trying to push, help it along
  771. NodeLink_t &oldTail = head; // just reuse local memory for head to build old tail
  772. oldTail.value.sequence = tailSequence; // reuse head pNode
  773. FinishPush( pNext, oldTail );
  774. continue;
  775. }
  776. if ( pNext != End() )
  777. {
  778. elem = pNext->elem; // NOTE: next could be a freed node here, by design
  779. newHead.value.pNode = pNext;
  780. newHead.value.sequence = head.value.sequence + 1;
  781. if ( InterlockedCompareExchangeNodeLink( pHead, newHead, head ) )
  782. {
  783. ThreadMemoryBarrier();
  784. if ( bTestOptimizer )
  785. {
  786. head.value.pNode->pNext = TSQUEUE_BAD_NODE_LINK;
  787. }
  788. break;
  789. }
  790. }
  791. }
  792. m_Count--;
  793. head.value.pNode->elem = elem;
  794. return head.value.pNode;
  795. }
  796. void FreeNode( Node_t *pNode )
  797. {
  798. m_FreeNodes.Push( (TSLNodeBase_t *)pNode );
  799. }
  800. void PushItem( const T &init )
  801. {
  802. Node_t *pNode = (Node_t *)m_FreeNodes.Pop();
  803. if ( pNode )
  804. {
  805. pNode->elem = init;
  806. }
  807. else
  808. {
  809. pNode = new Node_t( init );
  810. }
  811. Push( pNode );
  812. }
  813. bool PopItem( T *pResult )
  814. {
  815. Node_t *pNode = Pop();
  816. if ( !pNode )
  817. return false;
  818. *pResult = pNode->elem;
  819. m_FreeNodes.Push( (TSLNodeBase_t *)pNode );
  820. return true;
  821. }
  822. int Count() const
  823. {
  824. return m_Count;
  825. }
  826. private:
  827. Node_t *End() { return (Node_t *)this; } // just need a unique signifier
  828. Node_t *InterlockedCompareExchangeNode( Node_t * volatile *ppNode, Node_t *value, Node_t *comperand )
  829. {
  830. return (Node_t *)::ThreadInterlockedCompareExchangePointer( (void **)ppNode, value, comperand );
  831. }
  832. bool InterlockedCompareExchangeNodeLink( NodeLink_t volatile *pLink, const NodeLink_t &value, const NodeLink_t &comperand )
  833. {
  834. return ThreadInterlockedAssignIf64x128( &pLink->value64x128, value.value64x128, comperand.value64x128 );
  835. }
  836. NodeLink_t m_Head;
  837. NodeLink_t m_Tail;
  838. CInterlockedInt m_Count;
  839. CTSListBase m_FreeNodes;
  840. } TSLIST_NODE_ALIGN_POST;
  841. #if defined( _WIN32 )
  842. // Suppress this spurious warning:
  843. // warning C4700: uninitialized local variable 'oldHead' used
  844. #pragma warning( pop )
  845. #endif
  846. #endif // TSLIST_H