Files
rust/src/rt/sync/lock_and_signal.cpp
T
Brian Anderson f76e6c39f6 rt: Fix lock_held_by_current_thread
This simplifies the check for thread ownership by removing the _locked flag
and just comparing against the thread ID of the last thread to take the lock.
If the running thread took the lock _holding_thread will be equal to
pthread_self(); if _holding_thread is some other value then the running thread
does not have the lock.

Setting a pthread_t to 0 like this is not portable but should work on every
platform we are likely to care about for the near future.
2012-02-01 16:06:58 -08:00

168 lines
4.0 KiB
C++

#include <assert.h>
#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"
// FIXME: This is not a portable way of specifying an invalid pthread_t
#define INVALID_THREAD 0
#if defined(__WIN32__)
lock_and_signal::lock_and_signal()
: _holding_thread(INVALID_THREAD)
{
// 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()
: _holding_thread(INVALID_THREAD)
{
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
}
void lock_and_signal::lock() {
#if defined(__WIN32__)
EnterCriticalSection(&_cs);
_holding_thread = GetCurrentThreadId();
#else
CHECKED(pthread_mutex_lock(&_mutex));
_holding_thread = pthread_self();
#endif
}
void lock_and_signal::unlock() {
_holding_thread = INVALID_THREAD;
#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) {
assert(lock_held_by_current_thread());
_holding_thread = INVALID_THREAD;
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
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 _holding_thread == GetCurrentThreadId();
#else
return pthread_equal(_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: