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
25 #include "util/Threads.h"
28 # error "This implementation is for WIN32 platforms."
31 using namespace xmltooling::logging;
32 using namespace xmltooling;
35 // base error code for a routine to return on failure
36 #define THREAD_ERROR_TIMEOUT (1)
37 #define THREAD_ERROR_WAKE_OTHER (2)
38 #define THREAD_ERROR (3)
40 // windows returns non zero for success pthreads returns zero
41 static int XMLTOOL_DLLLOCAL map_windows_error_status_to_pthreads(int rc=0) {
44 Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("error from thread operation (%d)", GetLastError());
48 namespace xmltooling {
50 // two levels of classes are needed here
51 // in case InitializeCriticalSection
52 // throws an exception we can keep from
53 // calling the critical_section destructor
54 // on unitilized data, or it could be done with a flag
55 struct XMLTOOL_DLLLOCAL critical_section_data {
57 critical_section_data(){
58 InitializeCriticalSection(&cs);
62 class XMLTOOL_DLLLOCAL critical_section {
64 critical_section_data cse;
68 DeleteCriticalSection (&cse.cs);
71 EnterCriticalSection(&cse.cs);
74 LeaveCriticalSection(&cse.cs);
78 // hold a critical section over the lifetime of this object
79 // used to make a stack variable that unlocks automaticly
81 class XMLTOOL_DLLLOCAL with_crit_section {
85 with_crit_section(critical_section& acs):cs(acs){
93 class XMLTOOL_DLLLOCAL ThreadImpl : public Thread {
97 ThreadImpl(void* (*start_routine)(void*), void* arg) : thread_id(0) {
98 thread_id=CreateThread(
99 0, // security attributes
100 0, // use default stack size, maybe this should be setable
101 (LPTHREAD_START_ROUTINE ) start_routine,
103 0, // flags, default is ignore stacksize and don't create suspended which is what we want
106 map_windows_error_status_to_pthreads();
107 throw ThreadingException("Thread creation failed.");
118 int rc=map_windows_error_status_to_pthreads(CloseHandle(thread_id));
123 int join(void** thread_return) {
126 if (thread_return!=0)
128 int rc=WaitForSingleObject(thread_id,INFINITE);
132 map_windows_error_status_to_pthreads(GetExitCodeThread(thread_id,(unsigned long*)thread_return));
139 int kill(int signo) {
142 return map_windows_error_status_to_pthreads(TerminateThread(thread_id,signo));
146 class XMLTOOL_DLLLOCAL MutexImpl : public Mutex {
150 MutexImpl() : mhandle(CreateMutex(0,false,0)) {
152 map_windows_error_status_to_pthreads();
153 throw ThreadingException("Mutex creation failed.");
158 if((mhandle!=0) && (!CloseHandle(mhandle)))
159 map_windows_error_status_to_pthreads();
163 int rc=WaitForSingleObject(mhandle,INFINITE);
169 return map_windows_error_status_to_pthreads();
174 return map_windows_error_status_to_pthreads(ReleaseMutex(mhandle));
178 class XMLTOOL_DLLLOCAL CondWaitImpl : public CondWait {
183 CondWaitImpl() : cond(CreateEvent(0,false,false,0)) {
185 map_windows_error_status_to_pthreads();
186 throw ThreadingException("Event creation failed.");
191 if((cond!=0) && (!CloseHandle(cond)))
192 map_windows_error_status_to_pthreads();
195 int wait(Mutex* mutex) {
196 return timedwait(mutex,INFINITE);
201 return map_windows_error_status_to_pthreads();
206 throw ThreadingException("Broadcast not implemented on Win32 platforms.");
209 // wait for myself to signal and this mutex or the timeout
210 int timedwait(Mutex* mutex, int delay_seconds) {
211 int rc=mutex->unlock();
215 int delay_ms=delay_seconds;
216 if(delay_seconds!=INFINITE)
218 rc=WaitForSingleObject(cond,delay_ms);
219 int rc2=mutex->lock();
228 return map_windows_error_status_to_pthreads();
234 class XMLTOOL_DLLLOCAL RWLockImpl : public RWLock {
236 // used to protect read or write to the data below
238 // event handle threads wait on when the lock they want is busy
239 // normally set to signaled all the time, if some thread can't get what
240 // they want they reset it and sleep. on releasing a lock set it to
241 // signaled if someone may have wanted what you just released
243 // number of threads holding a read lock
245 // true iff there a writer has our lock
249 RWLockImpl() : wake_waiters(0), num_readers(0), have_writer(true) {
250 with_crit_section acs(cs);
251 wake_waiters=CreateEvent(0,true,true,0);
253 if (wake_waiters==0) {
254 map_windows_error_status_to_pthreads();
255 throw ThreadingException("Event creation for shared lock failed.");
260 with_crit_section acs(cs);
261 if ((wake_waiters!=0) && (!CloseHandle(wake_waiters)))
262 map_windows_error_status_to_pthreads();
267 // wait for the lock maybe being availible
268 // we will find out for sure inside the critical section
269 if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0)
270 return map_windows_error_status_to_pthreads();
272 with_crit_section alock(cs);
273 // invariant not locked for reading and writing
274 if ((num_readers!=0) && (have_writer))
276 // if no writer we can join any existing readers
282 // have a writer, mark the synchronization object
283 // so everyone waits, when the writer unlocks it will wake us
284 if (!ResetEvent(wake_waiters))
285 return map_windows_error_status_to_pthreads();
292 // wait for the lock maybe being availible
293 // we will find out for sure inside the critical section
294 if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0)
295 return map_windows_error_status_to_pthreads();
297 with_crit_section bla(cs);
298 // invariant not locked for reading and writing
299 if ((num_readers!=0) && (have_writer))
302 // if no writer and no readers we can become the writer
303 if ((num_readers==0) && (!have_writer)) {
308 // lock is busy, the unlocker will wake us
309 if (!ResetEvent(wake_waiters))
310 return map_windows_error_status_to_pthreads();
316 with_crit_section mumble(cs);
317 // invariant not locked for reading and writing
318 if ((num_readers!=0) && (have_writer))
321 // error if nothing locked
322 if ((num_readers==0) && (!have_writer))
325 // if there was a writer it has to be us so unlock write lock
328 // if there where any reades there is one less now
332 // if no readers left wake up any readers/writers waiting
333 // to have a go at it
335 if (!SetEvent(wake_waiters))
336 return map_windows_error_status_to_pthreads();
341 typedef void (*destroy_hook_type)(void*);
343 class XMLTOOL_DLLLOCAL ThreadKeyImpl : public ThreadKey {
345 destroy_hook_type destroy_hook;
349 ThreadKeyImpl(void (*destroy_fcn)(void*)) : destroy_hook(destroy_fcn) {
353 virtual ~ThreadKeyImpl() {
355 destroy_hook(TlsGetValue(key));
359 int setData(void* data) {
360 TlsSetValue(key,data);
364 void* getData() const {
365 return TlsGetValue(key);
372 // public "static" creation functions
375 Thread* Thread::create(void* (*start_routine)(void*), void* arg)
377 return new ThreadImpl(start_routine, arg);
380 void Thread::exit(void* return_val)
382 ExitThread((DWORD)return_val);
385 void Thread::sleep(int seconds)
387 Sleep(seconds * 1000);
390 Mutex * Mutex::create()
392 return new MutexImpl();
395 CondWait * CondWait::create()
397 return new CondWaitImpl();
400 RWLock * RWLock::create()
402 return new RWLockImpl();
405 ThreadKey* ThreadKey::create (void (*destroy_fcn)(void*))
407 return new ThreadKeyImpl(destroy_fcn);