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_PATCH 3)
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})

View File

@ -18,12 +18,12 @@ struct LogContext;
struct GlobalSharedMutex {
void lock();
bool try_lock();
void lock_async(std::function<void()> &&callback);
void lock_async(std::move_only_function<void()> &&callback);
void unlock();
void 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();
private:
@ -33,8 +33,8 @@ private:
std::condition_variable m_cv;
std::uint32_t m_sharedOwners = 0;
bool m_exclusivelyOwned = false;
std::list<std::function<void()>> m_sharedCallbacks;
std::function<void()> m_exclusiveCallback;
std::list<std::move_only_function<void()>> m_sharedCallbacks;
std::move_only_function<void()> m_exclusiveCallback;
};
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);
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);
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.
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();
@ -171,6 +173,14 @@ constexpr std::string_view lockName(std::unique_lock<GlobalSharedMutex> &)
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... 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]] UniqueLoggingLock tryLockToWrite(LogContext &log, std::string &&name);
[[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:
mutable GlobalSharedMutex m_mutex;
@ -233,6 +245,24 @@ inline UniqueLoggingLock GlobalLockable::lockToWrite(LogContext &log, std::strin
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
#endif // LIBREPOMGR_GLOBAL_LOCK_H

View File

@ -16,6 +16,7 @@
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ssl/context.hpp>
#include <functional>
#include <memory>
#include <thread>
#include <vector>
@ -178,6 +179,8 @@ struct LIBREPOMGR_EXPORT ServiceSetup : public LibPkg::Lockable {
[[nodiscard]] SharedLoggingLock acquireToRead(LogContext &log, std::string &&lockName);
[[nodiscard]] UniqueLoggingLock acquireToWrite(LogContext &log, std::string &&lockName);
[[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();
static std::string forDatabase(std::string_view dbName, std::string_view dbArch);
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));
}
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()
{
return std::make_pair(&m_locksByName, std::unique_lock(m_cleanupMutex));