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.
1142 lines
25 KiB
1142 lines
25 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
|
|
|
|
/*****************************************************************************/
|