mirror of
https://github.com/debauchee/barrier.git
synced 2026-05-09 16:02:46 +08:00
Refactored some platform dependent code into a new library,
lib/arch. This should make porting easier. Will probably continue to refactor a little more, moving platform dependent event handling stuff into lib/platform.
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "CCondVar.h"
|
||||
#include "CStopwatch.h"
|
||||
#include "CArch.h"
|
||||
|
||||
//
|
||||
// CCondVarBase
|
||||
@@ -21,17 +22,14 @@
|
||||
|
||||
CCondVarBase::CCondVarBase(CMutex* mutex) :
|
||||
m_mutex(mutex)
|
||||
#if WINDOWS_LIKE
|
||||
, m_waitCountMutex()
|
||||
#endif
|
||||
{
|
||||
assert(m_mutex != NULL);
|
||||
init();
|
||||
m_cond = ARCH->newCondVar();
|
||||
}
|
||||
|
||||
CCondVarBase::~CCondVarBase()
|
||||
{
|
||||
fini();
|
||||
ARCH->closeCondVar(m_cond);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -46,67 +44,16 @@ CCondVarBase::unlock() const
|
||||
m_mutex->unlock();
|
||||
}
|
||||
|
||||
bool
|
||||
CCondVarBase::wait(double timeout) const
|
||||
{
|
||||
CStopwatch timer(true);
|
||||
return wait(timer, timeout);
|
||||
}
|
||||
|
||||
CMutex*
|
||||
CCondVarBase::getMutex() const
|
||||
{
|
||||
return m_mutex;
|
||||
}
|
||||
|
||||
#if HAVE_PTHREAD
|
||||
|
||||
#include "CThread.h"
|
||||
#include <pthread.h>
|
||||
#if TIME_WITH_SYS_TIME
|
||||
# include <sys/time.h>
|
||||
# include <time.h>
|
||||
#else
|
||||
# if HAVE_SYS_TIME_H
|
||||
# include <sys/time.h>
|
||||
# else
|
||||
# include <time.h>
|
||||
# endif
|
||||
#endif
|
||||
#include <cerrno>
|
||||
|
||||
void
|
||||
CCondVarBase::init()
|
||||
{
|
||||
pthread_cond_t* cond = new pthread_cond_t;
|
||||
int status = pthread_cond_init(cond, NULL);
|
||||
assert(status == 0);
|
||||
m_cond = reinterpret_cast<pthread_cond_t*>(cond);
|
||||
}
|
||||
|
||||
void
|
||||
CCondVarBase::fini()
|
||||
{
|
||||
pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(m_cond);
|
||||
int status = pthread_cond_destroy(cond);
|
||||
assert(status == 0);
|
||||
delete cond;
|
||||
}
|
||||
|
||||
void
|
||||
CCondVarBase::signal()
|
||||
{
|
||||
pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(m_cond);
|
||||
int status = pthread_cond_signal(cond);
|
||||
assert(status == 0);
|
||||
ARCH->signalCondVar(m_cond);
|
||||
}
|
||||
|
||||
void
|
||||
CCondVarBase::broadcast()
|
||||
{
|
||||
pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(m_cond);
|
||||
int status = pthread_cond_broadcast(cond);
|
||||
assert(status == 0);
|
||||
ARCH->broadcastCondVar(m_cond);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -118,221 +65,17 @@ CCondVarBase::wait(CStopwatch& timer, double timeout) const
|
||||
if (timeout < 0.0)
|
||||
return false;
|
||||
}
|
||||
|
||||
// get condition variable and mutex
|
||||
pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(m_cond);
|
||||
pthread_mutex_t* mutex = reinterpret_cast<pthread_mutex_t*>(m_mutex->m_mutex);
|
||||
|
||||
// get final time
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
struct timespec finalTime;
|
||||
finalTime.tv_sec = now.tv_sec;
|
||||
finalTime.tv_nsec = now.tv_usec * 1000;
|
||||
if (timeout >= 0.0) {
|
||||
const long timeout_sec = (long)timeout;
|
||||
const long timeout_nsec = (long)(1000000000.0 * (timeout - timeout_sec));
|
||||
finalTime.tv_sec += timeout_sec;
|
||||
finalTime.tv_nsec += timeout_nsec;
|
||||
if (finalTime.tv_nsec >= 1000000000) {
|
||||
finalTime.tv_nsec -= 1000000000;
|
||||
finalTime.tv_sec += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// repeat until we reach the final time
|
||||
int status;
|
||||
for (;;) {
|
||||
// compute the next timeout
|
||||
gettimeofday(&now, NULL);
|
||||
struct timespec endTime;
|
||||
endTime.tv_sec = now.tv_sec;
|
||||
endTime.tv_nsec = now.tv_usec * 1000 + 50000000;
|
||||
if (endTime.tv_nsec >= 1000000000) {
|
||||
endTime.tv_nsec -= 1000000000;
|
||||
endTime.tv_sec += 1;
|
||||
}
|
||||
|
||||
// see if we should cancel this thread
|
||||
CThread::testCancel();
|
||||
|
||||
// done if past final timeout
|
||||
if (timeout >= 0.0) {
|
||||
if (endTime.tv_sec > finalTime.tv_sec ||
|
||||
(endTime.tv_sec == finalTime.tv_sec &&
|
||||
endTime.tv_nsec >= finalTime.tv_nsec)) {
|
||||
status = ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// wait
|
||||
status = pthread_cond_timedwait(cond, mutex, &endTime);
|
||||
|
||||
// check for cancel again
|
||||
CThread::testCancel();
|
||||
|
||||
// check wait status
|
||||
if (status != ETIMEDOUT && status != EINTR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case 0:
|
||||
// success
|
||||
return true;
|
||||
|
||||
case ETIMEDOUT:
|
||||
return false;
|
||||
|
||||
default:
|
||||
assert(0 && "condition variable wait error");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAVE_PTHREAD
|
||||
|
||||
#if WINDOWS_LIKE
|
||||
|
||||
#include "CLock.h"
|
||||
#include "CThreadRep.h"
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
//
|
||||
// note -- implementation taken from
|
||||
// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
|
||||
// titled "Strategies for Implementing POSIX Condition Variables
|
||||
// on Win32." it also provides an implementation that doesn't
|
||||
// suffer from the incorrectness problem described in our
|
||||
// corresponding header but it is slower, still unfair, and
|
||||
// can cause busy waiting.
|
||||
//
|
||||
|
||||
void
|
||||
CCondVarBase::init()
|
||||
{
|
||||
// prepare events
|
||||
HANDLE* events = new HANDLE[2];
|
||||
events[kSignal] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
events[kBroadcast] = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
// prepare members
|
||||
m_cond = reinterpret_cast<void*>(events);
|
||||
m_waitCount = 0;
|
||||
}
|
||||
|
||||
void
|
||||
CCondVarBase::fini()
|
||||
{
|
||||
HANDLE* events = reinterpret_cast<HANDLE*>(m_cond);
|
||||
CloseHandle(events[kSignal]);
|
||||
CloseHandle(events[kBroadcast]);
|
||||
delete[] events;
|
||||
}
|
||||
|
||||
void
|
||||
CCondVarBase::signal()
|
||||
{
|
||||
// is anybody waiting?
|
||||
bool hasWaiter;
|
||||
{
|
||||
CLock lock(&m_waitCountMutex);
|
||||
hasWaiter = (m_waitCount > 0);
|
||||
}
|
||||
|
||||
// wake one thread if anybody is waiting
|
||||
if (hasWaiter) {
|
||||
SetEvent(reinterpret_cast<HANDLE*>(m_cond)[kSignal]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CCondVarBase::broadcast()
|
||||
{
|
||||
// is anybody waiting?
|
||||
bool hasWaiter;
|
||||
{
|
||||
CLock lock(&m_waitCountMutex);
|
||||
hasWaiter = (m_waitCount > 0);
|
||||
}
|
||||
|
||||
// wake all threads if anybody is waiting
|
||||
if (hasWaiter) {
|
||||
SetEvent(reinterpret_cast<HANDLE*>(m_cond)[kBroadcast]);
|
||||
}
|
||||
return wait(timeout);
|
||||
}
|
||||
|
||||
bool
|
||||
CCondVarBase::wait(CStopwatch& timer, double timeout) const
|
||||
CCondVarBase::wait(double timeout) const
|
||||
{
|
||||
// check timeout against timer
|
||||
if (timeout >= 0.0) {
|
||||
timeout -= timer.getTime();
|
||||
if (timeout < 0.0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// prepare to wait
|
||||
CThreadPtr currentRep = CThreadRep::getCurrentThreadRep();
|
||||
const DWORD winTimeout = (timeout < 0.0) ? INFINITE :
|
||||
static_cast<DWORD>(1000.0 * timeout);
|
||||
HANDLE* events = reinterpret_cast<HANDLE*>(m_cond);
|
||||
HANDLE handles[3];
|
||||
handles[0] = events[kSignal];
|
||||
handles[1] = events[kBroadcast];
|
||||
handles[2] = currentRep->getCancelEvent();
|
||||
const DWORD n = currentRep->isCancellable() ? 3 : 2;
|
||||
|
||||
// update waiter count
|
||||
{
|
||||
CLock lock(&m_waitCountMutex);
|
||||
++m_waitCount;
|
||||
}
|
||||
|
||||
// release mutex. this should be atomic with the wait so that it's
|
||||
// impossible for another thread to signal us between the unlock and
|
||||
// the wait, which would lead to a lost signal on broadcasts.
|
||||
// however, we're using a manual reset event for broadcasts which
|
||||
// stays set until we reset it, so we don't lose the broadcast.
|
||||
m_mutex->unlock();
|
||||
|
||||
// wait for a signal or broadcast
|
||||
DWORD result = WaitForMultipleObjects(n, handles, FALSE, winTimeout);
|
||||
|
||||
// cancel takes priority
|
||||
if (n == 3 && result != WAIT_OBJECT_0 + 2 &&
|
||||
WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) {
|
||||
result = WAIT_OBJECT_0 + 2;
|
||||
}
|
||||
|
||||
// update the waiter count and check if we're the last waiter
|
||||
bool last;
|
||||
{
|
||||
CLock lock(&m_waitCountMutex);
|
||||
--m_waitCount;
|
||||
last = (result == WAIT_OBJECT_0 + 1 && m_waitCount == 0);
|
||||
}
|
||||
|
||||
// reset the broadcast event if we're the last waiter
|
||||
if (last) {
|
||||
ResetEvent(events[kBroadcast]);
|
||||
}
|
||||
|
||||
// reacquire the mutex
|
||||
m_mutex->lock();
|
||||
|
||||
// cancel thread if necessary
|
||||
if (result == WAIT_OBJECT_0 + 2) {
|
||||
currentRep->testCancel();
|
||||
}
|
||||
|
||||
// return success or failure
|
||||
return (result == WAIT_OBJECT_0 + 0 ||
|
||||
result == WAIT_OBJECT_0 + 1);
|
||||
return ARCH->waitCondVar(m_cond, m_mutex->m_mutex, timeout);
|
||||
}
|
||||
|
||||
#endif // WINDOWS_LIKE
|
||||
CMutex*
|
||||
CCondVarBase::getMutex() const
|
||||
{
|
||||
return m_mutex;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user