Commit cc049da2 authored by Giuseppe Ottaviano's avatar Giuseppe Ottaviano Committed by Facebook GitHub Bot

Allow creation of dismissed ScopeGuards

Summary:
This diff enables a symmetrical pattern to `makeGuard()`/`dismiss()`: `makeDismissedGuard()` will create a guard that will not execute unless `rehired()`'d.

This is for use cases where the guard's scope is wider than where the condition on whether it should execute is evaluated. For example
```
State state;
auto guard = makeDismissedGuard([&] { cleanup(state); });
if (modifyStateInAWayThatRequiresCleanup(state)) {
  guard.rehire();
}
// Use state.
```

Reviewed By: yfeldblum

Differential Revision: D27931274

fbshipit-source-id: fbf7048dc991b73553cd6429f5e73345e4104d49
parent a99615fe
......@@ -33,12 +33,15 @@ namespace folly {
namespace detail {
struct ScopeGuardDismissed {};
class ScopeGuardImplBase {
public:
void dismiss() noexcept { dismissed_ = true; }
void rehire() noexcept { dismissed_ = false; }
protected:
ScopeGuardImplBase() noexcept : dismissed_(false) {}
ScopeGuardImplBase(bool dismissed = false) noexcept : dismissed_(dismissed) {}
static void warnAboutToCrash() noexcept;
static ScopeGuardImplBase makeEmptyScopeGuard() noexcept {
......@@ -77,6 +80,11 @@ class ScopeGuardImpl : public ScopeGuardImplBase {
makeFailsafe(
std::is_nothrow_move_constructible<FunctionType>{}, &fn)) {}
explicit ScopeGuardImpl(FunctionType&& fn, ScopeGuardDismissed) noexcept(
std::is_nothrow_move_constructible<FunctionType>::value)
// No need for failsafe in this case, as the guard is dismissed.
: ScopeGuardImplBase{true}, function_(std::forward<FunctionType>(fn)) {}
ScopeGuardImpl(ScopeGuardImpl&& other) noexcept(
std::is_nothrow_move_constructible<FunctionType>::value)
: function_(std::move_if_noexcept(other.function_)) {
......@@ -163,6 +171,15 @@ using ScopeGuardImplDecay = ScopeGuardImpl<typename std::decay<F>::type, INE>;
* guard.dismiss();
* }
*
* It is also possible to create a guard in dismissed state with
* makeDismissedGuard(), and later rehire it with the rehire()
* method.
*
* makeDismissedGuard() is not just syntactic sugar for creating a guard and
* immediately dismissing it, but it has a subtle behavior difference if
* move-construction of the passed function can throw: if it does, the function
* will be called by makeGuard(), but not by makeDismissedGuard().
*
* Examine ScopeGuardTest.cpp for some more sample usage.
*
* Stolen from:
......@@ -179,6 +196,15 @@ FOLLY_NODISCARD detail::ScopeGuardImplDecay<F, true> makeGuard(F&& f) noexcept(
return detail::ScopeGuardImplDecay<F, true>(static_cast<F&&>(f));
}
template <typename F>
FOLLY_NODISCARD detail::ScopeGuardImplDecay<F, true>
makeDismissedGuard(F&& f) noexcept(
noexcept(detail::ScopeGuardImplDecay<F, true>(
static_cast<F&&>(f), detail::ScopeGuardDismissed{}))) {
return detail::ScopeGuardImplDecay<F, true>(
static_cast<F&&>(f), detail::ScopeGuardDismissed{});
}
namespace detail {
#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS) || \
......
......@@ -25,6 +25,7 @@
#include <folly/portability/GTest.h>
using folly::makeDismissedGuard;
using folly::makeGuard;
using std::vector;
......@@ -173,6 +174,22 @@ TEST(ScopeGuard, UndoAction) {
testUndoAction(false);
}
TEST(ScopeGuard, MakeDismissedGuard) {
auto test = [](bool shouldFire) {
bool fired = false;
{
auto guard = makeDismissedGuard([&] { fired = true; });
if (shouldFire) {
guard.rehire();
}
}
EXPECT_EQ(shouldFire, fired);
};
test(true);
test(false);
}
/**
* Sometimes in a try catch block we want to execute a piece of code
* regardless if an exception happened or not. For example, you want
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment