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