1/*
2 * Copyright 2011-present Facebook, Inc.
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
32namespace folly {
33
34namespace detail {
35
36class ScopeGuardImplBase {
37 public:
38 void dismiss() noexcept {
39 dismissed_ = true;
40 }
41
42 protected:
43 ScopeGuardImplBase() noexcept : dismissed_(false) {}
44
45 static void warnAboutToCrash() noexcept;
46 static ScopeGuardImplBase makeEmptyScopeGuard() noexcept {
47 return ScopeGuardImplBase{};
48 }
49
50 template <typename T>
51 static const T& asConst(const T& t) noexcept {
52 return t;
53 }
54
55 bool dismissed_;
56};
57
58template <typename FunctionType, bool InvokeNoexcept>
59class ScopeGuardImpl : public ScopeGuardImplBase {
60 public:
61 explicit ScopeGuardImpl(FunctionType& fn) noexcept(
62 std::is_nothrow_copy_constructible<FunctionType>::value)
63 : ScopeGuardImpl(
64 asConst(fn),
65 makeFailsafe(
66 std::is_nothrow_copy_constructible<FunctionType>{},
67 &fn)) {}
68
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>{},
75 &fn)) {}
76
77 explicit ScopeGuardImpl(FunctionType&& fn) noexcept(
78 std::is_nothrow_move_constructible<FunctionType>::value)
79 : ScopeGuardImpl(
80 std::move_if_noexcept(fn),
81 makeFailsafe(
82 std::is_nothrow_move_constructible<FunctionType>{},
83 &fn)) {}
84
85 ScopeGuardImpl(ScopeGuardImpl&& other) noexcept(
86 std::is_nothrow_move_constructible<FunctionType>::value)
87 : function_(std::move_if_noexcept(other.function_)) {
88 // If the above line attempts a copy and the copy throws, other is
89 // left owning the cleanup action and will execute it (or not) depending
90 // on the value of other.dismissed_. The following lines only execute
91 // if the move/copy succeeded, in which case *this assumes ownership of
92 // the cleanup action and dismisses other.
93 dismissed_ = exchange(other.dismissed_, true);
94 }
95
96 ~ScopeGuardImpl() noexcept(InvokeNoexcept) {
97 if (!dismissed_) {
98 execute();
99 }
100 }
101
102 private:
103 static ScopeGuardImplBase makeFailsafe(std::true_type, const void*) noexcept {
104 return makeEmptyScopeGuard();
105 }
106
107 template <typename Fn>
108 static auto makeFailsafe(std::false_type, Fn* fn) noexcept
109 -> ScopeGuardImpl<decltype(std::ref(*fn)), InvokeNoexcept> {
110 return ScopeGuardImpl<decltype(std::ref(*fn)), InvokeNoexcept>{
111 std::ref(*fn)};
112 }
113
114 template <typename Fn>
115 explicit ScopeGuardImpl(Fn&& fn, ScopeGuardImplBase&& failsafe)
116 : ScopeGuardImplBase{}, function_(std::forward<Fn>(fn)) {
117 failsafe.dismiss();
118 }
119
120 void* operator new(std::size_t) = delete;
121
122 void execute() noexcept(InvokeNoexcept) {
123 if (InvokeNoexcept) {
124 using R = decltype(function_());
125 auto catcher = []() -> R { warnAboutToCrash(), std::terminate(); };
126 catch_exception(function_, catcher);
127 } else {
128 function_();
129 }
130 }
131
132 FunctionType function_;
133};
134
135template <typename F, bool INE>
136using ScopeGuardImplDecay = ScopeGuardImpl<typename std::decay<F>::type, INE>;
137
138} // namespace detail
139
140/**
141 * ScopeGuard is a general implementation of the "Initialization is
142 * Resource Acquisition" idiom. Basically, it guarantees that a function
143 * is executed upon leaving the currrent scope unless otherwise told.
144 *
145 * The makeGuard() function is used to create a new ScopeGuard object.
146 * It can be instantiated with a lambda function, a std::function<void()>,
147 * a functor, or a void(*)() function pointer.
148 *
149 *
150 * Usage example: Add a friend to memory if and only if it is also added
151 * to the db.
152 *
153 * void User::addFriend(User& newFriend) {
154 * // add the friend to memory
155 * friends_.push_back(&newFriend);
156 *
157 * // If the db insertion that follows fails, we should
158 * // remove it from memory.
159 * auto guard = makeGuard([&] { friends_.pop_back(); });
160 *
161 * // this will throw an exception upon error, which
162 * // makes the ScopeGuard execute UserCont::pop_back()
163 * // once the Guard's destructor is called.
164 * db_->addFriend(GetName(), newFriend.GetName());
165 *
166 * // an exception was not thrown, so don't execute
167 * // the Guard.
168 * guard.dismiss();
169 * }
170 *
171 * Examine ScopeGuardTest.cpp for some more sample usage.
172 *
173 * Stolen from:
174 * Andrei's and Petru Marginean's CUJ article:
175 * http://drdobbs.com/184403758
176 * and the loki library:
177 * http://loki-lib.sourceforge.net/index.php?n=Idioms.ScopeGuardPointer
178 * and triendl.kj article:
179 * http://www.codeproject.com/KB/cpp/scope_guard.aspx
180 */
181template <typename F>
182FOLLY_NODISCARD detail::ScopeGuardImplDecay<F, true> makeGuard(F&& f) noexcept(
183 noexcept(detail::ScopeGuardImplDecay<F, true>(static_cast<F&&>(f)))) {
184 return detail::ScopeGuardImplDecay<F, true>(static_cast<F&&>(f));
185}
186
187namespace detail {
188
189#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS) || \
190 defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD) || \
191 defined(FOLLY_EXCEPTION_COUNT_USE_STD)
192
193/**
194 * ScopeGuard used for executing a function when leaving the current scope
195 * depending on the presence of a new uncaught exception.
196 *
197 * If the executeOnException template parameter is true, the function is
198 * executed if a new uncaught exception is present at the end of the scope.
199 * If the parameter is false, then the function is executed if no new uncaught
200 * exceptions are present at the end of the scope.
201 *
202 * Used to implement SCOPE_FAIL and SCOPE_SUCCESS below.
203 */
204template <typename FunctionType, bool ExecuteOnException>
205class ScopeGuardForNewException {
206 public:
207 explicit ScopeGuardForNewException(const FunctionType& fn) : guard_(fn) {}
208
209 explicit ScopeGuardForNewException(FunctionType&& fn)
210 : guard_(std::move(fn)) {}
211
212 ScopeGuardForNewException(ScopeGuardForNewException&& other) = default;
213
214 ~ScopeGuardForNewException() noexcept(ExecuteOnException) {
215 if (ExecuteOnException != (exceptionCounter_ < uncaught_exceptions())) {
216 guard_.dismiss();
217 }
218 }
219
220 private:
221 void* operator new(std::size_t) = delete;
222 void operator delete(void*) = delete;
223
224 ScopeGuardImpl<FunctionType, ExecuteOnException> guard_;
225 int exceptionCounter_{uncaught_exceptions()};
226};
227
228/**
229 * Internal use for the macro SCOPE_FAIL below
230 */
231enum class ScopeGuardOnFail {};
232
233template <typename FunctionType>
234ScopeGuardForNewException<typename std::decay<FunctionType>::type, true>
235operator+(detail::ScopeGuardOnFail, FunctionType&& fn) {
236 return ScopeGuardForNewException<
237 typename std::decay<FunctionType>::type,
238 true>(std::forward<FunctionType>(fn));
239}
240
241/**
242 * Internal use for the macro SCOPE_SUCCESS below
243 */
244enum class ScopeGuardOnSuccess {};
245
246template <typename FunctionType>
247ScopeGuardForNewException<typename std::decay<FunctionType>::type, false>
248operator+(ScopeGuardOnSuccess, FunctionType&& fn) {
249 return ScopeGuardForNewException<
250 typename std::decay<FunctionType>::type,
251 false>(std::forward<FunctionType>(fn));
252}
253
254#endif // native uncaught_exception() supported
255
256/**
257 * Internal use for the macro SCOPE_EXIT below
258 */
259enum class ScopeGuardOnExit {};
260
261template <typename FunctionType>
262ScopeGuardImpl<typename std::decay<FunctionType>::type, true> operator+(
263 detail::ScopeGuardOnExit,
264 FunctionType&& fn) {
265 return ScopeGuardImpl<typename std::decay<FunctionType>::type, true>(
266 std::forward<FunctionType>(fn));
267}
268} // namespace detail
269
270} // namespace folly
271
272#define SCOPE_EXIT \
273 auto FB_ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) = \
274 ::folly::detail::ScopeGuardOnExit() + [&]() noexcept
275
276#if defined(FOLLY_EXCEPTION_COUNT_USE_CXA_GET_GLOBALS) || \
277 defined(FOLLY_EXCEPTION_COUNT_USE_GETPTD) || \
278 defined(FOLLY_EXCEPTION_COUNT_USE_STD)
279#define SCOPE_FAIL \
280 auto FB_ANONYMOUS_VARIABLE(SCOPE_FAIL_STATE) = \
281 ::folly::detail::ScopeGuardOnFail() + [&]() noexcept
282
283#define SCOPE_SUCCESS \
284 auto FB_ANONYMOUS_VARIABLE(SCOPE_SUCCESS_STATE) = \
285 ::folly::detail::ScopeGuardOnSuccess() + [&]()
286#endif // native uncaught_exception() supported
287