ScopeGuardTest.cpp 7.7 KB

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