-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
530 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#include <dice/template-library/mutex.hpp> | ||
|
||
#include <cassert> | ||
#include <thread> | ||
|
||
int main() { | ||
dice::template_library::mutex<int> mut{0}; | ||
|
||
std::thread thrd{[&]() { | ||
*mut.lock() = 5; | ||
}}; | ||
|
||
thrd.join(); | ||
assert(*mut.lock() == 5); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#include <dice/template-library/shared_mutex.hpp> | ||
|
||
#include <cassert> | ||
#include <thread> | ||
|
||
int main() { | ||
dice::template_library::shared_mutex<int> mut{0}; | ||
|
||
std::thread thrd1{[&]() { | ||
*mut.lock() = 5; | ||
}}; | ||
|
||
thrd1.join(); | ||
|
||
std::thread thrd2{[&]() { | ||
assert(*mut.lock_shared() == 5); | ||
}}; | ||
|
||
thrd2.join(); | ||
|
||
assert(*mut.lock() == 5); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
#ifndef DICE_TEMPLATELIBRARY_MUTEX_HPP | ||
#define DICE_TEMPLATELIBRARY_MUTEX_HPP | ||
|
||
#include <mutex> | ||
#include <optional> | ||
#include <type_traits> | ||
#include <utility> | ||
|
||
namespace dice::template_library { | ||
|
||
template<typename T, typename Mutex = std::mutex> | ||
struct mutex; | ||
|
||
/** | ||
* An RAII guard for a value behind a mutex. | ||
* When this mutex_guard is dropped the lock is automatically released. | ||
* | ||
* @note to use this correctly it is important to understand that unlike rust, C++ cannot | ||
* enforce that you do not use pointers given out by this type beyond its lifetime. | ||
* You may not store pointers or references given out via this wrapper beyond its lifetime, otherwise behaviour is undefined. | ||
* | ||
* @tparam T the value type protected by the mutex | ||
* @tparam Mutex the mutex type | ||
*/ | ||
template<typename T, typename Mutex = std::mutex> | ||
struct mutex_guard { | ||
using value_type = T; | ||
using mutex_type = Mutex; | ||
|
||
private: | ||
friend struct mutex<T, Mutex>; | ||
|
||
value_type *value_ptr_; | ||
std::unique_lock<mutex_type> lock_; | ||
|
||
mutex_guard(std::unique_lock<mutex_type> &&lock, T &value) noexcept | ||
: value_ptr_{&value}, | ||
lock_{std::move(lock)} { | ||
} | ||
|
||
public: | ||
mutex_guard() = delete; | ||
mutex_guard(mutex_guard const &other) noexcept = delete; | ||
mutex_guard &operator=(mutex_guard const &other) noexcept = delete; | ||
mutex_guard(mutex_guard &&other) noexcept = default; | ||
mutex_guard &operator=(mutex_guard &&other) noexcept = default; | ||
~mutex_guard() = default; | ||
|
||
value_type *operator->() const noexcept { | ||
return value_ptr_; | ||
} | ||
|
||
value_type &operator*() const noexcept { | ||
return *value_ptr_; | ||
} | ||
|
||
friend void swap(mutex_guard const &lhs, mutex_guard const &rhs) noexcept { | ||
using std::swap; | ||
swap(lhs.value_ptr_, rhs.value_ptr_); | ||
swap(lhs.lock_, rhs.lock_); | ||
} | ||
}; | ||
|
||
/** | ||
* A rust-like mutex type (https://doc.rust-lang.org/std/sync/struct.Mutex.html) that holds its data instead of living next to it. | ||
* | ||
* @note Because this is C++ it cannot be fully safe, like the rust version is, for more details see doc comment on mutex_guard. | ||
* @note This type is non-movable and non-copyable, if you need to do either of these things use `std::unique_ptr<mutex<T>>` or `std::shared_ptr<mutex<T>>` | ||
* | ||
* @tparam T value type stored | ||
* @tparam Mutex the mutex type | ||
*/ | ||
template<typename T, typename Mutex> | ||
struct mutex { | ||
using value_type = T; | ||
using mutex_type = Mutex; | ||
|
||
private: | ||
value_type value_; | ||
mutex_type mutex_; | ||
|
||
public: | ||
constexpr mutex() noexcept(std::is_nothrow_default_constructible_v<value_type>) = default; | ||
constexpr ~mutex() noexcept(std::is_nothrow_destructible_v<value_type>) = default; | ||
|
||
mutex(mutex const &other) = delete; | ||
mutex(mutex &&other) = delete; | ||
mutex &operator=(mutex const &other) = delete; | ||
mutex &operator=(mutex &&other) = delete; | ||
|
||
explicit constexpr mutex(value_type const &value) noexcept(std::is_nothrow_copy_constructible_v<value_type>) | ||
: value_{value} { | ||
} | ||
|
||
explicit constexpr mutex(value_type &&value) noexcept(std::is_nothrow_move_constructible_v<value_type>) | ||
: value_{std::move(value)} { | ||
} | ||
|
||
template<typename ...Args> | ||
explicit constexpr mutex(std::in_place_t, Args &&...args) noexcept(std::is_nothrow_constructible_v<value_type, decltype(std::forward<Args>(args))...>) | ||
: value_{std::forward<Args>(args)...} { | ||
} | ||
|
||
/** | ||
* Lock the mutex and return a guard that will keep it locked until it goes out of scope and | ||
* allows access to the inner value. | ||
* | ||
* @return mutex guard for the inner value | ||
* @throws std::system_error in case the underlying mutex implementation throws it | ||
*/ | ||
[[nodiscard]] mutex_guard<value_type> lock() { | ||
return mutex_guard<value_type>{std::unique_lock<mutex_type>{mutex_}, value_}; | ||
} | ||
|
||
/** | ||
* Attempt to lock the mutex and return a guard that will keep it locked until it goes out of scope and | ||
* allows access to the inner value. | ||
* | ||
* @return nullopt in case the mutex could not be locked, otherwise a mutex guard for the inner value | ||
* @throws std::system_error in case the underlying mutex implementation throws it | ||
*/ | ||
[[nodiscard]] std::optional<mutex_guard<value_type>> try_lock() { | ||
std::unique_lock<mutex_type> lock{mutex_, std::try_to_lock}; | ||
if (!lock.owns_lock()) { | ||
return std::nullopt; | ||
} | ||
|
||
return mutex_guard<value_type>{std::move(lock), value_}; | ||
} | ||
}; | ||
|
||
} // namespace dice::template_library | ||
|
||
#endif // DICE_TEMPLATELIBRARY_MUTEX_HPP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
#ifndef DICE_TEMPLATELIBRARY_SHAREDMUTEX_HPP | ||
#define DICE_TEMPLATELIBRARY_SHAREDMUTEX_HPP | ||
|
||
#include <mutex> | ||
#include <shared_mutex> | ||
#include <optional> | ||
#include <type_traits> | ||
#include <utility> | ||
|
||
namespace dice::template_library { | ||
|
||
template<typename T, typename Mutex = std::shared_mutex> | ||
struct shared_mutex; | ||
|
||
/** | ||
* An RAII guard for a value behind a shared_mutex. | ||
* When this shared_mutex_guard is dropped the lock is automatically released. | ||
* | ||
* @note to use this correctly it is important to understand that unlike rust, C++ cannot | ||
* enforce that you do not use pointers given out by this type beyond its lifetime. | ||
* You may not store pointers or references given out via this wrapper beyond its lifetime, otherwise behaviour is undefined. | ||
* | ||
* @tparam T the value type protected by the mutex | ||
* @tparam Mutex the mutex type | ||
*/ | ||
template<typename T, typename Mutex = std::shared_mutex> | ||
struct shared_mutex_guard { | ||
using value_type = T; | ||
using mutex_type = Mutex; | ||
using lock_type = std::conditional_t<std::is_const_v<T>, std::shared_lock<mutex_type>, std::unique_lock<mutex_type>>; | ||
|
||
private: | ||
friend struct shared_mutex<std::remove_const_t<T>, Mutex>; | ||
|
||
value_type *value_ptr_; | ||
lock_type lock_; | ||
|
||
shared_mutex_guard(lock_type &&lock, T &value) noexcept | ||
: value_ptr_{&value}, | ||
lock_{std::move(lock)} { | ||
} | ||
|
||
public: | ||
shared_mutex_guard() = delete; | ||
shared_mutex_guard(shared_mutex_guard const &other) noexcept = delete; | ||
shared_mutex_guard &operator=(shared_mutex_guard const &other) noexcept = delete; | ||
shared_mutex_guard(shared_mutex_guard &&other) noexcept = default; | ||
shared_mutex_guard &operator=(shared_mutex_guard &&other) noexcept = default; | ||
~shared_mutex_guard() = default; | ||
|
||
value_type *operator->() const noexcept { | ||
return value_ptr_; | ||
} | ||
|
||
value_type &operator*() const noexcept { | ||
return *value_ptr_; | ||
} | ||
|
||
friend void swap(shared_mutex_guard const &lhs, shared_mutex_guard const &rhs) noexcept { | ||
using std::swap; | ||
swap(lhs.value_ptr_, rhs.value_ptr_); | ||
swap(lhs.lock_, rhs.lock_); | ||
} | ||
}; | ||
|
||
/** | ||
* A rust-like shared_mutex type (https://doc.rust-lang.org/std/sync/struct.RwLock.html) that holds its data instead of living next to it. | ||
* | ||
* @note Because this is C++ it cannot be fully safe, like the rust version is, for more details see doc comment on mutex_guard. | ||
* @note This type is non-movable and non-copyable, if you need to do either of these things use `std::unique_ptr<shared_mutex<T>>` or `std::shared_ptr<shared_mutex<T>>` | ||
* | ||
* @tparam T value type stored | ||
* @tparam Mutex the mutex type | ||
*/ | ||
template<typename T, typename Mutex> | ||
struct shared_mutex { | ||
using value_type = T; | ||
using mutex_type = Mutex; | ||
|
||
private: | ||
value_type value_; | ||
mutex_type mutex_; | ||
|
||
public: | ||
constexpr shared_mutex() noexcept(std::is_nothrow_default_constructible_v<value_type>) = default; | ||
constexpr ~shared_mutex() noexcept(std::is_nothrow_destructible_v<value_type>) = default; | ||
|
||
shared_mutex(shared_mutex const &other) = delete; | ||
shared_mutex(shared_mutex &&other) = delete; | ||
shared_mutex &operator=(shared_mutex const &other) = delete; | ||
shared_mutex &operator=(shared_mutex &&other) = delete; | ||
|
||
explicit constexpr shared_mutex(value_type const &value) noexcept(std::is_nothrow_copy_constructible_v<value_type>) | ||
: value_{value} { | ||
} | ||
|
||
explicit constexpr shared_mutex(value_type &&value) noexcept(std::is_nothrow_move_constructible_v<value_type>) | ||
: value_{std::move(value)} { | ||
} | ||
|
||
template<typename ...Args> | ||
explicit constexpr shared_mutex(std::in_place_t, Args &&...args) noexcept(std::is_nothrow_constructible_v<value_type, decltype(std::forward<Args>(args))...>) | ||
: value_{std::forward<Args>(args)...} { | ||
} | ||
|
||
/** | ||
* Lock the mutex and return a guard that will keep it locked until it goes out of scope and | ||
* allows access to the inner value. | ||
* | ||
* @return mutex guard for the inner value | ||
* @throws std::system_error in case the underlying mutex implementation throws it | ||
*/ | ||
[[nodiscard]] shared_mutex_guard<value_type> lock() { | ||
return shared_mutex_guard<value_type>{std::unique_lock<mutex_type>{mutex_}, value_}; | ||
} | ||
|
||
/** | ||
* Attempt to lock the mutex and return a guard that will keep it locked until it goes out of scope and | ||
* allows access to the inner value. | ||
* | ||
* @return nullopt in case the mutex could not be locked, otherwise a mutex guard for the inner value | ||
* @throws std::system_error in case the underlying mutex implementation throws it | ||
*/ | ||
[[nodiscard]] std::optional<shared_mutex_guard<value_type>> try_lock() { | ||
std::unique_lock<mutex_type> lock{mutex_, std::try_to_lock}; | ||
if (!lock.owns_lock()) { | ||
return std::nullopt; | ||
} | ||
|
||
return shared_mutex_guard<value_type>{std::move(lock), value_}; | ||
} | ||
|
||
/** | ||
* Lock the mutex for shared ownership and return a guard that will keep it locked until it goes out of scope and | ||
* allows access to the inner value. | ||
* | ||
* @return mutex guard for the inner value | ||
* @throws std::system_error in case the underlying mutex implementation throws it | ||
*/ | ||
[[nodiscard]] shared_mutex_guard<value_type const> lock_shared() { | ||
return shared_mutex_guard<value_type const>{std::shared_lock<mutex_type>{mutex_}, value_}; | ||
} | ||
|
||
/** | ||
* Attempt to lock the mutex for shared ownership and return a guard that will keep it locked until it goes out of scope and | ||
* allows access to the inner value. | ||
* | ||
* @return nullopt in case the mutex could not be locked, otherwise a mutex guard for the inner value | ||
* @throws std::system_error in case the underlying mutex implementation throws it | ||
*/ | ||
[[nodiscard]] std::optional<shared_mutex_guard<value_type const>> try_lock_shared() { | ||
std::shared_lock<mutex_type> lock{mutex_, std::try_to_lock}; | ||
if (!lock.owns_lock()) { | ||
return std::nullopt; | ||
} | ||
|
||
return shared_mutex_guard<value_type const>{std::move(lock), value_}; | ||
} | ||
}; | ||
|
||
} // namespace dice::template_library | ||
|
||
#endif // DICE_TEMPLATELIBRARY_SHAREDMUTEX_HPP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.