2 * Copyright 2001-2007 Internet2
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 * Thread and locking wrappers for Win32 platforms
24 #include "util/Threads.h"
26 #include <log4cpp/Category.hh>
29 # error "This implementation is for WIN32 platforms."
32 using namespace xmltooling;
33 using namespace log4cpp;
36 // base error code for a routine to return on failure
37 #define THREAD_ERROR_TIMEOUT (1)
38 #define THREAD_ERROR_WAKE_OTHER (2)
39 #define THREAD_ERROR (3)
41 // windows returns non zero for success pthreads returns zero
42 static int XMLTOOL_DLLLOCAL map_windows_error_status_to_pthreads(int rc=0) {
45 Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("error from thread operation (%d)", GetLastError());
49 namespace xmltooling {
51 // two levels of classes are needed here
52 // in case InitializeCriticalSection
53 // throws an exception we can keep from
54 // calling the critical_section destructor
55 // on unitilized data, or it could be done with a flag
56 struct XMLTOOL_DLLLOCAL critical_section_data {
58 critical_section_data(){
59 InitializeCriticalSection(&cs);
63 class XMLTOOL_DLLLOCAL critical_section {
65 critical_section_data cse;
69 DeleteCriticalSection (&cse.cs);
72 EnterCriticalSection(&cse.cs);
75 LeaveCriticalSection(&cse.cs);
79 // hold a critical section over the lifetime of this object
80 // used to make a stack variable that unlocks automaticly
82 class XMLTOOL_DLLLOCAL with_crit_section {
86 with_crit_section(critical_section& acs):cs(acs){
94 class XMLTOOL_DLLLOCAL ThreadImpl : public Thread {
98 ThreadImpl(void* (*start_routine)(void*), void* arg) : thread_id(0) {
99 thread_id=CreateThread(
100 0, // security attributes
101 0, // use default stack size, maybe this should be setable
102 (LPTHREAD_START_ROUTINE ) start_routine,
104 0, // flags, default is ignore stacksize and don't create suspended which is what we want
107 map_windows_error_status_to_pthreads();
108 throw ThreadingException("Thread creation failed.");
119 int rc=map_windows_error_status_to_pthreads(CloseHandle(thread_id));
124 int join(void** thread_return) {
127 if (thread_return!=0)
129 int rc=WaitForSingleObject(thread_id,INFINITE);
133 map_windows_error_status_to_pthreads(GetExitCodeThread(thread_id,(unsigned long*)thread_return));
140 int kill(int signo) {
143 return map_windows_error_status_to_pthreads(TerminateThread(thread_id,signo));
147 class XMLTOOL_DLLLOCAL MutexImpl : public Mutex {
151 MutexImpl() : mhandle(CreateMutex(0,false,0)) {
153 map_windows_error_status_to_pthreads();
154 throw ThreadingException("Mutex creation failed.");
159 if((mhandle!=0) && (!CloseHandle(mhandle)))
160 map_windows_error_status_to_pthreads();
164 int rc=WaitForSingleObject(mhandle,INFINITE);
170 return map_windows_error_status_to_pthreads();
175 return map_windows_error_status_to_pthreads(ReleaseMutex(mhandle));
179 class XMLTOOL_DLLLOCAL CondWaitImpl : public CondWait {
184 CondWaitImpl() : cond(CreateEvent(0,false,false,0)) {
186 map_windows_error_status_to_pthreads();
187 throw ThreadingException("Event creation failed.");
192 if((cond!=0) && (!CloseHandle(cond)))
193 map_windows_error_status_to_pthreads();
196 int wait(Mutex* mutex) {
197 return timedwait(mutex,INFINITE);
202 return map_windows_error_status_to_pthreads();
207 throw ThreadingException("Broadcast not implemented on Win32 platforms.");
210 // wait for myself to signal and this mutex or the timeout
211 int timedwait(Mutex* mutex, int delay_seconds) {
212 int rc=mutex->unlock();
216 int delay_ms=delay_seconds;
217 if(delay_seconds!=INFINITE)
219 rc=WaitForSingleObject(cond,delay_ms);
220 int rc2=mutex->lock();
229 return map_windows_error_status_to_pthreads();
235 class XMLTOOL_DLLLOCAL RWLockImpl : public RWLock {
237 // used to protect read or write to the data below
239 // event handle threads wait on when the lock they want is busy
240 // normally set to signaled all the time, if some thread can't get what
241 // they want they reset it and sleep. on releasing a lock set it to
242 // signaled if someone may have wanted what you just released
244 // number of threads holding a read lock
246 // true iff there a writer has our lock
250 RWLockImpl() : wake_waiters(0), num_readers(0), have_writer(true) {
251 with_crit_section acs(cs);
252 wake_waiters=CreateEvent(0,true,true,0);
254 if (wake_waiters==0) {
255 map_windows_error_status_to_pthreads();
256 throw ThreadingException("Event creation for shared lock failed.");
261 with_crit_section acs(cs);
262 if ((wake_waiters!=0) && (!CloseHandle(wake_waiters)))
263 map_windows_error_status_to_pthreads();
268 // wait for the lock maybe being availible
269 // we will find out for sure inside the critical section
270 if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0)
271 return map_windows_error_status_to_pthreads();
273 with_crit_section alock(cs);
274 // invariant not locked for reading and writing
275 if ((num_readers!=0) && (have_writer))
277 // if no writer we can join any existing readers
283 // have a writer, mark the synchronization object
284 // so everyone waits, when the writer unlocks it will wake us
285 if (!ResetEvent(wake_waiters))
286 return map_windows_error_status_to_pthreads();
293 // wait for the lock maybe being availible
294 // we will find out for sure inside the critical section
295 if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0)
296 return map_windows_error_status_to_pthreads();
298 with_crit_section bla(cs);
299 // invariant not locked for reading and writing
300 if ((num_readers!=0) && (have_writer))
303 // if no writer and no readers we can become the writer
304 if ((num_readers==0) && (!have_writer)) {
309 // lock is busy, the unlocker will wake us
310 if (!ResetEvent(wake_waiters))
311 return map_windows_error_status_to_pthreads();
317 with_crit_section mumble(cs);
318 // invariant not locked for reading and writing
319 if ((num_readers!=0) && (have_writer))
322 // error if nothing locked
323 if ((num_readers==0) && (!have_writer))
326 // if there was a writer it has to be us so unlock write lock
329 // if there where any reades there is one less now
333 // if no readers left wake up any readers/writers waiting
334 // to have a go at it
336 if (!SetEvent(wake_waiters))
337 return map_windows_error_status_to_pthreads();
342 typedef void (*destroy_hook_type)(void*);
344 class XMLTOOL_DLLLOCAL ThreadKeyImpl : public ThreadKey {
346 destroy_hook_type destroy_hook;
350 ThreadKeyImpl(void (*destroy_fcn)(void*)) : destroy_hook(destroy_fcn) {
354 virtual ~ThreadKeyImpl() {
356 destroy_hook(TlsGetValue(key));
360 int setData(void* data) {
361 TlsSetValue(key,data);
365 void* getData() const {
366 return TlsGetValue(key);
373 // public "static" creation functions
376 Thread* Thread::create(void* (*start_routine)(void*), void* arg)
378 return new ThreadImpl(start_routine, arg);
381 void Thread::exit(void* return_val)
383 ExitThread((DWORD)return_val);
386 void Thread::sleep(int seconds)
388 Sleep(seconds * 1000);
391 Mutex * Mutex::create()
393 return new MutexImpl();
396 CondWait * CondWait::create()
398 return new CondWaitImpl();
401 RWLock * RWLock::create()
403 return new RWLockImpl();
406 ThreadKey* ThreadKey::create (void (*destroy_fcn)(void*))
408 return new ThreadKeyImpl(destroy_fcn);