Created
September 23, 2024 14:34
-
-
Save nbougalis/aa81d365489b1b4ef7c26b852bf24879 to your computer and use it in GitHub Desktop.
Given a lock and a callable, invokes the callable under a lock in an exception-safe manner and returns its result (if any).
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
// I wrote this little helper function while working on a disaster of a | |
// codebase with little to no locking. One of my main goals was to make | |
// it thread safe. | |
// | |
// I needed an easy to grab a lock, do some work (which could sometimes | |
// throw an exception) and then release the lock, but I did not want to | |
// have lock_guard objects littered in every part of the code. | |
#include <utility> | |
#include <type_traits> | |
#include <mutex> | |
#include <functional> | |
// Type trait to check if a type T is BasicLockable | |
template <typename T, typename = void> | |
struct is_basic_lockable : std::false_type {}; | |
template <typename T> | |
struct is_basic_lockable<T, std::void_t< | |
decltype(std::declval<T&>().lock()), | |
decltype(std::declval<T&>().unlock()) | |
>> : std::true_type {}; | |
template<typename Lock, typename Callable, typename... Args> | |
decltype(auto) with_lock(Lock& lock, Callable&& callable, Args&&... args) | |
noexcept(noexcept(std::declval<Lock&>().lock()) && | |
noexcept(std::declval<Lock&>().unlock()) && | |
std::is_nothrow_invocable_v<Callable, Args...>) | |
{ | |
static_assert(is_basic_lockable<Lock>::value, | |
"Lockable type must meet the BasicLockable requirements"); | |
std::lock_guard guard(lock); | |
return std::invoke(std::forward<Callable>(callable), std::forward<Args>(args)...); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment