Convert from NULL macro to nullptr.
[shibboleth/cpp-xmltooling.git] / xmltooling / util / Win32Threads.cpp
index 5018485..222603a 100644 (file)
@@ -1,6 +1,6 @@
 /*
- *  Copyright 2001-2007 Internet2
- * 
+ *  Copyright 2001-2010 Internet2
+ *
  * Licensed 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
 
 /**
  * Win32Threads.cpp
- * 
- * Thread and locking wrappers for Win32 platforms
+ *
+ * Thread and locking wrappers for Win32 platforms.
  */
 
 #include "internal.h"
+#include "logging.h"
 #include "util/Threads.h"
 
-#include <log4cpp/Category.hh>
+#include <algorithm>
 
 #ifndef WIN32
 # error "This implementation is for WIN32 platforms."
 #endif
 
+using namespace xmltooling::logging;
 using namespace xmltooling;
-using namespace log4cpp;
 using namespace std;
 
 // base error code for a routine to return on failure
@@ -56,10 +57,10 @@ namespace xmltooling {
     struct XMLTOOL_DLLLOCAL critical_section_data {
         CRITICAL_SECTION cs;
         critical_section_data(){
-            InitializeCriticalSection(&cs);    
+            InitializeCriticalSection(&cs);
         }
     };
-    
+
     class XMLTOOL_DLLLOCAL critical_section {
     private:
         critical_section_data  cse;
@@ -75,7 +76,7 @@ namespace xmltooling {
             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
@@ -90,18 +91,18 @@ namespace xmltooling {
             cs.leave();
         }
     };
-    
+
     class XMLTOOL_DLLLOCAL ThreadImpl : public Thread {
     private:
         HANDLE thread_id;
     public:
-        ThreadImpl(void* (*start_routine)(void*), void* arg) : thread_id(0) {
+        ThreadImpl(void* (*start_routine)(void*), void* arg, size_t stacksize) : thread_id(0) {
             thread_id=CreateThread(
                 0, // security attributes
-                0, // use default stack size, maybe this should be setable
+                stacksize, // 0 just means the default size anyway
                 (LPTHREAD_START_ROUTINE ) start_routine,
                 arg,
-                0, // flags, default is ignore stacksize and don't create suspended which is what we want
+                0, // flags, default is don't create suspended which is what we want
                 0);
             if (thread_id==0) {
                 map_windows_error_status_to_pthreads();
@@ -112,7 +113,7 @@ namespace xmltooling {
         ~ThreadImpl() {
             (void)detach();
         }
-    
+
         int detach() {
             if (thread_id==0)
                 return THREAD_ERROR;
@@ -136,50 +137,41 @@ namespace xmltooling {
             }
             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:
-        HANDLE mhandle;
+        CRITICAL_SECTION mhandle;
     public:
-        MutexImpl() : mhandle(CreateMutex(0,false,0)) {
-            if (mhandle==0) {
-                map_windows_error_status_to_pthreads();
-                throw ThreadingException("Mutex creation failed.");
-            }
+        MutexImpl() {
+            InitializeCriticalSection(&mhandle);
         }
-        
+
         ~MutexImpl() {
-            if((mhandle!=0) && (!CloseHandle(mhandle))) 
-                map_windows_error_status_to_pthreads();
+            DeleteCriticalSection(&mhandle);
         }
-        
+
         int lock() {
-            int rc=WaitForSingleObject(mhandle,INFINITE);
-            switch(rc) {
-                case WAIT_ABANDONED:
-                case WAIT_OBJECT_0:
-                    return 0;
-                default:
-                    return map_windows_error_status_to_pthreads();
-            }
+            EnterCriticalSection(&mhandle);
+            return 0;
         }
-        
+
         int unlock() {
-            return map_windows_error_status_to_pthreads(ReleaseMutex(mhandle));
+            LeaveCriticalSection(&mhandle);
+            return 0;
         }
     };
-    
+
     class XMLTOOL_DLLLOCAL CondWaitImpl : public CondWait {
     private:
         HANDLE cond;
-    
+
     public:
         CondWaitImpl() : cond(CreateEvent(0,false,false,0)) {
             if(cond==0) {
@@ -187,32 +179,32 @@ namespace xmltooling {
                throw ThreadingException("Event creation failed.");
             }
         };
-    
+
         ~CondWaitImpl() {
-            if((cond!=0) && (!CloseHandle(cond))) 
+            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;
@@ -231,7 +223,7 @@ namespace xmltooling {
             return 0;
         }
     };
-    
+
     class XMLTOOL_DLLLOCAL RWLockImpl : public RWLock {
     private:
         // used to protect read or write to the data below
@@ -245,7 +237,7 @@ namespace xmltooling {
         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);
@@ -256,20 +248,20 @@ namespace xmltooling {
                 throw ThreadingException("Event creation for shared lock failed.");
             }
         }
-        
-        ~RWLockImpl() { 
+
+        ~RWLockImpl() {
             with_crit_section acs(cs);
-            if ((wake_waiters!=0) && (!CloseHandle(wake_waiters))) 
+            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) 
+                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))
@@ -279,7 +271,7 @@ namespace xmltooling {
                     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))
@@ -287,12 +279,12 @@ namespace xmltooling {
             }
             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) 
+                if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0)
                     return map_windows_error_status_to_pthreads();
 
                 with_crit_section bla(cs);
@@ -305,31 +297,31 @@ namespace xmltooling {
                     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 
+
+            // 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)
@@ -338,33 +330,49 @@ namespace xmltooling {
             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)
+            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);
+            TlsSetValue(key, data);
             return 0;
         }
-        
+
         void* getData() const {
             return TlsGetValue(key);
         }
+
+        void onDetach() const {
+            if (destroy_hook) {
+                destroy_hook(TlsGetValue(key));
+                TlsSetValue(key, nullptr);
+            }
+        }
     };
 
 };
@@ -373,9 +381,9 @@ namespace xmltooling {
 // public "static" creation functions
 //
 
-Thread* Thread::create(void* (*start_routine)(void*), void* arg)
+Thread* Thread::create(void* (*start_routine)(void*), void* arg, size_t stacksize)
 {
-    return new ThreadImpl(start_routine, arg);
+    return new ThreadImpl(start_routine, arg, stacksize);
 }
 
 void Thread::exit(void* return_val)
@@ -403,7 +411,16 @@ 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));
+}