text: /* -*- 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 <utility>
  
  #include "mozilla/Attributes.h"
  
  namespace mozilla {
  
  template <typename ExitFunction>
  class MOZ_STACK_CLASS ScopeExit {
    ExitFunction mExitFunction;
    bool mExecuteOnDestruction;
  
   public:
    explicit ScopeExit(ExitFunction&& cleanup)
        : mExitFunction(cleanup), mExecuteOnDestruction(true) {}
  
    ScopeExit(ScopeExit&& rhs)
        : mExitFunction(std::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>
  [[nodiscard]] ScopeExit<ExitFunction> MakeScopeExit(
      ExitFunction&& exitFunction) {
    return ScopeExit<ExitFunction>(std::move(exitFunction));
  }
  
  } /* namespace mozilla */
  
  #endif /* mozilla_ScopeExit_h */
  