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