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 | |
32 | namespace folly { |
33 | |
34 | namespace detail { |
35 | |
36 | class 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 | |
58 | template <typename FunctionType, bool InvokeNoexcept> |
59 | class 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 | |
135 | template <typename F, bool INE> |
136 | using 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 | */ |
181 | template <typename F> |
182 | FOLLY_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 | |
187 | namespace 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 | */ |
204 | template <typename FunctionType, bool ExecuteOnException> |
205 | class 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 | */ |
231 | enum class ScopeGuardOnFail {}; |
232 | |
233 | template <typename FunctionType> |
234 | ScopeGuardForNewException<typename std::decay<FunctionType>::type, true> |
235 | operator+(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 | */ |
244 | enum class ScopeGuardOnSuccess {}; |
245 | |
246 | template <typename FunctionType> |
247 | ScopeGuardForNewException<typename std::decay<FunctionType>::type, false> |
248 | operator+(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 | */ |
259 | enum class ScopeGuardOnExit {}; |
260 | |
261 | template <typename FunctionType> |
262 | ScopeGuardImpl<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 | |