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