lock_free_slist.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. * Copyright (c) 1997-1999
  3. * Silicon Graphics Computer Systems, Inc.
  4. *
  5. * Copyright (c) 1999
  6. * Boris Fomitchev
  7. *
  8. * This material is provided "as is", with absolutely no warranty expressed
  9. * or implied. Any use is at your own risk.
  10. *
  11. * Permission to use or copy this software for any purpose is hereby granted
  12. * without fee, provided the above notices are retained on all copies.
  13. * Permission to modify the code and to distribute modified code is granted,
  14. * provided the above notices are retained, and a notice that the code was
  15. * modified is included with the above copyright notice.
  16. *
  17. */
  18. #ifndef _STLP_LOCK_FREE_SLIST_H
  19. #define _STLP_LOCK_FREE_SLIST_H
  20. #if defined(_STLP_PTHREADS)
  21. # include <pthread.h>
  22. # if defined (__GNUC__) && defined (__i386__)
  23. # define _STLP_HAS_ATOMIC_FREELIST
  24. /**
  25. * Class that implements a non-blocking and thread-safe freelist.
  26. * It is used for the lock-free node allocation engine.
  27. *
  28. * @author felixw@inin.com
  29. */
  30. class _STLP_atomic_freelist {
  31. public:
  32. /**
  33. * Type representing items of the freelist
  34. */
  35. struct item {
  36. item* _M_next;
  37. };
  38. _STLP_atomic_freelist() {
  39. // Statically assert layout of member is as expected by assembly code
  40. _STLP_STATIC_ASSERT(sizeof(_M) == 8)
  41. _M._M_data._M_top = 0;
  42. _M._M_data._M_sequence = 0;
  43. }
  44. /**
  45. * Atomically pushes the specified item onto the freelist.
  46. *
  47. * @param __item [in] Item to add to the front of the list
  48. */
  49. void push(item* __item) {
  50. // NOTE: GCC uses ebx as the PIC register for globals in shared libraries.
  51. // The GCC version I'm using (3.4.1) won't temporarily spill it if it's
  52. // used as input, output, or clobber. Instead, it complains with a
  53. // "can't find a register in class `BREG' while reloading `asm'" error.
  54. // This is probably a compiler bug, but as the cmpxchg8b instruction
  55. // requires ebx, I work around this here by using ecx for the '__item'
  56. // input and spilling ebx into edi. This also precludes us from using
  57. // a "m" operand for the cmpxchg8b argument (GCC might think it can make
  58. // it relative to ebx). Instead, we're using esi for the address of _M_data.
  59. //
  60. int __tmp1; // These dummy variables are used to tell GCC that the eax, ecx,
  61. int __tmp2; // and edx registers will not have the same value as their input.
  62. int __tmp3; // The optimizer will remove them as their values are not used.
  63. __asm__ __volatile__
  64. (" movl %%ebx, %%edi\n\t"
  65. " movl %%ecx, %%ebx\n\t"
  66. "L1_%=: movl %%eax, (%%ebx)\n\t" // __item._M_next = _M._M_data._M_top
  67. " leal 1(%%edx),%%ecx\n\t" // new sequence = _M._M_data._M_sequence + 1
  68. "lock; cmpxchg8b (%%esi)\n\t"
  69. " jne L1_%=\n\t" // Failed, retry! (edx:eax now contain most recent _M_sequence:_M_top)
  70. " movl %%edi, %%ebx"
  71. :"=a" (__tmp1), "=d" (__tmp2), "=c" (__tmp3)
  72. :"a" (_M._M_data._M_top), "d" (_M._M_data._M_sequence), "c" (__item), "S" (&_M._M_data)
  73. :"edi", "memory", "cc");
  74. }
  75. /**
  76. * Atomically removes the topmost item from the freelist and returns a
  77. * pointer to it. Returns NULL if the list is empty.
  78. *
  79. * @return Item that was removed from front of list; NULL if list empty
  80. */
  81. item* pop() {
  82. item* __result;
  83. int __tmp;
  84. __asm__ __volatile__
  85. (" movl %%ebx, %%edi\n\t"
  86. "L1_%=: testl %%eax, %%eax\n\t" // _M_top == NULL?
  87. " je L2_%=\n\t" // If yes, we're done
  88. " movl (%%eax), %%ebx\n\t" // new top = _M._M_data._M_top->_M_next
  89. " leal 1(%%edx),%%ecx\n\t" // new sequence = _M._M_data._M_sequence + 1
  90. "lock; cmpxchg8b (%%esi)\n\t"
  91. " jne L1_%=\n\t" // We failed, retry! (edx:eax now contain most recent _M_sequence:_M_top)
  92. "L2_%=: movl %%edi, %%ebx"
  93. :"=a" (__result), "=d" (__tmp)
  94. :"a" (_M._M_data._M_top), "d" (_M._M_data._M_sequence), "S" (&_M._M_data)
  95. :"edi", "ecx", "memory", "cc");
  96. return __result;
  97. }
  98. /**
  99. * Atomically detaches all items from the list and returns a pointer to the
  100. * topmost item. The items are still chained and may be traversed safely as
  101. * they're now "owned" by the calling thread.
  102. *
  103. * @return Pointer to topmost item in the list; NULL if list empty
  104. */
  105. item* clear() {
  106. item* __result;
  107. int __tmp;
  108. __asm__ __volatile__
  109. (" movl %%ebx, %%edi\n\t"
  110. "L1_%=: testl %%eax, %%eax\n\t" // _M_top == NULL?
  111. " je L2_%=\n\t" // If yes, we're done
  112. " xorl %%ebx, %%ebx\n\t" // We're attempting to set _M_top to NULL
  113. " leal 1(%%edx),%%ecx\n\t" // new sequence = _M._M_data._M_sequence + 1
  114. "lock; cmpxchg8b (%%esi)\n\t"
  115. " jne L1_%=\n\t" // Failed, retry! (edx:eax now contain most recent _M_sequence:_M_top)
  116. "L2_%=: movl %%edi, %%ebx"
  117. :"=a" (__result), "=d" (__tmp)
  118. :"a" (_M._M_data._M_top), "d" (_M._M_data._M_sequence), "S" (&_M._M_data)
  119. :"edi", "ecx", "memory", "cc");
  120. return __result;
  121. }
  122. private:
  123. union {
  124. long long _M_align;
  125. struct {
  126. item* _M_top; // Topmost element in the freelist
  127. unsigned int _M_sequence; // Sequence counter to prevent "ABA problem"
  128. } _M_data;
  129. } _M;
  130. _STLP_atomic_freelist(const _STLP_atomic_freelist&);
  131. _STLP_atomic_freelist& operator=(const _STLP_atomic_freelist&);
  132. };
  133. # endif /* if defined(__GNUC__) && defined(__i386__) */
  134. #elif defined (_STLP_WIN32THREADS)
  135. # if !defined (_WIN64)
  136. # define _STLP_USE_ASM_IMPLEMENTATION
  137. # endif
  138. // Here are the compiler/platform requirements for the thread safe and
  139. // lock free singly linked list implementation:
  140. # if defined (_STLP_USE_ASM_IMPLEMENTATION)
  141. // For the asm version:
  142. # if defined (_STLP_MSVC) && defined (_M_IX86) && (_M_IX86 >= 500)
  143. # define _STLP_HAS_ATOMIC_FREELIST
  144. # endif
  145. # else
  146. // For the API based version:
  147. # if defined (_STLP_NEW_PLATFORM_SDK) && (!defined (WINVER) || (WINVER >= 0x0501)) && \
  148. (!defined (_WIN32_WINNT) || (_WIN32_WINNT >= 0x0501))
  149. # define _STLP_HAS_ATOMIC_FREELIST
  150. # endif
  151. # endif
  152. # if defined (_STLP_HAS_ATOMIC_FREELIST)
  153. # if defined (_STLP_USE_ASM_IMPLEMENTATION)
  154. # if defined (_STLP_MSVC) && (_STLP_MSVC < 1300) || defined (__ICL)
  155. # pragma warning (push)
  156. # pragma warning (disable : 4035) //function has no return value
  157. # endif
  158. # endif
  159. /**
  160. * Class that implements a non-blocking and thread-safe freelist.
  161. * It is used for the lock-free node allocation engine.
  162. *
  163. * @author felixw@inin.com
  164. */
  165. class _STLP_atomic_freelist {
  166. public:
  167. /**
  168. * Type representing items of the freelist
  169. */
  170. # if defined (_STLP_USE_ASM_IMPLEMENTATION)
  171. struct item {
  172. item* _M_next;
  173. };
  174. # else
  175. typedef SLIST_ENTRY item;
  176. # endif
  177. _STLP_atomic_freelist() {
  178. // Statically assert layout of member is as expected by assembly code
  179. # if defined (_STLP_USE_ASM_IMPLEMENTATION)
  180. _STLP_STATIC_ASSERT((sizeof(item) == sizeof(item*)) && (sizeof(_M) == 8))
  181. _M._M_data._M_top = 0;
  182. _M._M_data._M_sequence = 0;
  183. # else
  184. InitializeSListHead(&_M_head);
  185. # endif
  186. }
  187. /**
  188. * Atomically pushes the specified item onto the freelist.
  189. *
  190. * @param __item [in] Item to add to the front of the list
  191. */
  192. void push(item* __item) {
  193. # if defined (_STLP_USE_ASM_IMPLEMENTATION)
  194. __asm
  195. {
  196. mov esi, this
  197. mov ebx, __item
  198. mov eax, [esi] // _M._M_data._M_top
  199. mov edx, [esi+4] // _M._M_data._M_sequence
  200. L1: mov [ebx], eax // __item._M_next = _M._M_data._M_top
  201. lea ecx, [edx+1] // new sequence = _M._M_data._M_sequence + 1
  202. lock cmpxchg8b qword ptr [esi]
  203. jne L1 // Failed, retry! (edx:eax now contain most recent _M_sequence:_M_top)
  204. }
  205. # else
  206. InterlockedPushEntrySList(&_M_head, __item);
  207. # endif
  208. }
  209. /**
  210. * Atomically removes the topmost item from the freelist and returns a
  211. * pointer to it. Returns NULL if the list is empty.
  212. *
  213. * @return Item that was removed from front of list; NULL if list empty
  214. */
  215. item* pop() {
  216. # if defined (_STLP_USE_ASM_IMPLEMENTATION)
  217. __asm
  218. {
  219. mov esi, this
  220. mov eax, [esi] // _M._M_data._M_top
  221. mov edx, [esi+4] // _M._M_data._M_sequence
  222. L1: test eax, eax // _M_top == NULL?
  223. je L2 // Yes, we're done
  224. mov ebx, [eax] // new top = _M._M_data._M_top->_M_next
  225. lea ecx, [edx+1] // new sequence = _M._M_data._M_sequence + 1
  226. lock cmpxchg8b qword ptr [esi]
  227. jne L1 // Failed, retry! (edx:eax now contain most recent _M_sequence:_M_top)
  228. L2:
  229. }
  230. # else
  231. return InterlockedPopEntrySList(&_M_head);
  232. # endif
  233. }
  234. /**
  235. * Atomically detaches all items from the list and returns pointer to the
  236. * topmost. The items are still chained and may be traversed safely as
  237. * they're now "owned" by the calling thread.
  238. *
  239. * @return Pointer to topmost item in the list; NULL if list empty
  240. */
  241. item* clear() {
  242. # if defined (_STLP_USE_ASM_IMPLEMENTATION)
  243. __asm
  244. {
  245. mov esi, this
  246. mov eax, [esi] // _M._M_data._M_top
  247. mov edx, [esi+4] // _M._M_data._M_sequence
  248. L1: test eax, eax // _M_top == NULL?
  249. je L2 // Yes, we're done
  250. xor ebx,ebx // We're attempting to set _M._M_data._M_top to NULL
  251. lea ecx, [edx+1] // new sequence = _M._M_data._M_sequence + 1
  252. lock cmpxchg8b qword ptr [esi]
  253. jne L1 // Failed, retry! (edx:eax now contain most recent _M_sequence:_M_top)
  254. L2:
  255. }
  256. # else
  257. return InterlockedFlushSList(&_M_head);
  258. # endif
  259. }
  260. private:
  261. # if defined (_STLP_USE_ASM_IMPLEMENTATION)
  262. union {
  263. __int64 _M_align;
  264. struct {
  265. item* _M_top; // Topmost element in the freelist
  266. unsigned int _M_sequence; // Sequence counter to prevent "ABA problem"
  267. } _M_data;
  268. } _M;
  269. # else
  270. SLIST_HEADER _M_head;
  271. # endif
  272. _STLP_atomic_freelist(const _STLP_atomic_freelist&);
  273. _STLP_atomic_freelist& operator = (const _STLP_atomic_freelist&);
  274. };
  275. # if defined (_STLP_USE_ASM_IMPLEMENTATION)
  276. # if defined (_STLP_MSVC) && (_STLP_MSVC < 1300) || defined (__ICL)
  277. # pragma warning (pop)
  278. # endif
  279. # endif
  280. # endif /* _STLP_HAS_ATOMIC_FREELIST */
  281. #endif
  282. #endif /* _STLP_LOCK_FREE_SLIST_H */