1 
2 //          Copyright 2018 - 2021 Michael D. Parker
3 // Distributed under the Boost Software License, Version 1.0.
4 //    (See accompanying file LICENSE_1_0.txt or copy at
5 //          http://www.boost.org/LICENSE_1_0.txt)
6 
7 module bindbc.sdl.bind.sdlatomic;
8 
9 version(SDL_No_Atomics) {}
10 else:
11 
12 import bindbc.sdl.config;
13 import bindbc.sdl.bind.sdlstdinc : SDL_bool;
14 
15 alias SDL_SpinLock = int;
16 
17 struct SDL_atomic_t {
18     int value;
19 }
20 
21 /*
22 The best way I can see to implement the barrier macros is to depend on
23 core.atomic.atomicFence. That should be okay even in BetterC mode since
24 it's a template. I've already got a dependency on DRuntime (e.g. core.stdc.config),
25 so I'll import it rather than copy/paste it. I'll change it if it somehow
26 becomes a problem in the future.
27 */
28 import core.atomic : atomicFence;
29 alias SDL_CompilerBarrier = atomicFence!();
30 alias SDL_MemoryBarrierRelease = SDL_CompilerBarrier;
31 alias SDL_MemoryBarrierAcquire = SDL_CompilerBarrier;
32 
33 static if(staticBinding) {
34     extern(C) @nogc nothrow {
35         SDL_bool SDL_AtomicTryLock(SDL_SpinLock* lock);
36         void SDL_AtomicLock(SDL_SpinLock* lock);
37         void SDL_AtomicUnlock(SDL_SpinLock* lock);
38     }
39 }
40 else {
41     extern(C) @nogc nothrow {
42         alias pSDL_AtomicTryLock = SDL_bool function(SDL_SpinLock* lock);
43         alias pSDL_AtomicLock = void function(SDL_SpinLock* lock);
44         alias pSDL_AtomicUnlock = void function(SDL_SpinLock* lock);
45     }
46 
47     __gshared {
48         pSDL_AtomicTryLock SDL_AtomicTryLock;
49         pSDL_AtomicLock SDL_AtomicLock;
50         pSDL_AtomicUnlock SDL_AtomicUnlock;
51     }
52 }
53 
54 // Perhaps the following could be replaced with the platform-specific intrinsics for GDC, like
55 // the GCC macros in SDL_atomic.h. I'll have to investigate.
56 static if(staticBinding) {
57     extern(C) @nogc nothrow {
58         SDL_bool SDL_AtomicCAS(SDL_atomic_t* a, int oldval, int newval);
59         SDL_bool SDL_AtomicCASPtr(void** a, void* oldval, void* newval);
60     }
61 }
62 else {
63     extern(C) @nogc nothrow {
64         alias pSDL_AtomicCAS = SDL_bool function(SDL_atomic_t* a, int oldval, int newval);
65         alias pSDL_AtomicCASPtr = SDL_bool function(void** a, void* oldval, void* newval);
66     }
67 
68     __gshared {
69         pSDL_AtomicCAS SDL_AtomicCAS;
70         pSDL_AtomicCASPtr SDL_AtomicCASPtr;
71     }
72 }
73 
74 static if(sdlSupport >= SDLSupport.sdl203) {
75     static if(staticBinding) {
76         extern(C) @nogc nothrow {
77             int SDL_AtomicSet(SDL_atomic_t* a, int v);
78             int SDL_AtomicGet(SDL_atomic_t* a);
79             int SDL_AtomicAdd(SDL_atomic_t* a, int v);
80             void* SDL_AtomicSetPtr(void** a, void* v);
81             void* SDL_AtomicGetPtr(void** a);
82         }
83     }
84     else {
85         extern(C) @nogc nothrow {
86             alias pSDL_AtomicSet = int function(SDL_atomic_t* a, int v);
87             alias pSDL_AtomicGet = int function(SDL_atomic_t* a);
88             alias pSDL_AtomicAdd = int function(SDL_atomic_t* a, int v);
89             alias pSDL_AtomicSetPtr = void* function(void** a, void* v);
90             alias pSDL_AtomicGetPtr = void* function(void** a);
91         }
92 
93         __gshared {
94             pSDL_AtomicSet SDL_AtomicSet;
95             pSDL_AtomicGet SDL_AtomicGet;
96             pSDL_AtomicAdd SDL_AtomicAdd;
97             pSDL_AtomicSetPtr SDL_AtomicSetPtr;
98             pSDL_AtomicGetPtr SDL_AtomicGetPtr;
99         }
100     }
101 }
102 else {
103     int SDL_AtomicSet(SDL_atomic_t* a, int v) {
104         pragma(inline, true)
105         int value;
106         do {
107             value = a.value;
108         } while(!SDL_AtomicCAS(a, value, v));
109         return value;
110     }
111 
112     int SDL_AtomicGet(SDL_atomic_t* a) {
113         pragma(inline, true)
114         int value = a.value;
115         SDL_CompilerBarrier();
116         return value;
117     }
118 
119     int SDL_AtomicAdd(SDL_atomic_t* a, int v) {
120         pragma(inline, true)
121         int value;
122         do {
123             value = a.value;
124         } while(!SDL_AtomicCAS(a, value, value + v));
125         return value;
126     }
127 
128     void* SDL_AtomicSetPtr(void** a, void* v) {
129         pragma(inline, true)
130         void* value;
131         do {
132             value = *a;
133         } while(!SDL_AtomicCASPtr(a, value, v));
134         return value;
135     }
136 
137     void* SDL_AtomicGetPtr(void** a) {
138         pragma(inline, true)
139         void* value = *a;
140         SDL_CompilerBarrier();
141         return value;
142     }
143 }
144 
145 int SDL_AtomicIncRef(SDL_atomic_t* a) {
146     pragma(inline, true)
147     return SDL_AtomicAdd(a, 1);
148 }
149 
150 SDL_bool SDL_AtomicDecRef(SDL_atomic_t* a) {
151     pragma(inline, true)
152     return cast(SDL_bool)(SDL_AtomicAdd(a, -1) == 1);
153 }