This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_atomic.h
311 lines (269 loc) · 9.19 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2010 Sam Lantinga
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Sam Lantinga
slouken@libsdl.org
*/
/**
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
* \file SDL_atomic.h
*
* Atomic operations.
*
* 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.
*
* The list of "safe" functions to use are:
* SDL_AtomicLock()
* SDL_AtomicUnlock()
* SDL_AtomicIncRef()
* SDL_AtomicDecRef()
*
* Seriously, here be dragons!
41
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
42
*
43
44
45
46
47
* You can find out a little more about lockless programming and the
* subtle issues that can arise here:
* http://msdn.microsoft.com/en-us/library/ee418650%28v=vs.85%29.aspx
*
* These operations may or may not actually be implemented using
48
49
50
51
52
53
* 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.
54
55
56
57
58
59
60
61
62
63
*/
#ifndef _SDL_atomic_h_
#define _SDL_atomic_h_
#include "SDL_stdinc.h"
#include "SDL_platform.h"
#include "begin_code.h"
64
65
/* 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 */
66
#if defined(_MSC_VER) && (_MSC_VER >= 1500) && !defined(_WIN32_WCE)
67
68
69
70
#include <intrin.h>
#define HAVE_MSC_ATOMICS
#endif
71
72
73
74
75
76
77
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
/* *INDENT-OFF* */
extern "C" {
/* *INDENT-ON* */
#endif
78
/**
79
80
81
82
83
84
85
86
87
88
89
90
91
* \name SDL AtomicLock
*
* 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.
92
*/
93
/*@{*/
94
95
96
97
98
99
100
101
102
103
104
typedef int SDL_SpinLock;
/**
* \brief Try to lock a spin lock by setting it to a non-zero value.
*
* \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);
105
106
/**
107
108
109
* \brief Lock a spin lock by setting it to a non-zero value.
*
* \param lock Points to the lock.
110
*/
111
extern DECLSPEC void SDLCALL SDL_AtomicLock(SDL_SpinLock *lock);
112
113
/**
114
* \brief Unlock a spin lock by setting it to 0. Always returns immediately
115
*
116
* \param lock Points to the lock.
117
*/
118
119
extern DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock);
120
/*@}*//*SDL AtomicLock*/
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/* The compiler barrier prevents the compiler from reordering
reads and writes to globally visible variables across the call.
*/
#ifdef _MSC_VER
void _ReadWriteBarrier(void);
#pragma intrinsic(_ReadWriteBarrier)
#define SDL_CompilerBarrier() _ReadWriteBarrier()
#elif __GNUC__
#define SDL_CompilerBarrier() __asm__ __volatile__ ("" : : : "memory")
#else
#define SDL_CompilerBarrier() \
({ SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp); SDL_AtomicUnlock(&_tmp); })
#endif
137
138
139
140
141
/* Platform specific optimized versions of the atomic functions,
* you can disable these by defining SDL_DISABLE_ATOMIC_INLINE
*/
#ifndef SDL_DISABLE_ATOMIC_INLINE
142
#if HAVE_MSC_ATOMICS
143
144
145
146
#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))
147
#define SDL_AtomicSetPtr(a, v) _InterlockedExchangePointer((a), (v))
148
149
150
151
152
#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
153
154
#elif __MACOSX__
155
156
#include <libkern/OSAtomic.h>
157
#define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((oldval), (newval), &(a)->value)
158
159
160
161
162
163
#if SIZEOF_VOIDP == 4
#define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))
#elif SIZEOF_VOIDP == 8
#define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a))
#endif
164
#elif HAVE_GCC_ATOMICS
165
166
167
#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)
168
#define SDL_AtomicSetPtr(a, v) __sync_lock_test_and_set(a, v)
169
170
171
172
#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
173
174
175
176
#endif /* !SDL_DISABLE_ATOMIC_INLINE */
177
/**
178
179
* \brief A type representing an atomic integer value. It is a struct
* so people don't accidentally use numeric operations on it.
180
*/
181
182
183
#ifndef SDL_atomic_t_defined
typedef struct { int value; } SDL_atomic_t;
#endif
184
185
/**
186
187
188
* \brief Set an atomic variable to a value.
*
* \return The previous value of the atomic variable.
189
*/
190
#ifndef SDL_AtomicSet
191
192
193
194
195
196
197
198
#define SDL_AtomicSet(a, v) \
({ \
int _value; \
do { \
_value = (a)->value; \
} while (!SDL_AtomicCAS(a, _value, (v))); \
_value; \
})
199
#endif
200
201
/**
202
* \brief Get the value of an atomic variable
203
*/
204
#ifndef SDL_AtomicGet
205
206
207
208
209
210
#define SDL_AtomicGet(a) \
({ \
int _value = (a)->value; \
SDL_CompilerBarrier(); \
_value; \
})
211
#endif
212
213
/**
214
* \brief Add to an atomic variable.
215
216
*
* \return The previous value of the atomic variable.
217
218
*
* \note This same style can be used for any number operation
219
*/
220
#ifndef SDL_AtomicAdd
221
222
223
224
225
226
227
228
#define SDL_AtomicAdd(a, v) \
({ \
int _value; \
do { \
_value = (a)->value; \
} while (!SDL_AtomicCAS(a, _value, (_value + (v)))); \
_value; \
})
229
#endif
230
231
/**
232
* \brief Increment an atomic variable used as a reference count.
233
*/
234
#ifndef SDL_AtomicIncRef
235
#define SDL_AtomicIncRef(a) SDL_AtomicAdd(a, 1)
236
#endif
237
238
/**
239
240
* \brief Decrement an atomic variable used as a reference count.
*
241
* \return SDL_TRUE if the variable reached zero after decrementing,
242
* SDL_FALSE otherwise
243
*/
244
#ifndef SDL_AtomicDecRef
245
#define SDL_AtomicDecRef(a) (SDL_AtomicAdd(a, -1) == 1)
246
#endif
247
248
/**
249
250
* \brief Set an atomic variable to a new value if it is currently an old value.
*
251
* \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.
252
253
254
255
*
* \note If you don't know what this function is for, you shouldn't use it!
*/
#ifndef SDL_AtomicCAS
256
#define SDL_AtomicCAS SDL_AtomicCAS_
257
#endif
258
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS_(SDL_atomic_t *a, int oldval, int newval);
259
260
/**
261
* \brief Set a pointer to a value atomically.
262
263
*
* \return The previous value of the pointer.
264
*/
265
#ifndef SDL_AtomicSetPtr
266
267
268
269
270
271
272
273
#define SDL_AtomicSetPtr(a, v) \
({ \
void* _value; \
do { \
_value = *(a); \
} while (!SDL_AtomicCASPtr(a, _value, (v))); \
_value; \
})
274
#endif
275
276
/**
277
* \brief Get the value of a pointer atomically.
278
*/
279
#ifndef SDL_AtomicGetPtr
280
281
282
283
284
285
#define SDL_AtomicGetPtr(a) \
({ \
void* _value = *(a); \
SDL_CompilerBarrier(); \
_value; \
})
286
#endif
287
288
/**
289
290
* \brief Set a pointer to a new value if it is currently an old value.
*
291
* \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
292
293
294
295
*
* \note If you don't know what this function is for, you shouldn't use it!
*/
#ifndef SDL_AtomicCASPtr
296
#define SDL_AtomicCASPtr SDL_AtomicCASPtr_
297
#endif
298
extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr_(void **a, void *oldval, void *newval);
299
300
301
302
303
304
305
306
307
308
309
310
311
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
/* *INDENT-OFF* */
}
/* *INDENT-ON* */
#endif
#include "close_code.h"
#endif /* _SDL_atomic_h_ */
/* vi: set ts=4 sw=4 expandtab: */