From: Timo Kreuzer Date: Sun, 3 Feb 2019 23:06:35 +0000 (+0100) Subject: [CRT] Implement thread/fiber safe support for MSVC and Clang-CL X-Git-Tag: 0.4.13-dev~403 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=2a5bf9689144d2ec78f4114d4d0f43758399f427 [CRT] Implement thread/fiber safe support for MSVC and Clang-CL This is the most trivial (but also most efficient) implementation possible. Should be good enough for now. --- diff --git a/sdk/lib/crt/msvcrtex.cmake b/sdk/lib/crt/msvcrtex.cmake index cd058cf089e..c46751007ad 100644 --- a/sdk/lib/crt/msvcrtex.cmake +++ b/sdk/lib/crt/msvcrtex.cmake @@ -41,7 +41,10 @@ list(APPEND MSVCRTEX_SOURCE misc/iswblank.c misc/ofmt_stub.c) -if(NOT MSVC) +if(MSVC) + list(APPEND MSVCRTEX_SOURCE + startup/threadSafeInit.c) +else() list(APPEND MSVCRTEX_SOURCE startup/pseudo-reloc.c startup/pseudo-reloc-list.c) diff --git a/sdk/lib/crt/startup/threadSafeInit.c b/sdk/lib/crt/startup/threadSafeInit.c new file mode 100644 index 00000000000..4ceb5ded6d2 --- /dev/null +++ b/sdk/lib/crt/startup/threadSafeInit.c @@ -0,0 +1,60 @@ +/* + * PROJECT: ReactOS CRT library + * LICENSE: CC0-1.0 (https://spdx.org/licenses/CC0-1.0) + * PURPOSE: Thread safe initialization support routines for MSVC and Clang-CL + * COPYRIGHT: Copyright 2019 Timo Kreuzer (timo.kreuzer@reactos.org) + */ + +#include + +int _stdcall SwitchToThread(void); + +unsigned int _tls_array; +unsigned int _tls_index; +long _Init_global_epoch; +long _Init_thread_epoch; + +/* + This function tries to acquire a lock on the initialization for the static + variable by changing the value saved in *ptss to -1. If *ptss is 0, the + variable was not initialized yet and the function tries to set it to -1. + If that succeeds, the function will return. If the value is already -1, + another thread is in the process of doing the initialization and we + wait for it. If it is any other value the initialization is complete. + After returning the compiler generated code will check the value: + if it is -1 it will continue with the initialization, otherwise the + initialization must be complete and will be skipped. +*/ +void +_Init_thread_header(volatile int* ptss) +{ + while (1) + { + /* Try to acquire the first initialization lock */ + int oldTss = _InterlockedCompareExchange((long*)ptss, -1, 0); + if (oldTss == -1) + { + /* Busy, wait for the other thread to do the initialization */ + SwitchToThread(); + continue; + } + + /* Either we acquired the lock and the caller will do the initializaion + or the initialization is complete and the caller will skip it */ + break; + } +} + +void +_Init_thread_footer(volatile int* ptss) +{ + /* Initialization is complete */ + *ptss = _InterlockedIncrement(&_Init_global_epoch); +} + +void +_Init_thread_abort(volatile int* ptss) +{ + /* Abort the initialization */ + _InterlockedAnd((volatile long*)ptss, 0); +}