This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_atomic.c
495 lines (409 loc) · 9.62 KB
2
3
SDL - Simple DirectMedia Layer
Copyright (C) 1997-2009 Sam Lantinga
5
6
7
8
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.
10
11
12
13
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.
15
16
17
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
19
20
21
22
Sam Lantinga
slouken@libsdl.org
Contributed by Bob Pendleton, bob@pendleton.com
25
#include "SDL_stdinc.h"
26
27
#include "SDL_atomic.h"
28
29
#include "SDL_error.h"
31
This file provides 32, and 64 bit atomic operations. If the
32
33
operations are provided by the native hardware and operating system
they are used. If they are not then the operations are emulated
34
35
using the SDL spin lock operations. If spin lock can not be
implemented then these functions must fail.
39
LINUX/GCC VERSION.
41
42
43
44
This version of the code assumes support of the atomic builtins as
documented at gcc.gnu.org/onlinedocs/gcc/Atomic-Builtins.html This
code should work on any modern x86 or other processor supported by
GCC.
46
47
48
Some processors will only support some of these operations so
#ifdefs will have to be added as incompatibilities are discovered
*/
50
51
52
/*
Native spinlock routines.
*/
54
55
void
SDL_AtomicLock(SDL_SpinLock *lock)
57
while (0 != __sync_lock_test_and_set(lock, 1))
62
63
void
SDL_AtomicUnlock(SDL_SpinLock *lock)
65
__sync_lock_test_and_set(lock, 0);
68
69
70
71
72
/*
Note that platform specific versions can be built from this version
by changing the #undefs to #defines and adding platform specific
code.
*/
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#define nativeTestThenSet32
#define nativeClear32
#define nativeFetchThenIncrement32
#define nativeFetchThenDecrement32
#define nativeFetchThenAdd32
#define nativeFetchThenSubtract32
#define nativeIncrementThenFetch32
#define nativeDecrementThenFetch32
#define nativeAddThenFetch32
#define nativeSubtractThenFetch32
#define nativeTestThenSet64
#define nativeClear64
#define nativeFetchThenIncrement64
#define nativeFetchThenDecrement64
#define nativeFetchThenAdd64
#define nativeFetchThenSubtract64
#define nativeIncrementThenFetch64
#define nativeDecrementThenFetch64
#define nativeAddThenFetch64
#define nativeSubtractThenFetch64
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
If any of the operations are not provided then we must emulate some
of them. That means we need a nice implementation of spin locks
that avoids the "one big lock" problem. We use a vector of spin
locks and pick which one to use based on the address of the operand
of the function.
To generate the index of the lock we first shift by 3 bits to get
rid on the zero bits that result from 32 and 64 bit allignment of
data. We then mask off all but 5 bits and use those 5 bits as an
index into the table.
Picking the lock this way insures that accesses to the same data at
the same time will go to the same lock. OTOH, accesses to different
data have only a 1/32 chance of hitting the same lock. That should
pretty much eliminate the chances of several atomic operations on
different data from waiting on the same "big lock". If it isn't
then the table of locks can be expanded to a new size so long as
the new size if a power of two.
*/
117
118
119
120
121
122
static SDL_SpinLock locks[32] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
124
125
static __inline__ void
privateWaitLock(volatile void *ptr)
127
128
129
130
#if SIZEOF_VOIDP == 4
Uint32 index = ((((Uint32)ptr) >> 3) & 0x1f);
#elif SIZEOF_VOIDP == 8
Uint64 index = ((((Uint64)ptr) >> 3) & 0x1f);
133
SDL_AtomicLock(&locks[index]);
136
137
static __inline__ void
privateUnlock(volatile void *ptr)
139
140
141
142
#if SIZEOF_VOIDP == 4
Uint32 index = ((((Uint32)ptr) >> 3) & 0x1f);
#elif SIZEOF_VOIDP == 8
Uint64 index = ((((Uint64)ptr) >> 3) & 0x1f);
145
SDL_AtomicUnlock(&locks[index]);
146
147
148
149
}
/* 32 bit atomic operations */
151
SDL_AtomicTestThenSet32(volatile Uint32 * ptr)
153
#ifdef nativeTestThenSet32
154
return 0 == __sync_lock_test_and_set(ptr, 1);
155
156
157
#else
SDL_bool result = SDL_FALSE;
158
privateWaitLock(ptr);
159
160
161
162
163
result = (*ptr == 0);
if (result)
{
*ptr = 1;
}
164
privateUnlock(ptr);
165
166
167
return result;
#endif
171
SDL_AtomicClear32(volatile Uint32 * ptr)
173
#ifdef nativeClear32
174
175
__sync_lock_test_and_set(ptr, 0);
return;
177
privateWaitLock(ptr);
179
privateUnlock(ptr);
180
181
182
return;
#endif
186
SDL_AtomicFetchThenIncrement32(volatile Uint32 * ptr)
188
#ifdef nativeFetchThenIncrement32
189
return __sync_fetch_and_add(ptr, 1);
191
Uint32 tmp = 0;
193
privateWaitLock(ptr);
194
195
tmp = *ptr;
(*ptr)+= 1;
196
privateUnlock(ptr);
197
198
199
return tmp;
#endif
203
SDL_AtomicFetchThenDecrement32(volatile Uint32 * ptr)
205
#ifdef nativeFetchThenDecrement32
206
return __sync_fetch_and_sub(ptr, 1);
208
Uint32 tmp = 0;
210
privateWaitLock(ptr);
211
212
tmp = *ptr;
(*ptr) -= 1;
213
privateUnlock(ptr);
214
215
216
return tmp;
#endif
220
SDL_AtomicFetchThenAdd32(volatile Uint32 * ptr, Uint32 value)
222
#ifdef nativeFetchThenAdd32
223
return __sync_fetch_and_add(ptr, value);
225
Uint32 tmp = 0;
227
privateWaitLock(ptr);
228
229
tmp = *ptr;
(*ptr)+= value;
230
privateUnlock(ptr);
231
232
233
return tmp;
#endif
237
SDL_AtomicFetchThenSubtract32(volatile Uint32 * ptr, Uint32 value)
239
#ifdef nativeFetchThenSubtract32
240
return __sync_fetch_and_sub(ptr, value);
242
Uint32 tmp = 0;
244
privateWaitLock(ptr);
245
246
tmp = *ptr;
(*ptr)-= value;
247
privateUnlock(ptr);
248
249
250
return tmp;
#endif
254
SDL_AtomicIncrementThenFetch32(volatile Uint32 * ptr)
256
#ifdef nativeIncrementThenFetch32
257
return __sync_add_and_fetch(ptr, 1);
259
Uint32 tmp = 0;
261
privateWaitLock(ptr);
262
263
(*ptr)+= 1;
tmp = *ptr;
264
privateUnlock(ptr);
265
266
267
return tmp;
#endif
271
SDL_AtomicDecrementThenFetch32(volatile Uint32 * ptr)
273
#ifdef nativeDecrementThenFetch32
274
return __sync_sub_and_fetch(ptr, 1);
276
Uint32 tmp = 0;
278
privateWaitLock(ptr);
279
280
(*ptr)-= 1;
tmp = *ptr;
281
privateUnlock(ptr);
282
283
284
return tmp;
#endif
288
SDL_AtomicAddThenFetch32(volatile Uint32 * ptr, Uint32 value)
290
#ifdef nativeAddThenFetch32
291
return __sync_add_and_fetch(ptr, value);
293
Uint32 tmp = 0;
295
privateWaitLock(ptr);
296
297
(*ptr)+= value;
tmp = *ptr;
298
privateUnlock(ptr);
299
300
301
return tmp;
#endif
305
SDL_AtomicSubtractThenFetch32(volatile Uint32 * ptr, Uint32 value)
307
#ifdef nativeSubtractThenFetch32
308
return __sync_sub_and_fetch(ptr, value);
310
Uint32 tmp = 0;
312
privateWaitLock(ptr);
313
314
(*ptr)-= value;
tmp = *ptr;
315
privateUnlock(ptr);
316
317
318
return tmp;
#endif
321
322
/* 64 bit atomic operations */
#ifdef SDL_HAS_64BIT_TYPE
325
SDL_AtomicTestThenSet64(volatile Uint64 * ptr)
327
#ifdef nativeTestThenSet64
328
return 0 == __sync_lock_test_and_set(ptr, 1);
329
330
331
#else
SDL_bool result = SDL_FALSE;
332
privateWaitLock(ptr);
333
334
335
336
337
result = (*ptr == 0);
if (result)
{
*ptr = 1;
}
338
privateUnlock(ptr);
339
340
341
return result;
#endif
345
SDL_AtomicClear64(volatile Uint64 * ptr)
347
#ifdef nativeClear64
348
349
__sync_lock_test_and_set(ptr, 0);
return;
351
privateWaitLock(ptr);
353
privateUnlock(ptr);
354
355
356
return;
#endif
360
SDL_AtomicFetchThenIncrement64(volatile Uint64 * ptr)
362
#ifdef nativeFetchThenIncrement64
363
return __sync_fetch_and_add(ptr, 1);
365
Uint64 tmp = 0;
367
privateWaitLock(ptr);
368
369
tmp = *ptr;
(*ptr)+= 1;
370
privateUnlock(ptr);
371
372
373
return tmp;
#endif
377
SDL_AtomicFetchThenDecrement64(volatile Uint64 * ptr)
379
#ifdef nativeFetchThenDecrement64
380
return __sync_fetch_and_sub(ptr, 1);
382
Uint64 tmp = 0;
384
privateWaitLock(ptr);
385
386
tmp = *ptr;
(*ptr) -= 1;
387
privateUnlock(ptr);
388
389
390
return tmp;
#endif
394
SDL_AtomicFetchThenAdd64(volatile Uint64 * ptr, Uint64 value)
396
#ifdef nativeFetchThenAdd64
397
return __sync_fetch_and_add(ptr, value);
399
Uint64 tmp = 0;
401
privateWaitLock(ptr);
402
403
tmp = *ptr;
(*ptr)+= value;
404
privateUnlock(ptr);
405
406
407
return tmp;
#endif
411
SDL_AtomicFetchThenSubtract64(volatile Uint64 * ptr, Uint64 value)
413
#ifdef nativeFetchThenSubtract64
414
return __sync_fetch_and_sub(ptr, value);
416
Uint64 tmp = 0;
418
privateWaitLock(ptr);
419
420
tmp = *ptr;
(*ptr)-= value;
421
privateUnlock(ptr);
422
423
424
return tmp;
#endif
428
SDL_AtomicIncrementThenFetch64(volatile Uint64 * ptr)
430
#ifdef nativeIncrementThenFetch64
431
return __sync_add_and_fetch(ptr, 1);
433
Uint64 tmp = 0;
435
privateWaitLock(ptr);
436
437
(*ptr)+= 1;
tmp = *ptr;
438
privateUnlock(ptr);
439
440
441
return tmp;
#endif
445
SDL_AtomicDecrementThenFetch64(volatile Uint64 * ptr)
447
#ifdef nativeDecrementThenFetch64
448
return __sync_sub_and_fetch(ptr, 1);
450
Uint64 tmp = 0;
452
privateWaitLock(ptr);
453
454
(*ptr)-= 1;
tmp = *ptr;
455
privateUnlock(ptr);
456
457
458
return tmp;
#endif
462
SDL_AtomicAddThenFetch64(volatile Uint64 * ptr, Uint64 value)
464
#ifdef nativeAddThenFetch64
465
return __sync_add_and_fetch(ptr, value);
467
Uint64 tmp = 0;
469
privateWaitLock(ptr);
470
471
(*ptr)+= value;
tmp = *ptr;
472
privateUnlock(ptr);
473
474
475
return tmp;
#endif
479
SDL_AtomicSubtractThenFetch64(volatile Uint64 * ptr, Uint64 value)
481
#ifdef nativeSubtractThenFetch64
482
return __sync_sub_and_fetch(ptr, value);
484
Uint64 tmp = 0;
486
privateWaitLock(ptr);
487
488
(*ptr)-= value;
tmp = *ptr;
489
privateUnlock(ptr);
490
491
492
return tmp;
#endif
495
#endif /* SDL_HAS_64BIT_TYPE */