+/*\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
+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