diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 498f834..8fa868a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -41,7 +41,7 @@ test_dmd_betterC: stage: test image: frolvlad/alpine-glibc script: - - binaries/dmd_release_unittest_bc + - binaries/dmd_debug_unittest_bc artifacts: reports: junit: test_report.xml diff --git a/demos/dub.json b/demos/dub.json index 417b831..1c1fe65 100644 --- a/demos/dub.json +++ b/demos/dub.json @@ -19,8 +19,7 @@ "libs-linux-x86_64": ["cimgui","SDL2","SDL2_image"], "lflags-linux-x86_64": ["-rpath=libs/linux/x64/","-Llibs/linux/x64/"], "dflags-ldc" : [ - "--ffast-math", - "-enable-cross-module-inlining" + "--ffast-math" ], "configurations" : [ { diff --git a/demos/external/sources/mmutils/thread_pool.d b/demos/external/sources/mmutils/thread_pool.d index bd05fe5..9156674 100644 --- a/demos/external/sources/mmutils/thread_pool.d +++ b/demos/external/sources/mmutils/thread_pool.d @@ -1,22 +1,14 @@ module mmutils.thread_pool; -import bubel.ecs.atomic; - -//import core.stdc.stdio; -//import core.stdc.stdlib : free, malloc, realloc; -//import core.stdc.string : memcpy; - -//import std.stdio; import std.algorithm : map; version = MM_NO_LOGS; // Disable log creation //version = MM_USE_POSIX_THREADS; // Use posix threads insted of standard library, required for betterC -version (Posix)version = MM_USE_POSIX_THREADS; - version (WebAssembly) { version = MM_NO_LOGS; + version = MM_USE_POSIX_THREADS; extern(C) struct FILE { @@ -33,8 +25,15 @@ else version (D_BetterC) { - import bubel.ecs.std; - extern (C) __gshared int _d_eh_personality(int, int, size_t, void*, void*) + version (Posix) version = MM_USE_POSIX_THREADS; + + extern (C) void free(void*) @nogc nothrow @system; + extern (C) void* malloc(size_t size) @nogc nothrow @system; + extern (C) void* realloc(void*, size_t size) @nogc nothrow @system; + extern (C) void* memcpy(return void*, scope const void*, size_t size) @nogc nothrow @system; + + //hacks for LDC + /*extern (C) __gshared int _d_eh_personality(int, int, size_t, void*, void*) { return 0; } @@ -47,7 +46,7 @@ version (D_BetterC) extern (C) void* _d_allocmemory(size_t sz) { return malloc(sz); - } + }*/ } else { @@ -56,8 +55,132 @@ else } ////////////////////////////////////////////// -//////////////////// Alloc /////////////////// +/////////////// Atomics ////////////////////// ////////////////////////////////////////////// + +version (ECSEmscripten) +{ + import std.traits; + + enum MemoryOrder + { + acq, + acq_rel, + raw, + rel, + seq + } + + extern (C) ubyte emscripten_atomic_cas_u8(void* addr, ubyte oldVal, ubyte newVal) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_cas_u16(void* addr, ushort oldVal, ushort newVal) @nogc nothrow pure; + extern (C) uint emscripten_atomic_cas_u32(void* addr, uint oldVal, uint newVal) @nogc nothrow pure; + + extern (C) ubyte emscripten_atomic_load_u8(const void* addr) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_load_u16(const void* addr) @nogc nothrow pure; + extern (C) uint emscripten_atomic_load_u32(const void* addr) @nogc nothrow pure; + + extern (C) ubyte emscripten_atomic_store_u8(void* addr, ubyte val) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_store_u16(void* addr, ushort val) @nogc nothrow pure; + extern (C) uint emscripten_atomic_store_u32(void* addr, uint val) @nogc nothrow pure; + + extern (C) ubyte emscripten_atomic_add_u8(void* addr, ubyte val) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_add_u16(void* addr, ushort val) @nogc nothrow pure; + extern (C) uint emscripten_atomic_add_u32(void* addr, uint val) @nogc nothrow pure; + + extern (C) ubyte emscripten_atomic_sub_u8(void* addr, ubyte val) @nogc nothrow pure; + extern (C) ushort emscripten_atomic_sub_u16(void* addr, ushort val) @nogc nothrow pure; + extern (C) uint emscripten_atomic_sub_u32(void* addr, uint val) @nogc nothrow pure; + + public pure nothrow @nogc Unqual!T atomicOp(string op, T, V1)(ref shared T val, V1 mod) + { + static if (op == "+=") + { + static if (is(T == byte) || is(T == ubyte)) + return cast(Unqual!T)(emscripten_atomic_add_u8(cast(void*)&val, + cast(Unqual!T) mod) + 1); + else static if (is(T == short) || is(T == ushort)) + return cast(Unqual!T)(emscripten_atomic_add_u16(cast(void*)&val, + cast(Unqual!T) mod) + 1); + else static if (is(T == int) || is(T == uint)) + return cast(Unqual!T)(emscripten_atomic_add_u32(cast(void*)&val, + cast(Unqual!T) mod) + 1); + else + static assert(0); + } + else static if (op == "-=") + { + static if (is(T == byte) || is(T == ubyte)) + return cast(Unqual!T)(emscripten_atomic_sub_u8(cast(void*)&val, + cast(Unqual!T) mod) - 1); + else static if (is(T == short) || is(T == ushort)) + return cast(Unqual!T)(emscripten_atomic_sub_u16(cast(void*)&val, + cast(Unqual!T) mod) - 1); + else static if (is(T == int) || is(T == uint)) + return cast(Unqual!T)(emscripten_atomic_sub_u32(cast(void*)&val, + cast(Unqual!T) mod) - 1); + else + static assert(0); + } + } + + public pure nothrow @nogc @trusted void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, + V newval) + { + alias UT = Unqual!T; + static if (is(UT == bool) || is(UT == byte) || is(UT == ubyte)) + emscripten_atomic_store_u8(cast(void*)&val, cast(UT) newval); + else static if (is(UT == short) || is(UT == ushort)) + emscripten_atomic_store_u16(cast(void*)&val, cast(UT) newval); + else static if (is(UT == int) || is(UT == uint)) + emscripten_atomic_store_u32(cast(void*)&val, cast(UT) newval); + else + static assert(0); + } + + public pure nothrow @nogc @trusted T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)( + ref const T val) + { + alias UT = Unqual!T; + static if (is(UT == bool)) + return emscripten_atomic_load_u8(cast(const void*)&val) != 0; + else static if (is(UT == byte) || is(UT == ubyte)) + return emscripten_atomic_load_u8(cast(const void*)&val); + else static if (is(UT == short) || is(UT == ushort)) + return emscripten_atomic_load_u16(cast(const void*)&val); + else static if (is(UT == int) || is(UT == uint)) + return emscripten_atomic_load_u32(cast(const void*)&val); + else + static assert(0); + } + + public pure nothrow @nogc @trusted bool cas(MemoryOrder succ = MemoryOrder.seq, + MemoryOrder fail = MemoryOrder.seq, T, V1, V2)(T* here, V1 ifThis, V2 writeThis) + { + alias UT = Unqual!T; + static if (is(UT == bool)) + return emscripten_atomic_cas_u8(cast(void*) here, + cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; + else static if (is(UT == byte) || is(UT == ubyte)) + return emscripten_atomic_cas_u8(cast(void*) here, + cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; + else static if (is(UT == short) || is(UT == ushort)) + return emscripten_atomic_cas_u16(cast(void*) here, + cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; + else static if (is(UT == int) || is(UT == uint)) + return emscripten_atomic_cas_u32(cast(void*) here, + cast(Unqual!T) ifThis, cast(Unqual!T) writeThis) == ifThis; + else + static assert(0); + } +} +else +{ + public import core.atomic; +} + +////////////////////////////////////////////////// +//////////////////// Allocator /////////////////// +////////////////////////////////////////////////// T* makeVar(T)(T init) { T* el = cast(T*) malloc(T.sizeof); @@ -120,21 +243,7 @@ long useconds() { version (WebAssembly) { - //import core.sys.posix.sys.time : gettimeofday, timeval; - - /*timeval t; - gettimeofday(&t, null); - - return t.tv_sec * 1_000_000 + t.tv_usec;*/ - - //time_t time; - //timespec spec; - - //lock_gettime(CLOCK_REALTIME, &spec); return cast(long)(emscripten_get_now() * 1000.0); - - //time = spec.tv_sec; - //return spec.tv_sec * 1000_000 + spec.tv_nsec / 1000; } else version (Posix) { @@ -146,6 +255,7 @@ long useconds() } else version (Windows) { + //TODO: implement timer on windows /*import core.sys.windows.windows : QueryPerformanceFrequency; __gshared double mul = -1; @@ -243,7 +353,7 @@ version (MM_USE_POSIX_THREADS) version (WebAssembly) { extern(C): - //alias uint time_t; + struct pthread_attr_t { union @@ -259,19 +369,11 @@ version (MM_USE_POSIX_THREADS) uint x; } - /*struct timespec - { - time_t tv_sec; - int tv_nsec; - }*/ - // pthread int pthread_create(pthread_t*, in pthread_attr_t*, void* function(void*), void*); int pthread_join(pthread_t, void**); void pthread_exit(void *retval); - // semaphore.h - //alias sem_t = void*; struct sem_t { shared int[4] __val; @@ -282,8 +384,6 @@ version (MM_USE_POSIX_THREADS) int sem_post(sem_t*); int sem_destroy(sem_t*); int sem_timedwait(sem_t* sem, const timespec* abstime); - //import core.sys.posix.pthread; - //import core.sys.posix.semaphore; } else version (Posix) { @@ -347,7 +447,6 @@ version (MM_USE_POSIX_THREADS) bool tryWait() { - //return true; int ret = sem_trywait(&mutex); return (ret == 0); } @@ -411,91 +510,7 @@ version (MM_USE_POSIX_THREADS) } else version(D_BetterC) { - version(Posix) - { - import core.sys.posix.pthread; - import core.sys.posix.semaphore; - - struct Semaphore - { - sem_t mutex; - - void initialize() - { - sem_init(&mutex, 0, 0); - } - - void wait() - { - int ret = sem_wait(&mutex); - assert(ret == 0); - } - - bool tryWait() - { - //return true; - int ret = sem_trywait(&mutex); - return (ret == 0); - } - - bool timedWait(int usecs) - { - timespec tv; - // if there is no such a function look at it: https://stackoverflow.com/questions/5404277/porting-clock-gettime-to-windows - clock_gettime(CLOCK_REALTIME, &tv); - tv.tv_sec += usecs / 1_000_000; - tv.tv_nsec += (usecs % 1_000_000) * 1_000; - - int ret = sem_timedwait(&mutex, &tv); - return (ret == 0); - } - - void post() - { - int ret = sem_post(&mutex); - assert(ret == 0); - } - - void destroy() - { - sem_destroy(&mutex); - } - } - - private extern (C) void* threadRunFunc(void* threadVoid) - { - Thread* th = cast(Thread*) threadVoid; - - th.threadStart(); - - pthread_exit(null); - return null; - } - - struct Thread - { - alias DG = void delegate(); - - DG threadStart; - pthread_t handle; - - void start(DG dg) - { - threadStart = dg; - int ok = pthread_create(&handle, null, &threadRunFunc, cast(void*)&this); - if(!ok)handle = pthread_t(); - //assert(ok == 0); - } - - void join() - { - pthread_join(handle, null); - handle = handle.init; - threadStart = null; - } - } - } - else version(Windows) + version(Windows) { import core.stdc.stdint : uintptr_t; import core.sys.windows.windows; @@ -551,15 +566,13 @@ else version(D_BetterC) case WAIT_TIMEOUT: return false; default: - assert(0);//throw new SyncError( "Unable to wait for semaphore" ); + assert(0, "Unable to wait for semaphore" ); } } void post() { assert(ReleaseSemaphore( handle, 1, null )); - //if ( !ReleaseSemaphore( m_hndl, 1, null ) ) - //throw new SyncError( "Unable to notify semaphore" ); } void destroy() @@ -575,7 +588,6 @@ else version(D_BetterC) th.threadStart(); - //(null); ExitThread(0); return 0; } @@ -591,21 +603,22 @@ else version(D_BetterC) { threadStart = dg; handle = cast(HANDLE) _beginthreadex( null, 0, &threadRunFunc, cast(void*)&this, 0, null ); - //int ok = pthread_create(&handle, null, &threadRunFunc, cast(void*)&this); - //assert(handle != null); } void join() { if ( WaitForSingleObject( handle, INFINITE ) == WAIT_OBJECT_0 )assert(0); CloseHandle( handle ); - //pthread_join(handle, null); + handle = handle.init; threadStart = null; } } } - + else + { + static assert(0, "Platform is unsupported in betterC mode!"); + } } else { @@ -674,7 +687,7 @@ else ///////////////// ThreadPool ///////////////// ////////////////////////////////////////////// -private enum gMaxThreadsNum = 32; +private enum gMaxThreadsNum = 64; alias JobDelegate = void delegate(ThreadData*, JobData*); diff --git a/demos/source/game_core/job_updater.d b/demos/source/game_core/job_updater.d index 1389c45..adbae4c 100644 --- a/demos/source/game_core/job_updater.d +++ b/demos/source/game_core/job_updater.d @@ -18,6 +18,27 @@ else } } +version(Android) +{ + alias pthread_key_t = uint; + + extern (C) int pthread_key_create(pthread_key_t *, void* function(void *)) @nogc nothrow; + extern (C) int pthread_key_delete(pthread_key_t) @nogc nothrow; + extern (C) void* pthread_getspecific(pthread_key_t) @nogc nothrow; + extern (C) int pthread_setspecific(pthread_key_t, const void *) @nogc nothrow; +} +else version(WebAssembly) +{ + alias pthread_key_t = uint; + + extern (C) int pthread_key_create(pthread_key_t *, void* function(void *)) @nogc nothrow; + extern (C) int pthread_key_delete(pthread_key_t) @nogc nothrow; + extern (C) void* pthread_getspecific(pthread_key_t) @nogc nothrow; + extern (C) int pthread_setspecific(pthread_key_t, const void *) @nogc nothrow; + + extern (C) void emscripten_main_thread_process_queued_calls(); +} + struct ECSJobUpdater { diff --git a/demos/utils/dub.json b/demos/utils/dub.json index d47af9e..749bd4b 100644 --- a/demos/utils/dub.json +++ b/demos/utils/dub.json @@ -29,7 +29,7 @@ "subConfigurations": { "bindbc-sdl": "static", - "ecs":"library" + "bubel_ecs":"library" } }, { @@ -41,7 +41,7 @@ "subConfigurations": { "bindbc-sdl": "staticBC", - "ecs":"library-betterC" + "bubel_ecs":"library-betterC" } } ] diff --git a/dub.json b/dub.json index 8cf891d..8e2a1b3 100755 --- a/dub.json +++ b/dub.json @@ -50,8 +50,7 @@ ], "dflags": [ "-unittest" - ], - "targetName" : "ecs" + ] }, { "name": "unittest-runner-cov", @@ -65,8 +64,7 @@ "dflags": [ "-unittest", "-cov" - ], - "targetName" : "ecs" + ] }, { "name" : "library-betterC", @@ -129,8 +127,7 @@ "excludedSourceFiles":[ "source\/win_dll.d", "tests/tests.d" - ], - "targetName" : "ecs" + ] } ] } \ No newline at end of file