1
0
Fork 0
Univerxel/src/core/utils/mutex.hpp

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));
}
};