]> pd.if.org Git - pdclib/commitdiff
win32: initial pass at thread support
authorOwen Shepherd <owen.shepherd@e43.eu>
Wed, 22 Aug 2012 22:23:05 +0000 (23:23 +0100)
committerOwen Shepherd <owen.shepherd@e43.eu>
Wed, 22 Aug 2012 22:23:05 +0000 (23:23 +0100)
12 files changed:
platform/win32/Config.jam
platform/win32/functions/_PDCLIB/_PDCLIB_runTlsCallbacks.c [new file with mode: 0644]
platform/win32/functions/_PDCLIB/_tls_used.c [new file with mode: 0644]
platform/win32/functions/threads/call_once.c [new file with mode: 0644]
platform/win32/functions/threads/mtx_init.c [new file with mode: 0644]
platform/win32/functions/threads/mtx_lock.c [new file with mode: 0644]
platform/win32/functions/threads/mtx_unlock.c [new file with mode: 0644]
platform/win32/functions/threads/tss_create.c [new file with mode: 0644]
platform/win32/functions/threads/tss_delete.c [new file with mode: 0644]
platform/win32/functions/threads/tss_get.c [new file with mode: 0644]
platform/win32/functions/threads/tss_set.c [new file with mode: 0644]
platform/win32/internals/_PDCLIB_threadconfig.h [new file with mode: 0644]

index 1cd357a9a2f0b41dc29dcc869849f0d92c26f20f..538f72b15ba8bd79dc228bc8a4007dd670d2b36f 100644 (file)
@@ -23,6 +23,6 @@ if $(PDCLIB_TOOLCHAIN) = "gcc" {
     EXIT ;\r
 }\r
 \r
-PDCLIB_OPTIONS = nothread notime dlmalloc ;\r
+PDCLIB_OPTIONS = notime dlmalloc ;\r
 \r
 CRT0 = [ FDirName platform win32 crt0$(SUFOBJ) ] ;
\ No newline at end of file
diff --git a/platform/win32/functions/_PDCLIB/_PDCLIB_runTlsCallbacks.c b/platform/win32/functions/_PDCLIB/_PDCLIB_runTlsCallbacks.c
new file mode 100644 (file)
index 0000000..7dcf7a6
--- /dev/null
@@ -0,0 +1,34 @@
+#include <stddef.h>\r
+#include <windows.h>\r
+\r
+#ifndef REGTEST\r
+extern PIMAGE_TLS_CALLBACK __crt_xl_start__;\r
+#ifdef __GNUC__\r
+__attribute__((section(".CRT$XLZZZ")))\r
+#else\r
+__declspec(allocate(".CRT$XLZZZ")) \r
+#endif\r
+PIMAGE_TLS_CALLBACK __crt_xl_end__ = NULL;\r
+\r
+/* Runs all TLS callbacks registered in the executable\r
+ */\r
+\r
+void NTAPI _PDCLIB_runTlsCallbacks(void * image, DWORD reason, PVOID pv);\r
+void NTAPI _PDCLIB_runTlsCallbacks(void * image, DWORD reason, PVOID pv)\r
+{\r
+    PIMAGE_TLS_CALLBACK * pcb = &__crt_xl_start__;\r
+\r
+    while(*pcb) (*(pcb++))(image, reason, pv);\r
+}\r
+#endif\r
+\r
+#ifdef TEST\r
+#include <_PDCLIB_test.h>\r
+\r
+/* Tested in tss_get.c */\r
+int main( void )\r
+{\r
+    return TEST_RESULTS;\r
+}\r
+\r
+#endif
\ No newline at end of file
diff --git a/platform/win32/functions/_PDCLIB/_tls_used.c b/platform/win32/functions/_PDCLIB/_tls_used.c
new file mode 100644 (file)
index 0000000..a7e5a69
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef REGTEST\r
+#include <stddef.h>\r
+#include <stdint.h>\r
+#include <windows.h>\r
+\r
+/* Win32 TLS support\r
+ *\r
+ * Components which depend upon TLS must express a dependency on the symbol \r
+ * _tls_used. This will cause said symbol to be emitted.\r
+ *\r
+ * The linker (in the case of both Microsoft's linker and Binutils, at least)\r
+ * will point the TLS directory entry in the PE header to _tls_used.\r
+ *\r
+ * NOTE: On Windows versions < NT 6.0, the TLS support _only_ works for \r
+ *       the main executable and any DLLs loaded as dependencies of it\r
+ */\r
+\r
+extern char __tls_start__[], __tls_end__[];\r
+ULONG _tls_index = TLS_OUT_OF_INDEXES;\r
+\r
+extern void NTAPI _PDCLIB_runTlsCallbacks(void * image, DWORD reason, PVOID pv);\r
+static PIMAGE_TLS_CALLBACK tlsCallbacks[] = {\r
+    &_PDCLIB_runTlsCallbacks,\r
+    NULL,\r
+};\r
+\r
+#ifdef __GNUC__\r
+__attribute__((__section__(".rdata$T")))\r
+#else\r
+__declspec(allocate(".rdata$T"))\r
+#endif\r
+#ifdef _WIN64\r
+const IMAGE_TLS_DIRECTORY64 _tls_used = {\r
+#else\r
+const IMAGE_TLS_DIRECTORY _tls_used = {\r
+#endif\r
+        (uintptr_t) &__tls_start__,\r
+        (uintptr_t) &__tls_end__,\r
+        (uintptr_t) &_tls_index,        // TLS index\r
+        (uintptr_t) &tlsCallbacks[0],   // TLS callback array\r
+        (ULONG) 0,                      // Size of zero fill\r
+        (ULONG) 0                       // Characteristics\r
+};\r
+#endif\r
+\r
+#ifdef TEST\r
+#include <_PDCLIB_test.h>\r
+\r
+/* Tested in tss_get.c */\r
+int main( void )\r
+{\r
+    return TEST_RESULTS;\r
+}\r
+\r
+#endif
\ No newline at end of file
diff --git a/platform/win32/functions/threads/call_once.c b/platform/win32/functions/threads/call_once.c
new file mode 100644 (file)
index 0000000..675e000
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef REGTEST\r
+#include <threads.h>\r
+#include <windows.h>\r
+\r
+static volatile HANDLE onceHandle = NULL;\r
+\r
+static void initOnceHandle( _PDCLIB_once_flag *flag )\r
+{\r
+    HANDLE tOnceHandle = CreateEvent(NULL, TRUE, TRUE, NULL);\r
+    HANDLE oldVal \r
+        = InterlockedCompareExchangePointer(&flag->_Handle, tOnceHandle, NULL);\r
+    if(oldVal != NULL)\r
+        CloseHandle(tOnceHandle);\r
+}\r
+\r
+void _PDCLIB_call_once(_PDCLIB_once_flag *flag, void (*func)(void))\r
+{\r
+   if(!flag->_Handle) initOnceHandle(flag);\r
+\r
+   long oldVal = InterlockedCompareExchange(&flag->_State, 1, -1);\r
+   for(;;) {\r
+        if(oldVal == 0) {\r
+            // Initialized\r
+            return;\r
+        } else if(oldVal == -1) {\r
+            // We are doing the initialization\r
+            func();\r
+            if(InterlockedDecrement(&flag->_State) == 0)\r
+                CloseHandle(flag->_Handle);\r
+            SetEvent(flag->_Handle);\r
+            return;\r
+        } else {\r
+            // Somebody else is initializing - we are waiting\r
+            long newOldVal = InterlockedCompareExchange(&flag->_State, oldVal, \r
+                                                        oldVal+1);\r
+            if(newOldVal == oldVal) {\r
+                // We incremented the "waiters" counter\r
+                if(WaitForSingleObject(flag->_Handle, INFINITE) != WAIT_OBJECT_0)\r
+                    abort();\r
+                if(InterlockedDecrement(&flag->_State) == 0)\r
+                    CloseHandle(flag->_Handle);\r
+                return;\r
+            } else {\r
+                oldVal = newOldVal;\r
+                continue;\r
+            }\r
+        }\r
+    }\r
+}\r
+#endif\r
+\r
+#ifdef TEST\r
+#include <_PDCLIB_test.h>\r
+\r
+#ifndef REGTEST\r
+static int count = 0;\r
+static once_flag once = ONCE_FLAG_INIT;\r
+\r
+static void do_once(void)\r
+{\r
+    count++;\r
+}\r
+#endif\r
+\r
+int main( void )\r
+{\r
+#ifndef REGTEST\r
+    TESTCASE(count == 0);\r
+    call_once(&once, do_once);\r
+    TESTCASE(count == 1);\r
+    call_once(&once, do_once);\r
+    TESTCASE(count == 1);\r
+    do_once();\r
+    TESTCASE(count == 2);\r
+#endif\r
+    return TEST_RESULTS;\r
+}\r
+\r
+#endif\r
diff --git a/platform/win32/functions/threads/mtx_init.c b/platform/win32/functions/threads/mtx_init.c
new file mode 100644 (file)
index 0000000..19db390
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef REGTEST\r
+#include <threads.h>\r
+#include <windows.h>\r
+\r
+int mtx_init(mtx_t *mtx, int type)\r
+{\r
+    mtx->_WaitEvHandle = CreateEvent(NULL, \r
+        /* bManualReset*/   FALSE, \r
+        /* bInitialState*/  FALSE, \r
+        /* name*/           NULL);\r
+    if(mtx->_WaitEvHandle == NULL)\r
+        return thrd_error;\r
+    \r
+    mtx->_State        = -1;\r
+    mtx->_ThreadId     = 0;\r
+    mtx->_NestCount    = 0;;\r
+\r
+    return thrd_success;\r
+}\r
+#endif\r
+\r
+#ifdef TEST\r
+#include <_PDCLIB_test.h>\r
+\r
+int main( void )\r
+{\r
+    return TEST_RESULTS;\r
+}\r
+\r
+#endif
\ No newline at end of file
diff --git a/platform/win32/functions/threads/mtx_lock.c b/platform/win32/functions/threads/mtx_lock.c
new file mode 100644 (file)
index 0000000..70af8ae
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef REGTEST\r
+#include <threads.h>\r
+#include <windows.h>\r
+\r
+int mtx_lock(mtx_t *mtx)\r
+{\r
+    DWORD myId = GetCurrentThreadId();\r
+\r
+    if(mtx->_ThreadId == myId) {\r
+        mtx->_NestCount++;\r
+        return thrd_success;\r
+    }\r
+\r
+    DWORD res = InterlockedIncrement(&mtx->_State);\r
+    if(res == 0) {\r
+        mtx->_ThreadId = myId;\r
+        return thrd_success;\r
+    }\r
+\r
+    // If that increment didn't leave the state == 0, then we have contention\r
+    //  -> block on the wait event handle\r
+    DWORD rv = WaitForSingleObject(mtx->_WaitEvHandle, INFINITE);\r
+    if(rv != WAIT_OBJECT_0) \r
+        return thrd_error;\r
+\r
+    // We now own the mutex - so set it up for our use\r
+    mtx->_ThreadId = myId;\r
+    return thrd_success;\r
+}\r
+#endif\r
+\r
+#ifdef TEST\r
+#include <_PDCLIB_test.h>\r
+\r
+int main( void )\r
+{\r
+    return TEST_RESULTS;\r
+}\r
+\r
+#endif
\ No newline at end of file
diff --git a/platform/win32/functions/threads/mtx_unlock.c b/platform/win32/functions/threads/mtx_unlock.c
new file mode 100644 (file)
index 0000000..27b7aef
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef REGTEST\r
+#include <threads.h>\r
+#include <windows.h>\r
+\r
+extern void _PDCLIB_w32errno( void );\r
+int mtx_unlock(mtx_t *mtx)\r
+{\r
+    if(mtx->_NestCount) {\r
+        mtx->_NestCount--;\r
+        return thrd_success;\r
+    }\r
+\r
+    mtx->_ThreadId = 0;\r
+\r
+    DWORD res = InterlockedDecrement(&mtx->_State);\r
+    if(res == (DWORD) -1) {\r
+        // We reset the state to -1; success!\r
+        return thrd_success;\r
+    }\r
+\r
+    DWORD rv = SetEvent(mtx->_WaitEvHandle);\r
+    if(rv == 0) {\r
+        _PDCLIB_w32errno();\r
+        return thrd_error;\r
+    }\r
+\r
+    return thrd_success;\r
+}\r
+#endif\r
+\r
+#ifdef TEST\r
+#include <_PDCLIB_test.h>\r
+\r
+int main( void )\r
+{\r
+    return TEST_RESULTS;\r
+}\r
+\r
+#endif
\ No newline at end of file
diff --git a/platform/win32/functions/threads/tss_create.c b/platform/win32/functions/threads/tss_create.c
new file mode 100644 (file)
index 0000000..af6da3f
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef REGTEST\r
+#include <threads.h>\r
+#include <stdbool.h>\r
+#include <windows.h>\r
+\r
+/* Pull in TLS support */\r
+extern char _tls_used[];\r
+\r
+struct _PDCLIB_tss * _PDCLIB_tss_first = NULL;\r
+\r
+int tss_create( tss_t *key, tss_dtor_t dtor )\r
+{\r
+    *key = malloc( sizeof *key );\r
+    if( !*key ) {\r
+        return thrd_nomem;\r
+    }\r
+\r
+    (*key)->_Key = TlsAlloc();\r
+    if((*key)->_Key == TLS_OUT_OF_INDEXES) {\r
+        return thrd_error;\r
+    }\r
+    (*key)->_Destructor = dtor;\r
+    (*key)->_Next = _PDCLIB_tss_first;\r
+\r
+    // TODO: make this atomic (& validate no other TLS blocks have been \r
+    // simultaneously allocated)\r
+    _PDCLIB_tss_first = *key;\r
+\r
+    return thrd_success;\r
+}\r
+\r
+static void NTAPI runTlsDestructors( void * image, DWORD reason, PVOID pv )\r
+{\r
+    if( reason == DLL_THREAD_DETACH ) {\r
+        for(unsigned i = 0; i < TSS_DTOR_ITERATIONS; i++) {\r
+            struct _PDCLIB_tss * tss = _PDCLIB_tss_first;\r
+            bool destructorsRan = false;\r
+            while( tss ) {\r
+                void * val = TlsGetValue( tss->_Key );\r
+                if( val ) {\r
+                    TlsSetValue( tss->_Key, NULL );\r
+                    if( tss->_Destructor ) {\r
+                        tss->_Destructor( val );\r
+                        destructorsRan = true;\r
+                    }\r
+                }\r
+\r
+                tss = tss->_Next;\r
+            }\r
+            if(!destructorsRan) break;\r
+        }\r
+    }\r
+}\r
+\r
+#ifdef __GNUC__\r
+__attribute__((__section__(".CRT$XLC")))\r
+#else\r
+__declspec(allocate(".CRT$XLC")) \r
+#endif\r
+PIMAGE_TLS_CALLBACK _PDCLIB_runTlsDestructors = runTlsDestructors;\r
+\r
+#endif\r
+\r
+#ifdef TEST\r
+#include <_PDCLIB_test.h>\r
+\r
+/* Tested in tss_get.c */\r
+int main( void )\r
+{\r
+    return TEST_RESULTS;\r
+}\r
+\r
+#endif
\ No newline at end of file
diff --git a/platform/win32/functions/threads/tss_delete.c b/platform/win32/functions/threads/tss_delete.c
new file mode 100644 (file)
index 0000000..57a43fd
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef REGTEST\r
+#include <windows.h>\r
+#include <threads.h>\r
+#include <stdlib.h>\r
+\r
+extern struct _PDCLIB_tss * _PDCLIB_tss_first;\r
+void tss_delete( tss_t key )\r
+{\r
+    struct _PDCLIB_tss * prev = NULL;\r
+    struct _PDCLIB_tss * cur  = _PDCLIB_tss_first;\r
+    while(cur) {\r
+        if(cur == key) {\r
+            if(prev) {\r
+                prev->_Next = key->_Next;\r
+            } else {\r
+                _PDCLIB_tss_first = key->_Next;\r
+            }\r
+\r
+            TlsFree(key->_Key);\r
+            free(key);\r
+            return;\r
+        }\r
+    }\r
+\r
+    // Not actually a TSS key\r
+    abort();\r
+}\r
+#endif\r
+\r
+#ifdef TEST\r
+#include <_PDCLIB_test.h>\r
+\r
+/* Tested in tss_get.c */\r
+int main( void )\r
+{\r
+    return TEST_RESULTS;\r
+}\r
+\r
+#endif
\ No newline at end of file
diff --git a/platform/win32/functions/threads/tss_get.c b/platform/win32/functions/threads/tss_get.c
new file mode 100644 (file)
index 0000000..b0a4441
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef REGTEST\r
+#include <threads.h>\r
+#include <windows.h>\r
+\r
+void *tss_get(tss_t key)\r
+{\r
+    return TlsGetValue(key->_Key);\r
+}\r
+#endif\r
+\r
+#ifdef TEST\r
+#include <_PDCLIB_test.h>\r
+\r
+#ifndef REGTEST\r
+static tss_t key;\r
+static char v;\r
+#endif\r
+\r
+// Todo: make a thread and test destruction!\r
+\r
+int main( void )\r
+{\r
+#ifndef REGTEST\r
+    TESTCASE(tss_create(&key, NULL) == thrd_success);\r
+    TESTCASE(tss_get(key) == NULL);\r
+    TESTCASE(tss_set(key, &v) == thrd_success);\r
+    TESTCASE(tss_get(key) == &v);\r
+    tss_delete(key);\r
+#endif\r
+    return TEST_RESULTS;\r
+}\r
+\r
+#endif
\ No newline at end of file
diff --git a/platform/win32/functions/threads/tss_set.c b/platform/win32/functions/threads/tss_set.c
new file mode 100644 (file)
index 0000000..5ab53f1
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef REGTEST\r
+#include <threads.h>\r
+#include <windows.h>\r
+\r
+int tss_set(tss_t key, void *val)\r
+{\r
+    if(TlsSetValue(key->_Key, val))\r
+        return thrd_success;\r
+    return thrd_error;\r
+}\r
+#endif\r
+\r
+#ifdef TEST\r
+#include <_PDCLIB_test.h>\r
+\r
+/* Tested in tss_get.c */\r
+int main( void )\r
+{\r
+    return TEST_RESULTS;\r
+}\r
+\r
+#endif
\ No newline at end of file
diff --git a/platform/win32/internals/_PDCLIB_threadconfig.h b/platform/win32/internals/_PDCLIB_threadconfig.h
new file mode 100644 (file)
index 0000000..d914ae0
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _PDCLIB_THREADCONFIG_H\r
+#define _PDCLIB_THREADCONFIG_H\r
+#include <_PDCLIB_aux.h>\r
+#include <_PDCLIB_int.h>\r
+\r
+_PDCLIB_BEGIN_EXTERN_C\r
+#define _PDCLIB_TSS_DTOR_ITERATIONS 3\r
+#define _PDCLIB_ONCE_FLAG_INIT { -1, 0 }\r
+#define _PDCLIB_ONCE_FLAG_IS_DONE( _f ) ((_f)->_State == 0)\r
+typedef struct {\r
+    long  _State;\r
+    void *_Handle;\r
+} _PDCLIB_once_flag;\r
+\r
+void _PDCLIB_call_once(_PDCLIB_once_flag *flag, void (*func)(void));\r
+\r
+//#define _PDCLIB_THRD_HAVE_MISC\r
+//#define _PDCLIB_CND_T char\r
+#define _PDCLIB_MTX_T struct _PDCLIB_mtx \r
+\r
+struct _PDCLIB_mtx {\r
+    void         * _WaitEvHandle;\r
+    volatile   signed long  _State;\r
+    volatile unsigned  int  _ThreadId;\r
+    volatile unsigned  int  _NestCount;\r
+};\r
+\r
+#define _PDCLIB_TSS_T struct _PDCLIB_tss *\r
+\r
+struct _PDCLIB_tss {\r
+    void    (*_Destructor)(void*);\r
+    struct _PDCLIB_tss * _Next;\r
+    unsigned int _Key;\r
+};\r
+\r
+_PDCLIB_END_EXTERN_C\r
+#endif\r