1/**
2 * Copyright 2016 Facebook
3 * @author Tudor Bosman (tudorb@fb.com)
4 */
5
6#pragma once
7
8#include <cstddef>
9#include <functional>
10#include <new>
11#include <type_traits>
12#include <utility>
13
14namespace caffe2 {
15
16// Copied from folly/ScopeGuard.h
17
18namespace detail {
19
20class ScopeGuardImplBase {
21 public:
22 void dismiss() noexcept {
23 dismissed_ = true;
24 }
25
26 protected:
27 ScopeGuardImplBase() noexcept : dismissed_(false) {}
28
29 static ScopeGuardImplBase makeEmptyScopeGuard() noexcept {
30 return ScopeGuardImplBase{};
31 }
32
33 template <typename T>
34 static const T& asConst(const T& t) noexcept {
35 return t;
36 }
37
38 bool dismissed_;
39};
40
41template <typename FunctionType>
42class ScopeGuardImpl : public ScopeGuardImplBase {
43 public:
44 explicit ScopeGuardImpl(FunctionType& fn) noexcept(
45 std::is_nothrow_copy_constructible<FunctionType>::value)
46 : ScopeGuardImpl(
47 asConst(fn),
48 makeFailsafe(std::is_nothrow_copy_constructible<FunctionType>{},
49 &fn)) {}
50
51 explicit ScopeGuardImpl(const FunctionType& fn) noexcept(
52 std::is_nothrow_copy_constructible<FunctionType>::value)
53 : ScopeGuardImpl(
54 fn,
55 makeFailsafe(std::is_nothrow_copy_constructible<FunctionType>{},
56 &fn)) {}
57
58 explicit ScopeGuardImpl(FunctionType&& fn) noexcept(
59 std::is_nothrow_move_constructible<FunctionType>::value)
60 : ScopeGuardImpl(
61 std::move_if_noexcept(fn),
62 makeFailsafe(std::is_nothrow_move_constructible<FunctionType>{},
63 &fn)) {}
64
65 ScopeGuardImpl(ScopeGuardImpl&& other) noexcept(
66 std::is_nothrow_move_constructible<FunctionType>::value)
67 : function_(std::move_if_noexcept(other.function_)) {
68 // If the above line attempts a copy and the copy throws, other is
69 // left owning the cleanup action and will execute it (or not) depending
70 // on the value of other.dismissed_. The following lines only execute
71 // if the move/copy succeeded, in which case *this assumes ownership of
72 // the cleanup action and dismisses other.
73 dismissed_ = other.dismissed_;
74 other.dismissed_ = true;
75 }
76
77 ~ScopeGuardImpl() noexcept {
78 if (!dismissed_) {
79 execute();
80 }
81 }
82
83 private:
84 static ScopeGuardImplBase makeFailsafe(std::true_type, const void*) noexcept {
85 return makeEmptyScopeGuard();
86 }
87
88 template <typename Fn>
89 static auto makeFailsafe(std::false_type, Fn* fn) noexcept
90 -> ScopeGuardImpl<decltype(std::ref(*fn))> {
91 return ScopeGuardImpl<decltype(std::ref(*fn))>{std::ref(*fn)};
92 }
93
94 template <typename Fn>
95 explicit ScopeGuardImpl(Fn&& fn, ScopeGuardImplBase&& failsafe)
96 : ScopeGuardImplBase{}, function_(std::forward<Fn>(fn)) {
97 failsafe.dismiss();
98 }
99
100 void* operator new(std::size_t) = delete;
101
102 void execute() noexcept { function_(); }
103
104 FunctionType function_;
105};
106
107template <typename F>
108using ScopeGuardImplDecay = ScopeGuardImpl<typename std::decay<F>::type>;
109
110} // namespace detail
111
112/**
113 * ScopeGuard is a general implementation of the "Initialization is
114 * Resource Acquisition" idiom. Basically, it guarantees that a function
115 * is executed upon leaving the current scope unless otherwise told.
116 *
117 * The MakeGuard() function is used to create a new ScopeGuard object.
118 * It can be instantiated with a lambda function, a std::function<void()>,
119 * a functor, or a void(*)() function pointer.
120 *
121 *
122 * Usage example: Add a friend to memory iff it is also added to the db.
123 *
124 * void User::addFriend(User& newFriend) {
125 * // add the friend to memory
126 * friends_.push_back(&newFriend);
127 *
128 * // If the db insertion that follows fails, we should
129 * // remove it from memory.
130 * auto guard = MakeGuard([&] { friends_.pop_back(); });
131 *
132 * // this will throw an exception upon error, which
133 * // makes the ScopeGuard execute UserCont::pop_back()
134 * // once the Guard's destructor is called.
135 * db_->addFriend(GetName(), newFriend.GetName());
136 *
137 * // an exception was not thrown, so don't execute
138 * // the Guard.
139 * guard.dismiss();
140 * }
141 *
142 * Examine ScopeGuardTest.cpp for some more sample usage.
143 *
144 * Stolen from:
145 * Andrei's and Petru Marginean's CUJ article:
146 * http://drdobbs.com/184403758
147 * and the loki library:
148 * http://loki-lib.sourceforge.net/index.php?n=Idioms.ScopeGuardPointer
149 * and triendl.kj article:
150 * http://www.codeproject.com/KB/cpp/scope_guard.aspx
151 */
152template <typename F>
153detail::ScopeGuardImplDecay<F> MakeGuard(F&& f) noexcept(
154 noexcept(detail::ScopeGuardImplDecay<F>(static_cast<F&&>(f)))) {
155 return detail::ScopeGuardImplDecay<F>(static_cast<F&&>(f));
156}
157
158} // namespaces
159