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
1
/*
2
3
SDL - Simple DirectMedia Layer
Copyright (C) 1997-2009 Sam Lantinga
4
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.
9
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.
14
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
18
19
20
21
22
Sam Lantinga
slouken@libsdl.org
Contributed by Bob Pendleton, bob@pendleton.com
23
24
*/
25
#include "SDL_stdinc.h"
26
27
#include "SDL_atomic.h"
28
29
#include "SDL_error.h"
30
/*
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.
36
37
38
*/
/*
39
LINUX/GCC VERSION.
40
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.
45
46
47
48
Some processors will only support some of these operations so
#ifdefs will have to be added as incompatibilities are discovered
*/
49
50
51
52
/*
Native spinlock routines.
*/
53
54
55
void
SDL_AtomicLock(SDL_SpinLock *lock)
56
{
57
while (0 != __sync_lock_test_and_set(lock, 1))
58
59
60
61
{
}
}
62
63
void
SDL_AtomicUnlock(SDL_SpinLock *lock)
64
{
65
__sync_lock_test_and_set(lock, 0);
66
67
}
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.
*/
73
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
95
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.
*/
116
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,
};
123
124
125
static __inline__ void
privateWaitLock(volatile void *ptr)
126
{
127
128
129
130
#if SIZEOF_VOIDP == 4
Uint32 index = ((((Uint32)ptr) >> 3) & 0x1f);
#elif SIZEOF_VOIDP == 8
Uint64 index = ((((Uint64)ptr) >> 3) & 0x1f);
131
132
#endif
133
SDL_AtomicLock(&locks[index]);
134
135
}
136
137
static __inline__ void
privateUnlock(volatile void *ptr)
138
{
139
140
141
142
#if SIZEOF_VOIDP == 4
Uint32 index = ((((Uint32)ptr) >> 3) & 0x1f);
#elif SIZEOF_VOIDP == 8
Uint64 index = ((((Uint64)ptr) >> 3) & 0x1f);
143
144
#endif
145
SDL_AtomicUnlock(&locks[index]);
146
147
148
149
}
/* 32 bit atomic operations */
150
SDL_bool
151
SDL_AtomicTestThenSet32(volatile Uint32 * ptr)
152
{
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
168
169
170
}
void
171
SDL_AtomicClear32(volatile Uint32 * ptr)
172
{
173
#ifdef nativeClear32
174
175
__sync_lock_test_and_set(ptr, 0);
return;
176
#else
177
privateWaitLock(ptr);
178
*ptr = 0;
179
privateUnlock(ptr);
180
181
182
return;
#endif
183
184
185
}
Uint32
186
SDL_AtomicFetchThenIncrement32(volatile Uint32 * ptr)
187
{
188
#ifdef nativeFetchThenIncrement32
189
return __sync_fetch_and_add(ptr, 1);
190
#else
191
Uint32 tmp = 0;
192
193
privateWaitLock(ptr);
194
195
tmp = *ptr;
(*ptr)+= 1;
196
privateUnlock(ptr);
197
198
199
return tmp;
#endif
200
201
202
}
Uint32
203
SDL_AtomicFetchThenDecrement32(volatile Uint32 * ptr)
204
{
205
#ifdef nativeFetchThenDecrement32
206
return __sync_fetch_and_sub(ptr, 1);
207
#else
208
Uint32 tmp = 0;
209
210
privateWaitLock(ptr);
211
212
tmp = *ptr;
(*ptr) -= 1;
213
privateUnlock(ptr);
214
215
216
return tmp;
#endif
217
218
219
}
Uint32
220
SDL_AtomicFetchThenAdd32(volatile Uint32 * ptr, Uint32 value)
221
{
222
#ifdef nativeFetchThenAdd32
223
return __sync_fetch_and_add(ptr, value);
224
#else
225
Uint32 tmp = 0;
226
227
privateWaitLock(ptr);
228
229
tmp = *ptr;
(*ptr)+= value;
230
privateUnlock(ptr);
231
232
233
return tmp;
#endif
234
235
236
}
Uint32
237
SDL_AtomicFetchThenSubtract32(volatile Uint32 * ptr, Uint32 value)
238
{
239
#ifdef nativeFetchThenSubtract32
240
return __sync_fetch_and_sub(ptr, value);
241
#else
242
Uint32 tmp = 0;
243
244
privateWaitLock(ptr);
245
246
tmp = *ptr;
(*ptr)-= value;
247
privateUnlock(ptr);
248
249
250
return tmp;
#endif
251
252
253
}
Uint32
254
SDL_AtomicIncrementThenFetch32(volatile Uint32 * ptr)
255
{
256
#ifdef nativeIncrementThenFetch32
257
return __sync_add_and_fetch(ptr, 1);
258
#else
259
Uint32 tmp = 0;
260
261
privateWaitLock(ptr);
262
263
(*ptr)+= 1;
tmp = *ptr;
264
privateUnlock(ptr);
265
266
267
return tmp;
#endif
268
269
270
}
Uint32
271
SDL_AtomicDecrementThenFetch32(volatile Uint32 * ptr)
272
{
273
#ifdef nativeDecrementThenFetch32
274
return __sync_sub_and_fetch(ptr, 1);
275
#else
276
Uint32 tmp = 0;
277
278
privateWaitLock(ptr);
279
280
(*ptr)-= 1;
tmp = *ptr;
281
privateUnlock(ptr);
282
283
284
return tmp;
#endif
285
286
287
}
Uint32
288
SDL_AtomicAddThenFetch32(volatile Uint32 * ptr, Uint32 value)
289
{
290
#ifdef nativeAddThenFetch32
291
return __sync_add_and_fetch(ptr, value);
292
#else
293
Uint32 tmp = 0;
294
295
privateWaitLock(ptr);
296
297
(*ptr)+= value;
tmp = *ptr;
298
privateUnlock(ptr);
299
300
301
return tmp;
#endif
302
303
304
}
Uint32
305
SDL_AtomicSubtractThenFetch32(volatile Uint32 * ptr, Uint32 value)
306
{
307
#ifdef nativeSubtractThenFetch32
308
return __sync_sub_and_fetch(ptr, value);
309
#else
310
Uint32 tmp = 0;
311
312
privateWaitLock(ptr);
313
314
(*ptr)-= value;
tmp = *ptr;
315
privateUnlock(ptr);
316
317
318
return tmp;
#endif
319
320
}
321
322
/* 64 bit atomic operations */
#ifdef SDL_HAS_64BIT_TYPE
323
324
SDL_bool
325
SDL_AtomicTestThenSet64(volatile Uint64 * ptr)
326
{
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
342
343
344
}
void
345
SDL_AtomicClear64(volatile Uint64 * ptr)
346
{
347
#ifdef nativeClear64
348
349
__sync_lock_test_and_set(ptr, 0);
return;
350
#else
351
privateWaitLock(ptr);
352
*ptr = 0;
353
privateUnlock(ptr);
354
355
356
return;
#endif
357
358
359
}
Uint64
360
SDL_AtomicFetchThenIncrement64(volatile Uint64 * ptr)
361
{
362
#ifdef nativeFetchThenIncrement64
363
return __sync_fetch_and_add(ptr, 1);
364
#else
365
Uint64 tmp = 0;
366
367
privateWaitLock(ptr);
368
369
tmp = *ptr;
(*ptr)+= 1;
370
privateUnlock(ptr);
371
372
373
return tmp;
#endif
374
375
376
}
Uint64
377
SDL_AtomicFetchThenDecrement64(volatile Uint64 * ptr)
378
{
379
#ifdef nativeFetchThenDecrement64
380
return __sync_fetch_and_sub(ptr, 1);
381
#else
382
Uint64 tmp = 0;
383
384
privateWaitLock(ptr);
385
386
tmp = *ptr;
(*ptr) -= 1;
387
privateUnlock(ptr);
388
389
390
return tmp;
#endif
391
392
393
}
Uint64
394
SDL_AtomicFetchThenAdd64(volatile Uint64 * ptr, Uint64 value)
395
{
396
#ifdef nativeFetchThenAdd64
397
return __sync_fetch_and_add(ptr, value);
398
#else
399
Uint64 tmp = 0;
400
401
privateWaitLock(ptr);
402
403
tmp = *ptr;
(*ptr)+= value;
404
privateUnlock(ptr);
405
406
407
return tmp;
#endif
408
409
410
}
Uint64
411
SDL_AtomicFetchThenSubtract64(volatile Uint64 * ptr, Uint64 value)
412
{
413
#ifdef nativeFetchThenSubtract64
414
return __sync_fetch_and_sub(ptr, value);
415
#else
416
Uint64 tmp = 0;
417
418
privateWaitLock(ptr);
419
420
tmp = *ptr;
(*ptr)-= value;
421
privateUnlock(ptr);
422
423
424
return tmp;
#endif
425
426
427
}
Uint64
428
SDL_AtomicIncrementThenFetch64(volatile Uint64 * ptr)
429
{
430
#ifdef nativeIncrementThenFetch64
431
return __sync_add_and_fetch(ptr, 1);
432
#else
433
Uint64 tmp = 0;
434
435
privateWaitLock(ptr);
436
437
(*ptr)+= 1;
tmp = *ptr;
438
privateUnlock(ptr);
439
440
441
return tmp;
#endif
442
443
444
}
Uint64
445
SDL_AtomicDecrementThenFetch64(volatile Uint64 * ptr)
446
{
447
#ifdef nativeDecrementThenFetch64
448
return __sync_sub_and_fetch(ptr, 1);
449
#else
450
Uint64 tmp = 0;
451
452
privateWaitLock(ptr);
453
454
(*ptr)-= 1;
tmp = *ptr;
455
privateUnlock(ptr);
456
457
458
return tmp;
#endif
459
460
461
}
Uint64
462
SDL_AtomicAddThenFetch64(volatile Uint64 * ptr, Uint64 value)
463
{
464
#ifdef nativeAddThenFetch64
465
return __sync_add_and_fetch(ptr, value);
466
#else
467
Uint64 tmp = 0;
468
469
privateWaitLock(ptr);
470
471
(*ptr)+= value;
tmp = *ptr;
472
privateUnlock(ptr);
473
474
475
return tmp;
#endif
476
477
478
}
Uint64
479
SDL_AtomicSubtractThenFetch64(volatile Uint64 * ptr, Uint64 value)
480
{
481
#ifdef nativeSubtractThenFetch64
482
return __sync_sub_and_fetch(ptr, value);
483
#else
484
Uint64 tmp = 0;
485
486
privateWaitLock(ptr);
487
488
(*ptr)-= value;
tmp = *ptr;
489
privateUnlock(ptr);
490
491
492
return tmp;
#endif
493
}
494
495
#endif /* SDL_HAS_64BIT_TYPE */