2 * Copyright 2001-2009 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
25 #include "util/Threads.h"
30 # error "This implementation is for WIN32 platforms."
33 using namespace xmltooling::logging;
34 using namespace xmltooling;
37 // base error code for a routine to return on failure
38 #define THREAD_ERROR_TIMEOUT (1)
39 #define THREAD_ERROR_WAKE_OTHER (2)
40 #define THREAD_ERROR (3)
42 // windows returns non zero for success pthreads returns zero
43 static int XMLTOOL_DLLLOCAL map_windows_error_status_to_pthreads(int rc=0) {
46 Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("error from thread operation (%d)", GetLastError());
50 namespace xmltooling {
52 // two levels of classes are needed here
53 // in case InitializeCriticalSection
54 // throws an exception we can keep from
55 // calling the critical_section destructor
56 // on unitilized data, or it could be done with a flag
57 struct XMLTOOL_DLLLOCAL critical_section_data {
59 critical_section_data(){
60 InitializeCriticalSection(&cs);
64 class XMLTOOL_DLLLOCAL critical_section {
66 critical_section_data cse;
70 DeleteCriticalSection (&cse.cs);
73 EnterCriticalSection(&cse.cs);
76 LeaveCriticalSection(&cse.cs);
80 // hold a critical section over the lifetime of this object
81 // used to make a stack variable that unlocks automaticly
83 class XMLTOOL_DLLLOCAL with_crit_section {
87 with_crit_section(critical_section& acs):cs(acs){
95 class XMLTOOL_DLLLOCAL ThreadImpl : public Thread {
99 ThreadImpl(void* (*start_routine)(void*), void* arg) : thread_id(0) {
100 thread_id=CreateThread(
101 0, // security attributes
102 0, // use default stack size, maybe this should be setable
103 (LPTHREAD_START_ROUTINE ) start_routine,
105 0, // flags, default is ignore stacksize and don't create suspended which is what we want
108 map_windows_error_status_to_pthreads();
109 throw ThreadingException("Thread creation failed.");
120 int rc=map_windows_error_status_to_pthreads(CloseHandle(thread_id));
125 int join(void** thread_return) {
128 if (thread_return!=0)
130 int rc=WaitForSingleObject(thread_id,INFINITE);
134 map_windows_error_status_to_pthreads(GetExitCodeThread(thread_id,(unsigned long*)thread_return));
141 int kill(int signo) {
144 return map_windows_error_status_to_pthreads(TerminateThread(thread_id,signo));
148 class XMLTOOL_DLLLOCAL MutexImpl : public Mutex {
150 CRITICAL_SECTION mhandle;
153 InitializeCriticalSection(&mhandle);
157 DeleteCriticalSection(&mhandle);
161 EnterCriticalSection(&mhandle);
166 LeaveCriticalSection(&mhandle);
171 class XMLTOOL_DLLLOCAL CondWaitImpl : public CondWait {
176 CondWaitImpl() : cond(CreateEvent(0,false,false,0)) {
178 map_windows_error_status_to_pthreads();
179 throw ThreadingException("Event creation failed.");
184 if((cond!=0) && (!CloseHandle(cond)))
185 map_windows_error_status_to_pthreads();
188 int wait(Mutex* mutex) {
189 return timedwait(mutex,INFINITE);
194 return map_windows_error_status_to_pthreads();
199 throw ThreadingException("Broadcast not implemented on Win32 platforms.");
202 // wait for myself to signal and this mutex or the timeout
203 int timedwait(Mutex* mutex, int delay_seconds) {
204 int rc=mutex->unlock();
208 int delay_ms=delay_seconds;
209 if(delay_seconds!=INFINITE)
211 rc=WaitForSingleObject(cond,delay_ms);
212 int rc2=mutex->lock();
221 return map_windows_error_status_to_pthreads();
227 class XMLTOOL_DLLLOCAL RWLockImpl : public RWLock {
229 // used to protect read or write to the data below
231 // event handle threads wait on when the lock they want is busy
232 // normally set to signaled all the time, if some thread can't get what
233 // they want they reset it and sleep. on releasing a lock set it to
234 // signaled if someone may have wanted what you just released
236 // number of threads holding a read lock
238 // true iff there a writer has our lock
242 RWLockImpl() : wake_waiters(0), num_readers(0), have_writer(true) {
243 with_crit_section acs(cs);
244 wake_waiters=CreateEvent(0,true,true,0);
246 if (wake_waiters==0) {
247 map_windows_error_status_to_pthreads();
248 throw ThreadingException("Event creation for shared lock failed.");
253 with_crit_section acs(cs);
254 if ((wake_waiters!=0) && (!CloseHandle(wake_waiters)))
255 map_windows_error_status_to_pthreads();
260 // wait for the lock maybe being availible
261 // we will find out for sure inside the critical section
262 if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0)
263 return map_windows_error_status_to_pthreads();
265 with_crit_section alock(cs);
266 // invariant not locked for reading and writing
267 if ((num_readers!=0) && (have_writer))
269 // if no writer we can join any existing readers
275 // have a writer, mark the synchronization object
276 // so everyone waits, when the writer unlocks it will wake us
277 if (!ResetEvent(wake_waiters))
278 return map_windows_error_status_to_pthreads();
285 // wait for the lock maybe being availible
286 // we will find out for sure inside the critical section
287 if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0)
288 return map_windows_error_status_to_pthreads();
290 with_crit_section bla(cs);
291 // invariant not locked for reading and writing
292 if ((num_readers!=0) && (have_writer))
295 // if no writer and no readers we can become the writer
296 if ((num_readers==0) && (!have_writer)) {
301 // lock is busy, the unlocker will wake us
302 if (!ResetEvent(wake_waiters))
303 return map_windows_error_status_to_pthreads();
309 with_crit_section mumble(cs);
310 // invariant not locked for reading and writing
311 if ((num_readers!=0) && (have_writer))
314 // error if nothing locked
315 if ((num_readers==0) && (!have_writer))
318 // if there was a writer it has to be us so unlock write lock
321 // if there where any reades there is one less now
325 // if no readers left wake up any readers/writers waiting
326 // to have a go at it
328 if (!SetEvent(wake_waiters))
329 return map_windows_error_status_to_pthreads();
334 typedef void (*destroy_hook_type)(void*);
336 class XMLTOOL_DLLLOCAL ThreadKeyImpl : public ThreadKey {
338 destroy_hook_type destroy_hook;
340 static critical_section cs;
341 static set<ThreadKeyImpl*> m_keys;
342 friend class ThreadKey;
344 ThreadKeyImpl(void (*destroy_fcn)(void*)) : destroy_hook(destroy_fcn) {
347 with_crit_section wcs(cs);
352 virtual ~ThreadKeyImpl() {
354 destroy_hook(TlsGetValue(key));
355 with_crit_section wcs(cs);
361 int setData(void* data) {
362 TlsSetValue(key, data);
366 void* getData() const {
367 return TlsGetValue(key);
370 void onDetach() const {
372 destroy_hook(TlsGetValue(key));
373 TlsSetValue(key, NULL);
381 // public "static" creation functions
384 Thread* Thread::create(void* (*start_routine)(void*), void* arg)
386 return new ThreadImpl(start_routine, arg);
389 void Thread::exit(void* return_val)
391 ExitThread((DWORD)return_val);
394 void Thread::sleep(int seconds)
396 Sleep(seconds * 1000);
399 Mutex * Mutex::create()
401 return new MutexImpl();
404 CondWait * CondWait::create()
406 return new CondWaitImpl();
409 RWLock * RWLock::create()
411 return new RWLockImpl();
414 critical_section ThreadKeyImpl::cs;
415 set<ThreadKeyImpl*> ThreadKeyImpl::m_keys;
417 ThreadKey* ThreadKey::create (void (*destroy_fcn)(void*))
419 return new ThreadKeyImpl(destroy_fcn);
422 void ThreadKey::onDetach()
424 with_crit_section wcs(ThreadKeyImpl::cs);
425 for_each(ThreadKeyImpl::m_keys.begin(), ThreadKeyImpl::m_keys.end(), mem_fun<void,ThreadKeyImpl>(&ThreadKeyImpl::onDetach));