480 lines
12 KiB
C++
480 lines
12 KiB
C++
#pragma once
|
|
|
|
#include <memory>
|
|
#include <shared_mutex>
|
|
|
|
namespace mutex {
|
|
|
|
/// Base for virtual unique guards
|
|
template <typename T>
|
|
class abstract_unique_guard {
|
|
public:
|
|
class deleter;
|
|
class impl;
|
|
using handle = std::unique_ptr<T, deleter>;
|
|
|
|
virtual ~abstract_unique_guard() { }
|
|
|
|
[[nodiscard]] virtual handle lock() = 0;
|
|
|
|
/// May return a null handle
|
|
[[nodiscard]] virtual handle try_lock() = 0;
|
|
};
|
|
|
|
template <typename T>
|
|
class abstract_unique_guard<T>::impl {
|
|
public:
|
|
using pointer = T *;
|
|
|
|
virtual ~impl() { }
|
|
|
|
virtual void operator()(pointer) = 0;
|
|
};
|
|
|
|
template <typename T>
|
|
class abstract_unique_guard<T>::deleter {
|
|
public:
|
|
using pointer = T *;
|
|
|
|
deleter(std::unique_ptr<impl> lock):
|
|
lock(std::move(lock)) { }
|
|
|
|
void operator()(pointer ptr) {
|
|
if (lock) (*lock)(ptr);
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<impl> lock;
|
|
};
|
|
|
|
/// Base for virtual shared guards
|
|
template <typename T>
|
|
class abstract_shared_guard {
|
|
public:
|
|
class deleter;
|
|
class impl;
|
|
using handle = std::unique_ptr<const T, deleter>;
|
|
|
|
virtual ~abstract_shared_guard() { }
|
|
|
|
[[nodiscard]] virtual handle lock_shared() const = 0;
|
|
|
|
/// May return a null handle
|
|
[[nodiscard]] virtual handle try_lock_shared() const = 0;
|
|
};
|
|
|
|
template <typename T>
|
|
class abstract_shared_guard<T>::impl {
|
|
public:
|
|
using pointer = const T *;
|
|
|
|
virtual ~impl() { }
|
|
|
|
virtual void operator()(pointer) = 0;
|
|
};
|
|
|
|
template <typename T>
|
|
class abstract_shared_guard<T>::deleter {
|
|
public:
|
|
using pointer = const T *;
|
|
|
|
deleter(std::unique_ptr<impl> lock):
|
|
lock(std::move(lock)) { }
|
|
|
|
void operator()(pointer ptr) {
|
|
if (lock) (*lock)(ptr);
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<impl> lock;
|
|
};
|
|
|
|
template <typename T, typename M = std::mutex>
|
|
class unique_guard {
|
|
private:
|
|
class deleter;
|
|
class impl;
|
|
|
|
public:
|
|
using handle = std::unique_ptr<T, deleter>;
|
|
using virtual_handle = typename abstract_unique_guard<T>::handle;
|
|
|
|
template <typename... _Args>
|
|
unique_guard(_Args &&... __args):
|
|
data(std::forward<_Args>(__args)...) { }
|
|
|
|
[[nodiscard]] handle lock();
|
|
[[nodiscard]] virtual_handle lock_abstract();
|
|
|
|
/// May return a null handle
|
|
[[nodiscard]] handle try_lock();
|
|
|
|
/// May return a null handle
|
|
/// Requires try_lock_for method on mutex type M
|
|
template <class Duration>
|
|
[[nodiscard]] handle try_lock_for(const Duration &duration);
|
|
|
|
private:
|
|
T data;
|
|
M mutex;
|
|
};
|
|
|
|
template <typename T, typename M>
|
|
class unique_guard<T, M>::deleter {
|
|
public:
|
|
using pointer = T *;
|
|
|
|
deleter(std::unique_lock<M> lock):
|
|
lock(std::move(lock)) { }
|
|
|
|
void operator()(pointer) {
|
|
if (lock.owns_lock())
|
|
lock.unlock();
|
|
}
|
|
|
|
private:
|
|
std::unique_lock<M> lock;
|
|
};
|
|
|
|
template <typename T, typename M>
|
|
class unique_guard<T, M>::impl final:
|
|
public abstract_unique_guard<T>::impl {
|
|
public:
|
|
using pointer = T *;
|
|
|
|
impl(std::unique_lock<M> lock):
|
|
lock(std::move(lock)) { }
|
|
|
|
void operator()(pointer) override {
|
|
if (lock.owns_lock())
|
|
lock.unlock();
|
|
}
|
|
|
|
private:
|
|
std::unique_lock<M> lock;
|
|
};
|
|
|
|
template <typename T, typename M>
|
|
auto unique_guard<T, M>::lock() -> handle {
|
|
std::unique_lock<M> lock(mutex);
|
|
return handle(&data, deleter(std::move(lock)));
|
|
}
|
|
template <typename T, typename M>
|
|
auto unique_guard<T, M>::lock_abstract() -> virtual_handle {
|
|
std::unique_lock<M> lock(mutex);
|
|
auto impl = std::make_unique<unique_guard<T, M>::impl>(std::move(lock));
|
|
return virtual_handle(&data, abstract_unique_guard<T>::deleter(std::move(impl)));
|
|
}
|
|
|
|
template <typename T, typename M>
|
|
auto unique_guard<T, M>::try_lock() -> handle {
|
|
std::unique_lock<M> lock(mutex, std::try_to_lock);
|
|
return handle(lock.owns_lock() ? &data : nullptr,
|
|
deleter(std::move(lock)));
|
|
}
|
|
|
|
template <typename T, typename M>
|
|
template <typename Duration>
|
|
auto unique_guard<T, M>::try_lock_for(const Duration &d) -> handle {
|
|
std::unique_lock<M> lock(mutex, d);
|
|
return handle(lock.owns_lock() ? &data : nullptr,
|
|
deleter(std::move(lock)));
|
|
}
|
|
|
|
template <typename T, typename M = std::shared_mutex, typename L = std::shared_lock<M>>
|
|
class shared_guard_ref;
|
|
|
|
template <typename T, typename M = std::shared_mutex, typename L = std::shared_lock<M>>
|
|
class shared_guard {
|
|
private:
|
|
class deleter;
|
|
class impl;
|
|
class shared_deleter;
|
|
class shared_impl;
|
|
|
|
public:
|
|
using handle = std::unique_ptr<T, deleter>;
|
|
using shared_handle = std::unique_ptr<const T, shared_deleter>;
|
|
using virtual_handle = typename abstract_unique_guard<T>::handle;
|
|
using virtual_shared_handle = typename abstract_shared_guard<T>::handle;
|
|
|
|
template <typename... _Args>
|
|
shared_guard(_Args &&... __args):
|
|
data(std::forward<_Args>(__args)...) { }
|
|
|
|
[[nodiscard]] handle lock();
|
|
[[nodiscard]] virtual_handle lock_abstract();
|
|
|
|
/// May return a null handle
|
|
[[nodiscard]] handle try_lock();
|
|
|
|
/// May return a null handle
|
|
/// Requires try_lock_for method on mutex type M
|
|
template <class Duration>
|
|
[[nodiscard]] handle try_lock_for(const Duration &duration);
|
|
|
|
|
|
[[nodiscard]] shared_handle lock_shared() const;
|
|
[[nodiscard]] virtual_shared_handle lock_shared_abstract() const;
|
|
|
|
/// May return a null handle
|
|
[[nodiscard]] shared_handle try_lock_shared() const;
|
|
|
|
/// May return a null handle
|
|
/// Requires try_lock_for method on mutex type M
|
|
template <class Duration>
|
|
[[nodiscard]] shared_handle try_lock_shared_for(const Duration &duration) const;
|
|
|
|
/// Shared only ref
|
|
template <class Tr>
|
|
[[nodiscard]] shared_guard_ref<Tr, M, L> make_ref() const;
|
|
|
|
private:
|
|
T data;
|
|
mutable M mutex;
|
|
};
|
|
|
|
template <typename T, typename M, typename L>
|
|
class shared_guard<T, M, L>::deleter {
|
|
public:
|
|
using pointer = T *;
|
|
|
|
deleter(std::unique_lock<M> lock):
|
|
lock(std::move(lock)) { }
|
|
|
|
void operator()(pointer) {
|
|
if (lock.owns_lock())
|
|
lock.unlock();
|
|
}
|
|
|
|
private:
|
|
std::unique_lock<M> lock;
|
|
};
|
|
|
|
template <typename T, typename M, typename L>
|
|
class shared_guard<T, M, L>::impl final:
|
|
public abstract_unique_guard<T>::impl {
|
|
public:
|
|
using pointer = T *;
|
|
|
|
impl(std::unique_lock<M> lock):
|
|
lock(std::move(lock)) { }
|
|
|
|
void operator()(pointer) override {
|
|
if (lock.owns_lock())
|
|
lock.unlock();
|
|
}
|
|
|
|
private:
|
|
std::unique_lock<M> lock;
|
|
};
|
|
|
|
template <typename T, typename M, typename L>
|
|
class shared_guard<T, M, L>::shared_deleter {
|
|
public:
|
|
using pointer = const T *;
|
|
|
|
shared_deleter(L lock):
|
|
lock(std::move(lock)) { }
|
|
|
|
void operator()(pointer) {
|
|
if (lock.owns_lock())
|
|
lock.unlock();
|
|
}
|
|
|
|
private:
|
|
L lock;
|
|
};
|
|
|
|
template <typename T, typename M, typename L>
|
|
class shared_guard<T, M, L>::shared_impl final:
|
|
public abstract_shared_guard<T>::impl {
|
|
public:
|
|
using pointer = const T *;
|
|
|
|
shared_impl(L lock):
|
|
lock(std::move(lock)) { }
|
|
|
|
void operator()(pointer) override {
|
|
if (lock.owns_lock())
|
|
lock.unlock();
|
|
}
|
|
|
|
private:
|
|
L lock;
|
|
};
|
|
|
|
template <typename T, typename M, typename L>
|
|
auto shared_guard<T, M, L>::lock() -> handle {
|
|
std::unique_lock<M> lock(mutex);
|
|
return handle(&data, deleter(std::move(lock)));
|
|
}
|
|
template <typename T, typename M, typename L>
|
|
auto shared_guard<T, M, L>::lock_abstract() -> virtual_handle {
|
|
std::unique_lock<M> lock(mutex);
|
|
auto impl = std::make_unique<shared_guard<T, M, L>::impl>(std::move(lock));
|
|
return virtual_handle(&data, abstract_unique_guard<T>::deleter(std::move(impl)));
|
|
}
|
|
|
|
template <typename T, typename M, typename L>
|
|
auto shared_guard<T, M, L>::try_lock() -> handle {
|
|
std::unique_lock<M> lock(mutex, std::try_to_lock);
|
|
return handle(lock.owns_lock() ? &data : nullptr,
|
|
deleter(std::move(lock)));
|
|
}
|
|
|
|
template <typename T, typename M, typename L>
|
|
template <typename Duration>
|
|
auto shared_guard<T, M, L>::try_lock_for(const Duration &d) -> handle {
|
|
std::unique_lock<M> lock(mutex, d);
|
|
return handle(lock.owns_lock() ? &data : nullptr,
|
|
deleter(std::move(lock)));
|
|
}
|
|
|
|
template <typename T, typename M, typename L>
|
|
auto shared_guard<T, M, L>::lock_shared() const -> shared_handle {
|
|
L lock(mutex);
|
|
return shared_handle(&data, shared_deleter(std::move(lock)));
|
|
}
|
|
template <typename T, typename M, typename L>
|
|
auto shared_guard<T, M, L>::lock_shared_abstract() const -> virtual_shared_handle {
|
|
L lock(mutex);
|
|
auto impl = std::make_unique<shared_guard<T, M, L>::shared_impl>(std::move(lock));
|
|
return virtual_shared_handle(&data, abstract_shared_guard<T>::deleter(std::move(impl)));
|
|
}
|
|
|
|
template <typename T, typename M, typename L>
|
|
auto shared_guard<T, M, L>::try_lock_shared() const -> shared_handle {
|
|
L lock(mutex, std::try_to_lock);
|
|
return shared_handle(lock.owns_lock() ? &data : nullptr,
|
|
shared_deleter(std::move(lock)));
|
|
}
|
|
|
|
template <typename T, typename M, typename L>
|
|
template <typename Duration>
|
|
auto shared_guard<T, M, L>::try_lock_shared_for(const Duration &d) const -> shared_handle {
|
|
L lock(mutex, d);
|
|
return shared_handle(lock.owns_lock() ? &data : nullptr,
|
|
shared_deleter(std::move(lock)));
|
|
}
|
|
|
|
template <typename T, typename M, typename L>
|
|
class shared_guard_ref {
|
|
private:
|
|
class shared_deleter;
|
|
class shared_impl;
|
|
|
|
public:
|
|
using shared_handle = std::unique_ptr<const T, shared_deleter>;
|
|
using virtual_shared_handle = typename abstract_shared_guard<T>::handle;
|
|
|
|
shared_guard_ref(const T* data, M* mutex):
|
|
data(data), mutex(mutex) { }
|
|
shared_guard_ref(): shared_guard_ref(nullptr, nullptr) { }
|
|
|
|
[[nodiscard]] shared_handle lock_shared() const;
|
|
[[nodiscard]] virtual_shared_handle lock_shared_abstract() const;
|
|
|
|
/// May return a null handle
|
|
[[nodiscard]] shared_handle try_lock_shared() const;
|
|
|
|
/// May return a null handle
|
|
/// Requires try_lock_for method on mutex type M
|
|
template <class Duration>
|
|
[[nodiscard]] shared_handle try_lock_shared_for(const Duration &duration) const;
|
|
|
|
private:
|
|
const T* data;
|
|
mutable M* mutex;
|
|
};
|
|
|
|
template <typename T, typename M, typename L>
|
|
template <typename Tr>
|
|
auto shared_guard<T, M, L>::make_ref() const -> shared_guard_ref<Tr, M, L> {
|
|
return shared_guard_ref<Tr, M, L>((const Tr*)&data, &mutex);
|
|
}
|
|
|
|
template <typename T, typename M, typename L>
|
|
class shared_guard_ref<T, M, L>::shared_deleter {
|
|
public:
|
|
using pointer = const T *;
|
|
|
|
shared_deleter(L lock):
|
|
lock(std::move(lock)) { }
|
|
|
|
void operator()(pointer) {
|
|
if (lock.owns_lock())
|
|
lock.unlock();
|
|
}
|
|
|
|
private:
|
|
L lock;
|
|
};
|
|
|
|
template <typename T, typename M, typename L>
|
|
class shared_guard_ref<T, M, L>::shared_impl final:
|
|
public abstract_shared_guard<T>::impl {
|
|
public:
|
|
using pointer = const T *;
|
|
|
|
shared_impl(L lock):
|
|
lock(std::move(lock)) { }
|
|
|
|
void operator()(pointer) override {
|
|
if (lock.owns_lock())
|
|
lock.unlock();
|
|
}
|
|
|
|
private:
|
|
L lock;
|
|
};
|
|
|
|
template <typename T, typename M, typename L>
|
|
auto shared_guard_ref<T, M, L>::lock_shared() const -> shared_handle {
|
|
L lock(*mutex);
|
|
return shared_handle(data, shared_deleter(std::move(lock)));
|
|
}
|
|
template <typename T, typename M, typename L>
|
|
auto shared_guard_ref<T, M, L>::lock_shared_abstract() const -> virtual_shared_handle {
|
|
L lock(*mutex);
|
|
auto impl = std::make_unique<shared_impl>(std::move(lock));
|
|
return virtual_shared_handle(data, typename abstract_shared_guard<T>::deleter(std::move(impl)));
|
|
}
|
|
|
|
template <typename T, typename M, typename L>
|
|
auto shared_guard_ref<T, M, L>::try_lock_shared() const -> shared_handle {
|
|
L lock(*mutex, std::try_to_lock);
|
|
return shared_handle(lock.owns_lock() ? data : nullptr,
|
|
shared_deleter(std::move(lock)));
|
|
}
|
|
|
|
template <typename T, typename M, typename L>
|
|
template <typename Duration>
|
|
auto shared_guard_ref<T, M, L>::try_lock_shared_for(const Duration &d) const -> shared_handle {
|
|
L lock(*mutex, d);
|
|
return shared_handle(lock.owns_lock() ? data : nullptr,
|
|
shared_deleter(std::move(lock)));
|
|
}
|
|
|
|
/// Fake lock for abstract guards
|
|
template <typename T>
|
|
class not_guard {
|
|
public:
|
|
using handle = typename abstract_unique_guard<T>::handle;
|
|
using shared_handle = typename abstract_shared_guard<T>::handle;
|
|
|
|
static handle OfUnique(T*);
|
|
static shared_handle OfShared(const T*);
|
|
};
|
|
|
|
template <typename T>
|
|
auto not_guard<T>::OfUnique(T* ptr) -> handle {
|
|
return handle(ptr, typename abstract_unique_guard<T>::deleter(nullptr));
|
|
}
|
|
|
|
template <typename T>
|
|
auto not_guard<T>::OfShared(const T* ptr) -> shared_handle {
|
|
return shared_handle(ptr, typename abstract_shared_guard<T>::deleter(nullptr));
|
|
}
|
|
|
|
}; |