1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #pragma once
18
19 #include <cstddef>
20 #include <cstdlib>
21 #include <functional>
22 #include <new>
23 #include <type_traits>
24 #include <utility>
25
26 #include <folly/Portability.h>
27 #include <folly/Preprocessor.h>
28 #include <folly/Utility.h>
29 #include <folly/lang/Exception.h>
30 #include <folly/lang/UncaughtExceptions.h>
31
32 namespace folly {
33
34 namespace detail {
35
36 struct ScopeGuardDismissed {};
37
38 class ScopeGuardImplBase {
39 public:
dismiss()40 void dismiss() noexcept { dismissed_ = true; }
rehire()41 void rehire() noexcept { dismissed_ = false; }
42
43 protected:
dismissed_(dismissed)44 ScopeGuardImplBase(bool dismissed = false) noexcept : dismissed_(dismissed) {}
45
46 [[noreturn]] static void terminate() noexcept;
makeEmptyScopeGuard()47 static ScopeGuardImplBase makeEmptyScopeGuard() noexcept {
48 return ScopeGuardImplBase{};
49 }
50
51 template <typename T>
asConst(const T & t)52 static const T& asConst(const T& t) noexcept {
53 return t;
54 }
55
56 bool dismissed_;
57 };
58
59 template <typename FunctionType, bool InvokeNoexcept>
60 class ScopeGuardImpl : public ScopeGuardImplBase {
61 public:
ScopeGuardImpl(FunctionType & fn)62 explicit ScopeGuardImpl(FunctionType& fn) noexcept(
63 std::is_nothrow_copy_constructible<FunctionType>::value)
64 : ScopeGuardImpl(
65 asConst(fn),
66 makeFailsafe(
67 std::is_nothrow_copy_constructible<FunctionType>{}, &fn)) {}
68
ScopeGuardImpl(const FunctionType & fn)69 explicit ScopeGuardImpl(const FunctionType& fn) noexcept(
70 std::is_nothrow_copy_constructible<FunctionType>::value)
71 : ScopeGuardImpl(
72 fn,
73 makeFailsafe(
74 std::is_nothrow_copy_constructible<FunctionType>{}, &fn)) {}
75
ScopeGuardImpl(FunctionType && fn)76 explicit ScopeGuardImpl(FunctionType&& fn) noexcept(
77 std::is_nothrow_move_constructible<FunctionType>::value)
78 : ScopeGuardImpl(
79 std::move_if_noexcept(fn),
80 makeFailsafe(
81 std::is_nothrow_move_constructible<FunctionType>{}, &fn)) {}
82
ScopeGuardImpl(FunctionType && fn,ScopeGuardDismissed)83 explicit ScopeGuardImpl(FunctionType&& fn, ScopeGuardDismissed) noexcept(
84 std::is_nothrow_move_constructible<FunctionType>::value)
85 // No need for failsafe in this case, as the guard is dismissed.
86 : ScopeGuardImplBase{true}, function_(std::forward<FunctionType>(fn)) {}
87
noexcept(std::is_nothrow_move_constructible<FunctionType>::value)88 ScopeGuardImpl(ScopeGuardImpl&& other) noexcept(
89 std::is_nothrow_move_constructible<FunctionType>::value)
90 : function_(std::move_if_noexcept(other.function_)) {
91 // If the above line attempts a copy and the copy throws, other is
92 // left owning the cleanup action and will execute it (or not) depending
93 // on the value of other.dismissed_. The following lines only execute
94 // if the move/copy succeeded, in which case *this assumes ownership of
95 // the cleanup action and dismisses other.
96 dismissed_ = std::exchange(other.dismissed_, true);
97 }
98
noexcept(InvokeNoexcept)99 ~ScopeGuardImpl() noexcept(InvokeNoexcept) {
100 if (!dismissed_) {
101 execute();
102 }
103 }
104
105 private:
makeFailsafe(std::true_type,const void *)106 static ScopeGuardImplBase makeFailsafe(std::true_type, const void*) noexcept {
107 return makeEmptyScopeGuard();
108 }
109
110 template <typename Fn>
111 static auto makeFailsafe(std::false_type, Fn* fn) noexcept
112 -> ScopeGuardImpl<decltype(std::ref(*fn)), InvokeNoexcept> {
113 return ScopeGuardImpl<decltype(std::ref(*fn)), InvokeNoexcept>{
114 std::ref(*fn)};
115 }
116
117 template <typename Fn>
ScopeGuardImpl(Fn && fn,ScopeGuardImplBase && failsafe)118 explicit ScopeGuardImpl(Fn&& fn, ScopeGuardImplBase&& failsafe)
119 : ScopeGuardImplBase{}, function_(std::forward<Fn>(fn)) {
120 failsafe.dismiss();
121 }
122
123 void* operator new(std::size_t) = delete;
124
execute()125 void execute() noexcept(InvokeNoexcept) {
126 if (InvokeNoexcept) {
127 using R = decltype(function_());
128 auto catcher_word = reinterpret_cast<uintptr_t>(&terminate);
129 auto catcher = reinterpret_cast<R (*)()>(catcher_word);
130 catch_exception(function_, catcher);
131 } else {
132 function_();
133 }
134 }
135
136 FunctionType function_;
137 };
138
139 template <typename F, bool INE>
140 using ScopeGuardImplDecay = ScopeGuardImpl<typename std::decay<F>::type, INE>;
141
142 } // namespace detail
143
144 /**
145 * ScopeGuard is a general implementation of the "Initialization is
146 * Resource Acquisition" idiom. Basically, it guarantees that a function
147 * is executed upon leaving the current scope unless otherwise told.
148 *
149 * The makeGuard() function is used to create a new ScopeGuard object.
150 * It can be instantiated with a lambda function, a std::function<void()>,
151 * a functor, or a void(*)() function pointer.
152 *
153 *
154 * Usage example: Add a friend to memory if and only if it is also added
155 * to the db.
156 *
157 * void User::addFriend(User& newFriend) {
158 * // add the friend to memory
159 * friends_.push_back(&newFriend);
160 *
161 * // If the db insertion that follows fails, we should
162 * // remove it from memory.
163 * auto guard = makeGuard([&] { friends_.pop_back(); });
164 *
165 * // this will throw an exception upon error, which
166 * // makes the ScopeGuard execute UserCont::pop_back()
167 * // once the Guard's destructor is called.
168 * db_->addFriend(GetName(), newFriend.GetName());
169 *
170 * // an exception was not thrown, so don't execute
171 * // the Guard.
172 * guard.dismiss();
173 * }
174 *
175 * It is also possible to create a guard in dismissed state with
176 * makeDismissedGuard(), and later rehire it with the rehire()
177 * method.
178 *
179 * makeDismissedGuard() is not just syntactic sugar for creating a guard and
180 * immediately dismissing it, but it has a subtle behavior difference if
181 * move-construction of the passed function can throw: if it does, the function
182 * will be called by makeGuard(), but not by makeDismissedGuard().
183 *
184 * Examine ScopeGuardTest.cpp for some more sample usage.
185 *
186 * Stolen from:
187 * Andrei's and Petru Marginean's CUJ article:
188 * http://drdobbs.com/184403758
189 * and the loki library:
190 * http://loki-lib.sourceforge.net/index.php?n=Idioms.ScopeGuardPointer
191 * and triendl.kj article:
192 * http://www.codeproject.com/KB/cpp/scope_guard.aspx
193 */
194 template <typename F>
makeGuard(F && f)195 FOLLY_NODISCARD detail::ScopeGuardImplDecay<F, true> makeGuard(F&& f) noexcept(
196 noexcept(detail::ScopeGuardImplDecay<F, true>(static_cast<F&&>(f)))) {
197 return detail::ScopeGuardImplDecay<F, true>(static_cast<F&&>(f));
198 }
199
200 template <typename F>
201 FOLLY_NODISCARD detail::ScopeGuardImplDecay<F, true>
makeDismissedGuard(F && f)202 makeDismissedGuard(F&& f) noexcept(
203 noexcept(detail::ScopeGuardImplDecay<F, true>(
204 static_cast<F&&>(f), detail::ScopeGuardDismissed{}))) {
205 return detail::ScopeGuardImplDecay<F, true>(
206 static_cast<F&&>(f), detail::ScopeGuardDismissed{});
207 }
208
209 namespace detail {
210
211 #if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS) || \
212 defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD) || \
213 defined(FOLLY_EXCEPTION_COUNT_USE_STD)
214
215 /**
216 * ScopeGuard used for executing a function when leaving the current scope
217 * depending on the presence of a new uncaught exception.
218 *
219 * If the executeOnException template parameter is true, the function is
220 * executed if a new uncaught exception is present at the end of the scope.
221 * If the parameter is false, then the function is executed if no new uncaught
222 * exceptions are present at the end of the scope.
223 *
224 * Used to implement SCOPE_FAIL and SCOPE_SUCCESS below.
225 */
226 template <typename FunctionType, bool ExecuteOnException>
227 class ScopeGuardForNewException {
228 public:
ScopeGuardForNewException(const FunctionType & fn)229 explicit ScopeGuardForNewException(const FunctionType& fn) : guard_(fn) {}
230
ScopeGuardForNewException(FunctionType && fn)231 explicit ScopeGuardForNewException(FunctionType&& fn)
232 : guard_(std::move(fn)) {}
233
234 ScopeGuardForNewException(ScopeGuardForNewException&& other) = default;
235
noexcept(ExecuteOnException)236 ~ScopeGuardForNewException() noexcept(ExecuteOnException) {
237 if (ExecuteOnException != (exceptionCounter_ < uncaught_exceptions())) {
238 guard_.dismiss();
239 }
240 }
241
242 private:
243 void* operator new(std::size_t) = delete;
244 void operator delete(void*) = delete;
245
246 ScopeGuardImpl<FunctionType, ExecuteOnException> guard_;
247 int exceptionCounter_{uncaught_exceptions()};
248 };
249
250 /**
251 * Internal use for the macro SCOPE_FAIL below
252 */
253 enum class ScopeGuardOnFail {};
254
255 template <typename FunctionType>
256 ScopeGuardForNewException<typename std::decay<FunctionType>::type, true>
257 operator+(detail::ScopeGuardOnFail, FunctionType&& fn) {
258 return ScopeGuardForNewException<
259 typename std::decay<FunctionType>::type,
260 true>(std::forward<FunctionType>(fn));
261 }
262
263 /**
264 * Internal use for the macro SCOPE_SUCCESS below
265 */
266 enum class ScopeGuardOnSuccess {};
267
268 template <typename FunctionType>
269 ScopeGuardForNewException<typename std::decay<FunctionType>::type, false>
270 operator+(ScopeGuardOnSuccess, FunctionType&& fn) {
271 return ScopeGuardForNewException<
272 typename std::decay<FunctionType>::type,
273 false>(std::forward<FunctionType>(fn));
274 }
275
276 #endif // native uncaught_exception() supported
277
278 /**
279 * Internal use for the macro SCOPE_EXIT below
280 */
281 enum class ScopeGuardOnExit {};
282
283 template <typename FunctionType>
284 ScopeGuardImpl<typename std::decay<FunctionType>::type, true> operator+(
285 detail::ScopeGuardOnExit, FunctionType&& fn) {
286 return ScopeGuardImpl<typename std::decay<FunctionType>::type, true>(
287 std::forward<FunctionType>(fn));
288 }
289 } // namespace detail
290
291 } // namespace folly
292
293 #define SCOPE_EXIT \
294 auto FB_ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) = \
295 ::folly::detail::ScopeGuardOnExit() + [&]() noexcept
296
297 #if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS) || \
298 defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD) || \
299 defined(FOLLY_EXCEPTION_COUNT_USE_STD)
300 #define SCOPE_FAIL \
301 auto FB_ANONYMOUS_VARIABLE(SCOPE_FAIL_STATE) = \
302 ::folly::detail::ScopeGuardOnFail() + [&]() noexcept
303
304 #define SCOPE_SUCCESS \
305 auto FB_ANONYMOUS_VARIABLE(SCOPE_SUCCESS_STATE) = \
306 ::folly::detail::ScopeGuardOnSuccess() + [&]()
307 #endif // native uncaught_exception() supported
308