2 * Copyright (c) 1997-1999
3 * Silicon Graphics Computer Systems, Inc.
8 * This material is provided "as is", with absolutely no warranty expressed
9 * or implied. Any use is at your own risk.
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.
19 #ifndef _STLP_LOCK_FREE_SLIST_H
20 #define _STLP_LOCK_FREE_SLIST_H
22 #if defined(_STLP_PTHREADS)
25 # if defined (__GNUC__) && defined (__i386__)
27 # define _STLP_HAS_ATOMIC_FREELIST
29 * Class that implements a non-blocking and thread-safe freelist.
30 * It is used for the lock-free node allocation engine.
32 * @author felixw@inin.com
34 class _STLP_atomic_freelist {
37 * Type representing items of the freelist
43 _STLP_atomic_freelist() {
44 // Statically assert layout of member is as expected by assembly code
45 _STLP_STATIC_ASSERT(sizeof(_M) == 8)
46 _M._M_data._M_top = 0;
47 _M._M_data._M_sequence = 0;
51 * Atomically pushes the specified item onto the freelist.
53 * @param __item [in] Item to add to the front of the list
55 void push(item* __item) {
56 // NOTE: GCC uses ebx as the PIC register for globals in shared libraries.
57 // The GCC version I'm using (3.4.1) won't temporarily spill it if it's
58 // used as input, output, or clobber. Instead, it complains with a
59 // "can't find a register in class `BREG' while reloading `asm'" error.
60 // This is probably a compiler bug, but as the cmpxchg8b instruction
61 // requires ebx, I work around this here by using ecx for the '__item'
62 // input and spilling ebx into edi. This also precludes us from using
63 // a "m" operand for the cmpxchg8b argument (GCC might think it can make
64 // it relative to ebx). Instead, we're using esi for the address of _M_data.
66 int __tmp1; // These dummy variables are used to tell GCC that the eax, ecx,
67 int __tmp2; // and edx registers will not have the same value as their input.
68 int __tmp3; // The optimizer will remove them as their values are not used.
70 (" movl %%ebx, %%edi\n\t"
71 " movl %%ecx, %%ebx\n\t"
72 "L1_%=: movl %%eax, (%%ebx)\n\t" // __item._M_next = _M._M_data._M_top
73 " leal 1(%%edx),%%ecx\n\t" // new sequence = _M._M_data._M_sequence + 1
74 "lock; cmpxchg8b (%%esi)\n\t"
75 " jne L1_%=\n\t" // Failed, retry! (edx:eax now contain most recent _M_sequence:_M_top)
77 :"=a" (__tmp1), "=d" (__tmp2), "=c" (__tmp3)
78 :"a" (_M._M_data._M_top), "d" (_M._M_data._M_sequence), "c" (__item), "S" (&_M._M_data)
79 :"edi", "memory", "cc");
83 * Atomically removes the topmost item from the freelist and returns a
84 * pointer to it. Returns NULL if the list is empty.
86 * @return Item that was removed from front of list; NULL if list empty
92 (" movl %%ebx, %%edi\n\t"
93 "L1_%=: testl %%eax, %%eax\n\t" // _M_top == NULL?
94 " je L2_%=\n\t" // If yes, we're done
95 " movl (%%eax), %%ebx\n\t" // new top = _M._M_data._M_top->_M_next
96 " leal 1(%%edx),%%ecx\n\t" // new sequence = _M._M_data._M_sequence + 1
97 "lock; cmpxchg8b (%%esi)\n\t"
98 " jne L1_%=\n\t" // We failed, retry! (edx:eax now contain most recent _M_sequence:_M_top)
99 "L2_%=: movl %%edi, %%ebx"
100 :"=a" (__result), "=d" (__tmp)
101 :"a" (_M._M_data._M_top), "d" (_M._M_data._M_sequence), "S" (&_M._M_data)
102 :"edi", "ecx", "memory", "cc");
107 * Atomically detaches all items from the list and returns a pointer to the
108 * topmost item. The items are still chained and may be traversed safely as
109 * they're now "owned" by the calling thread.
111 * @return Pointer to topmost item in the list; NULL if list empty
117 (" movl %%ebx, %%edi\n\t"
118 "L1_%=: testl %%eax, %%eax\n\t" // _M_top == NULL?
119 " je L2_%=\n\t" // If yes, we're done
120 " xorl %%ebx, %%ebx\n\t" // We're attempting to set _M_top to NULL
121 " leal 1(%%edx),%%ecx\n\t" // new sequence = _M._M_data._M_sequence + 1
122 "lock; cmpxchg8b (%%esi)\n\t"
123 " jne L1_%=\n\t" // Failed, retry! (edx:eax now contain most recent _M_sequence:_M_top)
124 "L2_%=: movl %%edi, %%ebx"
125 :"=a" (__result), "=d" (__tmp)
126 :"a" (_M._M_data._M_top), "d" (_M._M_data._M_sequence), "S" (&_M._M_data)
127 :"edi", "ecx", "memory", "cc");
135 item* _M_top; // Topmost element in the freelist
136 unsigned int _M_sequence; // Sequence counter to prevent "ABA problem"
140 _STLP_atomic_freelist(const _STLP_atomic_freelist&);
141 _STLP_atomic_freelist& operator=(const _STLP_atomic_freelist&);
144 # endif /* if defined(__GNUC__) && defined(__i386__) */
146 #elif defined (_STLP_WIN32THREADS)
148 # if !defined (_WIN64)
149 # define _STLP_USE_ASM_IMPLEMENTATION
152 // Here are the compiler/platform requirements for the thread safe and
153 // lock free singly linked list implementation:
154 # if defined (_STLP_USE_ASM_IMPLEMENTATION)
155 // For the asm version:
156 # if defined (_STLP_MSVC) && defined (_M_IX86) && (_M_IX86 >= 500)
157 # define _STLP_HAS_ATOMIC_FREELIST
160 // For the API based version:
161 # if defined (_STLP_NEW_PLATFORM_SDK) && (!defined (WINVER) || (WINVER >= 0x0501)) && \
162 (!defined (_WIN32_WINDOWS) || (_WIN32_WINDOWS >= 0x0501))
163 # define _STLP_HAS_ATOMIC_FREELIST
167 # if defined (_STLP_HAS_ATOMIC_FREELIST)
168 # if !defined (_STLP_USE_ASM_IMPLEMENTATION)
169 # include <windows.h>
171 # if defined (_STLP_MSVC) && (_STLP_MSVC < 1300) || defined (__ICL)
172 # pragma warning (push)
173 # pragma warning (disable : 4035) //function has no return value
177 * Class that implements a non-blocking and thread-safe freelist.
178 * It is used for the lock-free node allocation engine.
180 * @author felixw@inin.com
182 class _STLP_atomic_freelist {
185 * Type representing items of the freelist
187 # if defined (_STLP_USE_ASM_IMPLEMENTATION)
192 typedef SLIST_ENTRY item;
195 _STLP_atomic_freelist() {
196 // Statically assert layout of member is as expected by assembly code
197 # if defined (_STLP_USE_ASM_IMPLEMENTATION)
198 _STLP_STATIC_ASSERT((sizeof(item) == sizeof(item*)) && (sizeof(_M) == 8))
199 _M._M_data._M_top = 0;
200 _M._M_data._M_sequence = 0;
202 InitializeSListHead(&_M_head);
207 * Atomically pushes the specified item onto the freelist.
209 * @param __item [in] Item to add to the front of the list
211 void push(item* __item) {
212 # if defined (_STLP_USE_ASM_IMPLEMENTATION)
217 mov eax, [esi] // _M._M_data._M_top
218 mov edx, [esi+4] // _M._M_data._M_sequence
219 L1: mov [ebx], eax // __item._M_next = _M._M_data._M_top
220 lea ecx, [edx+1] // new sequence = _M._M_data._M_sequence + 1
221 lock cmpxchg8b qword ptr [esi]
222 jne L1 // Failed, retry! (edx:eax now contain most recent _M_sequence:_M_top)
225 InterlockedPushEntrySList(&_M_head, __item);
230 * Atomically removes the topmost item from the freelist and returns a
231 * pointer to it. Returns NULL if the list is empty.
233 * @return Item that was removed from front of list; NULL if list empty
236 # if defined (_STLP_USE_ASM_IMPLEMENTATION)
240 mov eax, [esi] // _M._M_data._M_top
241 mov edx, [esi+4] // _M._M_data._M_sequence
242 L1: test eax, eax // _M_top == NULL?
243 je L2 // Yes, we're done
244 mov ebx, [eax] // new top = _M._M_data._M_top->_M_next
245 lea ecx, [edx+1] // new sequence = _M._M_data._M_sequence + 1
246 lock cmpxchg8b qword ptr [esi]
247 jne L1 // Failed, retry! (edx:eax now contain most recent _M_sequence:_M_top)
251 return InterlockedPopEntrySList(&_M_head);
256 * Atomically detaches all items from the list and returns pointer to the
257 * topmost. The items are still chained and may be traversed safely as
258 * they're now "owned" by the calling thread.
260 * @return Pointer to topmost item in the list; NULL if list empty
263 # if defined (_STLP_USE_ASM_IMPLEMENTATION)
267 mov eax, [esi] // _M._M_data._M_top
268 mov edx, [esi+4] // _M._M_data._M_sequence
269 L1: test eax, eax // _M_top == NULL?
270 je L2 // Yes, we're done
271 xor ebx,ebx // We're attempting to set _M._M_data._M_top to NULL
272 lea ecx, [edx+1] // new sequence = _M._M_data._M_sequence + 1
273 lock cmpxchg8b qword ptr [esi]
274 jne L1 // Failed, retry! (edx:eax now contain most recent _M_sequence:_M_top)
278 return InterlockedFlushSList(&_M_head);
283 # if defined (_STLP_USE_ASM_IMPLEMENTATION)
287 item* _M_top; // Topmost element in the freelist
288 unsigned int _M_sequence; // Sequence counter to prevent "ABA problem"
292 SLIST_HEADER _M_head;
295 _STLP_atomic_freelist(const _STLP_atomic_freelist&);
296 _STLP_atomic_freelist& operator = (const _STLP_atomic_freelist&);
299 # if defined (_STLP_USE_ASM_IMPLEMENTATION)
300 # if defined (_STLP_MSVC) && (_STLP_MSVC < 1300) || defined (__ICL)
301 # pragma warning (pop)
305 # endif /* _STLP_HAS_ATOMIC_FREELIST */
309 #endif /* _STLP_LOCK_FREE_SLIST_H */