1 /+
2 +            Copyright 2022 – 2024 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 import bindbc.sdl.config;
11 import bindbc.sdl.codegen;
12 
13 alias SDL_SpinLock = int;
14 
15 version(SDL_No_Atomics){
16 }else{
17 	import sdl.stdinc: SDL_bool;
18 	
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 				else 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 					else 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 					else 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 					else 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 
150 struct SDL_atomic_t{
151 	int value;
152 }
153 
154 mixin(joinFnBinds((){
155 	FnBind[] ret = [
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 		{q{SDL_bool}, q{SDL_AtomicCAS}, q{SDL_atomic_t* a, int oldVal, int newVal}},
160 		{q{SDL_bool}, q{SDL_AtomicCASPtr}, q{void** a, void* oldVal, void* newVal}},
161 	];
162 	if(sdlSupport >= SDLSupport.v2_0_3){
163 		FnBind[] add = [
164 			{q{int}, q{SDL_AtomicSet}, q{SDL_atomic_t* a, int v}},
165 			{q{int}, q{SDL_AtomicGet}, q{SDL_atomic_t* a}},
166 			{q{int}, q{SDL_AtomicAdd}, q{SDL_atomic_t* a, int v}},
167 			{q{void*}, q{SDL_AtomicSetPtr}, q{void** a, void* v}},
168 			{q{void*}, q{SDL_AtomicGetPtr}, q{void** a}},
169 		];
170 		ret ~= add;
171 	}
172 	if(sdlSupport >= SDLSupport.v2_0_6){
173 		FnBind[] add = [
174 			{q{void}, q{SDL_MemoryBarrierReleaseFunction}, q{}},
175 			{q{void}, q{SDL_MemoryBarrierAcquireFunction}, q{}},
176 		];
177 		ret ~= add;
178 	}
179 	return ret;
180 }()));