ScopeExit.h (3331B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* RAII class for executing arbitrary actions at scope end. */ 8 9 #ifndef mozilla_ScopeExit_h 10 #define mozilla_ScopeExit_h 11 12 /* 13 * See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189.pdf for a 14 * standards-track version of this. 15 * 16 * Error handling can be complex when various actions need to be performed that 17 * need to be undone if an error occurs midway. This can be handled with a 18 * collection of boolean state variables and gotos, which can get clunky and 19 * error-prone: 20 * 21 * { 22 * if (!a.setup()) 23 * goto fail; 24 * isASetup = true; 25 * 26 * if (!b.setup()) 27 * goto fail; 28 * isBSetup = true; 29 * 30 * ... 31 * return true; 32 * 33 * fail: 34 * if (isASetup) 35 * a.teardown(); 36 * if (isBSetup) 37 * b.teardown(); 38 * return false; 39 * } 40 * 41 * ScopeExit is a mechanism to simplify this pattern by keeping an RAII guard 42 * class that will perform the teardown on destruction, unless released. So the 43 * above would become: 44 * 45 * { 46 * if (!a.setup()) { 47 * return false; 48 * } 49 * auto guardA = MakeScopeExit([&] { 50 * a.teardown(); 51 * }); 52 * 53 * if (!b.setup()) { 54 * return false; 55 * } 56 * auto guardB = MakeScopeExit([&] { 57 * b.teardown(); 58 * }); 59 * 60 * ... 61 * guardA.release(); 62 * guardB.release(); 63 * return true; 64 * } 65 * 66 * This header provides: 67 * 68 * - |ScopeExit| - a container for a cleanup call, automically called at the 69 * end of the scope; 70 * - |MakeScopeExit| - a convenience function for constructing a |ScopeExit| 71 * with a given cleanup routine, commonly used with a lambda function. 72 * 73 * Note that the RAII classes defined in this header do _not_ perform any form 74 * of reference-counting or garbage-collection. These classes have exactly two 75 * behaviors: 76 * 77 * - if |release()| has not been called, the cleanup is always performed at 78 * the end of the scope; 79 * - if |release()| has been called, nothing will happen at the end of the 80 * scope. 81 */ 82 83 #include <utility> 84 85 #include "mozilla/Attributes.h" 86 87 namespace mozilla { 88 89 template <typename ExitFunction> 90 class MOZ_STACK_CLASS ScopeExit { 91 ExitFunction mExitFunction; 92 bool mExecuteOnDestruction; 93 94 public: 95 explicit ScopeExit(ExitFunction&& cleanup) 96 : mExitFunction(std::move(cleanup)), mExecuteOnDestruction(true) {} 97 98 ScopeExit(ScopeExit&& rhs) 99 : mExitFunction(std::move(rhs.mExitFunction)), 100 mExecuteOnDestruction(rhs.mExecuteOnDestruction) { 101 rhs.release(); 102 } 103 104 ~ScopeExit() { 105 if (mExecuteOnDestruction) { 106 mExitFunction(); 107 } 108 } 109 110 void release() { mExecuteOnDestruction = false; } 111 112 private: 113 explicit ScopeExit(const ScopeExit&) = delete; 114 ScopeExit& operator=(const ScopeExit&) = delete; 115 ScopeExit& operator=(ScopeExit&&) = delete; 116 }; 117 118 template <typename ExitFunction> 119 [[nodiscard]] ScopeExit<ExitFunction> MakeScopeExit( 120 ExitFunction&& exitFunction) { 121 return ScopeExit<ExitFunction>(std::move(exitFunction)); 122 } 123 124 } /* namespace mozilla */ 125 126 #endif /* mozilla_ScopeExit_h */