Implement helper functions for async global locks

This commit is contained in:
Martchus 2022-12-22 23:23:42 +01:00
parent e74f80a089
commit 8dfa948e03
3 changed files with 57 additions and 8 deletions

View File

@ -11,7 +11,9 @@ set(META_VERSION_MAJOR 0)
set(META_VERSION_MINOR 0) set(META_VERSION_MINOR 0)
set(META_VERSION_PATCH 3) set(META_VERSION_PATCH 3)
set(META_VERSION_EXACT_SONAME ON) set(META_VERSION_EXACT_SONAME ON)
set(META_CXX_STANDARD 20) #set(META_CXX_STANDARD 20)
set(META_CXX_STANDARD "any")
list(APPEND META_PUBLIC_COMPILE_OPTIONS "-std=c++2b")
project(${META_PROJECT_NAME}) project(${META_PROJECT_NAME})

View File

@ -18,12 +18,12 @@ struct LogContext;
struct GlobalSharedMutex { struct GlobalSharedMutex {
void lock(); void lock();
bool try_lock(); bool try_lock();
void lock_async(std::function<void()> &&callback); void lock_async(std::move_only_function<void()> &&callback);
void unlock(); void unlock();
void lock_shared(); void lock_shared();
bool try_lock_shared(); bool try_lock_shared();
void lock_shared_async(std::function<void()> &&callback); void lock_shared_async(std::move_only_function<void()> &&callback);
void unlock_shared(); void unlock_shared();
private: private:
@ -33,8 +33,8 @@ private:
std::condition_variable m_cv; std::condition_variable m_cv;
std::uint32_t m_sharedOwners = 0; std::uint32_t m_sharedOwners = 0;
bool m_exclusivelyOwned = false; bool m_exclusivelyOwned = false;
std::list<std::function<void()>> m_sharedCallbacks; std::list<std::move_only_function<void()>> m_sharedCallbacks;
std::function<void()> m_exclusiveCallback; std::move_only_function<void()> m_exclusiveCallback;
}; };
inline void GlobalSharedMutex::lock() inline void GlobalSharedMutex::lock()
@ -56,7 +56,7 @@ inline bool GlobalSharedMutex::try_lock()
} }
} }
inline void GlobalSharedMutex::lock_async(std::function<void()> &&callback) inline void GlobalSharedMutex::lock_async(std::move_only_function<void()> &&callback)
{ {
auto lock = std::unique_lock<std::mutex>(m_mutex); auto lock = std::unique_lock<std::mutex>(m_mutex);
if (m_sharedOwners || m_exclusivelyOwned) { if (m_sharedOwners || m_exclusivelyOwned) {
@ -94,7 +94,7 @@ inline bool GlobalSharedMutex::try_lock_shared()
} }
} }
inline void GlobalSharedMutex::lock_shared_async(std::function<void()> &&callback) inline void GlobalSharedMutex::lock_shared_async(std::move_only_function<void()> &&callback)
{ {
auto lock = std::unique_lock<std::mutex>(m_mutex); auto lock = std::unique_lock<std::mutex>(m_mutex);
if (m_exclusivelyOwned) { if (m_exclusivelyOwned) {
@ -143,7 +143,9 @@ inline void GlobalSharedMutex::notify(std::unique_lock<std::mutex> &lock)
/// \brief A wrapper around a standard lock which logs acquisition/release. /// \brief A wrapper around a standard lock which logs acquisition/release.
template <typename UnderlyingLockType> struct LoggingLock { template <typename UnderlyingLockType> struct LoggingLock {
template <typename... Args> LoggingLock(LogContext &log, std::string &&name, Args &&...args); using LockType = UnderlyingLockType;
explicit LoggingLock(LogContext &log, std::string &&name);
template <typename... Args> explicit LoggingLock(LogContext &log, std::string &&name, Args &&...args);
LoggingLock(LoggingLock &&) = default; LoggingLock(LoggingLock &&) = default;
~LoggingLock(); ~LoggingLock();
@ -171,6 +173,14 @@ constexpr std::string_view lockName(std::unique_lock<GlobalSharedMutex> &)
return "exclusive"; return "exclusive";
} }
template <typename UnderlyingLockType>
inline LoggingLock<UnderlyingLockType>::LoggingLock(LogContext &log, std::string &&name)
: m_log(log)
, m_name(std::move(name))
{
m_log("Acquiring ", lockName(m_lock), " lock \"", m_name, "\"\n");
}
template <typename UnderlyingLockType> template <typename UnderlyingLockType>
template <typename... Args> template <typename... Args>
inline LoggingLock<UnderlyingLockType>::LoggingLock(LogContext &log, std::string &&name, Args &&...args) inline LoggingLock<UnderlyingLockType>::LoggingLock(LogContext &log, std::string &&name, Args &&...args)
@ -202,6 +212,8 @@ struct GlobalLockable {
[[nodiscard]] SharedLoggingLock tryLockToRead(LogContext &log, std::string &&name) const; [[nodiscard]] SharedLoggingLock tryLockToRead(LogContext &log, std::string &&name) const;
[[nodiscard]] UniqueLoggingLock tryLockToWrite(LogContext &log, std::string &&name); [[nodiscard]] UniqueLoggingLock tryLockToWrite(LogContext &log, std::string &&name);
[[nodiscard]] UniqueLoggingLock lockToWrite(LogContext &log, std::string &&name, SharedLoggingLock &readLock); [[nodiscard]] UniqueLoggingLock lockToWrite(LogContext &log, std::string &&name, SharedLoggingLock &readLock);
void lockToRead(LogContext &log, std::string &&name, std::move_only_function<void(SharedLoggingLock &&lock)> &&callback) const;
void lockToWrite(LogContext &log, std::string &&name, std::move_only_function<void(UniqueLoggingLock &&lock)> &&callback);
private: private:
mutable GlobalSharedMutex m_mutex; mutable GlobalSharedMutex m_mutex;
@ -233,6 +245,24 @@ inline UniqueLoggingLock GlobalLockable::lockToWrite(LogContext &log, std::strin
return UniqueLoggingLock(log, std::move(name), m_mutex); return UniqueLoggingLock(log, std::move(name), m_mutex);
} }
inline void LibRepoMgr::GlobalLockable::lockToRead(
LogContext &log, std::string &&name, std::move_only_function<void(SharedLoggingLock &&)> &&callback) const
{
m_mutex.lock_shared_async([this, lock = SharedLoggingLock(log, std::move(name)), cb = std::move(callback)]() mutable {
lock.lock() = SharedLoggingLock::LockType(m_mutex, std::adopt_lock);
cb(std::move(lock));
});
}
inline void LibRepoMgr::GlobalLockable::lockToWrite(
LogContext &log, std::string &&name, std::move_only_function<void(UniqueLoggingLock &&)> &&callback)
{
m_mutex.lock_async([this, lock = UniqueLoggingLock(log, std::move(name)), cb = std::move(callback)]() mutable {
lock.lock() = UniqueLoggingLock::LockType(m_mutex, std::adopt_lock);
cb(std::move(lock));
});
}
} // namespace LibRepoMgr } // namespace LibRepoMgr
#endif // LIBREPOMGR_GLOBAL_LOCK_H #endif // LIBREPOMGR_GLOBAL_LOCK_H

View File

@ -16,6 +16,7 @@
#include <boost/asio/ip/address.hpp> #include <boost/asio/ip/address.hpp>
#include <boost/asio/ssl/context.hpp> #include <boost/asio/ssl/context.hpp>
#include <functional>
#include <memory> #include <memory>
#include <thread> #include <thread>
#include <vector> #include <vector>
@ -178,6 +179,8 @@ struct LIBREPOMGR_EXPORT ServiceSetup : public LibPkg::Lockable {
[[nodiscard]] SharedLoggingLock acquireToRead(LogContext &log, std::string &&lockName); [[nodiscard]] SharedLoggingLock acquireToRead(LogContext &log, std::string &&lockName);
[[nodiscard]] UniqueLoggingLock acquireToWrite(LogContext &log, std::string &&lockName); [[nodiscard]] UniqueLoggingLock acquireToWrite(LogContext &log, std::string &&lockName);
[[nodiscard]] std::pair<LockTable *, std::unique_lock<std::shared_mutex>> acquireLockTable(); [[nodiscard]] std::pair<LockTable *, std::unique_lock<std::shared_mutex>> acquireLockTable();
void acquireToRead(LogContext &log, std::string &&name, std::move_only_function<void(SharedLoggingLock &&lock)> &&callback);
void acquireToWrite(LogContext &log, std::string &&name, std::move_only_function<void(UniqueLoggingLock &&lock)> &&callback);
void clear(); void clear();
static std::string forDatabase(std::string_view dbName, std::string_view dbArch); static std::string forDatabase(std::string_view dbName, std::string_view dbArch);
static std::string forDatabase(const LibPkg::Database &db); static std::string forDatabase(const LibPkg::Database &db);
@ -217,6 +220,20 @@ inline UniqueLoggingLock ServiceSetup::Locks::acquireToWrite(LogContext &log, st
return namedLock(lockName).lockToWrite(log, std::move(lockName)); return namedLock(lockName).lockToWrite(log, std::move(lockName));
} }
inline void ServiceSetup::Locks::acquireToRead(
LogContext &log, std::string &&lockName, std::move_only_function<void(SharedLoggingLock &&lock)> &&callback)
{
const auto locktableLock = std::shared_lock(m_cleanupMutex);
namedLock(lockName).lockToRead(log, std::move(lockName), std::move(callback));
}
inline void ServiceSetup::Locks::acquireToWrite(
LogContext &log, std::string &&lockName, std::move_only_function<void(UniqueLoggingLock &&lock)> &&callback)
{
const auto locktableLock = std::shared_lock(m_cleanupMutex);
namedLock(lockName).lockToWrite(log, std::move(lockName), std::move(callback));
}
inline std::pair<ServiceSetup::Locks::LockTable *, std::unique_lock<std::shared_mutex>> ServiceSetup::Locks::acquireLockTable() inline std::pair<ServiceSetup::Locks::LockTable *, std::unique_lock<std::shared_mutex>> ServiceSetup::Locks::acquireLockTable()
{ {
return std::make_pair(&m_locksByName, std::unique_lock(m_cleanupMutex)); return std::make_pair(&m_locksByName, std::unique_lock(m_cleanupMutex));