2 * Licensed to the University Corporation for Advanced Internet
3 * Development, Inc. (UCAID) under one or more contributor license
4 * agreements. See the NOTICE file distributed with this work for
5 * additional information regarding copyright ownership.
7 * UCAID licenses this file to you under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except
9 * in compliance with the License. You may obtain a copy of the
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17 * either express or implied. See the License for the specific
18 * language governing permissions and limitations under the License.
24 * Thread and locking wrappers for Win32 platforms.
29 #include "util/Threads.h"
34 # error "This implementation is for WIN32 platforms."
37 using namespace xmltooling::logging;
38 using namespace xmltooling;
41 // base error code for a routine to return on failure
42 #define THREAD_ERROR_TIMEOUT (1)
43 #define THREAD_ERROR_WAKE_OTHER (2)
44 #define THREAD_ERROR (3)
46 // windows returns non zero for success pthreads returns zero
47 static int XMLTOOL_DLLLOCAL map_windows_error_status_to_pthreads(int rc=0) {
50 Category::getInstance(XMLTOOLING_LOGCAT".Threads").error("error from thread operation (%d)", GetLastError());
54 namespace xmltooling {
56 // two levels of classes are needed here
57 // in case InitializeCriticalSection
58 // throws an exception we can keep from
59 // calling the critical_section destructor
60 // on unitilized data, or it could be done with a flag
61 struct XMLTOOL_DLLLOCAL critical_section_data {
63 critical_section_data(){
64 InitializeCriticalSection(&cs);
68 class XMLTOOL_DLLLOCAL critical_section {
70 critical_section_data cse;
74 DeleteCriticalSection (&cse.cs);
77 EnterCriticalSection(&cse.cs);
80 LeaveCriticalSection(&cse.cs);
84 // hold a critical section over the lifetime of this object
85 // used to make a stack variable that unlocks automaticly
87 class XMLTOOL_DLLLOCAL with_crit_section {
91 with_crit_section(critical_section& acs):cs(acs){
99 class XMLTOOL_DLLLOCAL ThreadImpl : public Thread {
103 ThreadImpl(void* (*start_routine)(void*), void* arg, size_t stacksize) : thread_id(0) {
104 thread_id=CreateThread(
105 0, // security attributes
106 stacksize, // 0 just means the default size anyway
107 (LPTHREAD_START_ROUTINE ) start_routine,
109 0, // flags, default is don't create suspended which is what we want
112 map_windows_error_status_to_pthreads();
113 throw ThreadingException("Thread creation failed.");
124 int rc=map_windows_error_status_to_pthreads(CloseHandle(thread_id));
129 int join(void** thread_return) {
132 if (thread_return!=0)
134 int rc=WaitForSingleObject(thread_id,INFINITE);
138 map_windows_error_status_to_pthreads(GetExitCodeThread(thread_id,(unsigned long*)thread_return));
145 int kill(int signo) {
148 return map_windows_error_status_to_pthreads(TerminateThread(thread_id,signo));
152 class XMLTOOL_DLLLOCAL MutexImpl : public Mutex {
154 CRITICAL_SECTION mhandle;
157 InitializeCriticalSection(&mhandle);
161 DeleteCriticalSection(&mhandle);
165 EnterCriticalSection(&mhandle);
170 LeaveCriticalSection(&mhandle);
175 class XMLTOOL_DLLLOCAL CondWaitImpl : public CondWait {
180 CondWaitImpl() : cond(CreateEvent(0,false,false,0)) {
182 map_windows_error_status_to_pthreads();
183 throw ThreadingException("Event creation failed.");
188 if((cond!=0) && (!CloseHandle(cond)))
189 map_windows_error_status_to_pthreads();
192 int wait(Mutex* mutex) {
193 return timedwait(mutex,INFINITE);
198 return map_windows_error_status_to_pthreads();
203 throw ThreadingException("Broadcast not implemented on Win32 platforms.");
206 // wait for myself to signal and this mutex or the timeout
207 int timedwait(Mutex* mutex, int delay_seconds) {
208 int rc=mutex->unlock();
212 int delay_ms=delay_seconds;
213 if(delay_seconds!=INFINITE)
215 rc=WaitForSingleObject(cond,delay_ms);
216 int rc2=mutex->lock();
225 return map_windows_error_status_to_pthreads();
231 class XMLTOOL_DLLLOCAL RWLockImpl : public RWLock {
233 // used to protect read or write to the data below
235 // event handle threads wait on when the lock they want is busy
236 // normally set to signaled all the time, if some thread can't get what
237 // they want they reset it and sleep. on releasing a lock set it to
238 // signaled if someone may have wanted what you just released
240 // number of threads holding a read lock
242 // true iff there a writer has our lock
246 RWLockImpl() : wake_waiters(0), num_readers(0), have_writer(true) {
247 with_crit_section acs(cs);
248 wake_waiters=CreateEvent(0,true,true,0);
250 if (wake_waiters==0) {
251 map_windows_error_status_to_pthreads();
252 throw ThreadingException("Event creation for shared lock failed.");
257 with_crit_section acs(cs);
258 if ((wake_waiters!=0) && (!CloseHandle(wake_waiters)))
259 map_windows_error_status_to_pthreads();
264 // wait for the lock maybe being availible
265 // we will find out for sure inside the critical section
266 if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0)
267 return map_windows_error_status_to_pthreads();
269 with_crit_section alock(cs);
270 // invariant not locked for reading and writing
271 if ((num_readers!=0) && (have_writer))
273 // if no writer we can join any existing readers
279 // have a writer, mark the synchronization object
280 // so everyone waits, when the writer unlocks it will wake us
281 if (!ResetEvent(wake_waiters))
282 return map_windows_error_status_to_pthreads();
289 // wait for the lock maybe being availible
290 // we will find out for sure inside the critical section
291 if (WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0)
292 return map_windows_error_status_to_pthreads();
294 with_crit_section bla(cs);
295 // invariant not locked for reading and writing
296 if ((num_readers!=0) && (have_writer))
299 // if no writer and no readers we can become the writer
300 if ((num_readers==0) && (!have_writer)) {
305 // lock is busy, the unlocker will wake us
306 if (!ResetEvent(wake_waiters))
307 return map_windows_error_status_to_pthreads();
313 with_crit_section mumble(cs);
314 // invariant not locked for reading and writing
315 if ((num_readers!=0) && (have_writer))
318 // error if nothing locked
319 if ((num_readers==0) && (!have_writer))
322 // if there was a writer it has to be us so unlock write lock
325 // if there where any reades there is one less now
329 // if no readers left wake up any readers/writers waiting
330 // to have a go at it
332 if (!SetEvent(wake_waiters))
333 return map_windows_error_status_to_pthreads();
338 typedef void (*destroy_hook_type)(void*);
340 class XMLTOOL_DLLLOCAL ThreadKeyImpl : public ThreadKey {
342 destroy_hook_type destroy_hook;
344 static critical_section cs;
345 static set<ThreadKeyImpl*> m_keys;
346 friend class ThreadKey;
348 ThreadKeyImpl(void (*destroy_fcn)(void*)) : destroy_hook(destroy_fcn) {
351 with_crit_section wcs(cs);
356 virtual ~ThreadKeyImpl() {
358 destroy_hook(TlsGetValue(key));
359 with_crit_section wcs(cs);
365 int setData(void* data) {
366 TlsSetValue(key, data);
370 void* getData() const {
371 return TlsGetValue(key);
374 void onDetach() const {
376 destroy_hook(TlsGetValue(key));
377 TlsSetValue(key, nullptr);
385 // public "static" creation functions
388 Thread* Thread::create(void* (*start_routine)(void*), void* arg, size_t stacksize)
390 return new ThreadImpl(start_routine, arg, stacksize);
393 void Thread::exit(void* return_val)
395 ExitThread((DWORD)return_val);
398 void Thread::sleep(int seconds)
400 Sleep(seconds * 1000);
403 Mutex * Mutex::create()
405 return new MutexImpl();
408 CondWait * CondWait::create()
410 return new CondWaitImpl();
413 RWLock * RWLock::create()
415 return new RWLockImpl();
418 critical_section ThreadKeyImpl::cs;
419 set<ThreadKeyImpl*> ThreadKeyImpl::m_keys;
421 ThreadKey* ThreadKey::create (void (*destroy_fcn)(void*))
423 return new ThreadKeyImpl(destroy_fcn);
426 void ThreadKey::onDetach()
428 with_crit_section wcs(ThreadKeyImpl::cs);
429 for_each(ThreadKeyImpl::m_keys.begin(), ThreadKeyImpl::m_keys.end(), mem_fun<void,ThreadKeyImpl>(&ThreadKeyImpl::onDetach));