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.

858 lines
18 KiB

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