[CRT] Implement thread/fiber safe support for MSVC and Clang-CL
authorTimo Kreuzer <timo.kreuzer@reactos.org>
Sun, 3 Feb 2019 23:06:35 +0000 (00:06 +0100)
committerTimo Kreuzer <timo.kreuzer@reactos.org>
Tue, 12 Feb 2019 18:31:33 +0000 (19:31 +0100)
This is the most trivial (but also most efficient) implementation possible. Should be good enough for now.

sdk/lib/crt/msvcrtex.cmake
sdk/lib/crt/startup/threadSafeInit.c [new file with mode: 0644]

index cd058cf..c467510 100644 (file)
@@ -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 (file)
index 0000000..4ceb5de
--- /dev/null
@@ -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 <intrin.h>
+
+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);
+}