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 }()));