text: /*
   * Copyright 2015 Facebook, Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  /**
   * This module implements a Synchronized abstraction useful in
   * mutex-based concurrency.
   *
   * @author: Andrei Alexandrescu (andrei.alexandrescu@fb.com)
   */
  
  #ifndef SYNCHRONIZED_H_
  #define SYNCHRONIZED_H_
  
  #include <type_traits>
  #include <mutex>
  #include <boost/thread.hpp>
  #include <folly/Preprocessor.h>
  #include <folly/SharedMutex.h>
  #include <folly/Traits.h>
  
  namespace folly {
  
  namespace detail {
  enum InternalDoNotUse {};
  
  /**
   * Free function adaptors for std:: and boost::
   */
  
  // Android, OSX, and Cygwin don't have timed mutexes
  #if defined(ANDROID) || defined(__ANDROID__) || \
      defined(__APPLE__) || defined(__CYGWIN__)
  # define FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES 0
  #else
  # define FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES 1
  #endif
  
  /**
   * Yields true iff T has .lock() and .unlock() member functions. This
   * is done by simply enumerating the mutexes with this interface in
   * std and boost.
   */
  template <class T>
  struct HasLockUnlock {
    enum { value = IsOneOf<T
        , std::mutex
        , std::recursive_mutex
        , boost::mutex
        , boost::recursive_mutex
        , boost::shared_mutex
  #if FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
        , std::timed_mutex
        , std::recursive_timed_mutex
        , boost::timed_mutex
        , boost::recursive_timed_mutex
  #endif
        >::value };
  };
  
  /**
   * Yields true iff T has .lock_shared() and .unlock_shared() member functions.
   * This is done by simply enumerating the mutexes with this interface.
   */
  template <class T>
  struct HasLockSharedUnlockShared {
    enum { value = IsOneOf<T
        , boost::shared_mutex
        >::value };
  };
  
  /**
   * Acquires a mutex for reading by calling .lock().
   *
   * This variant is not appropriate for shared mutexes.
   */
  template <class T>
  typename std::enable_if<
    HasLockUnlock<T>::value && !HasLockSharedUnlockShared<T>::value>::type
  acquireRead(T& mutex) {
    mutex.lock();
  }
  
  /**
   * Acquires a mutex for reading by calling .lock_shared().
   *
   * This variant is not appropriate for nonshared mutexes.
   */
  template <class T>
  typename std::enable_if<HasLockSharedUnlockShared<T>::value>::type
  acquireRead(T& mutex) {
    mutex.lock_shared();
  }
  
  /**
   * Acquires a mutex for reading and writing by calling .lock().
   */
  template <class T>
  typename std::enable_if<HasLockUnlock<T>::value>::type
  acquireReadWrite(T& mutex) {
    mutex.lock();
  }
  
  #if FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
  /**
   * Acquires a mutex for reading by calling .try_lock_shared_for(). This applies
   * to boost::shared_mutex.
   */
  template <class T>
  typename std::enable_if<
    IsOneOf<T
        , boost::shared_mutex
        >::value, bool>::type
  acquireRead(T& mutex,
              unsigned int milliseconds) {
    return mutex.try_lock_shared_for(boost::chrono::milliseconds(milliseconds));
  }
  
  /**
   * Acquires a mutex for reading and writing with timeout by calling
   * .try_lock_for(). This applies to two of the std mutex classes as
   * enumerated below.
   */
  template <class T>
  typename std::enable_if<
    IsOneOf<T
        , std::timed_mutex
        , std::recursive_timed_mutex
        >::value, bool>::type
  acquireReadWrite(T& mutex,
                   unsigned int milliseconds) {
    // work around try_lock_for bug in some gcc versions, see
    // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54562
    // TODO: Fixed in gcc-4.9.0.
    return mutex.try_lock()
        || (milliseconds > 0 &&
            mutex.try_lock_until(std::chrono::system_clock::now() +
                                 std::chrono::milliseconds(milliseconds)));
  }
  
  /**
   * Acquires a mutex for reading and writing with timeout by calling
   * .try_lock_for(). This applies to three of the boost mutex classes as
   * enumerated below.
   */
  template <class T>
  typename std::enable_if<
    IsOneOf<T
        , boost::shared_mutex
        , boost::timed_mutex
        , boost::recursive_timed_mutex
        >::value, bool>::type
  acquireReadWrite(T& mutex,
                   unsigned int milliseconds) {
    return mutex.try_lock_for(boost::chrono::milliseconds(milliseconds));
  }
  #endif // FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
  
  /**
   * Releases a mutex previously acquired for reading by calling
   * .unlock(). The exception is boost::shared_mutex, which has a
   * special primitive called .unlock_shared().
   */
  template <class T>
  typename std::enable_if<
    HasLockUnlock<T>::value && !HasLockSharedUnlockShared<T>::value>::type
  releaseRead(T& mutex) {
    mutex.unlock();
  }
  
  /**
   * Special case for boost::shared_mutex.
   */
  template <class T>
  typename std::enable_if<HasLockSharedUnlockShared<T>::value>::type
  releaseRead(T& mutex) {
    mutex.unlock_shared();
  }
  
  /**
   * Releases a mutex previously acquired for reading-writing by calling
   * .unlock().
   */
  template <class T>
  typename std::enable_if<HasLockUnlock<T>::value>::type
  releaseReadWrite(T& mutex) {
    mutex.unlock();
  }
  
  } // namespace detail
  
  /**
   * Synchronized<T> encapsulates an object of type T (a "datum") paired
   * with a mutex. The only way to access the datum is while the mutex
   * is locked, and Synchronized makes it virtually impossible to do
   * otherwise. The code that would access the datum in unsafe ways
   * would look odd and convoluted, thus readily alerting the human
   * reviewer. In contrast, the code that uses Synchronized<T> correctly
   * looks simple and intuitive.
   *
   * The second parameter must be a mutex type. Supported mutexes are
   * std::mutex, std::recursive_mutex, std::timed_mutex,
   * std::recursive_timed_mutex, boost::mutex, boost::recursive_mutex,
   * boost::shared_mutex, boost::timed_mutex,
   * boost::recursive_timed_mutex, and the folly/RWSpinLock.h
   * classes.
   *
   * You may define Synchronized support by defining 4-6 primitives in
   * the same namespace as the mutex class (found via ADL).  The
   * primitives are: acquireRead, acquireReadWrite, releaseRead, and
   * releaseReadWrite. Two optional primitives for timout operations are
   * overloads of acquireRead and acquireReadWrite. For signatures,
   * refer to the namespace detail below, which implements the
   * primitives for mutexes in std and boost.
   */
  template <class T, class Mutex = SharedMutex>
  struct Synchronized {
    /**
     * Default constructor leaves both members call their own default
     * constructor.
     */
    Synchronized() = default;
  
   private:
    static constexpr bool nxCopyCtor{
        std::is_nothrow_copy_constructible<T>::value};
    static constexpr bool nxMoveCtor{
        std::is_nothrow_move_constructible<T>::value};
  
    /**
     * Helper constructors to enable Synchronized for
     * non-default constructible types T.
     * Guards are created in actual public constructors and are alive
     * for the time required to construct the object
     */
    template <typename Guard>
    Synchronized(const Synchronized& rhs,
                 const Guard& /*guard*/) noexcept(nxCopyCtor)
        : datum_(rhs.datum_) {}
  
    template <typename Guard>
    Synchronized(Synchronized&& rhs, const Guard& /*guard*/) noexcept(nxMoveCtor)
        : datum_(std::move(rhs.datum_)) {}
  
   public:
    /**
     * Copy constructor copies the data (with locking the source and
     * all) but does NOT copy the mutex. Doing so would result in
     * deadlocks.
     */
    Synchronized(const Synchronized& rhs) noexcept(nxCopyCtor)
        : Synchronized(rhs, rhs.operator->()) {}
  
    /**
     * Move constructor moves the data (with locking the source and all)
     * but does not move the mutex.
     */
    Synchronized(Synchronized&& rhs) noexcept(nxMoveCtor)
        : Synchronized(std::move(rhs), rhs.operator->()) {}
  
    /**
     * Constructor taking a datum as argument copies it. There is no
     * need to lock the constructing object.
     */
    explicit Synchronized(const T& rhs) noexcept(nxCopyCtor) : datum_(rhs) {}
  
    /**
     * Constructor taking a datum rvalue as argument moves it. Again,
     * there is no need to lock the constructing object.
     */
    explicit Synchronized(T&& rhs) noexcept(nxMoveCtor)
        : datum_(std::move(rhs)) {}
  
    /**
     * Lets you construct non-movable types in-place. Use the constexpr
     * instance `construct_in_place` as the first argument.
     */
    template <typename... Args>
    explicit Synchronized(construct_in_place_t, Args&&... args)
        : datum_(std::forward<Args>(args)...) {}
  
    /**
     * The canonical assignment operator only assigns the data, NOT the
     * mutex. It locks the two objects in ascending order of their
     * addresses.
     */
    Synchronized& operator=(const Synchronized& rhs) {
      if (this == &rhs) {
        // Self-assignment, pass.
      } else if (this < &rhs) {
        auto guard1 = operator->();
        auto guard2 = rhs.operator->();
        datum_ = rhs.datum_;
      } else {
        auto guard1 = rhs.operator->();
        auto guard2 = operator->();
        datum_ = rhs.datum_;
      }
      return *this;
    }
  
    /**
     * Move assignment operator, only assigns the data, NOT the
     * mutex. It locks the two objects in ascending order of their
     * addresses.
     */
    Synchronized& operator=(Synchronized&& rhs) {
      if (this == &rhs) {
        // Self-assignment, pass.
      } else if (this < &rhs) {
        auto guard1 = operator->();
        auto guard2 = rhs.operator->();
        datum_ = std::move(rhs.datum_);
      } else {
        auto guard1 = rhs.operator->();
        auto guard2 = operator->();
        datum_ = std::move(rhs.datum_);
      }
      return *this;
    }
  
    /**
     * Lock object, assign datum.
     */
    Synchronized& operator=(const T& rhs) {
      auto guard = operator->();
      datum_ = rhs;
      return *this;
    }
  
    /**
     * Lock object, move-assign datum.
     */
    Synchronized& operator=(T&& rhs) {
      auto guard = operator->();
      datum_ = std::move(rhs);
      return *this;
    }
  
    /**
     * A LockedPtr lp keeps a modifiable (i.e. non-const)
     * Synchronized<T> object locked for the duration of lp's
     * existence. Because of this, you get to access the datum's methods
     * directly by using lp->fun().
     */
    struct LockedPtr {
      /**
       * Found no reason to leave this hanging.
       */
      LockedPtr() = delete;
  
      /**
       * Takes a Synchronized and locks it.
       */
      explicit LockedPtr(Synchronized* parent) : parent_(parent) {
        acquire();
      }
  
      /**
       * Takes a Synchronized and attempts to lock it for some
       * milliseconds. If not, the LockedPtr will be subsequently null.
       */
      LockedPtr(Synchronized* parent, unsigned int milliseconds) {
        using namespace detail;
        if (acquireReadWrite(parent->mutex_, milliseconds)) {
          parent_ = parent;
          return;
        }
        // Could not acquire the resource, pointer is null
        parent_ = nullptr;
      }
  
      /**
       * This is used ONLY inside SYNCHRONIZED_DUAL. It initializes
       * everything properly, but does not lock the parent because it
       * "knows" someone else will lock it. Please do not use.
       */
      LockedPtr(Synchronized* parent, detail::InternalDoNotUse)
          : parent_(parent) {
      }
  
      /**
       * Copy ctor adds one lock.
       */
      LockedPtr(const LockedPtr& rhs) : parent_(rhs.parent_) {
        acquire();
      }
  
      /**
       * Assigning from another LockedPtr results in freeing the former
       * lock and acquiring the new one. The method works with
       * self-assignment (does nothing).
       */
      LockedPtr& operator=(const LockedPtr& rhs) {
        if (parent_ != rhs.parent_) {
          if (parent_) parent_->mutex_.unlock();
          parent_ = rhs.parent_;
          acquire();
        }
        return *this;
      }
  
      /**
       * Destructor releases.
       */
      ~LockedPtr() {
        using namespace detail;
        if (parent_) releaseReadWrite(parent_->mutex_);
      }
  
      /**
       * Safe to access the data. Don't save the obtained pointer by
       * invoking lp.operator->() by hand. Also, if the method returns a
       * handle stored inside the datum, don't use this idiom - use
       * SYNCHRONIZED below.
       */
      T* operator->() {
        return parent_ ? &parent_->datum_ : nullptr;
      }
  
      /**
       * This class temporarily unlocks a LockedPtr in a scoped
       * manner. It is used inside of the UNSYNCHRONIZED macro.
       */
      struct Unsynchronizer {
        explicit Unsynchronizer(LockedPtr* p) : parent_(p) {
          using namespace detail;
          releaseReadWrite(parent_->parent_->mutex_);
        }
        Unsynchronizer(const Unsynchronizer&) = delete;
        Unsynchronizer& operator=(const Unsynchronizer&) = delete;
        ~Unsynchronizer() {
          parent_->acquire();
        }
        LockedPtr* operator->() const {
          return parent_;
        }
      private:
        LockedPtr* parent_;
      };
      friend struct Unsynchronizer;
      Unsynchronizer typeHackDoNotUse();
  
      template <class P1, class P2>
      friend void lockInOrder(P1& p1, P2& p2);
  
    private:
      void acquire() {
        using namespace detail;
        if (parent_) acquireReadWrite(parent_->mutex_);
      }
  
      // This is the entire state of LockedPtr.
      Synchronized* parent_;
    };
  
    /**
     * ConstLockedPtr does exactly what LockedPtr does, but for const
     * Synchronized objects. Of interest is that ConstLockedPtr only
     * uses a read lock, which is faster but more restrictive - you only
     * get to call const methods of the datum.
     *
     * Much of the code between LockedPtr and
     * ConstLockedPtr is identical and could be factor out, but there
     * are enough nagging little differences to not justify the trouble.
     */
    struct ConstLockedPtr {
      ConstLockedPtr() = delete;
      explicit ConstLockedPtr(const Synchronized* parent) : parent_(parent) {
        acquire();
      }
      ConstLockedPtr(const Synchronized* parent, detail::InternalDoNotUse)
          : parent_(parent) {
      }
      ConstLockedPtr(const ConstLockedPtr& rhs) : parent_(rhs.parent_) {
        acquire();
      }
      explicit ConstLockedPtr(const LockedPtr& rhs) : parent_(rhs.parent_) {
        acquire();
      }
      ConstLockedPtr(const Synchronized* parent, unsigned int milliseconds) {
        using namespace detail;
        if (acquireRead(
              parent->mutex_,
              milliseconds)) {
          parent_ = parent;
          return;
        }
        // Could not acquire the resource, pointer is null
        parent_ = nullptr;
      }
  
      ConstLockedPtr& operator=(const ConstLockedPtr& rhs) {
        if (parent_ != rhs.parent_) {
          if (parent_) parent_->mutex_.unlock_shared();
          parent_ = rhs.parent_;
          acquire();
        }
      }
      ~ConstLockedPtr() {
        using namespace detail;
        if (parent_) releaseRead(parent_->mutex_);
      }
  
      const T* operator->() const {
        return parent_ ? &parent_->datum_ : nullptr;
      }
  
      struct Unsynchronizer {
        explicit Unsynchronizer(ConstLockedPtr* p) : parent_(p) {
          using namespace detail;
          releaseRead(parent_->parent_->mutex_);
        }
        Unsynchronizer(const Unsynchronizer&) = delete;
        Unsynchronizer& operator=(const Unsynchronizer&) = delete;
        ~Unsynchronizer() {
          using namespace detail;
          acquireRead(parent_->parent_->mutex_);
        }
        ConstLockedPtr* operator->() const {
          return parent_;
        }
      private:
        ConstLockedPtr* parent_;
      };
      friend struct Unsynchronizer;
      Unsynchronizer typeHackDoNotUse();
  
      template <class P1, class P2>
      friend void lockInOrder(P1& p1, P2& p2);
  
    private:
      void acquire() {
        using namespace detail;
        if (parent_) acquireRead(parent_->mutex_);
      }
  
      const Synchronized* parent_;
    };
  
    /**
     * This accessor offers a LockedPtr. In turn. LockedPtr offers
     * operator-> returning a pointer to T. The operator-> keeps
     * expanding until it reaches a pointer, so syncobj->foo() will lock
     * the object and call foo() against it.
    */
    LockedPtr operator->() {
      return LockedPtr(this);
    }
  
    /**
     * Same, for constant objects. You will be able to invoke only const
     * methods.
     */
    ConstLockedPtr operator->() const {
      return ConstLockedPtr(this);
    }
  
    /**
     * Attempts to acquire for a given number of milliseconds. If
     * acquisition is unsuccessful, the returned LockedPtr is NULL.
     */
    LockedPtr timedAcquire(unsigned int milliseconds) {
      return LockedPtr(this, milliseconds);
    }
  
    /**
     * As above, for a constant object.
     */
    ConstLockedPtr timedAcquire(unsigned int milliseconds) const {
      return ConstLockedPtr(this, milliseconds);
    }
  
    /**
     * Used by SYNCHRONIZED_DUAL.
     */
    LockedPtr internalDoNotUse() {
      return LockedPtr(this, detail::InternalDoNotUse());
    }
  
    /**
     * ditto
     */
    ConstLockedPtr internalDoNotUse() const {
      return ConstLockedPtr(this, detail::InternalDoNotUse());
    }
  
    /**
     * Sometimes, although you have a mutable object, you only want to
     * call a const method against it. The most efficient way to achieve
     * that is by using a read lock. You get to do so by using
     * obj.asConst()->method() instead of obj->method().
     */
    const Synchronized& asConst() const {
      return *this;
    }
  
    /**
     * Swaps with another Synchronized. Protected against
     * self-swap. Only data is swapped. Locks are acquired in increasing
     * address order.
     */
    void swap(Synchronized& rhs) {
      if (this == &rhs) {
        return;
      }
      if (this > &rhs) {
        return rhs.swap(*this);
      }
      auto guard1 = operator->();
      auto guard2 = rhs.operator->();
  
      using std::swap;
      swap(datum_, rhs.datum_);
    }
  
    /**
     * Swap with another datum. Recommended because it keeps the mutex
     * held only briefly.
     */
    void swap(T& rhs) {
      LockedPtr guard = operator->();
  
      using std::swap;
      swap(datum_, rhs);
    }
  
    /**
     * Copies datum to a given target.
     */
    void copy(T* target) const {
      ConstLockedPtr guard = operator->();
      *target = datum_;
    }
  
    /**
     * Returns a fresh copy of the datum.
     */
    T copy() const {
      ConstLockedPtr guard = operator->();
      return datum_;
    }
  
  private:
    T datum_;
    mutable Mutex mutex_;
  };
  
  // Non-member swap primitive
  template <class T, class M>
  void swap(Synchronized<T, M>& lhs, Synchronized<T, M>& rhs) {
    lhs.swap(rhs);
  }
  
  /**
   * SYNCHRONIZED is the main facility that makes Synchronized<T>
   * helpful. It is a pseudo-statement that introduces a scope where the
   * object is locked. Inside that scope you get to access the unadorned
   * datum.
   *
   * Example:
   *
   * Synchronized<vector<int>> svector;
   * ...
   * SYNCHRONIZED (svector) { ... use svector as a vector<int> ... }
   * or
   * SYNCHRONIZED (v, svector) { ... use v as a vector<int> ... }
   *
   * Refer to folly/docs/Synchronized.md for a detailed explanation and more
   * examples.
   */
  #define SYNCHRONIZED(...)                                       \
    _Pragma("GCC diagnostic push")                                \
    _Pragma("GCC diagnostic ignored \"-Wshadow\"")                \
    if (bool SYNCHRONIZED_state = false) {} else                  \
      for (auto SYNCHRONIZED_lockedPtr =                          \
             (FB_ARG_2_OR_1(__VA_ARGS__)).operator->();           \
           !SYNCHRONIZED_state; SYNCHRONIZED_state = true)        \
        for (auto& FB_ARG_1(__VA_ARGS__) =                        \
               *SYNCHRONIZED_lockedPtr.operator->();              \
             !SYNCHRONIZED_state; SYNCHRONIZED_state = true)      \
    _Pragma("GCC diagnostic pop")
  
  #define TIMED_SYNCHRONIZED(timeout, ...)                           \
    if (bool SYNCHRONIZED_state = false) {} else                     \
      for (auto SYNCHRONIZED_lockedPtr =                             \
             (FB_ARG_2_OR_1(__VA_ARGS__)).timedAcquire(timeout);     \
           !SYNCHRONIZED_state; SYNCHRONIZED_state = true)           \
        for (auto FB_ARG_1(__VA_ARGS__) =                            \
               SYNCHRONIZED_lockedPtr.operator->();                  \
             !SYNCHRONIZED_state; SYNCHRONIZED_state = true)
  
  /**
   * Similar to SYNCHRONIZED, but only uses a read lock.
   */
  #define SYNCHRONIZED_CONST(...)                         \
    SYNCHRONIZED(FB_ARG_1(__VA_ARGS__),                   \
                 (FB_ARG_2_OR_1(__VA_ARGS__)).asConst())
  
  /**
   * Similar to TIMED_SYNCHRONIZED, but only uses a read lock.
   */
  #define TIMED_SYNCHRONIZED_CONST(timeout, ...)                  \
    TIMED_SYNCHRONIZED(timeout, FB_ARG_1(__VA_ARGS__),            \
                       (FB_ARG_2_OR_1(__VA_ARGS__)).asConst())
  
  /**
   * Temporarily disables synchronization inside a SYNCHRONIZED block.
   */
  #define UNSYNCHRONIZED(name)                                    \
    for (decltype(SYNCHRONIZED_lockedPtr.typeHackDoNotUse())      \
           SYNCHRONIZED_state3(&SYNCHRONIZED_lockedPtr);          \
         !SYNCHRONIZED_state; SYNCHRONIZED_state = true)          \
      for (auto& name = *SYNCHRONIZED_state3.operator->();        \
           !SYNCHRONIZED_state; SYNCHRONIZED_state = true)
  
  /**
   * Locks two objects in increasing order of their addresses.
   */
  template <class P1, class P2>
  void lockInOrder(P1& p1, P2& p2) {
    if (static_cast<const void*>(p1.operator->()) >
        static_cast<const void*>(p2.operator->())) {
      p2.acquire();
      p1.acquire();
    } else {
      p1.acquire();
      p2.acquire();
    }
  }
  
  /**
   * Synchronizes two Synchronized objects (they may encapsulate
   * different data). Synchronization is done in increasing address of
   * object order, so there is no deadlock risk.
   */
  #define SYNCHRONIZED_DUAL(n1, e1, n2, e2)                       \
    if (bool SYNCHRONIZED_state = false) {} else                  \
      for (auto SYNCHRONIZED_lp1 = (e1).internalDoNotUse();       \
           !SYNCHRONIZED_state; SYNCHRONIZED_state = true)        \
        for (auto& n1 = *SYNCHRONIZED_lp1.operator->();           \
             !SYNCHRONIZED_state;  SYNCHRONIZED_state = true)     \
          for (auto SYNCHRONIZED_lp2 = (e2).internalDoNotUse();   \
               !SYNCHRONIZED_state;  SYNCHRONIZED_state = true)   \
            for (auto& n2 = *SYNCHRONIZED_lp2.operator->();       \
                 !SYNCHRONIZED_state; SYNCHRONIZED_state = true)  \
              if ((::folly::lockInOrder(                          \
                     SYNCHRONIZED_lp1, SYNCHRONIZED_lp2),         \
                   false)) {}                                     \
              else
  
  } /* namespace folly */
  
  #endif // SYNCHRONIZED_H_
  