diff --git a/src/lib/arch/IArchMultithread.cpp b/src/lib/arch/IArchMultithread.cpp
new file mode 100644
index 00000000..e5badfa5
--- /dev/null
+++ b/src/lib/arch/IArchMultithread.cpp
@@ -0,0 +1,51 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2002 Chris Schoeneman
+ *
+ * This package is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * found in the file LICENSE that should have accompanied this file.
+ *
+ * This package is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "IArchMultithread.h"
+
+bool IArchMultithread::waitCondVar(ArchCond cond, ArchMutex mutex,
+ double timeout)
+{
+ std::unique_lock lock{*mutex, std::adopt_lock};
+
+ // we can't wait on a condition variable and also wake it up for
+ // cancellation since we don't use posix cancellation. so we
+ // must wake up periodically to check for cancellation. we
+ // can't simply go back to waiting after the check since the
+ // condition may have changed and we'll have lost the signal.
+ // so we have to return to the caller. since the caller will
+ // always check for spurious wakeups the only drawback here is
+ // performance: we're waking up a lot more than desired.
+ static const double maxCancellationLatency = 0.1;
+ if (timeout < 0.0 || timeout > maxCancellationLatency) {
+ timeout = maxCancellationLatency;
+ }
+
+ // see if we should cancel this thread
+ testCancelThread();
+
+ auto ret = cond->wait_for(lock, seconds_to_chrono(timeout));
+ lock.release();
+
+ // check for cancel again
+ testCancelThread();
+
+ if (ret == std::cv_status::no_timeout)
+ return true;
+ return false;
+}
diff --git a/src/lib/arch/IArchMultithread.h b/src/lib/arch/IArchMultithread.h
index 6a57694d..98f7e655 100644
--- a/src/lib/arch/IArchMultithread.h
+++ b/src/lib/arch/IArchMultithread.h
@@ -20,35 +20,11 @@
#include "common/IInterface.h"
#include
+#include
+#include
-/*!
-\class ArchCondImpl
-\brief Internal condition variable data.
-An architecture dependent type holding the necessary data for a
-condition variable.
-*/
-class ArchCondImpl;
-
-/*!
-\var ArchCond
-\brief Opaque condition variable type.
-An opaque type representing a condition variable.
-*/
-typedef ArchCondImpl* ArchCond;
-
-/*!
-\class ArchMutexImpl
-\brief Internal mutex data.
-An architecture dependent type holding the necessary data for a mutex.
-*/
-class ArchMutexImpl;
-
-/*!
-\var ArchMutex
-\brief Opaque mutex type.
-An opaque type representing a mutex.
-*/
-typedef ArchMutexImpl* ArchMutex;
+using ArchCond = std::condition_variable*;
+using ArchMutex = std::mutex*;
/*!
\class ArchThreadImpl
@@ -64,6 +40,11 @@ An opaque type representing a thread.
*/
typedef ArchThreadImpl* ArchThread;
+inline std::chrono::nanoseconds seconds_to_chrono(double seconds)
+{
+ return std::chrono::nanoseconds(std::uint64_t(seconds * 1000000000.0));
+}
+
//! Interface for architecture dependent multithreading
/*!
This interface defines the multithreading operations required by
@@ -98,25 +79,22 @@ public:
//
//! Create a condition variable
- /*!
- The condition variable is an opaque data type.
- */
- virtual ArchCond newCondVar() = 0;
+ ArchCond newCondVar() { return new std::condition_variable; }
//! Destroy a condition variable
- virtual void closeCondVar(ArchCond) = 0;
+ void closeCondVar(ArchCond cv) { delete cv; }
//! Signal a condition variable
/*!
Signalling a condition variable releases one waiting thread.
*/
- virtual void signalCondVar(ArchCond) = 0;
+ void signalCondVar(ArchCond cv) { cv->notify_one(); }
//! Broadcast a condition variable
/*!
Broadcasting a condition variable releases all waiting threads.
*/
- virtual void broadcastCondVar(ArchCond) = 0;
+ void broadcastCondVar(ArchCond cv) { cv->notify_all(); }
//! Wait on a condition variable
/*!
@@ -129,7 +107,7 @@ public:
(Cancellation point)
*/
- virtual bool waitCondVar(ArchCond, ArchMutex, double timeout) = 0;
+ bool waitCondVar(ArchCond cv, ArchMutex mutex, double timeout);
//
// mutex methods
@@ -137,20 +115,22 @@ public:
//! Create a recursive mutex
/*!
- Creates a recursive mutex. A thread may lock a recursive mutex
+ Creates mutex. A thread may lock a recursive mutex
when it already holds a lock on that mutex. The mutex is an
opaque data type.
+
+ WARNING: this is recursive mutex on Windows and some code likely depends on it.
*/
- virtual ArchMutex newMutex() = 0;
+ ArchMutex newMutex() { return new std::mutex; }
//! Destroy a mutex
- virtual void closeMutex(ArchMutex) = 0;
+ void closeMutex(ArchMutex mutex) { delete mutex; }
//! Lock a mutex
- virtual void lockMutex(ArchMutex) = 0;
+ void lockMutex(ArchMutex mutex) { mutex->lock(); }
//! Unlock a mutex
- virtual void unlockMutex(ArchMutex) = 0;
+ void unlockMutex(ArchMutex mutex) { mutex->unlock(); }
//
// thread methods
diff --git a/src/lib/arch/unix/ArchMultithreadPosix.cpp b/src/lib/arch/unix/ArchMultithreadPosix.cpp
index 8400f9d8..ed84a666 100644
--- a/src/lib/arch/unix/ArchMultithreadPosix.cpp
+++ b/src/lib/arch/unix/ArchMultithreadPosix.cpp
@@ -158,162 +158,6 @@ ArchMultithreadPosix::getInstance()
return s_instance;
}
-ArchCond
-ArchMultithreadPosix::newCondVar()
-{
- ArchCondImpl* cond = new ArchCondImpl;
- int status = pthread_cond_init(&cond->m_cond, NULL);
- (void)status;
- assert(status == 0);
- return cond;
-}
-
-void
-ArchMultithreadPosix::closeCondVar(ArchCond cond)
-{
- int status = pthread_cond_destroy(&cond->m_cond);
- (void)status;
- assert(status == 0);
- delete cond;
-}
-
-void
-ArchMultithreadPosix::signalCondVar(ArchCond cond)
-{
- int status = pthread_cond_signal(&cond->m_cond);
- (void)status;
- assert(status == 0);
-}
-
-void
-ArchMultithreadPosix::broadcastCondVar(ArchCond cond)
-{
- int status = pthread_cond_broadcast(&cond->m_cond);
- (void)status;
- assert(status == 0);
-}
-
-bool
-ArchMultithreadPosix::waitCondVar(ArchCond cond,
- ArchMutex mutex, double timeout)
-{
- // we can't wait on a condition variable and also wake it up for
- // cancellation since we don't use posix cancellation. so we
- // must wake up periodically to check for cancellation. we
- // can't simply go back to waiting after the check since the
- // condition may have changed and we'll have lost the signal.
- // so we have to return to the caller. since the caller will
- // always check for spurious wakeups the only drawback here is
- // performance: we're waking up a lot more than desired.
- static const double maxCancellationLatency = 0.1;
- if (timeout < 0.0 || timeout > maxCancellationLatency) {
- timeout = maxCancellationLatency;
- }
-
- // see if we should cancel this thread
- testCancelThread();
-
- // 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;
- long timeout_sec = (long)timeout;
- long timeout_nsec = (long)(1.0e+9 * (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;
- }
-
- // wait
- int status = pthread_cond_timedwait(&cond->m_cond,
- &mutex->m_mutex, &finalTime);
-
- // check for cancel again
- testCancelThread();
-
- switch (status) {
- case 0:
- // success
- return true;
-
- case ETIMEDOUT:
- return false;
-
- default:
- assert(0 && "condition variable wait error");
- return false;
- }
-}
-
-ArchMutex
-ArchMultithreadPosix::newMutex()
-{
- pthread_mutexattr_t attr;
- int status = pthread_mutexattr_init(&attr);
- assert(status == 0);
- ArchMutexImpl* mutex = new ArchMutexImpl;
- status = pthread_mutex_init(&mutex->m_mutex, &attr);
- assert(status == 0);
- return mutex;
-}
-
-void
-ArchMultithreadPosix::closeMutex(ArchMutex mutex)
-{
- int status = pthread_mutex_destroy(&mutex->m_mutex);
- (void)status;
- assert(status == 0);
- delete mutex;
-}
-
-void
-ArchMultithreadPosix::lockMutex(ArchMutex mutex)
-{
- int status = pthread_mutex_lock(&mutex->m_mutex);
-
- switch (status) {
- case 0:
- // success
- return;
-
- case EDEADLK:
- assert(0 && "lock already owned");
- break;
-
- case EAGAIN:
- assert(0 && "too many recursive locks");
- break;
-
- default:
- assert(0 && "unexpected error");
- break;
- }
-}
-
-void
-ArchMultithreadPosix::unlockMutex(ArchMutex mutex)
-{
- int status = pthread_mutex_unlock(&mutex->m_mutex);
-
- switch (status) {
- case 0:
- // success
- return;
-
- case EPERM:
- assert(0 && "thread doesn't own a lock");
- break;
-
- default:
- assert(0 && "unexpected error");
- break;
- }
-}
-
ArchThread ArchMultithreadPosix::newThread(const std::function& func)
{
// initialize signal handler. we do this here instead of the
diff --git a/src/lib/arch/unix/ArchMultithreadPosix.h b/src/lib/arch/unix/ArchMultithreadPosix.h
index 798147a0..8b68333d 100644
--- a/src/lib/arch/unix/ArchMultithreadPosix.h
+++ b/src/lib/arch/unix/ArchMultithreadPosix.h
@@ -26,16 +26,6 @@
#define ARCH_MULTITHREAD ArchMultithreadPosix
-class ArchCondImpl {
-public:
- pthread_cond_t m_cond;
-};
-
-class ArchMutexImpl {
-public:
- pthread_mutex_t m_mutex;
-};
-
//! Posix implementation of IArchMultithread
class ArchMultithreadPosix : public IArchMultithread {
public:
@@ -58,15 +48,6 @@ public:
//@}
// IArchMultithread overrides
- virtual ArchCond newCondVar();
- virtual void closeCondVar(ArchCond);
- virtual void signalCondVar(ArchCond);
- virtual void broadcastCondVar(ArchCond);
- virtual bool waitCondVar(ArchCond, ArchMutex, double timeout);
- virtual ArchMutex newMutex();
- virtual void closeMutex(ArchMutex);
- virtual void lockMutex(ArchMutex);
- virtual void unlockMutex(ArchMutex);
virtual ArchThread newThread(const std::function& func);
virtual ArchThread newCurrentThread();
virtual ArchThread copyThread(ArchThread);
diff --git a/src/lib/arch/win32/ArchMultithreadWindows.cpp b/src/lib/arch/win32/ArchMultithreadWindows.cpp
index 43a7374a..74cf5d7c 100644
--- a/src/lib/arch/win32/ArchMultithreadWindows.cpp
+++ b/src/lib/arch/win32/ArchMultithreadWindows.cpp
@@ -149,144 +149,6 @@ ArchMultithreadWindows::getInstance()
return s_instance;
}
-ArchCond
-ArchMultithreadWindows::newCondVar()
-{
- ArchCondImpl* cond = new ArchCondImpl;
- cond->m_events[ArchCondImpl::kSignal] = CreateEvent(NULL,
- FALSE, FALSE, NULL);
- cond->m_events[ArchCondImpl::kBroadcast] = CreateEvent(NULL,
- TRUE, FALSE, NULL);
- cond->m_waitCountMutex = newMutex();
- cond->m_waitCount = 0;
- return cond;
-}
-
-void
-ArchMultithreadWindows::closeCondVar(ArchCond cond)
-{
- CloseHandle(cond->m_events[ArchCondImpl::kSignal]);
- CloseHandle(cond->m_events[ArchCondImpl::kBroadcast]);
- closeMutex(cond->m_waitCountMutex);
- delete cond;
-}
-
-void
-ArchMultithreadWindows::signalCondVar(ArchCond cond)
-{
- // is anybody waiting?
- lockMutex(cond->m_waitCountMutex);
- const bool hasWaiter = (cond->m_waitCount > 0);
- unlockMutex(cond->m_waitCountMutex);
-
- // wake one thread if anybody is waiting
- if (hasWaiter) {
- SetEvent(cond->m_events[ArchCondImpl::kSignal]);
- }
-}
-
-void
-ArchMultithreadWindows::broadcastCondVar(ArchCond cond)
-{
- // is anybody waiting?
- lockMutex(cond->m_waitCountMutex);
- const bool hasWaiter = (cond->m_waitCount > 0);
- unlockMutex(cond->m_waitCountMutex);
-
- // wake all threads if anybody is waiting
- if (hasWaiter) {
- SetEvent(cond->m_events[ArchCondImpl::kBroadcast]);
- }
-}
-
-bool
-ArchMultithreadWindows::waitCondVar(ArchCond cond,
- ArchMutex mutex, double timeout)
-{
- // prepare to wait
- const DWORD winTimeout = (timeout < 0.0) ? INFINITE :
- static_cast(1000.0 * timeout);
-
- // make a list of the condition variable events and the cancel event
- // for the current thread.
- HANDLE handles[4];
- handles[0] = cond->m_events[ArchCondImpl::kSignal];
- handles[1] = cond->m_events[ArchCondImpl::kBroadcast];
- handles[2] = getCancelEventForCurrentThread();
-
- // update waiter count
- lockMutex(cond->m_waitCountMutex);
- ++cond->m_waitCount;
- unlockMutex(cond->m_waitCountMutex);
-
- // 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.
- unlockMutex(mutex);
-
- // wait for a signal or broadcast
- // TODO: this doesn't always signal when kill signals are sent
- DWORD result = WaitForMultipleObjects(3, handles, FALSE, winTimeout);
-
- // cancel takes priority
- if (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
- lockMutex(cond->m_waitCountMutex);
- --cond->m_waitCount;
- const bool last = (result == WAIT_OBJECT_0 + 1 && cond->m_waitCount == 0);
- unlockMutex(cond->m_waitCountMutex);
-
- // reset the broadcast event if we're the last waiter
- if (last) {
- ResetEvent(cond->m_events[ArchCondImpl::kBroadcast]);
- }
-
- // reacquire the mutex
- lockMutex(mutex);
-
- // cancel thread if necessary
- if (result == WAIT_OBJECT_0 + 2) {
- ARCH->testCancelThread();
- }
-
- // return success or failure
- return (result == WAIT_OBJECT_0 + 0 ||
- result == WAIT_OBJECT_0 + 1);
-}
-
-ArchMutex
-ArchMultithreadWindows::newMutex()
-{
- ArchMutexImpl* mutex = new ArchMutexImpl;
- InitializeCriticalSection(&mutex->m_mutex);
- return mutex;
-}
-
-void
-ArchMultithreadWindows::closeMutex(ArchMutex mutex)
-{
- DeleteCriticalSection(&mutex->m_mutex);
- delete mutex;
-}
-
-void
-ArchMultithreadWindows::lockMutex(ArchMutex mutex)
-{
- EnterCriticalSection(&mutex->m_mutex);
-}
-
-void
-ArchMultithreadWindows::unlockMutex(ArchMutex mutex)
-{
- LeaveCriticalSection(&mutex->m_mutex);
-}
-
ArchThread ArchMultithreadWindows::newThread(const std::function& func)
{
lockMutex(m_threadMutex);
diff --git a/src/lib/arch/win32/ArchMultithreadWindows.h b/src/lib/arch/win32/ArchMultithreadWindows.h
index 31a2b304..73b78c89 100644
--- a/src/lib/arch/win32/ArchMultithreadWindows.h
+++ b/src/lib/arch/win32/ArchMultithreadWindows.h
@@ -26,20 +26,6 @@
#define ARCH_MULTITHREAD ArchMultithreadWindows
-class ArchCondImpl {
-public:
- enum { kSignal = 0, kBroadcast };
-
- HANDLE m_events[2];
- mutable int m_waitCount;
- ArchMutex m_waitCountMutex;
-};
-
-class ArchMutexImpl {
-public:
- CRITICAL_SECTION m_mutex;
-};
-
//! Win32 implementation of IArchMultithread
class ArchMultithreadWindows : public IArchMultithread {
public:
@@ -64,15 +50,6 @@ public:
//@}
// IArchMultithread overrides
- virtual ArchCond newCondVar();
- virtual void closeCondVar(ArchCond);
- virtual void signalCondVar(ArchCond);
- virtual void broadcastCondVar(ArchCond);
- virtual bool waitCondVar(ArchCond, ArchMutex, double timeout);
- virtual ArchMutex newMutex();
- virtual void closeMutex(ArchMutex);
- virtual void lockMutex(ArchMutex);
- virtual void unlockMutex(ArchMutex);
virtual ArchThread newThread(const std::function& func);
virtual ArchThread newCurrentThread();
virtual ArchThread copyThread(ArchThread);
diff --git a/src/test/unittests/CMakeLists.txt b/src/test/unittests/CMakeLists.txt
index 8cf5e9af..59c18012 100644
--- a/src/test/unittests/CMakeLists.txt
+++ b/src/test/unittests/CMakeLists.txt
@@ -65,4 +65,4 @@ endif()
add_executable(unittests ${sources})
target_link_libraries(unittests
- arch base client server common io net platform server synlib mt ipc ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES} ${libs} ${OPENSSL_LIBS})
+ base client server common io net platform server synlib mt arch ipc ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES} ${libs} ${OPENSSL_LIBS})