Counter Strike : Global Offensive Source Code
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.

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