|
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 |
|
19 #ifndef _STLP_LOCK_FREE_SLIST_H |
|
20 #define _STLP_LOCK_FREE_SLIST_H |
|
21 |
|
22 #if defined(_STLP_PTHREADS) |
|
23 # include <pthread.h> |
|
24 |
|
25 # if defined (__GNUC__) && defined (__i386__) |
|
26 |
|
27 # define _STLP_HAS_ATOMIC_FREELIST |
|
28 /** |
|
29 * Class that implements a non-blocking and thread-safe freelist. |
|
30 * It is used for the lock-free node allocation engine. |
|
31 * |
|
32 * @author felixw@inin.com |
|
33 */ |
|
34 class _STLP_atomic_freelist { |
|
35 public: |
|
36 /** |
|
37 * Type representing items of the freelist |
|
38 */ |
|
39 struct item { |
|
40 item* _M_next; |
|
41 }; |
|
42 |
|
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; |
|
48 } |
|
49 |
|
50 /** |
|
51 * Atomically pushes the specified item onto the freelist. |
|
52 * |
|
53 * @param __item [in] Item to add to the front of the list |
|
54 */ |
|
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. |
|
65 // |
|
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. |
|
69 __asm__ __volatile__ |
|
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) |
|
76 " movl %%edi, %%ebx" |
|
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"); |
|
80 } |
|
81 |
|
82 /** |
|
83 * Atomically removes the topmost item from the freelist and returns a |
|
84 * pointer to it. Returns NULL if the list is empty. |
|
85 * |
|
86 * @return Item that was removed from front of list; NULL if list empty |
|
87 */ |
|
88 item* pop() { |
|
89 item* __result; |
|
90 int __tmp; |
|
91 __asm__ __volatile__ |
|
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"); |
|
103 return __result; |
|
104 } |
|
105 |
|
106 /** |
|
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. |
|
110 * |
|
111 * @return Pointer to topmost item in the list; NULL if list empty |
|
112 */ |
|
113 item* clear() { |
|
114 item* __result; |
|
115 int __tmp; |
|
116 __asm__ __volatile__ |
|
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"); |
|
128 return __result; |
|
129 } |
|
130 |
|
131 private: |
|
132 union { |
|
133 long long _M_align; |
|
134 struct { |
|
135 item* _M_top; // Topmost element in the freelist |
|
136 unsigned int _M_sequence; // Sequence counter to prevent "ABA problem" |
|
137 } _M_data; |
|
138 } _M; |
|
139 |
|
140 _STLP_atomic_freelist(const _STLP_atomic_freelist&); |
|
141 _STLP_atomic_freelist& operator=(const _STLP_atomic_freelist&); |
|
142 }; |
|
143 |
|
144 # endif /* if defined(__GNUC__) && defined(__i386__) */ |
|
145 |
|
146 #elif defined (_STLP_WIN32THREADS) |
|
147 |
|
148 # if !defined (_WIN64) |
|
149 # define _STLP_USE_ASM_IMPLEMENTATION |
|
150 # endif |
|
151 |
|
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 |
|
158 # endif |
|
159 # else |
|
160 // For the API based version: |
|
161 # if defined (_STLP_NEW_PLATFORM_SDK) && (!defined (WINVER) || (WINVER >= 0x0501)) && \ |
|
162 (!defined (_WIN32_WINNT) || (_WIN32_WINNT >= 0x0501)) |
|
163 # define _STLP_HAS_ATOMIC_FREELIST |
|
164 # endif |
|
165 # endif |
|
166 |
|
167 # if defined (_STLP_HAS_ATOMIC_FREELIST) |
|
168 # if defined (_STLP_USE_ASM_IMPLEMENTATION) |
|
169 # if defined (_STLP_MSVC) && (_STLP_MSVC < 1300) || defined (__ICL) |
|
170 # pragma warning (push) |
|
171 # pragma warning (disable : 4035) //function has no return value |
|
172 # endif |
|
173 # endif |
|
174 /** |
|
175 * Class that implements a non-blocking and thread-safe freelist. |
|
176 * It is used for the lock-free node allocation engine. |
|
177 * |
|
178 * @author felixw@inin.com |
|
179 */ |
|
180 class _STLP_atomic_freelist { |
|
181 public: |
|
182 /** |
|
183 * Type representing items of the freelist |
|
184 */ |
|
185 # if defined (_STLP_USE_ASM_IMPLEMENTATION) |
|
186 struct item { |
|
187 item* _M_next; |
|
188 }; |
|
189 # else |
|
190 typedef SLIST_ENTRY item; |
|
191 # endif |
|
192 |
|
193 _STLP_atomic_freelist() { |
|
194 // Statically assert layout of member is as expected by assembly code |
|
195 # if defined (_STLP_USE_ASM_IMPLEMENTATION) |
|
196 _STLP_STATIC_ASSERT((sizeof(item) == sizeof(item*)) && (sizeof(_M) == 8)) |
|
197 _M._M_data._M_top = 0; |
|
198 _M._M_data._M_sequence = 0; |
|
199 # else |
|
200 InitializeSListHead(&_M_head); |
|
201 # endif |
|
202 } |
|
203 |
|
204 /** |
|
205 * Atomically pushes the specified item onto the freelist. |
|
206 * |
|
207 * @param __item [in] Item to add to the front of the list |
|
208 */ |
|
209 void push(item* __item) { |
|
210 # if defined (_STLP_USE_ASM_IMPLEMENTATION) |
|
211 __asm |
|
212 { |
|
213 mov esi, this |
|
214 mov ebx, __item |
|
215 mov eax, [esi] // _M._M_data._M_top |
|
216 mov edx, [esi+4] // _M._M_data._M_sequence |
|
217 L1: mov [ebx], eax // __item._M_next = _M._M_data._M_top |
|
218 lea ecx, [edx+1] // new sequence = _M._M_data._M_sequence + 1 |
|
219 lock cmpxchg8b qword ptr [esi] |
|
220 jne L1 // Failed, retry! (edx:eax now contain most recent _M_sequence:_M_top) |
|
221 } |
|
222 # else |
|
223 InterlockedPushEntrySList(&_M_head, __item); |
|
224 # endif |
|
225 } |
|
226 |
|
227 /** |
|
228 * Atomically removes the topmost item from the freelist and returns a |
|
229 * pointer to it. Returns NULL if the list is empty. |
|
230 * |
|
231 * @return Item that was removed from front of list; NULL if list empty |
|
232 */ |
|
233 item* pop() { |
|
234 # if defined (_STLP_USE_ASM_IMPLEMENTATION) |
|
235 __asm |
|
236 { |
|
237 mov esi, this |
|
238 mov eax, [esi] // _M._M_data._M_top |
|
239 mov edx, [esi+4] // _M._M_data._M_sequence |
|
240 L1: test eax, eax // _M_top == NULL? |
|
241 je L2 // Yes, we're done |
|
242 mov ebx, [eax] // new top = _M._M_data._M_top->_M_next |
|
243 lea ecx, [edx+1] // new sequence = _M._M_data._M_sequence + 1 |
|
244 lock cmpxchg8b qword ptr [esi] |
|
245 jne L1 // Failed, retry! (edx:eax now contain most recent _M_sequence:_M_top) |
|
246 L2: |
|
247 } |
|
248 # else |
|
249 return InterlockedPopEntrySList(&_M_head); |
|
250 # endif |
|
251 } |
|
252 |
|
253 /** |
|
254 * Atomically detaches all items from the list and returns pointer to the |
|
255 * topmost. The items are still chained and may be traversed safely as |
|
256 * they're now "owned" by the calling thread. |
|
257 * |
|
258 * @return Pointer to topmost item in the list; NULL if list empty |
|
259 */ |
|
260 item* clear() { |
|
261 # if defined (_STLP_USE_ASM_IMPLEMENTATION) |
|
262 __asm |
|
263 { |
|
264 mov esi, this |
|
265 mov eax, [esi] // _M._M_data._M_top |
|
266 mov edx, [esi+4] // _M._M_data._M_sequence |
|
267 L1: test eax, eax // _M_top == NULL? |
|
268 je L2 // Yes, we're done |
|
269 xor ebx,ebx // We're attempting to set _M._M_data._M_top to NULL |
|
270 lea ecx, [edx+1] // new sequence = _M._M_data._M_sequence + 1 |
|
271 lock cmpxchg8b qword ptr [esi] |
|
272 jne L1 // Failed, retry! (edx:eax now contain most recent _M_sequence:_M_top) |
|
273 L2: |
|
274 } |
|
275 # else |
|
276 return InterlockedFlushSList(&_M_head); |
|
277 # endif |
|
278 } |
|
279 |
|
280 private: |
|
281 # if defined (_STLP_USE_ASM_IMPLEMENTATION) |
|
282 union { |
|
283 __int64 _M_align; |
|
284 struct { |
|
285 item* _M_top; // Topmost element in the freelist |
|
286 unsigned int _M_sequence; // Sequence counter to prevent "ABA problem" |
|
287 } _M_data; |
|
288 } _M; |
|
289 # else |
|
290 SLIST_HEADER _M_head; |
|
291 # endif |
|
292 |
|
293 _STLP_atomic_freelist(const _STLP_atomic_freelist&); |
|
294 _STLP_atomic_freelist& operator = (const _STLP_atomic_freelist&); |
|
295 }; |
|
296 |
|
297 # if defined (_STLP_USE_ASM_IMPLEMENTATION) |
|
298 # if defined (_STLP_MSVC) && (_STLP_MSVC < 1300) || defined (__ICL) |
|
299 # pragma warning (pop) |
|
300 # endif |
|
301 # endif |
|
302 |
|
303 # endif /* _STLP_HAS_ATOMIC_FREELIST */ |
|
304 |
|
305 #endif |
|
306 |
|
307 #endif /* _STLP_LOCK_FREE_SLIST_H */ |