This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_atomic.c
512 lines (414 loc) · 9.67 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
30
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
31
32
#include "SDL_error.h"
33
/*
34
This file provides 32, and 64 bit atomic operations. If the
35
36
operations are provided by the native hardware and operating system
they are used. If they are not then the operations are emulated
37
38
using the SDL spin lock operations. If spin lock can not be
implemented then these functions must fail.
39
40
41
*/
/*
42
WIN32 VERSION.
43
44
This makes use of native Windows atomic operations.
45
*/
46
47
48
49
50
/*
Native spinlock routines. Because this is the dummy implementation
these will always call SDL_SetError() and do nothing.
*/
51
52
53
void
SDL_AtomicLock(SDL_SpinLock *lock)
54
{
55
56
57
58
59
60
61
62
63
long volatile * l = (long volatile *)lock;
Uint32 old = 0;
Uint32 new = 1;
old = InterlockedExchange(l, new);
while(1 == old)
{
old = InterlockedExchange(l, new);
}
64
65
}
66
67
void
SDL_AtomicUnlock(SDL_SpinLock *lock)
68
{
69
70
71
72
long volatile * l = (long volatile *)lock;
Uint32 new = 0;
InterlockedExchange(l, new);
73
74
}
75
76
77
78
79
/*
Note that platform specific versions can be built from this version
by changing the #undefs to #defines and adding platform specific
code.
*/
80
81
82
83
84
85
86
87
88
89
90
#define nativeTestThenSet32
#define nativeClear32
#define nativeFetchThenIncrement32
#define nativeFetchThenDecrement32
#define nativeFetchThenAdd32
#define nativeFetchThenSubtract32
#define nativeIncrementThenFetch32
#define nativeDecrementThenFetch32
#define nativeAddThenFetch32
#define nativeSubtractThenFetch32
91
92
93
94
95
96
97
98
99
100
101
#undef nativeTestThenSet64
#undef nativeClear64
#undef nativeFetchThenIncrement64
#undef nativeFetchThenDecrement64
#undef nativeFetchThenAdd64
#undef nativeFetchThenSubtract64
#undef nativeIncrementThenFetch64
#undef nativeDecrementThenFetch64
#undef nativeAddThenFetch64
#undef nativeSubtractThenFetch64
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*
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
121
the new size is a power of two.
122
*/
123
124
125
126
127
128
129
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,
};
130
131
132
static __inline__ void
privateWaitLock(volatile void *ptr)
133
{
134
135
136
137
#if SIZEOF_VOIDP == 4
Uint32 index = ((((Uint32)ptr) >> 3) & 0x1f);
#elif SIZEOF_VOIDP == 8
Uint64 index = ((((Uint64)ptr) >> 3) & 0x1f);
138
139
#endif
140
SDL_AtomicLock(&locks[index]);
141
142
}
143
144
static __inline__ void
privateUnlock(volatile void *ptr)
145
{
146
147
148
149
#if SIZEOF_VOIDP == 4
Uint32 index = ((((Uint32)ptr) >> 3) & 0x1f);
#elif SIZEOF_VOIDP == 8
Uint64 index = ((((Uint64)ptr) >> 3) & 0x1f);
150
151
#endif
152
SDL_AtomicUnlock(&locks[index]);
153
154
155
156
}
/* 32 bit atomic operations */
157
SDL_bool
158
SDL_AtomicTestThenSet32(volatile Uint32 * ptr)
159
{
160
#ifdef nativeTestThenSet32
161
162
163
164
long volatile * p = (long volatile *)ptr;
Uint32 new = 1;
return 0 == InterlockedExchange(p, new);
165
166
167
#else
SDL_bool result = SDL_FALSE;
168
privateWaitLock(ptr);
169
170
171
172
173
result = (*ptr == 0);
if (result)
{
*ptr = 1;
}
174
privateUnlock(ptr);
175
176
177
return result;
#endif
178
179
180
}
void
181
SDL_AtomicClear32(volatile Uint32 * ptr)
182
{
183
#ifdef nativeClear32
184
185
186
187
long volatile * p = (long volatile *)ptr;
Uint32 new = 0;
InterlockedExchange(p, new);
188
#else
189
privateWaitLock(ptr);
190
*ptr = 0;
191
privateUnlock(ptr);
192
193
194
return;
#endif
195
196
197
}
Uint32
198
SDL_AtomicFetchThenIncrement32(volatile Uint32 * ptr)
199
{
200
#ifdef nativeFetchThenIncrement32
201
202
203
long volatile * p = (long volatile *)ptr;
return InterlockedExchangeAdd(p, 1);
204
#else
205
Uint32 tmp = 0;
206
207
privateWaitLock(ptr);
208
209
tmp = *ptr;
(*ptr)+= 1;
210
privateUnlock(ptr);
211
212
213
return tmp;
#endif
214
215
216
}
Uint32
217
SDL_AtomicFetchThenDecrement32(volatile Uint32 * ptr)
218
{
219
#ifdef nativeFetchThenDecrement32
220
221
222
long volatile * p = (long volatile *)ptr;
return InterlockedExchangeAdd(p, -1);
223
#else
224
Uint32 tmp = 0;
225
226
privateWaitLock(ptr);
227
228
tmp = *ptr;
(*ptr) -= 1;
229
privateUnlock(ptr);
230
231
232
return tmp;
#endif
233
234
235
}
Uint32
236
SDL_AtomicFetchThenAdd32(volatile Uint32 * ptr, Uint32 value)
237
{
238
#ifdef nativeFetchThenAdd32
239
240
241
long volatile * p = (long volatile *)ptr;
return InterlockedExchangeAdd(p, value);
242
#else
243
Uint32 tmp = 0;
244
245
privateWaitLock(ptr);
246
247
tmp = *ptr;
(*ptr)+= value;
248
privateUnlock(ptr);
249
250
251
return tmp;
#endif
252
253
254
}
Uint32
255
SDL_AtomicFetchThenSubtract32(volatile Uint32 * ptr, Uint32 value)
256
{
257
#ifdef nativeFetchThenSubtract32
258
259
260
long volatile * p = (long volatile *)ptr;
return InterlockedExchangeAdd(p, (0 - value));
261
#else
262
Uint32 tmp = 0;
263
264
privateWaitLock(ptr);
265
266
tmp = *ptr;
(*ptr)-= value;
267
privateUnlock(ptr);
268
269
270
return tmp;
#endif
271
272
273
}
Uint32
274
SDL_AtomicIncrementThenFetch32(volatile Uint32 * ptr)
275
{
276
#ifdef nativeIncrementThenFetch32
277
278
279
long volatile * p = (LONG volatile *)ptr;
return InterlockedIncrement(p);
280
#else
281
Uint32 tmp = 0;
282
283
privateWaitLock(ptr);
284
285
(*ptr)+= 1;
tmp = *ptr;
286
privateUnlock(ptr);
287
288
289
return tmp;
#endif
290
291
292
}
Uint32
293
SDL_AtomicDecrementThenFetch32(volatile Uint32 * ptr)
294
{
295
#ifdef nativeDecrementThenFetch32
296
297
298
long volatile * p = (LONG volatile *)ptr;
return InterlockedDecrement(p);
299
#else
300
Uint32 tmp = 0;
301
302
privateWaitLock(ptr);
303
304
(*ptr)-= 1;
tmp = *ptr;
305
privateUnlock(ptr);
306
307
308
return tmp;
#endif
309
310
311
}
Uint32
312
SDL_AtomicAddThenFetch32(volatile Uint32 * ptr, Uint32 value)
313
{
314
#ifdef nativeAddThenFetch32
315
316
317
long volatile * p = (long volatile *)ptr;
return InterlockedExchangeAdd(p, value) + value;
318
#else
319
Uint32 tmp = 0;
320
321
privateWaitLock(ptr);
322
323
(*ptr)+= value;
tmp = *ptr;
324
privateUnlock(ptr);
325
326
327
return tmp;
#endif
328
329
330
}
Uint32
331
SDL_AtomicSubtractThenFetch32(volatile Uint32 * ptr, Uint32 value)
332
{
333
#ifdef nativeSubtractThenFetch32
334
335
336
long volatile * p = (long volatile *)ptr;
return InterlockedExchangeAdd(p, (0 - value)) - value;
337
#else
338
Uint32 tmp = 0;
339
340
privateWaitLock(ptr);
341
342
(*ptr)-= value;
tmp = *ptr;
343
privateUnlock(ptr);
344
345
346
347
return tmp;
#endif
}
348
349
/* 64 bit atomic operations */
350
351
352
#ifdef SDL_HAS_64BIT_TYPE
SDL_bool
353
SDL_AtomicTestThenSet64(volatile Uint64 * ptr)
354
{
355
356
357
358
#ifdef nativeTestThenSet64
#else
SDL_bool result = SDL_FALSE;
359
privateWaitLock(ptr);
360
361
362
363
364
result = (*ptr == 0);
if (result)
{
*ptr = 1;
}
365
privateUnlock(ptr);
366
367
368
return result;
#endif
369
370
371
}
void
372
SDL_AtomicClear64(volatile Uint64 * ptr)
373
{
374
375
#ifdef nativeClear64
#else
376
privateWaitLock(ptr);
377
*ptr = 0;
378
privateUnlock(ptr);
379
380
381
return;
#endif
382
383
384
}
Uint64
385
SDL_AtomicFetchThenIncrement64(volatile Uint64 * ptr)
386
{
387
388
#ifdef nativeFetchThenIncrement64
#else
389
Uint64 tmp = 0;
390
391
privateWaitLock(ptr);
392
393
tmp = *ptr;
(*ptr)+= 1;
394
privateUnlock(ptr);
395
396
397
return tmp;
#endif
398
399
400
}
Uint64
401
SDL_AtomicFetchThenDecrement64(volatile Uint64 * ptr)
402
{
403
404
#ifdef nativeFetchThenDecrement64
#else
405
Uint64 tmp = 0;
406
407
privateWaitLock(ptr);
408
409
tmp = *ptr;
(*ptr) -= 1;
410
privateUnlock(ptr);
411
412
413
return tmp;
#endif
414
415
416
}
Uint64
417
SDL_AtomicFetchThenAdd64(volatile Uint64 * ptr, Uint64 value)
418
{
419
420
#ifdef nativeFetchThenAdd64
#else
421
Uint64 tmp = 0;
422
423
privateWaitLock(ptr);
424
425
tmp = *ptr;
(*ptr)+= value;
426
privateUnlock(ptr);
427
428
429
return tmp;
#endif
430
431
432
}
Uint64
433
SDL_AtomicFetchThenSubtract64(volatile Uint64 * ptr, Uint64 value)
434
{
435
436
#ifdef nativeFetchThenSubtract64
#else
437
Uint64 tmp = 0;
438
439
privateWaitLock(ptr);
440
441
tmp = *ptr;
(*ptr)-= value;
442
privateUnlock(ptr);
443
444
445
return tmp;
#endif
446
447
448
}
Uint64
449
SDL_AtomicIncrementThenFetch64(volatile Uint64 * ptr)
450
{
451
452
#ifdef nativeIncrementThenFetch64
#else
453
Uint64 tmp = 0;
454
455
privateWaitLock(ptr);
456
457
(*ptr)+= 1;
tmp = *ptr;
458
privateUnlock(ptr);
459
460
461
return tmp;
#endif
462
463
464
}
Uint64
465
SDL_AtomicDecrementThenFetch64(volatile Uint64 * ptr)
466
{
467
468
#ifdef nativeDecrementThenFetch64
#else
469
Uint64 tmp = 0;
470
471
privateWaitLock(ptr);
472
473
(*ptr)-= 1;
tmp = *ptr;
474
privateUnlock(ptr);
475
476
477
return tmp;
#endif
478
479
480
}
Uint64
481
SDL_AtomicAddThenFetch64(volatile Uint64 * ptr, Uint64 value)
482
{
483
484
#ifdef nativeAddThenFetch64
#else
485
Uint64 tmp = 0;
486
487
privateWaitLock(ptr);
488
489
(*ptr)+= value;
tmp = *ptr;
490
privateUnlock(ptr);
491
492
493
return tmp;
#endif
494
495
496
}
Uint64
497
SDL_AtomicSubtractThenFetch64(volatile Uint64 * ptr, Uint64 value)
498
{
499
500
#ifdef nativeSubtractThenFetch64
#else
501
Uint64 tmp = 0;
502
503
privateWaitLock(ptr);
504
505
(*ptr)-= value;
tmp = *ptr;
506
privateUnlock(ptr);
507
508
return tmp;
509
#endif
510
}
511
#endif