Skip to content

Instantly share code, notes, and snippets.

@nbougalis
Created September 23, 2024 14:34
Show Gist options
  • Save nbougalis/aa81d365489b1b4ef7c26b852bf24879 to your computer and use it in GitHub Desktop.
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).
// 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