1 /+
2 +            Copyright 2022 – 2023 Aya Partridge
3 +          Copyright 2018 - 2022 Michael D. Parker
4 + Distributed under the Boost Software License, Version 1.0.
5 +     (See accompanying file LICENSE_1_0.txt or copy at
6 +           http://www.boost.org/LICENSE_1_0.txt)
7 +/
8 module sdl.atomic;
9 
10 version(SDL_No_Atomics){}
11 else:
12 
13 import bindbc.sdl.config;
14 import bindbc.sdl.codegen;
15 
16 import sdl.stdinc: SDL_bool;
17 
18 alias SDL_SpinLock = int;
19 
20 version(GNU) version = ExtAsm; //GDC
21 version(LDC) version = ExtAsm;
22 
23 pragma(inline, true) nothrow @nogc{
24 	void SDL_CompilerBarrier(){
25 		static if((){
26 			version(Emscripten) return false;
27 			version(ExtAsm)     return true;
28 			else return false;
29 		}()){
30 			asm nothrow @nogc{ "" : : : "memory"; } 
31 		}else version(DigitalMars){
32 			asm nothrow @nogc{}
33 		}else{
34 			__gshared SDL_SpinLock _tmp = 0;
35 			SDL_AtomicLock(&_tmp);
36 			SDL_AtomicUnlock(&_tmp);
37 		}
38 		
39 // 		#if defined(_MSC_VER) && (_MSC_VER > 1200) && !defined(__clang__)
40 // 		void _ReadWriteBarrier(void);
41 // 		#pragma intrinsic(_ReadWriteBarrier)
42 // 		#define SDL_CompilerBarrier()   _ReadWriteBarrier()
43 // 		#elif (defined(__GNUC__) && !defined(__EMSCRIPTEN__)) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x5120))
44 // 		/* This is correct for all CPUs when using GCC or Solaris Studio 12.1+. */
45 // 		#define SDL_CompilerBarrier()   __asm__ __volatile__ ("" : : : "memory")
46 // 		#elif defined(__WATCOMC__)
47 // 		extern __inline void SDL_CompilerBarrier(void);
48 // 		#pragma aux SDL_CompilerBarrier = "" parm [] modify exact [];
49 // 		#else
50 // 		#define SDL_CompilerBarrier()   \
51 // 		{ SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp); SDL_AtomicUnlock(&_tmp); }
52 // 		#endif
53 	}
54 	
55 	void SDL_MemoryBarrierRelease(){
56 		static if((){
57 			version(ExtAsm)
58 				version(PPC)        return true;
59 				else version(PPC64) return true;
60 				else return false;
61 			else return false;
62 		}()){
63 			asm nothrow @nogc{ "lwsync" : : : "memory"; }
64 		}else static if((){
65 			version(ExtAsm)
66 				version(AArch64) return true;
67 				else return false;
68 			else return false;
69 		}()){
70 			asm nothrow @nogc{ "dmb ish" : : : "memory"; }
71 		}else static if((){
72 			version(ExtAsm)
73 				version(ARM) return true;
74 				else return false;
75 			else return false;
76 		}()){
77 			asm nothrow @nogc{ "" : : : "memory"; }
78 		}else{
79 			SDL_CompilerBarrier();
80 		}
81 	}
82 	alias SDL_MemoryBarrierAcquire = SDL_MemoryBarrierRelease;
83 	
84 	void SDL_CPUPauseInstruction() pure{ //NOTE: added in 2.24.0
85 		version(ExtAsm){
86 			static if((){
87 				version(X86)    return true;
88 				version(X86_64) return true;
89 				else return false;
90 			}()){
91 				asm nothrow @nogc pure{ "rep nop"; }
92 			}else static if((){
93 				version(ARM)     return true;
94 				version(AArch64) return true;
95 				else return false;
96 			}()){
97 				asm nothrow @nogc pure{ "yield" : : : "memory"; }
98 			}else static if((){
99 				version(PPC)   return true;
100 				version(PPC64) return true;
101 				else return false;
102 			}()){
103 				asm nothrow @nogc pure{ "or 27,27,27"; }
104 			}
105 		}
106 	}
107 	
108 	int SDL_AtomicIncRef(SDL_atomic_t* a){
109 		return SDL_AtomicAdd(a, 1);
110 	}
111 	bool SDL_AtomicDecRef(SDL_atomic_t* a){
112 		return SDL_AtomicAdd(a, -1) == 1;
113 	}
114 	static if(sdlSupport < SDLSupport.v2_0_3){
115 		int SDL_AtomicSet(SDL_atomic_t* a, int v){
116 			int value;
117 			do{
118 				value = a.value;
119 			}while(!SDL_AtomicCAS(a, value, v));
120 			return value;
121 		}
122 		int SDL_AtomicGet(SDL_atomic_t* a){
123 			int value = a.value;
124 			SDL_CompilerBarrier();
125 			return value;
126 		}
127 		int SDL_AtomicAdd(SDL_atomic_t* a, int v){
128 			int value;
129 			do{
130 				value = a.value;
131 			}while(!SDL_AtomicCAS(a, value, value + v));
132 			return value;
133 		}
134 		void* SDL_AtomicSetPtr(void** a, void* v){
135 			void* value;
136 			do{
137 				value = *a;
138 			}while(!SDL_AtomicCASPtr(a, value, v));
139 			return value;
140 		}
141 		void* SDL_AtomicGetPtr(void** a){
142 			void* value = *a;
143 			SDL_CompilerBarrier();
144 			return value;
145 		}
146 	}
147 }
148 
149 struct SDL_atomic_t{
150 	int value;
151 }
152 
153 mixin(joinFnBinds((){
154 	string[][] ret;
155 	ret ~= makeFnBinds([
156 		[q{SDL_bool}, q{SDL_AtomicTryLock}, q{SDL_SpinLock* lock}],
157 		[q{void}, q{SDL_AtomicLock}, q{SDL_SpinLock* lock}],
158 		[q{void}, q{SDL_AtomicUnlock}, q{SDL_SpinLock* lock}],
159 		// Perhaps the following could be replaced with the platform-specific intrinsics for GDC, like
160 		// the GCC macros in SDL_atomic.h. I'll have to investigate.
161 		[q{SDL_bool}, q{SDL_AtomicCAS}, q{SDL_atomic_t* a, int oldval, int newval}],
162 		[q{SDL_bool}, q{SDL_AtomicCASPtr}, q{void** a, void* oldval, void* newval}],
163 	]);
164 	static if(sdlSupport >= SDLSupport.v2_0_3){
165 		ret ~= makeFnBinds([
166 			[q{int}, q{SDL_AtomicSet}, q{SDL_atomic_t* a, int v}],
167 			[q{int}, q{SDL_AtomicGet}, q{SDL_atomic_t* a}],
168 			[q{int}, q{SDL_AtomicAdd}, q{SDL_atomic_t* a, int v}],
169 			[q{void*}, q{SDL_AtomicSetPtr}, q{void** a, void* v}],
170 			[q{void*}, q{SDL_AtomicGetPtr}, q{void** a}],
171 		]);
172 	}
173 	static if(sdlSupport >= SDLSupport.v2_0_6){
174 		ret ~= makeFnBinds([
175 			[q{void}, q{SDL_MemoryBarrierReleaseFunction}, q{}],
176 			[q{void}, q{SDL_MemoryBarrierAcquireFunction}, q{}],
177 		]);
178 	}
179 	return ret;
180 }()));