diff --git a/include/ircd/ctx/ctx.h b/include/ircd/ctx/ctx.h index 5b3f6bcff..cabfc68f8 100644 --- a/include/ircd/ctx/ctx.h +++ b/include/ircd/ctx/ctx.h @@ -80,6 +80,7 @@ namespace ircd::ctx #include "queue.h" #include "mutex.h" #include "shared_mutex.h" +#include "upgrade_lock.h" #include "unlock_guard.h" #include "condition_variable.h" #include "peek.h" diff --git a/include/ircd/ctx/upgrade_lock.h b/include/ircd/ctx/upgrade_lock.h new file mode 100644 index 000000000..639970a78 --- /dev/null +++ b/include/ircd/ctx/upgrade_lock.h @@ -0,0 +1,173 @@ +// Matrix Construct +// +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2018 Jason Volk +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice is present in all copies. The +// full license for this software is available in the LICENSE file. + +#pragma once +#define HAVE_IRCD_CTX_UPGRADE_LOCK_H + +namespace ircd::ctx +{ + template struct upgrade_lock; +} + +/// Upgrade a lock from a shared reader to become a unique writer. This is a +/// two phase process. The first phase is the upgrade, which does not immediately +/// interfere with the shared readers but only one upgrade can exist at a time. +/// +/// The second phase is further upgrading the upgrade lock to a unique-lock +/// which blocks out the readers from reacquiring their shared-locks. This lock +/// then has exclusive access to the critical section. +/// +/// Upon release of the second phase the readers can reacquire their +/// shared-lock again. Upon release of the first phase another writer can +/// acquire an upgrade-lock. Sometimes it is desirable to not release the first +/// phase to ensure nothing is ABA'ed by another writer while allowing the +/// readers to continue consuming. +/// +template +struct ircd::ctx::upgrade_lock +{ + mutex *m {nullptr}; + + bool owns_lock() const; + + void lock(); + bool try_lock(); + template bool try_lock_for(duration&&); + template bool try_lock_until(time_point&&); + void unlock(); + + mutex *release() noexcept; + + template upgrade_lock(mutex &, const std::chrono::time_point &); + template upgrade_lock(mutex &, const std::chrono::duration &); + upgrade_lock(mutex &, std::try_to_lock_t); + upgrade_lock(mutex &, std::defer_lock_t); + upgrade_lock(mutex &, std::adopt_lock_t); + explicit upgrade_lock(mutex &); + upgrade_lock() = default; + upgrade_lock(upgrade_lock &&) noexcept; + upgrade_lock(const upgrade_lock &) = delete; + ~upgrade_lock() noexcept; +}; + +namespace ircd +{ + using ctx::upgrade_lock; +} + +template +ircd::ctx::upgrade_lock::upgrade_lock(mutex &m) +:m{&m} +{ + lock(); +} + +template +ircd::ctx::upgrade_lock::upgrade_lock(mutex &m, + std::defer_lock_t) +:m{&m} +{ +} + +template +ircd::ctx::upgrade_lock::upgrade_lock(mutex &m, + std::adopt_lock_t) +:m{&m} +{ + if(!m->upgrade()) + lock(); +} + +template +template +ircd::ctx::upgrade_lock::upgrade_lock(mutex &m, + const std::chrono::duration &rel) +:m{&m} +{ + try_lock_for(rel); +} + +template +template +ircd::ctx::upgrade_lock::upgrade_lock(mutex &m, + const std::chrono::time_point &abs) +:m{&m} +{ + try_lock_until(abs); +} + +template +ircd::ctx::upgrade_lock::upgrade_lock(upgrade_lock &&other) +noexcept +:m{other.release()} +{ +} + +template +ircd::ctx::upgrade_lock::~upgrade_lock() +noexcept +{ + if(owns_lock()) + unlock(); +} + +template +mutex * +ircd::ctx::upgrade_lock::release() +noexcept +{ + mutex *const m{this->m}; + this->m = nullptr; + return m; +} + +template +template +bool +ircd::ctx::upgrade_lock::try_lock_until(time_point&& tp) +{ + assert(m); + return m->try_lock_upgrade_until(std::forward(tp)); +} + +template +template +bool +ircd::ctx::upgrade_lock::try_lock_for(duration&& d) +{ + assert(m); + return m->try_lock_upgrade_for(std::forward(d)); +} + +template +bool +ircd::ctx::upgrade_lock::try_lock() +{ + assert(m); + return m->try_lock_upgrade(); +} + +template +void +ircd::ctx::upgrade_lock::lock() +{ + assert(m); + m->lock_upgrade(); +} + +template +bool +ircd::ctx::upgrade_lock::owns_lock() +const +{ + return m? m->upgrade() : false; +}