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 */