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 "mozilla/GuardObjects.h"
84 #include "mozilla/Move.h"
85
86 namespace mozilla {
87
88 template <typename ExitFunction>
89 class MOZ_STACK_CLASS ScopeExit {
90 ExitFunction mExitFunction;
91 bool mExecuteOnDestruction;
92 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
93
94 public:
ScopeExit(ExitFunction && cleanup MOZ_GUARD_OBJECT_NOTIFIER_PARAM)95 explicit ScopeExit(ExitFunction&& cleanup
96 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
97 : mExitFunction(cleanup)
98 , mExecuteOnDestruction(true)
99 {
100 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
101 }
102
ScopeExit(ScopeExit && rhs)103 ScopeExit(ScopeExit&& rhs)
104 : mExitFunction(mozilla::Move(rhs.mExitFunction))
105 , mExecuteOnDestruction(rhs.mExecuteOnDestruction)
106 {
107 rhs.release();
108 }
109
~ScopeExit()110 ~ScopeExit() {
111 if (mExecuteOnDestruction) {
112 mExitFunction();
113 }
114 }
115
release()116 void release() {
117 mExecuteOnDestruction = false;
118 }
119
120 private:
121 explicit ScopeExit(const ScopeExit&) = delete;
122 ScopeExit& operator=(const ScopeExit&) = delete;
123 ScopeExit& operator=(ScopeExit&&) = delete;
124 };
125
126 template <typename ExitFunction>
127 ScopeExit<ExitFunction>
MakeScopeExit(ExitFunction && exitFunction)128 MakeScopeExit(ExitFunction&& exitFunction)
129 {
130 return ScopeExit<ExitFunction>(mozilla::Move(exitFunction));
131 }
132
133 } /* namespace mozilla */
134
135 #endif /* mozilla_ScopeExit_h */
136