Skip to content
This repository has been archived by the owner on Feb 11, 2021. It is now read-only.

Latest commit

 

History

History
339 lines (295 loc) · 10.2 KB

SDL_atomic.h

File metadata and controls

339 lines (295 loc) · 10.2 KB
 
1
2
/*
Simple DirectMedia Layer
Feb 15, 2013
Feb 15, 2013
3
Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/**
* \file SDL_atomic.h
May 18, 2013
May 18, 2013
24
*
May 18, 2013
May 18, 2013
26
*
27
28
29
30
31
* IMPORTANT:
* If you are not an expert in concurrent lockless programming, you should
* only be using the atomic lock and reference counting functions in this
* file. In all other cases you should be protecting your data structures
* with full mutexes.
May 18, 2013
May 18, 2013
32
*
33
34
35
36
37
* The list of "safe" functions to use are:
* SDL_AtomicLock()
* SDL_AtomicUnlock()
* SDL_AtomicIncRef()
* SDL_AtomicDecRef()
May 18, 2013
May 18, 2013
38
*
39
40
41
* Seriously, here be dragons!
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
*
May 18, 2013
May 18, 2013
42
* You can find out a little more about lockless programming and the
43
44
45
46
47
* subtle issues that can arise here:
* http://msdn.microsoft.com/en-us/library/ee418650%28v=vs.85%29.aspx
*
* There's also lots of good information here:
* http://www.1024cores.net/home/lock-free-algorithms
Jul 11, 2013
Jul 11, 2013
48
* http://preshing.com/
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
*
* These operations may or may not actually be implemented using
* processor specific atomic operations. When possible they are
* implemented as true processor specific atomic operations. When that
* is not possible the are implemented using locks that *do* use the
* available atomic operations.
*
* All of the atomic operations that modify memory are full memory barriers.
*/
#ifndef _SDL_atomic_h_
#define _SDL_atomic_h_
#include "SDL_stdinc.h"
#include "SDL_platform.h"
#include "begin_code.h"
/* Need to do this here because intrin.h has C++ code in it */
/* Visual Studio 2005 has a bug where intrin.h conflicts with winnt.h */
#if defined(_MSC_VER) && (_MSC_VER >= 1500)
#include <intrin.h>
#define HAVE_MSC_ATOMICS 1
#endif
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/**
* \name SDL AtomicLock
May 18, 2013
May 18, 2013
81
*
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
* The atomic locks are efficient spinlocks using CPU instructions,
* but are vulnerable to starvation and can spin forever if a thread
* holding a lock has been terminated. For this reason you should
* minimize the code executed inside an atomic lock and never do
* expensive things like API or system calls while holding them.
*
* The atomic locks are not safe to lock recursively.
*
* Porting Note:
* The spin lock functions and type are required and can not be
* emulated because they are used in the atomic emulation code.
*/
/*@{*/
typedef int SDL_SpinLock;
/**
* \brief Try to lock a spin lock by setting it to a non-zero value.
May 18, 2013
May 18, 2013
100
*
101
102
103
104
105
106
107
108
* \param lock Points to the lock.
*
* \return SDL_TRUE if the lock succeeded, SDL_FALSE if the lock is already held.
*/
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicTryLock(SDL_SpinLock *lock);
/**
* \brief Lock a spin lock by setting it to a non-zero value.
May 18, 2013
May 18, 2013
109
*
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
* \param lock Points to the lock.
*/
extern DECLSPEC void SDLCALL SDL_AtomicLock(SDL_SpinLock *lock);
/**
* \brief Unlock a spin lock by setting it to 0. Always returns immediately
*
* \param lock Points to the lock.
*/
extern DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock);
/*@}*//*SDL AtomicLock*/
/**
* The compiler barrier prevents the compiler from reordering
* reads and writes to globally visible variables across the call.
*/
Feb 26, 2013
Feb 26, 2013
128
#if defined(_MSC_VER) && (_MSC_VER > 1200)
129
130
131
132
133
134
135
void _ReadWriteBarrier(void);
#pragma intrinsic(_ReadWriteBarrier)
#define SDL_CompilerBarrier() _ReadWriteBarrier()
#elif defined(__GNUC__)
#define SDL_CompilerBarrier() __asm__ __volatile__ ("" : : : "memory")
#else
#define SDL_CompilerBarrier() \
Feb 26, 2013
Feb 26, 2013
136
{ SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp); SDL_AtomicUnlock(&_tmp); }
Jul 11, 2013
Jul 11, 2013
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/**
* Memory barriers are designed to prevent reads and writes from being
* reordered by the compiler and being seen out of order on multi-core CPUs.
*
* A typical pattern would be for thread A to write some data and a flag,
* and for thread B to read the flag and get the data. In this case you
* would insert a release barrier between writing the data and the flag,
* guaranteeing that the data write completes no later than the flag is
* written, and you would insert an acquire barrier between reading the
* flag and reading the data, to ensure that all the reads associated
* with the flag have completed.
*
* In this pattern you should always see a release barrier paired with
* an acquire barrier and you should gate the data reads/writes with a
* single flag variable.
*
* For more information on these semantics, take a look at the blog post:
* http://preshing.com/20120913/acquire-and-release-semantics
*/
/* FIXME: This is correct for x86 and x64 but not other CPUs
For PPC we need the lwsync instruction, and on ARM some variant of dmb
*/
#define SDL_MemoryBarrierRelease() SDL_CompilerBarrier()
#define SDL_MemoryBarrierAcquire() SDL_CompilerBarrier()
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/* Platform specific optimized versions of the atomic functions,
* you can disable these by defining SDL_DISABLE_ATOMIC_INLINE
*/
#if defined(SDL_ATOMIC_DISABLED) && SDL_ATOMIC_DISABLED
#define SDL_DISABLE_ATOMIC_INLINE
#endif
#ifndef SDL_DISABLE_ATOMIC_INLINE
#ifdef HAVE_MSC_ATOMICS
#define SDL_AtomicSet(a, v) _InterlockedExchange((long*)&(a)->value, (v))
#define SDL_AtomicAdd(a, v) _InterlockedExchangeAdd((long*)&(a)->value, (v))
#define SDL_AtomicCAS(a, oldval, newval) (_InterlockedCompareExchange((long*)&(a)->value, (newval), (oldval)) == (oldval))
#define SDL_AtomicSetPtr(a, v) _InterlockedExchangePointer((a), (v))
#if _M_IX86
#define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchange((long*)(a), (long)(newval), (long)(oldval)) == (long)(oldval))
#else
#define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchangePointer((a), (newval), (oldval)) == (oldval))
#endif
#elif defined(__MACOSX__)
#include <libkern/OSAtomic.h>
#define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((oldval), (newval), &(a)->value)
#ifdef __LP64__
#define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a))
#else
#define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))
#endif
#elif defined(HAVE_GCC_ATOMICS)
#define SDL_AtomicSet(a, v) __sync_lock_test_and_set(&(a)->value, v)
#define SDL_AtomicAdd(a, v) __sync_fetch_and_add(&(a)->value, v)
#define SDL_AtomicSetPtr(a, v) __sync_lock_test_and_set(a, v)
#define SDL_AtomicCAS(a, oldval, newval) __sync_bool_compare_and_swap(&(a)->value, oldval, newval)
#define SDL_AtomicCASPtr(a, oldval, newval) __sync_bool_compare_and_swap(a, oldval, newval)
#endif
#endif /* !SDL_DISABLE_ATOMIC_INLINE */
/**
* \brief A type representing an atomic integer value. It is a struct
* so people don't accidentally use numeric operations on it.
*/
#ifndef SDL_atomic_t_defined
typedef struct { int value; } SDL_atomic_t;
#endif
/**
* \brief Set an atomic variable to a new value if it is currently an old value.
*
* \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.
*
* \note If you don't know what this function is for, you shouldn't use it!
*/
#ifndef SDL_AtomicCAS
Mar 8, 2013
Mar 8, 2013
224
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval);
225
226
227
228
229
230
231
232
#endif
/**
* \brief Set an atomic variable to a value.
*
* \return The previous value of the atomic variable.
*/
#ifndef SDL_AtomicSet
Mar 15, 2013
Mar 15, 2013
233
SDL_FORCE_INLINE int SDL_AtomicSet(SDL_atomic_t *a, int v)
234
235
236
237
238
239
240
241
242
243
244
245
246
{
int value;
do {
value = a->value;
} while (!SDL_AtomicCAS(a, value, v));
return value;
}
#endif
/**
* \brief Get the value of an atomic variable
*/
#ifndef SDL_AtomicGet
Mar 15, 2013
Mar 15, 2013
247
SDL_FORCE_INLINE int SDL_AtomicGet(SDL_atomic_t *a)
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
{
int value = a->value;
SDL_CompilerBarrier();
return value;
}
#endif
/**
* \brief Add to an atomic variable.
*
* \return The previous value of the atomic variable.
*
* \note This same style can be used for any number operation
*/
#ifndef SDL_AtomicAdd
Mar 15, 2013
Mar 15, 2013
263
SDL_FORCE_INLINE int SDL_AtomicAdd(SDL_atomic_t *a, int v)
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
{
int value;
do {
value = a->value;
} while (!SDL_AtomicCAS(a, value, (value + v)));
return value;
}
#endif
/**
* \brief Increment an atomic variable used as a reference count.
*/
#ifndef SDL_AtomicIncRef
#define SDL_AtomicIncRef(a) SDL_AtomicAdd(a, 1)
#endif
/**
* \brief Decrement an atomic variable used as a reference count.
*
* \return SDL_TRUE if the variable reached zero after decrementing,
* SDL_FALSE otherwise
*/
#ifndef SDL_AtomicDecRef
#define SDL_AtomicDecRef(a) (SDL_AtomicAdd(a, -1) == 1)
#endif
/**
* \brief Set a pointer to a new value if it is currently an old value.
*
* \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
*
* \note If you don't know what this function is for, you shouldn't use it!
*/
#ifndef SDL_AtomicCASPtr
Mar 8, 2013
Mar 8, 2013
298
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr(void* *a, void *oldval, void *newval);
299
300
301
302
303
304
305
306
#endif
/**
* \brief Set a pointer to a value atomically.
*
* \return The previous value of the pointer.
*/
#ifndef SDL_AtomicSetPtr
Mar 15, 2013
Mar 15, 2013
307
SDL_FORCE_INLINE void* SDL_AtomicSetPtr(void* *a, void* v)
308
309
310
311
312
313
314
315
316
317
318
319
320
{
void* value;
do {
value = *a;
} while (!SDL_AtomicCASPtr(a, value, v));
return value;
}
#endif
/**
* \brief Get the value of a pointer atomically.
*/
#ifndef SDL_AtomicGetPtr
Mar 15, 2013
Mar 15, 2013
321
SDL_FORCE_INLINE void* SDL_AtomicGetPtr(void* *a)
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
{
void* value = *a;
SDL_CompilerBarrier();
return value;
}
#endif
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include "close_code.h"
#endif /* _SDL_atomic_h_ */
/* vi: set ts=4 sw=4 expandtab: */