| /*****************************************************************************/ |
| // Copyright 2002-2008 Adobe Systems Incorporated |
| // All Rights Reserved. |
| // |
| // NOTICE: Adobe permits you to use, modify, and distribute this file in |
| // accordance with the terms of the Adobe license agreement accompanying it. |
| /*****************************************************************************/ |
| |
| /* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_pthread.cpp#2 $ */ |
| /* $DateTime: 2012/07/31 22:04:34 $ */ |
| /* $Change: 840853 $ */ |
| /* $Author: tknoll $ */ |
| |
| #include "dng_pthread.h" |
| |
| /*****************************************************************************/ |
| |
| #if qDNGThreadSafe |
| |
| /*****************************************************************************/ |
| |
| #include "dng_assertions.h" |
| |
| /*****************************************************************************/ |
| |
| #if qWinOS |
| |
| #pragma warning(disable : 4786) |
| |
| // Nothing in this file requires Unicode, |
| // However, CreateSemaphore has a path parameter |
| // (which is NULL always in this code) and thus |
| // does not work on Win98 if UNICODE is defined. |
| // So we force it off here. |
| |
| #undef UNICODE |
| #undef _UNICODE |
| |
| #include <windows.h> |
| #include <process.h> |
| #include <errno.h> |
| #include <memory> |
| #include <new> |
| #include <map> |
| |
| #else |
| |
| #include <sys/time.h> |
| |
| #endif |
| |
| /*****************************************************************************/ |
| |
| #if qWinOS |
| |
| /*****************************************************************************/ |
| |
| namespace { |
| struct waiter { |
| struct waiter *prev; |
| struct waiter *next; |
| HANDLE semaphore; |
| bool chosen_by_signal; |
| }; |
| } |
| |
| /*****************************************************************************/ |
| |
| struct dng_pthread_mutex_impl |
| { |
| CRITICAL_SECTION lock; |
| |
| dng_pthread_mutex_impl() { ::InitializeCriticalSection(&lock); } |
| ~dng_pthread_mutex_impl() { ::DeleteCriticalSection(&lock); } |
| void Lock() { ::EnterCriticalSection(&lock); } |
| void Unlock() { ::LeaveCriticalSection(&lock); } |
| private: |
| dng_pthread_mutex_impl &operator=(const dng_pthread_mutex_impl &); |
| dng_pthread_mutex_impl(const dng_pthread_mutex_impl &) { } |
| }; |
| |
| /*****************************************************************************/ |
| |
| struct dng_pthread_cond_impl |
| { |
| dng_pthread_mutex_impl lock; // Mutual exclusion on next two variables |
| waiter *head_waiter; // List of threads waiting on this condition |
| waiter *tail_waiter; // Used to get FIFO, rather than LIFO, behavior for pthread_cond_signal |
| unsigned int broadcast_generation; // Used as sort of a separator on broadcasts |
| // saves having to walk the waiters list setting |
| // each one's "chosen_by_signal" flag while the condition is locked |
| |
| dng_pthread_cond_impl() : head_waiter(NULL), tail_waiter(NULL), broadcast_generation(0) { } |
| ~dng_pthread_cond_impl() { } ; |
| |
| // Non copyable |
| private: |
| dng_pthread_cond_impl &operator=(const dng_pthread_cond_impl &); |
| dng_pthread_cond_impl(const dng_pthread_cond_impl &) { } |
| |
| }; |
| |
| /*****************************************************************************/ |
| |
| namespace |
| { |
| |
| struct ScopedLock |
| { |
| dng_pthread_mutex_impl *mutex; |
| |
| ScopedLock(dng_pthread_mutex_impl *arg) : mutex(arg) |
| { |
| mutex->Lock(); |
| } |
| ScopedLock(dng_pthread_mutex_impl &arg) : mutex(&arg) |
| { |
| mutex->Lock(); |
| } |
| ~ScopedLock() |
| { |
| mutex->Unlock(); |
| } |
| private: |
| ScopedLock &operator=(const ScopedLock &); |
| ScopedLock(const ScopedLock &) { } |
| }; |
| |
| dng_pthread_mutex_impl validationLock; |
| |
| void ValidateMutex(dng_pthread_mutex_t *mutex) |
| { |
| if (*mutex != DNG_PTHREAD_MUTEX_INITIALIZER) |
| return; |
| |
| ScopedLock lock(validationLock); |
| |
| if (*mutex == DNG_PTHREAD_MUTEX_INITIALIZER) |
| dng_pthread_mutex_init(mutex, NULL); |
| } |
| |
| void ValidateCond(dng_pthread_cond_t *cond) |
| { |
| if (*cond != DNG_PTHREAD_COND_INITIALIZER) |
| return; |
| |
| ScopedLock lock(validationLock); |
| |
| if (*cond == DNG_PTHREAD_COND_INITIALIZER) |
| dng_pthread_cond_init(cond, NULL); |
| } |
| |
| DWORD thread_wait_sema_TLS_index; |
| bool thread_wait_sema_inited = false; |
| dng_pthread_once_t once_thread_TLS = DNG_PTHREAD_ONCE_INIT; |
| |
| void init_thread_TLS() |
| { |
| thread_wait_sema_TLS_index = ::TlsAlloc(); |
| thread_wait_sema_inited = true; |
| } |
| |
| void finalize_thread_TLS() |
| { |
| if (thread_wait_sema_inited) |
| { |
| ::TlsFree(thread_wait_sema_TLS_index); |
| thread_wait_sema_inited = false; |
| } |
| } |
| |
| dng_pthread_mutex_impl primaryHandleMapLock; |
| |
| typedef std::map<DWORD, std::pair<HANDLE, void **> > ThreadMapType; |
| |
| // A map to make sure handles are freed and to allow returning a pointer sized result |
| // even on 64-bit Windows. |
| ThreadMapType primaryHandleMap; |
| |
| HANDLE GetThreadSemaphore() |
| { |
| dng_pthread_once(&once_thread_TLS, init_thread_TLS); |
| |
| HANDLE semaphore = ::TlsGetValue(thread_wait_sema_TLS_index); |
| if (semaphore == NULL) |
| { |
| semaphore = ::CreateSemaphore(NULL, 0, 1, NULL); |
| ::TlsSetValue(thread_wait_sema_TLS_index, semaphore); |
| } |
| |
| return semaphore; |
| } |
| |
| void FreeThreadSemaphore() |
| { |
| if (thread_wait_sema_inited) |
| { |
| HANDLE semaphore = (HANDLE)::TlsGetValue(thread_wait_sema_TLS_index); |
| |
| if (semaphore != NULL) |
| { |
| ::TlsSetValue(thread_wait_sema_TLS_index, NULL); |
| ::CloseHandle(semaphore); |
| } |
| } |
| } |
| |
| struct trampoline_args |
| { |
| void *(*func)(void *); |
| void *arg; |
| }; |
| |
| // This trampoline takes care of the return type being different |
| // between pthreads thread funcs and Windows C lib thread funcs |
| unsigned __stdcall trampoline(void *arg_arg) |
| { |
| trampoline_args *args_ptr = (trampoline_args *)arg_arg; |
| trampoline_args args = *args_ptr; |
| |
| delete args_ptr; |
| |
| GetThreadSemaphore(); |
| |
| void *result = args.func(args.arg); |
| |
| { |
| ScopedLock lockMap(primaryHandleMapLock); |
| |
| ThreadMapType::iterator iter = primaryHandleMap.find(pthread_self()); |
| if (iter != primaryHandleMap.end()) |
| *iter->second.second = result; |
| } |
| |
| FreeThreadSemaphore(); |
| |
| return S_OK; |
| } |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| extern "C" { |
| |
| /*****************************************************************************/ |
| |
| struct dng_pthread_attr_impl |
| { |
| size_t stacksize; |
| }; |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_attr_init(pthread_attr_t *attr) |
| { |
| dng_pthread_attr_impl *newAttrs; |
| |
| newAttrs = new (std::nothrow) dng_pthread_attr_impl; |
| if (newAttrs == NULL) |
| return -1; // ENOMEM; |
| |
| newAttrs->stacksize = 0; |
| |
| *attr = newAttrs; |
| |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_attr_destroy(pthread_attr_t *attr) |
| { |
| if (*attr == NULL) |
| return -1; // EINVAL |
| |
| delete *attr; |
| |
| *attr = NULL; |
| |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_attr_setstacksize(dng_pthread_attr_t *attr, size_t stacksize) |
| { |
| if (attr == NULL || (*attr) == NULL) |
| return -1; // EINVAL |
| |
| (*attr)->stacksize = stacksize; |
| |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_attr_getstacksize(const dng_pthread_attr_t *attr, size_t *stacksize) |
| { |
| if (attr == NULL || (*attr) == NULL || stacksize == NULL) |
| return -1; // EINVAL |
| |
| *stacksize = (*attr)->stacksize; |
| |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_create(dng_pthread_t *thread, const pthread_attr_t *attrs, void * (*func)(void *), void *arg) |
| { |
| try |
| { |
| uintptr_t result; |
| unsigned threadID; |
| std::auto_ptr<trampoline_args> args(new (std::nothrow) trampoline_args); |
| std::auto_ptr<void *> resultHolder(new (std::nothrow) (void *)); |
| |
| if (args.get() == NULL || resultHolder.get () == NULL) |
| return -1; // ENOMEM |
| |
| args->func = func; |
| args->arg = arg; |
| |
| size_t stacksize = 0; |
| |
| if (attrs != NULL) |
| dng_pthread_attr_getstacksize (attrs, &stacksize); |
| |
| { |
| ScopedLock lockMap(primaryHandleMapLock); |
| |
| result = _beginthreadex(NULL, (unsigned)stacksize, trampoline, args.get(), 0, &threadID); |
| if (result == NULL) |
| return -1; // ENOMEM |
| args.release(); |
| |
| std::pair<DWORD, std::pair<HANDLE, void **> > newMapEntry(threadID, |
| std::pair<HANDLE, void **>((HANDLE)result, resultHolder.get ())); |
| std::pair<ThreadMapType::iterator, bool> insertion = primaryHandleMap.insert(newMapEntry); |
| |
| // If there is a handle open on the thread, its ID should not be reused so assert that an insertion was made. |
| DNG_ASSERT(insertion.second, "pthread emulation logic error"); |
| } |
| |
| |
| resultHolder.release (); |
| |
| *thread = (dng_pthread_t)threadID; |
| return 0; |
| } |
| catch (const std::bad_alloc &) |
| { |
| return -1; |
| } |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_detach(dng_pthread_t thread) |
| { |
| HANDLE primaryHandle; |
| void **resultHolder = NULL; |
| |
| { |
| ScopedLock lockMap(primaryHandleMapLock); |
| |
| ThreadMapType::iterator iter = primaryHandleMap.find(thread); |
| if (iter == primaryHandleMap.end()) |
| return -1; |
| |
| primaryHandle = iter->second.first; |
| |
| // A join is waiting on the thread. |
| if (primaryHandle == NULL) |
| return -1; |
| |
| resultHolder = iter->second.second; |
| |
| primaryHandleMap.erase(iter); |
| } |
| |
| delete resultHolder; |
| |
| if (!::CloseHandle(primaryHandle)) |
| return -1; |
| |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_join(dng_pthread_t thread, void **result) |
| { |
| bool found = false; |
| HANDLE primaryHandle = NULL; |
| void **resultHolder = NULL; |
| |
| ThreadMapType::iterator iter; |
| |
| { |
| ScopedLock lockMap(primaryHandleMapLock); |
| |
| iter = primaryHandleMap.find(thread); |
| found = iter != primaryHandleMap.end(); |
| if (found) |
| { |
| primaryHandle = iter->second.first; |
| resultHolder = iter->second.second; |
| |
| // Set HANDLE to NULL to force any later join or detach to fail. |
| iter->second.first = NULL; |
| } |
| } |
| |
| // This case can happens when joining a thread not created with pthread_create, |
| // which is a bad idea, but it gets mapped to doing the join, but always returns NULL. |
| if (!found) |
| primaryHandle = ::OpenThread(SYNCHRONIZE|THREAD_QUERY_INFORMATION, FALSE, thread); |
| |
| if (primaryHandle == NULL) |
| return -1; |
| |
| DWORD err; |
| if (::WaitForSingleObject(primaryHandle, INFINITE) != WAIT_OBJECT_0) |
| { |
| err = ::GetLastError(); |
| return -1; |
| } |
| |
| { |
| ScopedLock lockMap(primaryHandleMapLock); |
| |
| if (iter != primaryHandleMap.end()) |
| primaryHandleMap.erase(iter); |
| } |
| |
| ::CloseHandle(primaryHandle); |
| if (result != NULL && resultHolder != NULL) |
| *result = *resultHolder; |
| |
| delete resultHolder; |
| |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| dng_pthread_t dng_pthread_self() |
| { |
| return (dng_pthread_t)::GetCurrentThreadId(); |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_pthread_exit(void *result) |
| { |
| { |
| ScopedLock lockMap(primaryHandleMapLock); |
| |
| ThreadMapType::iterator iter = primaryHandleMap.find(pthread_self()); |
| if (iter != primaryHandleMap.end()) |
| *iter->second.second = result; |
| } |
| |
| FreeThreadSemaphore(); |
| |
| _endthreadex(S_OK); |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_mutex_init(dng_pthread_mutex_t *mutex, void * /* attrs */) |
| { |
| dng_pthread_mutex_t result; |
| try { |
| result = new(dng_pthread_mutex_impl); |
| } catch (const std::bad_alloc &) |
| { |
| return -1; |
| } |
| |
| if (result == NULL) |
| return -1; |
| *mutex = result; |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_mutex_destroy(dng_pthread_mutex_t *mutex) |
| { |
| if (*mutex == DNG_PTHREAD_MUTEX_INITIALIZER) |
| { |
| *mutex = NULL; |
| return 0; |
| } |
| |
| delete *mutex; |
| *mutex = NULL; |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_cond_init(dng_pthread_cond_t *cond, void * /* attrs */) |
| { |
| dng_pthread_cond_t result; |
| try { |
| result = new(dng_pthread_cond_impl); |
| } catch (const std::bad_alloc &) |
| { |
| return -1; |
| } |
| |
| if (result == NULL) |
| return -1; |
| *cond = result; |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_cond_destroy(dng_pthread_cond_t *cond) |
| { |
| if (*cond == DNG_PTHREAD_COND_INITIALIZER) |
| { |
| *cond = NULL; |
| return 0; |
| } |
| |
| delete *cond; |
| *cond = NULL; |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_mutexattr_init(dng_pthread_mutexattr_t* mutexattr) |
| { |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_mutexattr_settype(dng_pthread_mutexattr_t* mutexattr, int type) |
| { |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_mutex_lock(dng_pthread_mutex_t *mutex) |
| { |
| ValidateMutex(mutex); |
| (*mutex)->Lock(); |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_mutex_unlock(dng_pthread_mutex_t *mutex) |
| { |
| ValidateMutex(mutex); |
| (*mutex)->Unlock(); |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| static int cond_wait_internal(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex, int timeout_milliseconds) |
| { |
| dng_pthread_cond_impl &real_cond = **cond; |
| dng_pthread_mutex_impl &real_mutex = **mutex; |
| |
| waiter this_wait; |
| HANDLE semaphore = GetThreadSemaphore(); |
| int my_generation; // The broadcast generation this waiter is in |
| |
| { |
| this_wait.next = NULL; |
| this_wait.semaphore = semaphore; |
| this_wait.chosen_by_signal = 0; |
| |
| ScopedLock lock1(real_cond.lock); |
| |
| // Add this waiter to the end of the list. |
| this_wait.prev = real_cond.tail_waiter; |
| if (real_cond.tail_waiter != NULL) |
| real_cond.tail_waiter->next = &this_wait; |
| real_cond.tail_waiter = &this_wait; |
| |
| // If the list was empty, set the head of the list to this waiter. |
| if (real_cond.head_waiter == NULL) |
| real_cond.head_waiter = &this_wait; |
| |
| // Note which broadcast generation this waiter belongs to. |
| my_generation = real_cond.broadcast_generation; |
| } |
| |
| real_mutex.Unlock(); |
| |
| DWORD result = ::WaitForSingleObject(semaphore, timeout_milliseconds); |
| |
| if (result == WAIT_TIMEOUT) |
| { |
| // If the wait timed out, this thread is likely still on the waiters list |
| // of the condition. However, there is a race in that the thread may have been |
| // signaled or broadcast between when WaitForSingleObject decided |
| // we had timed out and this code running. |
| |
| bool mustConsumeSemaphore = false; |
| { |
| ScopedLock lock2(real_cond.lock); |
| |
| bool chosen_by_signal = this_wait.chosen_by_signal; |
| bool chosen_by_broadcast = my_generation != real_cond.broadcast_generation; |
| |
| if (chosen_by_signal || chosen_by_broadcast) |
| mustConsumeSemaphore = true; |
| else |
| { |
| // Still on waiters list. Remove this waiter from list. |
| if (this_wait.next != NULL) |
| this_wait.next->prev = this_wait.prev; |
| else |
| real_cond.tail_waiter = this_wait.prev; |
| |
| if (this_wait.prev != NULL) |
| this_wait.prev->next = this_wait.next; |
| else |
| real_cond.head_waiter = this_wait.next; |
| } |
| } |
| |
| if (mustConsumeSemaphore) |
| { |
| ::WaitForSingleObject(semaphore, INFINITE); |
| result = WAIT_OBJECT_0; |
| } |
| } |
| else |
| DNG_ASSERT (result == WAIT_OBJECT_0, "pthread emulation logic error"); |
| |
| // reacquire the mutex |
| real_mutex.Lock(); |
| |
| return (result == WAIT_TIMEOUT) ? DNG_ETIMEDOUT : 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_cond_wait(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex) |
| { |
| ValidateCond(cond); |
| |
| return cond_wait_internal(cond, mutex, INFINITE); |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_cond_timedwait(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex, struct dng_timespec *latest_time) |
| { |
| ValidateCond(cond); |
| |
| struct dng_timespec sys_timespec; |
| |
| dng_pthread_now (&sys_timespec); |
| |
| __int64 sys_time = (__int64)sys_timespec.tv_sec * 1000000000 + sys_timespec.tv_nsec; |
| __int64 lock_time = (__int64)latest_time->tv_sec * 1000000000 + latest_time->tv_nsec; |
| |
| int wait_millisecs = (int)((lock_time - sys_time + 500000) / 1000000); |
| |
| if (wait_millisecs < 0) |
| wait_millisecs = 0; |
| |
| return cond_wait_internal(cond, mutex, wait_millisecs); |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_cond_signal(dng_pthread_cond_t *cond) |
| { |
| ValidateCond(cond); |
| |
| waiter *first; |
| dng_pthread_cond_impl &real_cond = **cond; |
| |
| { |
| ScopedLock lock(real_cond.lock); |
| |
| first = real_cond.head_waiter; |
| if (first != NULL) |
| { |
| if (first->next != NULL) |
| first->next->prev = NULL; |
| else |
| real_cond.tail_waiter = NULL; // Or first->prev, which is always NULL in this case |
| |
| first->chosen_by_signal = true; |
| |
| real_cond.head_waiter = first->next; |
| } |
| } |
| |
| if (first != NULL) |
| ::ReleaseSemaphore(first->semaphore, 1, NULL); |
| |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_cond_broadcast(dng_pthread_cond_t *cond) |
| { |
| ValidateCond(cond); |
| |
| waiter *first; |
| dng_pthread_cond_impl &real_cond = **cond; |
| |
| { |
| ScopedLock lock(real_cond.lock); |
| |
| first = real_cond.head_waiter; |
| real_cond.head_waiter = NULL; |
| real_cond.tail_waiter = NULL; |
| |
| real_cond.broadcast_generation++; |
| } |
| |
| while (first != NULL) |
| { |
| waiter *next = first->next; |
| ::ReleaseSemaphore(first->semaphore, 1, NULL); |
| first = next; |
| } |
| |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_once(dng_pthread_once_t *once, void (*init_func)()) |
| { |
| if (once == NULL || init_func == NULL) |
| return EINVAL; |
| |
| if (once->inited) |
| return 0; |
| |
| if (::InterlockedIncrement(&once->semaphore) == 0) |
| { |
| init_func(); |
| once->inited = 1; |
| } |
| else |
| { |
| while (!once->inited) |
| Sleep(0); |
| } |
| |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_key_create(dng_pthread_key_t * key, void (*destructor) (void *)) |
| { |
| if (destructor != NULL) |
| return -1; |
| |
| DWORD result = ::TlsAlloc(); |
| if (result == TLS_OUT_OF_INDEXES) |
| return -1; |
| *key = (unsigned long)result; |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_key_delete(dng_pthread_key_t key) |
| { |
| if (::TlsFree((DWORD)key)) |
| return 0; |
| return -1; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_setspecific(dng_pthread_key_t key, const void *value) |
| { |
| if (::TlsSetValue((DWORD)key, const_cast<void *>(value))) |
| return 0; |
| return -1; |
| } |
| |
| /*****************************************************************************/ |
| |
| void *dng_pthread_getspecific(dng_pthread_key_t key) |
| { |
| return ::TlsGetValue((DWORD)key); |
| } |
| |
| /*****************************************************************************/ |
| |
| namespace { |
| struct rw_waiter { |
| struct rw_waiter *prev; |
| struct rw_waiter *next; |
| HANDLE semaphore; |
| bool is_writer; |
| }; |
| } |
| |
| struct dng_pthread_rwlock_impl |
| { |
| dng_pthread_mutex_impl mutex; |
| |
| rw_waiter *head_waiter; |
| rw_waiter *tail_waiter; |
| |
| unsigned long readers_active; |
| unsigned long writers_waiting; |
| bool writer_active; |
| |
| dng_pthread_cond_impl read_wait; |
| dng_pthread_cond_impl write_wait; |
| |
| dng_pthread_rwlock_impl () |
| : mutex () |
| , head_waiter (NULL) |
| , tail_waiter (NULL) |
| , readers_active (0) |
| , writers_waiting (0) |
| , read_wait () |
| , write_wait () |
| , writer_active (false) |
| { |
| } |
| |
| ~dng_pthread_rwlock_impl () |
| { |
| } |
| |
| void WakeHeadWaiter () |
| { |
| HANDLE semaphore = head_waiter->semaphore; |
| |
| head_waiter = head_waiter->next; |
| if (head_waiter == NULL) |
| tail_waiter = NULL; |
| |
| ::ReleaseSemaphore(semaphore, 1, NULL); |
| } |
| |
| }; |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_rwlock_init(dng_pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attrs) |
| { |
| dng_pthread_rwlock_impl *newRWLock; |
| |
| newRWLock = new (std::nothrow) dng_pthread_rwlock_impl; |
| if (newRWLock == NULL) |
| return -1; // ENOMEM; |
| |
| *rwlock = newRWLock; |
| |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_rwlock_destroy(dng_pthread_rwlock_t *rwlock) |
| { |
| dng_pthread_rwlock_impl &real_rwlock = **rwlock; |
| |
| { |
| ScopedLock lock (real_rwlock.mutex); |
| |
| if (real_rwlock.head_waiter != NULL || |
| real_rwlock.readers_active != 0 || |
| real_rwlock.writers_waiting != 0 || |
| real_rwlock.writer_active) |
| return -1; // EBUSY |
| } |
| |
| delete *rwlock; |
| *rwlock = NULL; |
| return 0; |
| } |
| |
| /*****************************************************************************/ |
| |
| #define CHECK_RWLOCK_STATE(real_rwlock) \ |
| DNG_ASSERT (!real_rwlock.writer_active || real_rwlock.readers_active == 0, "dng_pthread_rwlock_t logic error") |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_rwlock_rdlock(dng_pthread_rwlock_t *rwlock) |
| { |
| dng_pthread_rwlock_impl &real_rwlock = **rwlock; |
| |
| struct rw_waiter this_wait; |
| bool doWait = false;; |
| int result = 0; |
| HANDLE semaphore=NULL; |
| |
| { |
| |
| ScopedLock lock (real_rwlock.mutex); |
| |
| CHECK_RWLOCK_STATE (real_rwlock); |
| |
| if (real_rwlock.writers_waiting > 0 || real_rwlock.writer_active) |
| { |
| semaphore = GetThreadSemaphore(); |
| |
| this_wait.next = NULL; |
| this_wait.semaphore = semaphore; |
| this_wait.is_writer = false; |
| |
| // Add this waiter to the end of the list. |
| this_wait.prev = real_rwlock.tail_waiter; |
| if (real_rwlock.tail_waiter != NULL) |
| real_rwlock.tail_waiter->next = &this_wait; |
| real_rwlock.tail_waiter = &this_wait; |
| |
| // If the list was empty, set the head of the list to this waiter. |
| if (real_rwlock.head_waiter == NULL) |
| real_rwlock.head_waiter = &this_wait; |
| |
| doWait = true; |
| } |
| else |
| real_rwlock.readers_active++; |
| } |
| |
| if (result == 0 && doWait) |
| result = (WaitForSingleObject(semaphore, INFINITE) == WAIT_OBJECT_0) ? 0 : -1; |
| |
| return result; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_rwlock_tryrdlock(dng_pthread_rwlock_t *rwlock) |
| { |
| dng_pthread_rwlock_impl &real_rwlock = **rwlock; |
| |
| ScopedLock lock (real_rwlock.mutex); |
| |
| CHECK_RWLOCK_STATE (real_rwlock); |
| |
| if (real_rwlock.writers_waiting == 0 && !real_rwlock.writer_active) |
| { |
| real_rwlock.readers_active++; |
| return 0; |
| } |
| |
| return -1; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_rwlock_trywrlock(dng_pthread_rwlock_t *rwlock) |
| { |
| dng_pthread_rwlock_impl &real_rwlock = **rwlock; |
| |
| ScopedLock lock (real_rwlock.mutex); |
| |
| CHECK_RWLOCK_STATE (real_rwlock); |
| |
| if (real_rwlock.readers_active == 0 && |
| real_rwlock.writers_waiting == 0 && |
| !real_rwlock.writer_active) |
| { |
| real_rwlock.writer_active = true; |
| return 0; |
| } |
| |
| return -1; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_rwlock_unlock(dng_pthread_rwlock_t *rwlock) |
| { |
| dng_pthread_rwlock_impl &real_rwlock = **rwlock; |
| |
| int result = 0; |
| |
| ScopedLock lock (real_rwlock.mutex); |
| |
| CHECK_RWLOCK_STATE (real_rwlock); |
| |
| if (real_rwlock.readers_active > 0) |
| --real_rwlock.readers_active; |
| else |
| real_rwlock.writer_active = false; |
| |
| while (real_rwlock.head_waiter != NULL) |
| { |
| if (real_rwlock.head_waiter->is_writer) |
| { |
| if (real_rwlock.readers_active == 0) |
| { |
| real_rwlock.writers_waiting--; |
| real_rwlock.writer_active = true; |
| real_rwlock.WakeHeadWaiter (); |
| } |
| |
| break; |
| } |
| else |
| { |
| ++real_rwlock.readers_active; |
| real_rwlock.WakeHeadWaiter (); |
| } |
| } |
| |
| return result; |
| } |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_rwlock_wrlock(dng_pthread_rwlock_t *rwlock) |
| { |
| dng_pthread_rwlock_impl &real_rwlock = **rwlock; |
| |
| int result = 0; |
| struct rw_waiter this_wait; |
| HANDLE semaphore=NULL; |
| bool doWait = false; |
| |
| { |
| ScopedLock lock (real_rwlock.mutex); |
| |
| CHECK_RWLOCK_STATE (real_rwlock); |
| |
| if (real_rwlock.readers_active || |
| real_rwlock.writers_waiting || |
| real_rwlock.writer_active) |
| { |
| semaphore = GetThreadSemaphore(); |
| |
| this_wait.next = NULL; |
| this_wait.semaphore = semaphore; |
| this_wait.is_writer = true; |
| |
| // Add this waiter to the end of the list. |
| this_wait.prev = real_rwlock.tail_waiter; |
| if (real_rwlock.tail_waiter != NULL) |
| real_rwlock.tail_waiter->next = &this_wait; |
| real_rwlock.tail_waiter = &this_wait; |
| |
| // If the list was empty, set the head of the list to this waiter. |
| if (real_rwlock.head_waiter == NULL) |
| real_rwlock.head_waiter = &this_wait; |
| |
| real_rwlock.writers_waiting++; |
| |
| doWait = true; |
| } |
| else |
| real_rwlock.writer_active = true; |
| } |
| |
| if (result == 0 && doWait) |
| result = (WaitForSingleObject(semaphore, INFINITE) == WAIT_OBJECT_0) ? 0 : -1; |
| |
| return result; |
| } |
| |
| /*****************************************************************************/ |
| |
| void dng_pthread_disassociate() |
| { |
| FreeThreadSemaphore(); |
| } |
| |
| void dng_pthread_terminate() |
| { |
| finalize_thread_TLS(); |
| } |
| |
| /*****************************************************************************/ |
| |
| } // extern "C" |
| |
| /*****************************************************************************/ |
| |
| #endif |
| |
| /*****************************************************************************/ |
| |
| int dng_pthread_now (struct timespec *now) |
| { |
| |
| if (now == NULL) |
| return -1; // EINVAL |
| |
| #if qWinOS |
| |
| FILETIME ft; |
| ::GetSystemTimeAsFileTime(&ft); |
| |
| __int64 sys_time = ((__int64)ft.dwHighDateTime << 32) + ft.dwLowDateTime; |
| |
| #define SecsFrom1601To1970 11644473600 |
| |
| sys_time -= SecsFrom1601To1970 * 10000000LL; |
| |
| sys_time *= 100; // Convert from 100ns to 1ns units |
| |
| now->tv_sec = (long)(sys_time / 1000000000); |
| now->tv_nsec = (long)(sys_time % 1000000000); |
| |
| #else |
| |
| struct timeval tv; |
| |
| if (gettimeofday (&tv, NULL) != 0) |
| return errno; |
| |
| now->tv_sec = tv.tv_sec; |
| now->tv_nsec = tv.tv_usec * 1000; |
| |
| #endif |
| |
| return 0; |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| #endif // qDNGThreadSafe |
| |
| /*****************************************************************************/ |