1 /************************************************************************************************************************
2 It's internal code. Can be used for atomics if emscripten backend will be used.
3 
4 This module contain atomic operations which include support for emscripten atomics functions.
5 Emscripten functions are contained in API similar to druntime.
6 
7 Copyright: Copyright © 2018-2023, Dawid Masiukiewicz, Michał Masiukiewicz
8 License: BSD 3-clause, see LICENSE file in project root folder.
9 */
10 module bubel.ecs.atomic;
11 
12 version (Emscripten) version = ECSEmscripten;
13 
14 version (ECSEmscripten)
15 {
16     import std.traits;
17 
18     enum MemoryOrder
19     {
20         acq,
21         acq_rel,
22         raw,
23         rel,
24         seq
25     }
26 
27     extern (C) ubyte emscripten_atomic_cas_u8(void* addr, ubyte oldVal, ubyte newVal) @nogc nothrow pure;
28     extern (C) ushort emscripten_atomic_cas_u16(void* addr, ushort oldVal, ushort newVal) @nogc nothrow pure;
29     extern (C) uint emscripten_atomic_cas_u32(void* addr, uint oldVal, uint newVal) @nogc nothrow pure;
30 
31     extern (C) ubyte emscripten_atomic_load_u8(const void* addr) @nogc nothrow pure;
32     extern (C) ushort emscripten_atomic_load_u16(const void* addr) @nogc nothrow pure;
33     extern (C) uint emscripten_atomic_load_u32(const void* addr) @nogc nothrow pure;
34 
35     extern (C) ubyte emscripten_atomic_store_u8(void* addr, ubyte val) @nogc nothrow pure;
36     extern (C) ushort emscripten_atomic_store_u16(void* addr, ushort val) @nogc nothrow pure;
37     extern (C) uint emscripten_atomic_store_u32(void* addr, uint val) @nogc nothrow pure;
38 
39     extern (C) ubyte emscripten_atomic_add_u8(void* addr, ubyte val) @nogc nothrow pure;
40     extern (C) ushort emscripten_atomic_add_u16(void* addr, ushort val) @nogc nothrow pure;
41     extern (C) uint emscripten_atomic_add_u32(void* addr, uint val) @nogc nothrow pure;
42 
43     extern (C) ubyte emscripten_atomic_sub_u8(void* addr, ubyte val) @nogc nothrow pure;
44     extern (C) ushort emscripten_atomic_sub_u16(void* addr, ushort val) @nogc nothrow pure;
45     extern (C) uint emscripten_atomic_sub_u32(void* addr, uint val) @nogc nothrow pure;
46 
47     public pure nothrow @nogc Unqual!T atomicOp(string op, T, V1)(ref shared T val, V1 mod)
48     {
49         static if (op == "+=")
50         {
51             static if (is(T == byte) || is(T == ubyte))
52                 return cast(Unqual!T)(emscripten_atomic_add_u8(cast(void*)&val,
53                         cast(Unqual!T) mod) + 1);
54             else static if (is(T == short) || is(T == ushort))
55                 return cast(Unqual!T)(emscripten_atomic_add_u16(cast(void*)&val,
56                         cast(Unqual!T) mod) + 1);
57             else static if (is(T == int) || is(T == uint))
58                 return cast(Unqual!T)(emscripten_atomic_add_u32(cast(void*)&val,
59                         cast(Unqual!T) mod) + 1);
60             else
61                 static assert(0);
62         }
63         else static if (op == "-=")
64         {
65             static if (is(T == byte) || is(T == ubyte))
66                 return cast(Unqual!T)(emscripten_atomic_sub_u8(cast(void*)&val,
67                         cast(Unqual!T) mod) - 1);
68             else static if (is(T == short) || is(T == ushort))
69                 return cast(Unqual!T)(emscripten_atomic_sub_u16(cast(void*)&val,
70                         cast(Unqual!T) mod) - 1);
71             else static if (is(T == int) || is(T == uint))
72                 return cast(Unqual!T)(emscripten_atomic_sub_u32(cast(void*)&val,
73                         cast(Unqual!T) mod) - 1);
74             else
75                 static assert(0);
76         }
77     }
78 
79     public pure nothrow @nogc @trusted void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val,
80             V newval)
81     {
82         alias UT = Unqual!T;
83         static if (is(UT == bool) || is(UT == byte) || is(UT == ubyte))
84             emscripten_atomic_store_u8(cast(void*)&val, cast(UT) newval);
85         else static if (is(UT == short) || is(UT == ushort))
86             emscripten_atomic_store_u16(cast(void*)&val, cast(UT) newval);
87         else static if (is(UT == int) || is(UT == uint))
88             emscripten_atomic_store_u32(cast(void*)&val, cast(UT) newval);
89         else
90             static assert(0);
91     }
92 
93     public pure nothrow @nogc @trusted T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(
94             ref const T val)
95     {
96         alias UT = Unqual!T;
97         static if (is(UT == bool))
98             return emscripten_atomic_load_u8(cast(const void*)&val) != 0;
99         else static if (is(UT == byte) || is(UT == ubyte))
100             return emscripten_atomic_load_u8(cast(const void*)&val);
101         else static if (is(UT == short) || is(UT == ushort))
102             return emscripten_atomic_load_u16(cast(const void*)&val);
103         else static if (is(UT == int) || is(UT == uint))
104             return emscripten_atomic_load_u32(cast(const void*)&val);
105         else
106             static assert(0);
107     }
108 
109     public pure nothrow @nogc @trusted bool cas(MemoryOrder succ = MemoryOrder.seq,
110             MemoryOrder fail = MemoryOrder.seq, T, V1, V2)(T* here, V1 ifThis, V2 writeThis)
111     {
112         alias UT = Unqual!T;
113         static if (is(UT == bool))
114             return emscripten_atomic_cas_u8(cast(void*) here,
115                     cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis;
116         else static if (is(UT == byte) || is(UT == ubyte))
117             return emscripten_atomic_cas_u8(cast(void*) here,
118                     cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis;
119         else static if (is(UT == short) || is(UT == ushort))
120             return emscripten_atomic_cas_u16(cast(void*) here,
121                     cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis;
122         else static if (is(UT == int) || is(UT == uint))
123             return emscripten_atomic_cas_u32(cast(void*) here,
124                     cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis;
125         else
126             static assert(0);
127     }
128 }
129 else
130 {
131     public import core.atomic;
132 }