165 lines
3.9 KiB
C++
165 lines
3.9 KiB
C++
#include "../globals.h"
|
|
|
|
/*
|
|
* A "lock-and-signal" pair. These are necessarily coupled on pthreads
|
|
* systems, and artificially coupled (by this file) on win32. Put
|
|
* together here to minimize ifdefs elsewhere; you must use them as
|
|
* if you're using a pthreads cvar+mutex pair.
|
|
*/
|
|
|
|
#include "lock_and_signal.h"
|
|
|
|
#if defined(__WIN32__)
|
|
lock_and_signal::lock_and_signal()
|
|
: alive(true)
|
|
{
|
|
// FIXME: In order to match the behavior of pthread_cond_broadcast on
|
|
// Windows, we create manual reset events. This however breaks the
|
|
// behavior of pthread_cond_signal, fixing this is quite involved:
|
|
// refer to: http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
|
|
|
|
_event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
InitializeCriticalSection(&_cs);
|
|
}
|
|
|
|
#else
|
|
lock_and_signal::lock_and_signal()
|
|
: _locked(false), alive(true)
|
|
{
|
|
CHECKED(pthread_cond_init(&_cond, NULL));
|
|
CHECKED(pthread_mutex_init(&_mutex, NULL));
|
|
}
|
|
#endif
|
|
|
|
lock_and_signal::~lock_and_signal() {
|
|
#if defined(__WIN32__)
|
|
CloseHandle(_event);
|
|
#else
|
|
CHECKED(pthread_cond_destroy(&_cond));
|
|
CHECKED(pthread_mutex_destroy(&_mutex));
|
|
#endif
|
|
alive = false;
|
|
}
|
|
|
|
void lock_and_signal::lock() {
|
|
#if defined(__WIN32__)
|
|
EnterCriticalSection(&_cs);
|
|
_holding_thread = GetCurrentThreadId();
|
|
#else
|
|
CHECKED(pthread_mutex_lock(&_mutex));
|
|
_holding_thread = pthread_self();
|
|
#endif
|
|
_locked = true;
|
|
}
|
|
|
|
void lock_and_signal::unlock() {
|
|
_locked = false;
|
|
#if defined(__WIN32__)
|
|
LeaveCriticalSection(&_cs);
|
|
#else
|
|
CHECKED(pthread_mutex_unlock(&_mutex));
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Wait indefinitely until condition is signaled.
|
|
*/
|
|
void lock_and_signal::wait() {
|
|
timed_wait(0);
|
|
}
|
|
|
|
bool lock_and_signal::timed_wait(size_t timeout_in_ms) {
|
|
_locked = false;
|
|
bool rv = true;
|
|
#if defined(__WIN32__)
|
|
LeaveCriticalSection(&_cs);
|
|
DWORD timeout = timeout_in_ms == 0 ? INFINITE : timeout_in_ms;
|
|
rv = WaitForSingleObject(_event, timeout) != WAIT_TIMEOUT;
|
|
EnterCriticalSection(&_cs);
|
|
_holding_thread = GetCurrentThreadId();
|
|
#else
|
|
if (timeout_in_ms == 0) {
|
|
CHECKED(pthread_cond_wait(&_cond, &_mutex));
|
|
} else {
|
|
timeval time_val;
|
|
gettimeofday(&time_val, NULL);
|
|
timespec time_spec;
|
|
time_spec.tv_sec = time_val.tv_sec + 0;
|
|
time_spec.tv_nsec = time_val.tv_usec * 1000 + timeout_in_ms * 1000000;
|
|
if(time_spec.tv_nsec >= 1000000000) {
|
|
time_spec.tv_sec++;
|
|
time_spec.tv_nsec -= 1000000000;
|
|
}
|
|
int cond_wait_status
|
|
= pthread_cond_timedwait(&_cond, &_mutex, &time_spec);
|
|
switch(cond_wait_status) {
|
|
case 0:
|
|
// successfully grabbed the lock.
|
|
break;
|
|
case ETIMEDOUT:
|
|
// Oops, we timed out.
|
|
rv = false;
|
|
break;
|
|
default:
|
|
// Error
|
|
CHECKED(cond_wait_status);
|
|
}
|
|
}
|
|
_holding_thread = pthread_self();
|
|
#endif
|
|
_locked = true;
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Signal condition, and resume the waiting thread.
|
|
*/
|
|
void lock_and_signal::signal() {
|
|
#if defined(__WIN32__)
|
|
SetEvent(_event);
|
|
#else
|
|
CHECKED(pthread_cond_signal(&_cond));
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Signal condition, and resume all waiting threads.
|
|
*/
|
|
void lock_and_signal::signal_all() {
|
|
#if defined(__WIN32__)
|
|
SetEvent(_event);
|
|
#else
|
|
CHECKED(pthread_cond_broadcast(&_cond));
|
|
#endif
|
|
}
|
|
|
|
bool lock_and_signal::lock_held_by_current_thread()
|
|
{
|
|
#if defined(__WIN32__)
|
|
return _locked && _holding_thread == GetCurrentThreadId();
|
|
#else
|
|
return _locked && _holding_thread == pthread_self();
|
|
#endif
|
|
}
|
|
|
|
scoped_lock::scoped_lock(lock_and_signal &lock)
|
|
: lock(lock)
|
|
{
|
|
lock.lock();
|
|
}
|
|
|
|
scoped_lock::~scoped_lock()
|
|
{
|
|
lock.unlock();
|
|
}
|
|
|
|
//
|
|
// Local Variables:
|
|
// mode: C++
|
|
// fill-column: 78;
|
|
// indent-tabs-mode: nil
|
|
// c-basic-offset: 4
|
|
// buffer-file-coding-system: utf-8-unix
|
|
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
|
// End:
|