summaryrefslogtreecommitdiffstats
path: root/src/threads/mutex.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/threads/mutex.cpp')
-rw-r--r--src/threads/mutex.cpp143
1 files changed, 130 insertions, 13 deletions
diff --git a/src/threads/mutex.cpp b/src/threads/mutex.cpp
index 7eaa329..4c8c078 100644
--- a/src/threads/mutex.cpp
+++ b/src/threads/mutex.cpp
@@ -94,7 +94,13 @@ t_mutex_guard::~t_mutex_guard() {
// t_rwmutex
///////////////////////////
-t_rwmutex::t_rwmutex()
+// Equivalent of an invalid thread ID, to be used when initializing t_rwmutex
+// or when releasing upgrade ownership; the use of pthread_self() is only to
+// provide a dummy value of the appropriate type.
+static const optional_pthread_t invalid_thread_id = { false, pthread_self() };
+
+t_rwmutex::t_rwmutex() :
+ _up_mutex_thread( invalid_thread_id )
{
int ret = pthread_rwlock_init(&_lock, nullptr);
if (ret != 0) throw string(
@@ -106,56 +112,167 @@ t_rwmutex::~t_rwmutex()
pthread_rwlock_destroy(&_lock);
}
-void t_rwmutex::lockRead()
+void t_rwmutex::getUpgradeOwnership()
+{
+ _up_mutex.lock();
+ _up_mutex_thread = { true, pthread_self() };
+}
+
+void t_rwmutex::releaseUpgradeOwnership()
+{
+ _up_mutex_thread = invalid_thread_id;
+ _up_mutex.unlock();
+}
+
+bool t_rwmutex::isUpgradeOwnershipOurs() const
+{
+ // Note that we don't need a mutex over _up_mutex_thread, being atomic
+ // is enough for our purposes. (We don't care about *who* owns
+ // _up_mutex, only about whether or not *we* own it, a fact which only
+ // our own thread can modify.)
+ optional_pthread_t lockOwner = _up_mutex_thread;
+ return lockOwner.has_value && pthread_equal(lockOwner.value, pthread_self());
+}
+
+void t_rwmutex::_lockRead()
{
int err = pthread_rwlock_rdlock(&_lock);
if (err != 0)
throw std::logic_error("Mutex lock failed");
}
-void t_rwmutex::lockWrite()
+void t_rwmutex::_lockWrite()
{
int err = pthread_rwlock_wrlock(&_lock);
if (err != 0)
throw std::logic_error("Mutex lock failed");
}
-void t_rwmutex::unlock()
+void t_rwmutex::_unlock()
{
pthread_rwlock_unlock(&_lock);
}
+void t_rwmutex::lockRead()
+{
+ if (isUpgradeOwnershipOurs()) {
+ throw std::logic_error("Acquiring read lock while holding update/write lock is not supported");
+ }
+
+ _lockRead();
+}
+
+void t_rwmutex::lockUpdate()
+{
+ if (isUpgradeOwnershipOurs()) {
+ throw std::logic_error("Acquiring update lock while holding update/write lock is not supported");
+ }
+
+ getUpgradeOwnership();
+ _lockRead();
+}
+
+void t_rwmutex::lockWrite()
+{
+ if (isUpgradeOwnershipOurs()) {
+ throw std::logic_error("Acquiring write lock while holding update/write lock is not supported");
+ }
+
+ getUpgradeOwnership();
+ _lockWrite();
+}
+
+void t_rwmutex::unlock()
+{
+ _unlock();
+
+ if (isUpgradeOwnershipOurs()) {
+ releaseUpgradeOwnership();
+ }
+}
+
+void t_rwmutex::upgradeLock()
+{
+ if (!isUpgradeOwnershipOurs()) {
+ throw std::logic_error("Attempting to upgrade a lock without upgrade ownership");
+ }
+
+ _unlock();
+ _lockWrite();
+}
+
+void t_rwmutex::downgradeLock()
+{
+ if (!isUpgradeOwnershipOurs()) {
+ throw std::logic_error("Attempting to downgrade a lock without upgrade ownership");
+ }
+
+ _unlock();
+ _lockRead();
+}
+
///////////////////////////
// t_rwmutex_guard
///////////////////////////
t_rwmutex_guard::t_rwmutex_guard(t_rwmutex& mutex) :
- _mutex(mutex)
+ _mutex(mutex),
+ _previously_owned_upgrade(_mutex.isUpgradeOwnershipOurs())
{
}
t_rwmutex_reader::t_rwmutex_reader(t_rwmutex& mutex) :
t_rwmutex_guard(mutex)
{
- // std::cout << "mtx rd lock " << (void*)&_mutex << std::endl;
- _mutex.lockRead();
+ // No-op if we are nested within a writer/future_writer guard
+ if (!_previously_owned_upgrade) {
+ _mutex.lockRead();
+ }
}
t_rwmutex_reader::~t_rwmutex_reader()
{
- // std::cout << "mtx rd unlock " << (void*)&_mutex << std::endl;
- _mutex.unlock();
+ // No-op if we are nested within a writer/future_writer guard
+ if (!_previously_owned_upgrade) {
+ _mutex.unlock();
+ }
+}
+
+t_rwmutex_future_writer::t_rwmutex_future_writer(t_rwmutex& mutex) :
+ t_rwmutex_guard(mutex)
+{
+ // No-op if we are nested within a future_writer guard
+ if (!_previously_owned_upgrade) {
+ _mutex.lockUpdate();
+ }
+}
+
+t_rwmutex_future_writer::~t_rwmutex_future_writer() {
+ // No-op if we are nested within a future_writer guard
+ if (!_previously_owned_upgrade) {
+ _mutex.unlock();
+ }
}
t_rwmutex_writer::t_rwmutex_writer(t_rwmutex& mutex) :
t_rwmutex_guard(mutex)
{
- // std::cout << "mtx wr lock " << (void*)&_mutex << std::endl;
- _mutex.lockWrite();
+ if (_previously_owned_upgrade) {
+ // Writer nested inside a future_writer: upgrade lock
+ _mutex.upgradeLock();
+ } else {
+ // Stand-alone writer guard
+ _mutex.lockWrite();
+ }
}
t_rwmutex_writer::~t_rwmutex_writer()
{
- // std::cout << "mtx wr unlock " << (void*)&_mutex << std::endl;
- _mutex.unlock();
+ if (_previously_owned_upgrade) {
+ // We were nested within a future_writer guard, so return
+ // the mutex to its previous state
+ _mutex.downgradeLock();
+ } else {
+ _mutex.unlock();
+ }
}