You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1141 lines
26 KiB
1141 lines
26 KiB
/*****************************************************************************/ |
|
// 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 |
|
|
|
/*****************************************************************************/
|
|
|