Bug 1180299 - Implement ScopeExit for running actions at the end of a scope, r=Waldo
authorSteve Fink <sfink@mozilla.com>
Fri, 03 Jul 2015 14:20:55 -0700
changeset 283606 2f9998ce7db7f16745c30d86e7cc7c8dde12e42d
parent 283582 803a6e0978273b5cfbb4e4ec48fcee189b47e366
child 283607 67f475daf729ca99a71abacd14409422d0241ac6
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1180299
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1180299 - Implement ScopeExit for running actions at the end of a scope, r=Waldo
mfbt/ScopeExit.h
mfbt/moz.build
mfbt/tests/TestScopeExit.cpp
mfbt/tests/moz.build
new file mode 100644
--- /dev/null
+++ b/mfbt/ScopeExit.h
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* RAII class for executing arbitrary actions at scope end. */
+
+#ifndef mozilla_ScopeExit_h
+#define mozilla_ScopeExit_h
+
+/*
+ * See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189.pdf for a
+ * standards-track version of this.
+ *
+ * Error handling can be complex when various actions need to be performed that
+ * need to be undone if an error occurs midway. This can be handled with a
+ * collection of boolean state variables and gotos, which can get clunky and
+ * error-prone:
+ *
+ * {
+ *   if (!a.setup())
+ *       goto fail;
+ *   isASetup = true;
+ *
+ *   if (!b.setup())
+ *       goto fail;
+ *   isBSetup = true;
+ *
+ *   ...
+ *   return true;
+ *
+ *   fail:
+ *     if (isASetup)
+ *         a.teardown();
+ *     if (isBSetup)
+ *         b.teardown();
+ *     return false;
+ *  }
+ *
+ * ScopeExit is a mechanism to simplify this pattern by keeping an RAII guard
+ * class that will perform the teardown on destruction, unless released. So the
+ * above would become:
+ *
+ * {
+ *   if (!a.setup()) {
+ *       return false;
+ *   }
+ *   auto guardA = MakeScopeExit([&] {
+ *       a.teardown();
+ *   });
+ *
+ *   if (!b.setup()) {
+ *       return false;
+ *   }
+ *   auto guardB = MakeScopeExit([&] {
+ *       b.teardown();
+ *   });
+ *
+ *   ...
+ *   guardA.release();
+ *   guardB.release();
+ *   return true;
+ * }
+ *
+ * This header provides:
+ *
+ * - |ScopeExit| - a container for a cleanup call, automically called at the
+ *   end of the scope;
+ * - |MakeScopeExit| - a convenience function for constructing a |ScopeExit|
+ *   with a given cleanup routine, commonly used with a lambda function.
+ *
+ * Note that the RAII classes defined in this header do _not_ perform any form
+ * of reference-counting or garbage-collection. These classes have exactly two
+ * behaviors:
+ *
+ * - if |release()| has not been called, the cleanup is always performed at
+ *   the end of the scope;
+ * - if |release()| has been called, nothing will happen at the end of the
+ *   scope.
+ */
+
+#include "mozilla/GuardObjects.h"
+#include "mozilla/Move.h"
+
+namespace mozilla {
+
+template <typename ExitFunction>
+class ScopeExit {
+  ExitFunction mExitFunction;
+  bool mExecuteOnDestruction;
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+
+public:
+  explicit ScopeExit(ExitFunction&& cleanup
+                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+   : mExitFunction(cleanup)
+   , mExecuteOnDestruction(true)
+  {
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+  }
+
+  ScopeExit(ScopeExit&& rhs)
+   : mExitFunction(mozilla::Move(rhs.mExitFunction))
+   , mExecuteOnDestruction(rhs.mExecuteOnDestruction)
+  {
+    rhs.release();
+  }
+
+  ~ScopeExit() {
+    if (mExecuteOnDestruction) {
+      mExitFunction();
+    }
+  }
+
+  void release() {
+    mExecuteOnDestruction = false;
+  }
+
+private:
+  explicit ScopeExit(const ScopeExit&) = delete;
+  ScopeExit& operator=(const ScopeExit&) = delete;
+  ScopeExit& operator=(ScopeExit&&) = delete;
+};
+
+template <typename ExitFunction>
+ScopeExit<ExitFunction>
+MakeScopeExit(ExitFunction&& exitFunction)
+{
+  return ScopeExit<ExitFunction>(mozilla::Move(exitFunction));
+}
+
+} /* namespace mozilla */
+
+#endif /* mozilla_ScopeExit_h */
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -66,16 +66,17 @@ EXPORTS.mozilla = [
     'RangedArray.h',
     'RangedPtr.h',
     'RefCountType.h',
     'ReentrancyGuard.h',
     'RefPtr.h',
     'ReverseIterator.h',
     'RollingMean.h',
     'Scoped.h',
+    'ScopeExit.h',
     'SegmentedVector.h',
     'SHA1.h',
     'SizePrintfMacros.h',
     'Snprintf.h',
     'SplayTree.h',
     'TaggedAnonymousMemory.h',
     'TemplateLib.h',
     'ThreadLocal.h',
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/TestScopeExit.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Assertions.h"
+#include "mozilla/ScopeExit.h"
+
+using mozilla::MakeScopeExit;
+
+#define CHECK(c) \
+  do { \
+    bool cond = !!(c); \
+    MOZ_RELEASE_ASSERT(cond, "Failed assertion: " #c); \
+    if (!cond) { \
+      return false; \
+    } \
+  } while (false)
+
+static bool
+Test()
+{
+  int a = 1;
+  int b = 1;
+
+  {
+    a++;
+    auto guardA = MakeScopeExit([&] {
+      a--;
+    });
+
+    b++;
+    auto guardB = MakeScopeExit([&] {
+      b--;
+    });
+
+    guardB.release();
+  }
+
+  CHECK(a == 1);
+  CHECK(b == 2);
+
+  return true;
+}
+
+int
+main()
+{
+  if (!Test()) {
+    return 1;
+  }
+  return 0;
+}
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -21,16 +21,17 @@ CppUnitTests([
     'TestIntegerRange',
     'TestJSONWriter',
     'TestMacroArgs',
     'TestMacroForEach',
     'TestMaybe',
     'TestPair',
     'TestRefPtr',
     'TestRollingMean',
+    'TestScopeExit',
     'TestSegmentedVector',
     'TestSHA1',
     'TestSplayTree',
     'TestTemplateLib',
     'TestTuple',
     'TestTypedEnum',
     'TestTypeTraits',
     'TestUniquePtr',