Skip to content

Instantly share code, notes, and snippets.

@saxbophone
Last active February 14, 2024 21:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save saxbophone/0d96c1ea4b5b9320360e61c3726cef38 to your computer and use it in GitHub Desktop.
Save saxbophone/0d96c1ea4b5b9320360e61c3726cef38 to your computer and use it in GitHub Desktop.
Seizable --a thread-safe convenience wrapper around std::shared_mutex that allows read-write lock semantics via getters and setters, as well as the ability to "seize" the protected value in order to operate on it more efficiently using reference semantics
#include <mutex>
#include <shared_mutex>
#include <type_traits>
// TOOD: reïmplement all methods non-inline so the prototype is nice and readable
template <typename T>
class Seizable {
public:
// TODO: change to std::shared_timed_mutex and add additional time-based locking methods
using Mutex = std::shared_mutex;
class Seized {
public:
Seized(Mutex& mutex, T& seizee) : _lock{mutex}, _seizee{seizee} {}
Seized(const Seized&) = delete;
Seized(Seized&& other)
: _lock{std::move(other._lock)}
, _seizee{other._seizee}
{}
Seized& operator=(const Seized&) = delete;
Seized& operator=(Seized&& other) {
_lock = std::move(other._lock);
_seizee = other._seizee;
return *this;
}
/*
* warning: using this return value is only thread-safe as long as the
* Seized object that returned it is still in-scope.
*/
operator T&() { return _seizee; }
private:
std::unique_lock<Mutex> _lock;
T& _seizee;
};
Seizable() = default;
Seizable(const T& value) : _seizee{value} {}
Seizable(T&& value) : _seizee{value} {}
Seizable(const Seizable&) = delete;
Seizable(Seizable&&) = delete;
Seizable& operator=(const Seizable&) = delete;
Seizable& operator=(Seizable&&) = delete;
T get() const {
std::shared_lock read_lock(_mutex);
return _seizee;
}
operator T() const { return get(); }
void set(const T& value) { _setter(value); }
void set(T&& value) { _setter(value); }
void operator=(const T& value) { set(value); }
void operator=(T&& value) { set(value); }
/*
* warning:
* using the reference returned by Seized's implicit conversion operator is
* only thread-safe if the Seized object that returned it is in-scope
*/
Seized seize() { return Seized(_mutex, _seizee); }
template <typename Callable>
decltype(auto) seize_and_do(Callable callback) {
auto seizure = seize();
T& seized = seizure;
return callback(seized);
}
private:
template <typename Ref>
void _setter(Ref value) {
std::lock_guard write_lock(_mutex);
_seizee = value;
}
mutable Mutex _mutex;
T _seizee;
};
int do_something_with_int(int& the_int) {
the_int += 443;
the_int *= (the_int - 5);
int partway = the_int;
the_int /= 663;
++++the_int;
return partway / the_int;
}
int main() {
Seizable<int> foo = 32;
int x = foo;
int y = 456;
foo = y;
{
auto seized = foo.seize();
seized += 43;
}
foo.seize_and_do([](int& seized) {
seized *= 2 + seized / 3 + 446 / seized * 585;
});
int answer = foo.seize_and_do(do_something_with_int);
return answer;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment