threadSafeDeque.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #ifndef _THREADSAFEDEQUE_H_
  23. #define _THREADSAFEDEQUE_H_
  24. #ifndef _THREADSAFEFREELIST_H_
  25. # include "platform/threads/threadSafeFreeList.h"
  26. #endif
  27. #include "platform/tmm_off.h"
  28. /// Fast, lock-free double-ended queue for concurrent access.
  29. ///
  30. /// @param T Type of list elements; must have default contructor.
  31. template< typename T >
  32. class ThreadSafeDeque
  33. {
  34. // Lock-free deques using just single-word atomic writes are
  35. // very tricky as each pointer update must immediately result
  36. // in a fully valid list state. The idea here is to maintain the
  37. // deque's head and tail pointers unreliably but otherwise keep a
  38. // regular double-linked list (since we never insert nodes in the
  39. // middle, single-word writes are all we need).
  40. //
  41. // Deletions are a bit less straightforward and require the threads
  42. // to work cooperatively. Since failure of a pointer update depends
  43. // on the deletion state, the deletion flag has to be encoded into
  44. // the link fields. However, as there are two link fields this creates
  45. // two independent deletion flags for each single node, one on the
  46. // next link and one on the prev link.
  47. //
  48. // This will not lead to a problem, though, as it only becomes relevant
  49. // when there is only a single value in the list which, even if the
  50. // respective node gets both deleted and appended/prepended a new node,
  51. // will result in a valid list state.
  52. public:
  53. typedef T ValueType;
  54. protected:
  55. class Node;
  56. class DeleteNode;
  57. typedef ThreadSafeRef< Node > NodeRef;
  58. /// List node.
  59. class Node : public ThreadSafeFreeListNode< Node, DeleteNode >
  60. {
  61. public:
  62. friend class DeleteNode; // mFreeList;
  63. typedef typename ThreadSafeDeque< T >::ValueType ValueType;
  64. /// Thread claim flag. This is to prevent two threads who concurrently
  65. /// do a tryPopFront() and tryPopBack() respectively on a deque with just
  66. /// a single node to both claim and return the same value (which would happen
  67. /// without the flag as otherwise both threads would use two different
  68. /// deletion bits for claiming the node).
  69. U32 mIsClaimed;
  70. /// Link to the freelist that the node has been
  71. /// allocated from.
  72. ThreadSafeFreeList< Node >& mFreeList;
  73. /// Value contained in the node.
  74. ValueType mValue;
  75. /// Reference to next node and deletion bit.
  76. NodeRef mNext;
  77. /// Reference to previous node and deletion bit.
  78. NodeRef mPrev;
  79. /// Construct an unlinked node allocated from "freeList".
  80. Node( ThreadSafeFreeList< Node >& freeList, const ValueType& value )
  81. : mIsClaimed( 0 ), mFreeList( freeList ), mValue( value ) {}
  82. };
  83. class DeleteNode
  84. {
  85. public:
  86. template< typename N >
  87. static void destroy( N* ptr )
  88. {
  89. AssertFatal( ptr->mIsClaimed,
  90. "ThreadSafeDeque::DeleteNode::destroy() - deleting unclaimed node" );
  91. destructInPlace( ptr );
  92. ptr->mFreeList.free( ptr );
  93. }
  94. };
  95. #ifdef TORQUE_DEBUG
  96. S32 mNumValues;
  97. #endif
  98. /// Reference to the head node.
  99. NodeRef mHead;
  100. ///
  101. NodeRef mTail;
  102. /// Free list for list nodes.
  103. ThreadSafeFreeList< Node > mFreeList;
  104. /// @return the leftmost node in the list.
  105. /// @note Updates the list state and may purge deleted nodes.
  106. NodeRef getHead();
  107. /// @return the rightmost node in the list.
  108. /// @note Updates the list state and may purge deleted nodes.
  109. NodeRef getTail();
  110. public:
  111. /// Construct an empty deque.
  112. ThreadSafeDeque()
  113. {
  114. #ifdef TORQUE_DEBUG
  115. mNumValues = 0;
  116. #endif
  117. }
  118. ~ThreadSafeDeque()
  119. {
  120. ValueType value;
  121. while( tryPopFront( value ) );
  122. AssertFatal( isEmpty(), "ThreadSafeDeque::~ThreadSafeDeque() - not empty" );
  123. }
  124. /// @return true if the queue is empty.
  125. bool isEmpty()
  126. {
  127. return ( !getHead() && !getTail() );
  128. }
  129. /// Prepend the given value to the list.
  130. void pushFront( const ValueType& value );
  131. /// Append the given value to the list.
  132. void pushBack( const ValueType& value );
  133. /// Try to take the leftmost value from the deque.
  134. /// Fails if the deque is empty at the time the method tries to
  135. /// take a node from the list.
  136. bool tryPopFront( ValueType& outValue );
  137. /// Try to take the rightmost value from the deque.
  138. /// Fails if the deque is empty at the time the method tries to
  139. /// take a node from the list.
  140. bool tryPopBack( ValueType& outValue );
  141. void dumpDebug()
  142. {
  143. #ifdef TORQUE_DEBUG
  144. Platform::outputDebugString( "[ThreadSafeDeque] numValues=%i", mNumValues );
  145. mFreeList.dumpDebug();
  146. #endif
  147. }
  148. };
  149. // The getHead() and getTail() code here is pretty much brute-force in order
  150. // to keep the complexities of synchronizing it bounded. We just let each
  151. // thread work as if it is the only thread but require each one to start from
  152. // scratch on each iteration.
  153. template< typename T >
  154. typename ThreadSafeDeque< T >::NodeRef ThreadSafeDeque< T >::getHead()
  155. {
  156. // Find leftmost node.
  157. NodeRef result;
  158. while( 1 )
  159. {
  160. // Iterate through to leftmost node.
  161. {
  162. NodeRef head = mHead;
  163. while( head != NULL )
  164. {
  165. NodeRef prev = head->mPrev;
  166. if( prev != NULL )
  167. mHead.trySetFromTo( head, prev, NodeRef::TAG_Unset );
  168. else
  169. break;
  170. head = mHead;
  171. }
  172. }
  173. // Clear out dead nodes at front of list.
  174. {
  175. NodeRef head = mHead;
  176. if( head && head->mPrev.isTagged() )
  177. {
  178. NodeRef next = head->mNext;
  179. mHead.trySetFromTo( head, next, NodeRef::TAG_Unset );
  180. mTail.trySetFromTo( head, next, NodeRef::TAG_Unset );
  181. if( next != NULL )
  182. next->mPrev.trySetFromTo( head, NULL );
  183. head->mNext.trySetFromTo( next, NULL, NodeRef::TAG_Set );
  184. continue; // Restart.
  185. }
  186. }
  187. // Try head.
  188. NodeRef head = mHead;
  189. if( head != NULL && !head->mPrev.isTagged() )
  190. {
  191. result = head;
  192. break;
  193. }
  194. // Try tail.
  195. if( !head )
  196. {
  197. head = mTail;
  198. if( !head )
  199. break;
  200. }
  201. // Update head.
  202. NodeRef prev = head->mPrev;
  203. if( head->mPrev != NULL )
  204. {
  205. if( !mHead.trySetFromTo( head, prev, NodeRef::TAG_Unset ) )
  206. mHead.trySetFromTo( NULL, prev );
  207. }
  208. else
  209. mHead.trySetFromTo( NULL, head );
  210. }
  211. AssertFatal( !result.isTagged(), "ThreadSafeDeque::getHead() - head got tagged" );
  212. return result;
  213. }
  214. template< typename T >
  215. typename ThreadSafeDeque< T >::NodeRef ThreadSafeDeque< T >::getTail()
  216. {
  217. // Find rightmost node.
  218. NodeRef result;
  219. while( 1 )
  220. {
  221. // Iterate through to rightmost node.
  222. {
  223. NodeRef tail = mTail;
  224. while( tail != NULL )
  225. {
  226. NodeRef next = tail->mNext;
  227. if( next != NULL )
  228. mTail.trySetFromTo( tail, next, NodeRef::TAG_Unset );
  229. else
  230. break;
  231. tail = mTail;
  232. }
  233. }
  234. // Clear out dead nodes at tail of list.
  235. {
  236. NodeRef tail = mTail;
  237. if( tail != NULL && tail->mNext.isTagged() )
  238. {
  239. NodeRef prev = tail->mPrev;
  240. mHead.trySetFromTo( tail, prev, NodeRef::TAG_Unset );
  241. mTail.trySetFromTo( tail, prev, NodeRef::TAG_Unset );
  242. if( prev != NULL )
  243. prev->mNext.trySetFromTo( tail, NULL );
  244. tail->mPrev.trySetFromTo( prev, NULL, NodeRef::TAG_Set );
  245. continue; // Restart.
  246. }
  247. }
  248. // Try tail.
  249. NodeRef tail = mTail;
  250. if( tail != NULL && !tail->mNext.isTagged() )
  251. {
  252. result = tail;
  253. break;
  254. }
  255. // Try head.
  256. if( !tail )
  257. {
  258. tail = mHead;
  259. if( !tail )
  260. break;
  261. }
  262. // Update tail.
  263. NodeRef next = tail->mNext;
  264. if( next != NULL )
  265. {
  266. if( !mTail.trySetFromTo( tail, next, NodeRef::TAG_Unset ) )
  267. mTail.trySetFromTo( NULL, next );
  268. }
  269. else
  270. mTail.trySetFromTo( NULL, tail );
  271. }
  272. AssertFatal( !result.isTagged(), "ThreadSafeDeque::getTail() - tail got tagged" );
  273. return result;
  274. }
  275. template< typename T >
  276. void ThreadSafeDeque< T >::pushFront( const ValueType& value )
  277. {
  278. NodeRef nextNode;
  279. NodeRef newNode;
  280. NodeRef::unsafeWrite( newNode, new ( mFreeList ) Node( mFreeList, value ) );
  281. while( 1 )
  282. {
  283. nextNode = getHead();
  284. if( !nextNode )
  285. {
  286. newNode->mNext = NULL;
  287. if( mHead.trySetFromTo( NULL, newNode ) )
  288. break;
  289. }
  290. else
  291. {
  292. newNode->mNext = nextNode;
  293. if( nextNode->mPrev.trySetFromTo( NULL, newNode, NodeRef::TAG_FailIfSet ) )
  294. break;
  295. }
  296. }
  297. #ifdef TORQUE_DEBUG
  298. dFetchAndAdd( mNumValues, 1 );
  299. #endif
  300. }
  301. template< typename T >
  302. void ThreadSafeDeque< T >::pushBack( const ValueType& value )
  303. {
  304. NodeRef prevNode;
  305. NodeRef newNode;
  306. NodeRef::unsafeWrite( newNode, new ( mFreeList ) Node( mFreeList, value ) );
  307. while( 1 )
  308. {
  309. prevNode = getTail();
  310. if( !prevNode )
  311. {
  312. newNode->mPrev = NULL;
  313. if( mHead.trySetFromTo( NULL, newNode ) ) // use head so we synchronize with pushFront
  314. break;
  315. }
  316. else
  317. {
  318. newNode->mPrev = prevNode;
  319. if( prevNode->mNext.trySetFromTo( NULL, newNode, NodeRef::TAG_FailIfSet ) )
  320. break;
  321. }
  322. }
  323. #ifdef TORQUE_DEBUG
  324. dFetchAndAdd( mNumValues, 1 );
  325. #endif
  326. }
  327. template< typename T >
  328. bool ThreadSafeDeque< T >::tryPopFront( ValueType& outValue )
  329. {
  330. NodeRef oldHead;
  331. while( 1 )
  332. {
  333. oldHead = getHead();
  334. if( !oldHead )
  335. return false;
  336. // Try to claim the node.
  337. if( oldHead->mPrev.trySetFromTo( NULL, NULL, NodeRef::TAG_SetOrFail ) )
  338. {
  339. if( dCompareAndSwap( oldHead->mIsClaimed, 0, 1 ) )
  340. break;
  341. else
  342. continue;
  343. }
  344. }
  345. outValue = oldHead->mValue;
  346. oldHead = NULL;
  347. // Cleanup.
  348. getHead();
  349. #ifdef TORQUE_DEBUG
  350. dFetchAndAdd( mNumValues, -1 );
  351. #endif
  352. return true;
  353. }
  354. template< typename T >
  355. bool ThreadSafeDeque< T >::tryPopBack( ValueType& outValue )
  356. {
  357. NodeRef oldTail;
  358. while( 1 )
  359. {
  360. oldTail = getTail();
  361. if( !oldTail )
  362. return false;
  363. // Try to claim the node.
  364. if( oldTail->mNext.trySetFromTo( NULL, NULL, NodeRef::TAG_SetOrFail ) )
  365. {
  366. if( dCompareAndSwap( oldTail->mIsClaimed, 0, 1 ) )
  367. break;
  368. }
  369. }
  370. outValue = oldTail->mValue;
  371. oldTail = NULL;
  372. // Cleanup.
  373. getTail();
  374. #ifdef TORQUE_DEBUG
  375. dFetchAndAdd( mNumValues, -1 );
  376. #endif
  377. return true;
  378. }
  379. #include "platform/tmm_on.h"
  380. #endif // _THREADSAFEDEQUE_H_