Line data Source code
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:
95 16743 : explicit ScopeExit(ExitFunction&& cleanup
96 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
97 : : mExitFunction(cleanup)
98 16743 : , mExecuteOnDestruction(true)
99 : {
100 16743 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
101 16743 : }
102 :
103 : ScopeExit(ScopeExit&& rhs)
104 : : mExitFunction(mozilla::Move(rhs.mExitFunction))
105 : , mExecuteOnDestruction(rhs.mExecuteOnDestruction)
106 : {
107 : rhs.release();
108 : }
109 :
110 16705 : ~ScopeExit() {
111 16705 : if (mExecuteOnDestruction) {
112 3858 : mExitFunction();
113 : }
114 16705 : }
115 :
116 12849 : void release() {
117 12849 : mExecuteOnDestruction = false;
118 12849 : }
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>
128 16743 : MakeScopeExit(ExitFunction&& exitFunction)
129 : {
130 16743 : return ScopeExit<ExitFunction>(mozilla::Move(exitFunction));
131 : }
132 :
133 : } /* namespace mozilla */
134 :
135 : #endif /* mozilla_ScopeExit_h */
|