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 #include <folly/ScopeGuard.h>
18 
19 #include <condition_variable>
20 #include <functional>
21 #include <stdexcept>
22 #include <thread>
23 
24 #include <glog/logging.h>
25 
26 #include <folly/portability/GTest.h>
27 
28 using folly::makeDismissedGuard;
29 using folly::makeGuard;
30 using std::vector;
31 
returnsDouble()32 double returnsDouble() {
33   return 0.0;
34 }
35 
36 class MyFunctor {
37  public:
MyFunctor(int * ptr)38   explicit MyFunctor(int* ptr) : ptr_(ptr) {}
39 
operator ()()40   void operator()() { ++*ptr_; }
41 
42  private:
43   int* ptr_;
44 };
45 
TEST(ScopeGuard,DifferentWaysToBind)46 TEST(ScopeGuard, DifferentWaysToBind) {
47   {
48     // There is implicit conversion from func pointer
49     // double (*)() to function<void()>.
50     auto g = makeGuard(returnsDouble);
51     (void)g;
52   }
53 
54   vector<int> v;
55   void (vector<int>::*push_back)(int const&) = &vector<int>::push_back;
56 
57   v.push_back(1);
58   {
59     // binding to member function.
60     auto g = makeGuard(std::bind(&vector<int>::pop_back, &v));
61     (void)g;
62   }
63   EXPECT_EQ(0, v.size());
64 
65   {
66     // bind member function with args. v is passed-by-value!
67     auto g = makeGuard(std::bind(push_back, v, 2));
68     (void)g;
69   }
70   EXPECT_EQ(0, v.size()); // push_back happened on a copy of v... fail!
71 
72   // pass in an argument by pointer so to avoid copy.
73   {
74     auto g = makeGuard(std::bind(push_back, &v, 4));
75     (void)g;
76   }
77   EXPECT_EQ(1, v.size());
78 
79   {
80     // pass in an argument by reference so to avoid copy.
81     auto g = makeGuard(std::bind(push_back, std::ref(v), 4));
82     (void)g;
83   }
84   EXPECT_EQ(2, v.size());
85 
86   // lambda with a reference to v
87   {
88     auto g = makeGuard([&] { v.push_back(5); });
89     (void)g;
90   }
91   EXPECT_EQ(3, v.size());
92 
93   // lambda with a copy of v
94   {
95     auto g = makeGuard([v]() mutable { v.push_back(6); });
96     (void)g;
97   }
98   EXPECT_EQ(3, v.size());
99 
100   // functor object
101   int n = 0;
102   {
103     MyFunctor f(&n);
104     auto g = makeGuard(f);
105     (void)g;
106   }
107   EXPECT_EQ(1, n);
108 
109   // temporary functor object
110   n = 0;
111   {
112     auto g = makeGuard(MyFunctor(&n));
113     (void)g;
114   }
115   EXPECT_EQ(1, n);
116 
117   // Use auto instead of ScopeGuard
118   n = 2;
119   {
120     auto g = makeGuard(MyFunctor(&n));
121     (void)g;
122   }
123   EXPECT_EQ(3, n);
124 
125   // Use const auto& instead of ScopeGuard
126   n = 10;
127   {
128     const auto& g = makeGuard(MyFunctor(&n));
129     (void)g;
130   }
131   EXPECT_EQ(11, n);
132 }
133 
TEST(ScopeGuard,GuardException)134 TEST(ScopeGuard, GuardException) {
135   EXPECT_DEATH(
136       (void)makeGuard(
137           [] { throw std::runtime_error("dtors should never throw!"); }),
138       "dtors should never throw!");
139 }
140 
141 /**
142  * Add an integer to a vector iff it was inserted into the
143  * db successfuly. Here is a schematic of how you would accomplish
144  * this with scope guard.
145  */
testUndoAction(bool failure)146 void testUndoAction(bool failure) {
147   vector<int64_t> v;
148   { // defines a "mini" scope
149 
150     // be optimistic and insert this into memory
151     v.push_back(1);
152 
153     // The guard is triggered to undo the insertion unless dismiss() is called.
154     auto guard = makeGuard([&] { v.pop_back(); });
155 
156     // Do some action; Use the failure argument to pretend
157     // if it failed or succeeded.
158 
159     // if there was no failure, dismiss the undo guard action.
160     if (!failure) {
161       guard.dismiss();
162     }
163   } // all stack allocated in the mini-scope will be destroyed here.
164 
165   if (failure) {
166     EXPECT_EQ(0, v.size()); // the action failed => undo insertion
167   } else {
168     EXPECT_EQ(1, v.size()); // the action succeeded => keep insertion
169   }
170 }
171 
TEST(ScopeGuard,UndoAction)172 TEST(ScopeGuard, UndoAction) {
173   testUndoAction(true);
174   testUndoAction(false);
175 }
176 
TEST(ScopeGuard,MakeDismissedGuard)177 TEST(ScopeGuard, MakeDismissedGuard) {
178   auto test = [](bool shouldFire) {
179     bool fired = false;
180     {
181       auto guard = makeDismissedGuard([&] { fired = true; });
182       if (shouldFire) {
183         guard.rehire();
184       }
185     }
186     EXPECT_EQ(shouldFire, fired);
187   };
188 
189   test(true);
190   test(false);
191 }
192 
193 /**
194  * Sometimes in a try catch block we want to execute a piece of code
195  * regardless if an exception happened or not. For example, you want
196  * to close a db connection regardless if an exception was thrown during
197  * insertion. In Java and other languages there is a finally clause that
198  * helps accomplish this:
199  *
200  *   try {
201  *     dbConn.doInsert(sql);
202  *   } catch (const DbException& dbe) {
203  *     dbConn.recordFailure(dbe);
204  *   } catch (const CriticalException& e) {
205  *     throw e; // re-throw the exception
206  *   } finally {
207  *     dbConn.closeConnection(); // executes no matter what!
208  *   }
209  *
210  * We can approximate this behavior in C++ with ScopeGuard.
211  */
212 enum class ErrorBehavior {
213   SUCCESS,
214   HANDLED_ERROR,
215   UNHANDLED_ERROR,
216 };
217 
testFinally(ErrorBehavior error)218 void testFinally(ErrorBehavior error) {
219   bool cleanupOccurred = false;
220 
221   try {
222     auto guard = makeGuard([&] { cleanupOccurred = true; });
223     (void)guard;
224 
225     try {
226       if (error == ErrorBehavior::HANDLED_ERROR) {
227         throw std::runtime_error("throwing an expected error");
228       } else if (error == ErrorBehavior::UNHANDLED_ERROR) {
229         throw "never throw raw strings";
230       }
231     } catch (const std::runtime_error&) {
232     }
233   } catch (...) {
234     // Outer catch to swallow the error for the UNHANDLED_ERROR behavior
235   }
236 
237   EXPECT_TRUE(cleanupOccurred);
238 }
239 
TEST(ScopeGuard,TryCatchFinally)240 TEST(ScopeGuard, TryCatchFinally) {
241   testFinally(ErrorBehavior::SUCCESS);
242   testFinally(ErrorBehavior::HANDLED_ERROR);
243   testFinally(ErrorBehavior::UNHANDLED_ERROR);
244 }
245 
TEST(ScopeGuard,TEST_SCOPE_EXIT)246 TEST(ScopeGuard, TEST_SCOPE_EXIT) {
247   int x = 0;
248   {
249     SCOPE_EXIT { ++x; };
250     EXPECT_EQ(0, x);
251   }
252   EXPECT_EQ(1, x);
253 }
254 
255 class Foo {
256  public:
Foo()257   Foo() {}
~Foo()258   ~Foo() {
259     try {
260       auto e = std::current_exception();
261       int test = 0;
262       {
263         SCOPE_EXIT { ++test; };
264         EXPECT_EQ(0, test);
265       }
266       EXPECT_EQ(1, test);
267     } catch (const std::exception& ex) {
268       LOG(FATAL) << "Unexpected exception: " << ex.what();
269     }
270   }
271 };
272 
TEST(ScopeGuard,TEST_SCOPE_FAILURE2)273 TEST(ScopeGuard, TEST_SCOPE_FAILURE2) {
274   try {
275     Foo f;
276     throw std::runtime_error("test");
277   } catch (...) {
278   }
279 }
280 
testScopeFailAndScopeSuccess(ErrorBehavior error,bool expectFail)281 void testScopeFailAndScopeSuccess(ErrorBehavior error, bool expectFail) {
282   bool scopeFailExecuted = false;
283   bool scopeSuccessExecuted = false;
284 
285   try {
286     SCOPE_FAIL { scopeFailExecuted = true; };
287     SCOPE_SUCCESS { scopeSuccessExecuted = true; };
288 
289     try {
290       if (error == ErrorBehavior::HANDLED_ERROR) {
291         throw std::runtime_error("throwing an expected error");
292       } else if (error == ErrorBehavior::UNHANDLED_ERROR) {
293         throw "never throw raw strings";
294       }
295     } catch (const std::runtime_error&) {
296     }
297   } catch (...) {
298     // Outer catch to swallow the error for the UNHANDLED_ERROR behavior
299   }
300 
301   EXPECT_EQ(expectFail, scopeFailExecuted);
302   EXPECT_EQ(!expectFail, scopeSuccessExecuted);
303 }
304 
TEST(ScopeGuard,TEST_SCOPE_FAIL_EXCEPTION_PTR)305 TEST(ScopeGuard, TEST_SCOPE_FAIL_EXCEPTION_PTR) {
306   bool catchExecuted = false;
307   bool failExecuted = false;
308 
309   try {
310     SCOPE_FAIL { failExecuted = true; };
311 
312     std::exception_ptr ep;
313     try {
314       throw std::runtime_error("test");
315     } catch (...) {
316       ep = std::current_exception();
317     }
318     std::rethrow_exception(ep);
319   } catch (const std::exception&) {
320     catchExecuted = true;
321   }
322 
323   EXPECT_TRUE(catchExecuted);
324   EXPECT_TRUE(failExecuted);
325 }
326 
TEST(ScopeGuard,TEST_SCOPE_FAIL_AND_SCOPE_SUCCESS)327 TEST(ScopeGuard, TEST_SCOPE_FAIL_AND_SCOPE_SUCCESS) {
328   testScopeFailAndScopeSuccess(ErrorBehavior::SUCCESS, false);
329   testScopeFailAndScopeSuccess(ErrorBehavior::HANDLED_ERROR, false);
330   testScopeFailAndScopeSuccess(ErrorBehavior::UNHANDLED_ERROR, true);
331 }
332 
TEST(ScopeGuard,TEST_SCOPE_SUCCESS_THROW)333 TEST(ScopeGuard, TEST_SCOPE_SUCCESS_THROW) {
334   auto lambda = []() {
335     SCOPE_SUCCESS { throw std::runtime_error("ehm"); };
336   };
337   EXPECT_THROW(lambda(), std::runtime_error);
338 }
339 
TEST(ScopeGuard,TEST_THROWING_CLEANUP_ACTION)340 TEST(ScopeGuard, TEST_THROWING_CLEANUP_ACTION) {
341   struct ThrowingCleanupAction {
342     // clang-format off
343     explicit ThrowingCleanupAction(int& scopeExitExecuted)
344         : scopeExitExecuted_(scopeExitExecuted) {}
345     [[noreturn]] ThrowingCleanupAction(const ThrowingCleanupAction& other)
346         : scopeExitExecuted_(other.scopeExitExecuted_) {
347       throw std::runtime_error("whoa");
348     }
349     // clang-format on
350     void operator()() { ++scopeExitExecuted_; }
351 
352    private:
353     int& scopeExitExecuted_;
354   };
355   int scopeExitExecuted = 0;
356   ThrowingCleanupAction onExit(scopeExitExecuted);
357   EXPECT_THROW((void)makeGuard(onExit), std::runtime_error);
358   EXPECT_EQ(scopeExitExecuted, 1);
359 }
360