Merge branch '1.x' of ssh://authdev.it.ohio-state.edu/~scantor/git/cpp-xmltooling...
[shibboleth/cpp-xmltooling.git] / xmltooling / util / Win32Threads.cpp
index 8e5cdc0..3f64975 100644 (file)
-/*\r
- *  Copyright 2001-2006 Internet2\r
- * \r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/**\r
- * Win32Threads.cpp\r
- * \r
- * Thread and locking wrappers for Win32 platforms\r
- */\r
-\r
-#include "internal.h"\r
-#include "util/Threads.h"\r
-\r
-#include <log4cpp/Category.hh>\r
-\r
-#ifndef WIN32\r
-# error "This implementation is for WIN32 platforms."\r
-#endif\r
-\r
-using namespace xmltooling;\r
-using namespace log4cpp;\r
-using namespace std;\r
-\r
-// base error code for a routine to return on failure\r
-#define THREAD_ERROR_TIMEOUT   (1)\r
-#define THREAD_ERROR_WAKE_OTHER (2)\r
-#define THREAD_ERROR               (3)\r
-\r
-// windows returns non zero for success pthreads returns zero\r
-static int XMLTOOL_DLLLOCAL map_windows_error_status_to_pthreads(int rc=0) {\r
-    if(rc!=0)  // success?\r
-        return 0;\r
-    Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("error from thread operation (%d)", GetLastError());\r
-    return THREAD_ERROR;\r
-}\r
-\r
-namespace xmltooling {\r
-\r
-    // two levels of classes are needed here\r
-    // in case InitializeCriticalSection\r
-    // throws an exception we can keep from\r
-    // calling the critical_section destructor\r
-    // on unitilized data, or it could be done with a flag\r
-    struct XMLTOOL_DLLLOCAL critical_section_data {\r
-        CRITICAL_SECTION cs;\r
-        critical_section_data(){\r
-            InitializeCriticalSection(&cs);    \r
-        }\r
-    };\r
-    \r
-    class XMLTOOL_DLLLOCAL critical_section {\r
-    private:\r
-        critical_section_data  cse;\r
-    public:\r
-        critical_section(){}\r
-        ~critical_section(){\r
-            DeleteCriticalSection (&cse.cs);\r
-        }\r
-        void enter(void) {\r
-            EnterCriticalSection(&cse.cs);\r
-        }\r
-        void leave(void) {\r
-            LeaveCriticalSection(&cse.cs);\r
-        }\r
-    };\r
-    \r
-    // hold a critical section over the lifetime of this object\r
-    // used to make a stack variable that unlocks automaticly\r
-    // on return/throw\r
-    class XMLTOOL_DLLLOCAL with_crit_section {\r
-    private:\r
-        critical_section& cs;\r
-    public:\r
-        with_crit_section(critical_section& acs):cs(acs){\r
-            cs.enter();\r
-        }\r
-        ~with_crit_section(){\r
-            cs.leave();\r
-        }\r
-    };\r
-    \r
-    class XMLTOOL_DLLLOCAL ThreadImpl : public Thread {\r
-    private:\r
-        HANDLE thread_id;\r
-    public:\r
-        ThreadImpl(void* (*start_routine)(void*), void* arg) : thread_id(0) {\r
-            thread_id=CreateThread(\r
-                0, // security attributes\r
-                0, // use default stack size, maybe this should be setable\r
-                (LPTHREAD_START_ROUTINE ) start_routine,\r
-                arg,\r
-                0, // flags, default is ignore stacksize and don't create suspended which is what we want\r
-                0);\r
-            if (thread_id==0) {\r
-                map_windows_error_status_to_pthreads();\r
-                throw ThreadingException("Thread creation failed.");\r
-            }\r
-        }\r
-\r
-        ~ThreadImpl() {\r
-            (void)detach();\r
-        }\r
-    \r
-        int detach() {\r
-            if (thread_id==0)\r
-                return THREAD_ERROR;\r
-            int rc=map_windows_error_status_to_pthreads(CloseHandle(thread_id));\r
-            thread_id=0;\r
-            return rc;\r
-        }\r
-\r
-        int join(void** thread_return) {\r
-            if (thread_id==0)\r
-                return THREAD_ERROR;\r
-            if (thread_return!=0)\r
-                *thread_return=0;\r
-            int rc=WaitForSingleObject(thread_id,INFINITE);\r
-            switch(rc) {\r
-                case WAIT_OBJECT_0:\r
-                    if (thread_return)\r
-                        map_windows_error_status_to_pthreads(GetExitCodeThread(thread_id,(unsigned long*)thread_return));\r
-                default:\r
-                    return THREAD_ERROR;\r
-            }\r
-            return 0;\r
-        }\r
-      \r
-        int kill(int signo) {\r
-            if (thread_id==0)\r
-                return THREAD_ERROR;\r
-            return map_windows_error_status_to_pthreads(TerminateThread(thread_id,signo));\r
-        }\r
-    };\r
-    \r
-    class XMLTOOL_DLLLOCAL MutexImpl : public Mutex {\r
-    private:\r
-        HANDLE mhandle;\r
-    public:\r
-        MutexImpl() : mhandle(CreateMutex(0,false,0)) {\r
-            if (mhandle==0) {\r
-                map_windows_error_status_to_pthreads();\r
-                throw ThreadingException("Mutex creation failed.");\r
-            }\r
-        }\r
-        \r
-        ~MutexImpl() {\r
-            if((mhandle!=0) && (!CloseHandle(mhandle))) \r
-                map_windows_error_status_to_pthreads();\r
-        }\r
-        \r
-        int lock() {\r
-            int rc=WaitForSingleObject(mhandle,INFINITE);\r
-            switch(rc) {\r
-                case WAIT_ABANDONED:\r
-                case WAIT_OBJECT_0:\r
-                    return 0;\r
-                default:\r
-                    return map_windows_error_status_to_pthreads();\r
-            }\r
-        }\r
-        \r
-        int unlock() {\r
-            return map_windows_error_status_to_pthreads(ReleaseMutex(mhandle));\r
-        }\r
-    };\r
-    \r
-    class XMLTOOL_DLLLOCAL CondWaitImpl : public CondWait {\r
-    private:\r
-        HANDLE cond;\r
-    \r
-    public:\r
-        CondWaitImpl() : cond(CreateEvent(0,false,false,0)) {\r
-            if(cond==0) {\r
-                map_windows_error_status_to_pthreads();\r
-               throw ThreadingException("Event creation failed.");\r
-            }\r
-        };\r
-    \r
-        ~CondWaitImpl() {\r
-            if((cond!=0) && (!CloseHandle(cond))) \r
-                map_windows_error_status_to_pthreads();\r
-        }\r
-    \r
-        int wait(Mutex* mutex) {\r
-            return timedwait(mutex,INFINITE);\r
-        }\r
-    \r
-        int signal() {\r
-            if(!SetEvent(cond))\r
-                return map_windows_error_status_to_pthreads();\r
-            return 0;\r
-        }\r
-      \r
-        int broadcast() {\r
-            throw ThreadingException("Broadcast not implemented on Win32 platforms.");\r
-        }\r
-    \r
-        // wait for myself to signal and this mutex or the timeout\r
-        int timedwait(Mutex* mutex, int delay_seconds) {\r
-            int rc=mutex->unlock();\r
-            if(rc!=0)\r
-                return rc;\r
-        \r
-            int delay_ms=delay_seconds;\r
-            if(delay_seconds!=INFINITE)\r
-                delay_ms*=1000;\r
-            rc=WaitForSingleObject(cond,delay_ms);\r
-            int rc2=mutex->lock();\r
-            if(rc2!=0)\r
-                return rc2;\r
-            switch(rc) {\r
-                case WAIT_ABANDONED:\r
-                case WAIT_OBJECT_0:\r
-                case WAIT_TIMEOUT:\r
-                    return 0;\r
-                default:\r
-                    return map_windows_error_status_to_pthreads();\r
-            }\r
-            return 0;\r
-        }\r
-    };\r
-    \r
-    class XMLTOOL_DLLLOCAL RWLockImpl : public RWLock {\r
-    private:\r
-        // used to protect read or write to the data below\r
-        critical_section cs;\r
-        // event handle threads wait on when the lock they want is busy\r
-        // normally set to signaled all the time, if some thread can't get what\r
-        // they want they reset it and sleep.  on releasing a lock set it to\r
-        // signaled if someone may have wanted what you just released\r
-        HANDLE wake_waiters;\r
-        // number of threads holding a read lock\r
-        int num_readers;\r
-        // true iff there a writer has our lock\r
-        bool have_writer;\r
-    \r
-    public:\r
-        RWLockImpl() : wake_waiters(0), num_readers(0), have_writer(true) {\r
-            with_crit_section acs(cs);\r
-            wake_waiters=CreateEvent(0,true,true,0);\r
-            have_writer=false;\r
-            if (wake_waiters==0) {\r
-                map_windows_error_status_to_pthreads();\r
-                throw ThreadingException("Event creation for shared lock failed.");\r
-            }\r
-        }\r
-        \r
-        ~RWLockImpl() { \r
-            with_crit_section acs(cs);\r
-            if ((wake_waiters!=0) && (!CloseHandle(wake_waiters))) \r
-                map_windows_error_status_to_pthreads();\r
-        }\r
-    \r
-        int rdlock() {\r
-            while(1) {\r
-                // wait for the lock maybe being availible\r
-                // we will find out for sure inside the critical section\r
-                if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0) \r
-                    return map_windows_error_status_to_pthreads();\r
-         \r
-                with_crit_section alock(cs);\r
-                // invariant not locked for reading and writing\r
-                if ((num_readers!=0) && (have_writer))\r
-                    return THREAD_ERROR;\r
-                // if no writer we can join any existing readers\r
-                if (!have_writer) {\r
-                    num_readers++;\r
-                    return 0;\r
-                }\r
-           \r
-                // have a writer, mark the synchronization object\r
-                // so everyone waits, when the writer unlocks it will wake us\r
-                if (!ResetEvent(wake_waiters))\r
-                    return map_windows_error_status_to_pthreads();\r
-            }\r
-            return THREAD_ERROR;\r
-        }\r
-    \r
-        int wrlock() {\r
-            while(1) {\r
-                // wait for the lock maybe being availible\r
-                // we will find out for sure inside the critical section\r
-                if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0) \r
-                    return map_windows_error_status_to_pthreads();\r
-\r
-                with_crit_section bla(cs);\r
-                // invariant not locked for reading and writing\r
-                if ((num_readers!=0) && (have_writer))\r
-                      return THREAD_ERROR;\r
-\r
-                // if no writer and no readers we can become the writer\r
-                if ((num_readers==0) && (!have_writer)) {\r
-                    have_writer=true;\r
-                    return 0;\r
-                }\r
-             \r
-                // lock is busy, the unlocker will wake us\r
-                if (!ResetEvent(wake_waiters))\r
-                    return map_windows_error_status_to_pthreads();\r
-            }\r
-            return THREAD_ERROR;\r
-        }\r
-    \r
-        int unlock() {\r
-            with_crit_section mumble(cs);\r
-            // invariant not locked for reading and writing\r
-            if ((num_readers!=0) && (have_writer))\r
-                return THREAD_ERROR;\r
-            \r
-            // error if nothing locked\r
-            if ((num_readers==0) && (!have_writer))\r
-                return THREAD_ERROR;\r
-            \r
-            // if there was a writer it has to be us so unlock write lock \r
-            have_writer=false;\r
-            \r
-            // if there where any reades there is one less now\r
-            if(num_readers>0)\r
-                num_readers--;\r
-            \r
-            // if no readers left wake up any readers/writers waiting\r
-            // to have a go at it\r
-            if (num_readers==0)\r
-                if (!SetEvent(wake_waiters))\r
-                    return map_windows_error_status_to_pthreads();\r
-            return 0;\r
-        }\r
-    };\r
-    \r
-    typedef void (*destroy_hook_type)(void*);\r
-    \r
-    class XMLTOOL_DLLLOCAL ThreadKeyImpl : public ThreadKey {\r
-    private:\r
-        destroy_hook_type destroy_hook;\r
-        DWORD key;\r
-    \r
-    public:\r
-        ThreadKeyImpl(void (*destroy_fcn)(void*)) : destroy_hook(destroy_fcn) {\r
-            key=TlsAlloc();\r
-        };\r
-        \r
-        virtual ~ThreadKeyImpl() {\r
-            if (destroy_hook)\r
-                destroy_hook(TlsGetValue(key));\r
-            TlsFree(key);\r
-        }\r
-    \r
-        int setData(void* data) {\r
-            TlsSetValue(key,data);\r
-            return 0;\r
-        }\r
-        \r
-        void* getData() const {\r
-            return TlsGetValue(key);\r
-        }\r
-    };\r
-\r
-};\r
-\r
-//\r
-// public "static" creation functions\r
-//\r
-\r
-Thread* Thread::create(void* (*start_routine)(void*), void* arg)\r
-{\r
-    return new ThreadImpl(start_routine, arg);\r
-}\r
-\r
-void Thread::exit(void* return_val)\r
-{\r
-    ExitThread((DWORD)return_val);\r
-}\r
-\r
-void Thread::sleep(int seconds)\r
-{\r
-    Sleep(seconds * 1000);\r
-}\r
-\r
-Mutex * Mutex::create()\r
-{\r
-    return new MutexImpl();\r
-}\r
-\r
-CondWait * CondWait::create()\r
-{\r
-    return new CondWaitImpl();\r
-}\r
-\r
-RWLock * RWLock::create()\r
-{\r
-    return new RWLockImpl();\r
-}\r
-\r
-ThreadKey* ThreadKey::create (void (*destroy_fcn)(void*))\r
-{\r
-    return new ThreadKeyImpl(destroy_fcn);\r
-}\r
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * UCAID licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ */
+
+/**
+ * Win32Threads.cpp
+ *
+ * Thread and locking wrappers for Win32 platforms.
+ */
+
+#include "internal.h"
+#include "logging.h"
+#include "util/Threads.h"
+
+#include <algorithm>
+
+#ifndef WIN32
+# error "This implementation is for WIN32 platforms."
+#endif
+
+using namespace xmltooling::logging;
+using namespace xmltooling;
+using namespace std;
+
+// base error code for a routine to return on failure
+#define THREAD_ERROR_TIMEOUT   (1)
+#define THREAD_ERROR_WAKE_OTHER (2)
+#define THREAD_ERROR               (3)
+
+// windows returns non zero for success pthreads returns zero
+static int XMLTOOL_DLLLOCAL map_windows_error_status_to_pthreads(int rc=0) {
+    if(rc!=0)  // success?
+        return 0;
+    Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("error from thread operation (%d)", GetLastError());
+    return THREAD_ERROR;
+}
+
+namespace xmltooling {
+
+    // two levels of classes are needed here
+    // in case InitializeCriticalSection
+    // throws an exception we can keep from
+    // calling the critical_section destructor
+    // on unitilized data, or it could be done with a flag
+    struct XMLTOOL_DLLLOCAL critical_section_data {
+        CRITICAL_SECTION cs;
+        critical_section_data(){
+            InitializeCriticalSection(&cs);
+        }
+    };
+
+    class XMLTOOL_DLLLOCAL critical_section {
+    private:
+        critical_section_data  cse;
+    public:
+        critical_section(){}
+        ~critical_section(){
+            DeleteCriticalSection (&cse.cs);
+        }
+        void enter(void) {
+            EnterCriticalSection(&cse.cs);
+        }
+        void leave(void) {
+            LeaveCriticalSection(&cse.cs);
+        }
+    };
+
+    // hold a critical section over the lifetime of this object
+    // used to make a stack variable that unlocks automaticly
+    // on return/throw
+    class XMLTOOL_DLLLOCAL with_crit_section {
+    private:
+        critical_section& cs;
+    public:
+        with_crit_section(critical_section& acs):cs(acs){
+            cs.enter();
+        }
+        ~with_crit_section(){
+            cs.leave();
+        }
+    };
+
+    class XMLTOOL_DLLLOCAL ThreadImpl : public Thread {
+    private:
+        HANDLE thread_id;
+    public:
+        ThreadImpl(void* (*start_routine)(void*), void* arg, size_t stacksize) : thread_id(0) {
+            thread_id=CreateThread(
+                0, // security attributes
+                stacksize, // 0 just means the default size anyway
+                (LPTHREAD_START_ROUTINE ) start_routine,
+                arg,
+                0, // flags, default is don't create suspended which is what we want
+                0);
+            if (thread_id==0) {
+                map_windows_error_status_to_pthreads();
+                throw ThreadingException("Thread creation failed.");
+            }
+        }
+
+        ~ThreadImpl() {
+            (void)detach();
+        }
+
+        int detach() {
+            if (thread_id==0)
+                return THREAD_ERROR;
+            int rc=map_windows_error_status_to_pthreads(CloseHandle(thread_id));
+            thread_id=0;
+            return rc;
+        }
+
+        int join(void** thread_return) {
+            if (thread_id==0)
+                return THREAD_ERROR;
+            if (thread_return!=0)
+                *thread_return=0;
+            int rc=WaitForSingleObject(thread_id,INFINITE);
+            switch(rc) {
+                case WAIT_OBJECT_0:
+                    if (thread_return)
+                        map_windows_error_status_to_pthreads(GetExitCodeThread(thread_id,(unsigned long*)thread_return));
+                default:
+                    return THREAD_ERROR;
+            }
+            return 0;
+        }
+
+        int kill(int signo) {
+            if (thread_id==0)
+                return THREAD_ERROR;
+            return map_windows_error_status_to_pthreads(TerminateThread(thread_id,signo));
+        }
+    };
+
+    class XMLTOOL_DLLLOCAL MutexImpl : public Mutex {
+    private:
+        CRITICAL_SECTION mhandle;
+    public:
+        MutexImpl() {
+            InitializeCriticalSection(&mhandle);
+        }
+
+        ~MutexImpl() {
+            DeleteCriticalSection(&mhandle);
+        }
+
+        int lock() {
+            EnterCriticalSection(&mhandle);
+            return 0;
+        }
+
+        int unlock() {
+            LeaveCriticalSection(&mhandle);
+            return 0;
+        }
+    };
+
+    class XMLTOOL_DLLLOCAL CondWaitImpl : public CondWait {
+    private:
+        HANDLE cond;
+
+    public:
+        CondWaitImpl() : cond(CreateEvent(0,false,false,0)) {
+            if(cond==0) {
+                map_windows_error_status_to_pthreads();
+               throw ThreadingException("Event creation failed.");
+            }
+        };
+
+        ~CondWaitImpl() {
+            if((cond!=0) && (!CloseHandle(cond)))
+                map_windows_error_status_to_pthreads();
+        }
+
+        int wait(Mutex* mutex) {
+            return timedwait(mutex,INFINITE);
+        }
+
+        int signal() {
+            if(!SetEvent(cond))
+                return map_windows_error_status_to_pthreads();
+            return 0;
+        }
+
+        int broadcast() {
+            throw ThreadingException("Broadcast not implemented on Win32 platforms.");
+        }
+
+        // wait for myself to signal and this mutex or the timeout
+        int timedwait(Mutex* mutex, int delay_seconds) {
+            int rc=mutex->unlock();
+            if(rc!=0)
+                return rc;
+
+            int delay_ms=delay_seconds;
+            if(delay_seconds!=INFINITE)
+                delay_ms*=1000;
+            rc=WaitForSingleObject(cond,delay_ms);
+            int rc2=mutex->lock();
+            if(rc2!=0)
+                return rc2;
+            switch(rc) {
+                case WAIT_ABANDONED:
+                case WAIT_OBJECT_0:
+                case WAIT_TIMEOUT:
+                    return 0;
+                default:
+                    return map_windows_error_status_to_pthreads();
+            }
+            return 0;
+        }
+    };
+
+    class XMLTOOL_DLLLOCAL RWLockImpl : public RWLock {
+    private:
+        // used to protect read or write to the data below
+        critical_section cs;
+        // event handle threads wait on when the lock they want is busy
+        // normally set to signaled all the time, if some thread can't get what
+        // they want they reset it and sleep.  on releasing a lock set it to
+        // signaled if someone may have wanted what you just released
+        HANDLE wake_waiters;
+        // number of threads holding a read lock
+        int num_readers;
+        // true iff there a writer has our lock
+        bool have_writer;
+
+    public:
+        RWLockImpl() : wake_waiters(0), num_readers(0), have_writer(true) {
+            with_crit_section acs(cs);
+            wake_waiters=CreateEvent(0,true,true,0);
+            have_writer=false;
+            if (wake_waiters==0) {
+                map_windows_error_status_to_pthreads();
+                throw ThreadingException("Event creation for shared lock failed.");
+            }
+        }
+
+        ~RWLockImpl() {
+            with_crit_section acs(cs);
+            if ((wake_waiters!=0) && (!CloseHandle(wake_waiters)))
+                map_windows_error_status_to_pthreads();
+        }
+
+        int rdlock() {
+            while(1) {
+                // wait for the lock maybe being availible
+                // we will find out for sure inside the critical section
+                if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0)
+                    return map_windows_error_status_to_pthreads();
+
+                with_crit_section alock(cs);
+                // invariant not locked for reading and writing
+                if ((num_readers!=0) && (have_writer))
+                    return THREAD_ERROR;
+                // if no writer we can join any existing readers
+                if (!have_writer) {
+                    num_readers++;
+                    return 0;
+                }
+
+                // have a writer, mark the synchronization object
+                // so everyone waits, when the writer unlocks it will wake us
+                if (!ResetEvent(wake_waiters))
+                    return map_windows_error_status_to_pthreads();
+            }
+            return THREAD_ERROR;
+        }
+
+        int wrlock() {
+            while(1) {
+                // wait for the lock maybe being availible
+                // we will find out for sure inside the critical section
+                if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0)
+                    return map_windows_error_status_to_pthreads();
+
+                with_crit_section bla(cs);
+                // invariant not locked for reading and writing
+                if ((num_readers!=0) && (have_writer))
+                      return THREAD_ERROR;
+
+                // if no writer and no readers we can become the writer
+                if ((num_readers==0) && (!have_writer)) {
+                    have_writer=true;
+                    return 0;
+                }
+
+                // lock is busy, the unlocker will wake us
+                if (!ResetEvent(wake_waiters))
+                    return map_windows_error_status_to_pthreads();
+            }
+            return THREAD_ERROR;
+        }
+
+        int unlock() {
+            with_crit_section mumble(cs);
+            // invariant not locked for reading and writing
+            if ((num_readers!=0) && (have_writer))
+                return THREAD_ERROR;
+
+            // error if nothing locked
+            if ((num_readers==0) && (!have_writer))
+                return THREAD_ERROR;
+
+            // if there was a writer it has to be us so unlock write lock
+            have_writer=false;
+
+            // if there where any reades there is one less now
+            if(num_readers>0)
+                num_readers--;
+
+            // if no readers left wake up any readers/writers waiting
+            // to have a go at it
+            if (num_readers==0)
+                if (!SetEvent(wake_waiters))
+                    return map_windows_error_status_to_pthreads();
+            return 0;
+        }
+    };
+
+    typedef void (*destroy_hook_type)(void*);
+
+    class XMLTOOL_DLLLOCAL ThreadKeyImpl : public ThreadKey {
+    private:
+        destroy_hook_type destroy_hook;
+        DWORD key;
+        static critical_section cs;
+        static set<ThreadKeyImpl*> m_keys;
+        friend class ThreadKey;
+    public:
+        ThreadKeyImpl(void (*destroy_fcn)(void*)) : destroy_hook(destroy_fcn) {
+            key=TlsAlloc();
+            if (destroy_fcn) {
+                with_crit_section wcs(cs);
+                m_keys.insert(this);
+            }
+        };
+
+        virtual ~ThreadKeyImpl() {
+            if (destroy_hook) {
+                destroy_hook(TlsGetValue(key));
+                with_crit_section wcs(cs);
+                m_keys.erase(this);
+            }
+            TlsFree(key);
+        }
+
+        int setData(void* data) {
+            TlsSetValue(key, data);
+            return 0;
+        }
+
+        void* getData() const {
+            return TlsGetValue(key);
+        }
+
+        void onDetach() const {
+            if (destroy_hook) {
+                destroy_hook(TlsGetValue(key));
+                TlsSetValue(key, nullptr);
+            }
+        }
+    };
+
+};
+
+//
+// public "static" creation functions
+//
+
+Thread* Thread::create(void* (*start_routine)(void*), void* arg, size_t stacksize)
+{
+    return new ThreadImpl(start_routine, arg, stacksize);
+}
+
+void Thread::exit(void* return_val)
+{
+    ExitThread((DWORD)return_val);
+}
+
+void Thread::sleep(int seconds)
+{
+    Sleep(seconds * 1000);
+}
+
+Mutex * Mutex::create()
+{
+    return new MutexImpl();
+}
+
+CondWait * CondWait::create()
+{
+    return new CondWaitImpl();
+}
+
+RWLock * RWLock::create()
+{
+    return new RWLockImpl();
+}
+
+critical_section ThreadKeyImpl::cs;
+set<ThreadKeyImpl*> ThreadKeyImpl::m_keys;
+
+ThreadKey* ThreadKey::create (void (*destroy_fcn)(void*))
+{
+    return new ThreadKeyImpl(destroy_fcn);
+}
+
+void ThreadKey::onDetach()
+{
+    with_crit_section wcs(ThreadKeyImpl::cs);
+    for_each(ThreadKeyImpl::m_keys.begin(), ThreadKeyImpl::m_keys.end(), mem_fun<void,ThreadKeyImpl>(&ThreadKeyImpl::onDetach));
+}