Adding a unit test for HHWheelTimer exercising the default timeout functionality.
[folly.git] / folly / Synchronized.h
index 94474037693c15608dadbc73035871cff8a41462..4fc47afd5ed7e22cb4afb9a04453df09e2b1b8b8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Facebook, Inc.
+ * 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.
@@ -27,8 +27,8 @@
 #include <type_traits>
 #include <mutex>
 #include <boost/thread.hpp>
-#include "folly/Preprocessor.h"
-#include "folly/Traits.h"
+#include <folly/Preprocessor.h>
+#include <folly/Traits.h>
 
 namespace folly {
 
@@ -39,6 +39,14 @@ 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
@@ -48,9 +56,11 @@ template <class T>
 struct HasLockUnlock {
   enum { value = IsOneOf<T,
          std::mutex, std::recursive_mutex,
-         std::timed_mutex, std::recursive_timed_mutex,
-         boost::mutex, boost::recursive_mutex, boost::shared_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 };
 };
 
@@ -95,6 +105,7 @@ acquireReadWrite(T& mutex) {
   mutex.lock();
 }
 
+#if FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
 /**
  * Acquires a mutex for reading and writing with timeout by calling
  * .try_lock_for(). This applies to two of the std mutex classes as
@@ -105,7 +116,12 @@ typename std::enable_if<
   IsOneOf<T, std::timed_mutex, std::recursive_timed_mutex>::value, bool>::type
 acquireReadWrite(T& mutex,
                  unsigned int milliseconds) {
-  return mutex.try_lock_for(std::chrono::milliseconds(milliseconds));
+  // work around try_lock_for bug in some gcc versions, see
+  // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54562
+  return mutex.try_lock()
+      || (milliseconds > 0 &&
+          mutex.try_lock_until(std::chrono::system_clock::now() +
+                               std::chrono::milliseconds(milliseconds)));
 }
 
 /**
@@ -121,6 +137,7 @@ acquireReadWrite(T& mutex,
                  unsigned int milliseconds) {
   return mutex.timed_lock(boost::posix_time::milliseconds(milliseconds));
 }
+#endif // FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES
 
 /**
  * Releases a mutex previously acquired for reading by calling
@@ -186,37 +203,56 @@ struct Synchronized {
    * 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) {
-    auto guard = rhs.operator->();
-    datum_ = rhs.datum_;
-  }
+  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) {
-    auto guard = rhs.operator->();
-    datum_ = std::move(rhs.datum_);
-  }
+  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) : datum_(rhs) {}
+  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) : datum_(std::move(rhs)) {}
+  explicit Synchronized(T&& rhs) noexcept(nxMoveCtor)
+      : datum_(std::move(rhs)) {}
 
   /**
    * The canonical assignment operator only assigns the data, NOT the
@@ -224,7 +260,9 @@ struct Synchronized {
    * addresses.
    */
   Synchronized& operator=(const Synchronized& rhs) {
-    if (this < *rhs) {
+    if (this == &rhs) {
+      // Self-assignment, pass.
+    } else if (this < &rhs) {
       auto guard1 = operator->();
       auto guard2 = rhs.operator->();
       datum_ = rhs.datum_;
@@ -236,6 +274,26 @@ struct Synchronized {
     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.
    */
@@ -245,6 +303,15 @@ struct Synchronized {
     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
@@ -275,7 +342,7 @@ struct Synchronized {
         return;
       }
       // Could not acquire the resource, pointer is null
-      parent_ = NULL;
+      parent_ = nullptr;
     }
 
     /**
@@ -323,7 +390,7 @@ struct Synchronized {
      * SYNCHRONIZED below.
      */
     T* operator->() {
-      return parent_ ? &parent_->datum_ : NULL;
+      return parent_ ? &parent_->datum_ : nullptr;
     }
 
     /**
@@ -387,13 +454,13 @@ struct Synchronized {
       acquire();
     }
     ConstLockedPtr(const Synchronized* parent, unsigned int milliseconds) {
-      if (parent->mutex_.timed_lock(
+      if (parent->mutex_.timed_lock_shared(
             boost::posix_time::milliseconds(milliseconds))) {
         parent_ = parent;
         return;
       }
       // Could not acquire the resource, pointer is null
-      parent_ = NULL;
+      parent_ = nullptr;
     }
 
     ConstLockedPtr& operator=(const ConstLockedPtr& rhs) {
@@ -409,7 +476,7 @@ struct Synchronized {
     }
 
     const T* operator->() const {
-      return parent_ ? &parent_->datum_ : NULL;
+      return parent_ ? &parent_->datum_ : nullptr;
     }
 
     struct Unsynchronizer {
@@ -515,7 +582,9 @@ struct Synchronized {
     }
     auto guard1 = operator->();
     auto guard2 = rhs.operator->();
-    datum_.swap(rhs.datum_);
+
+    using std::swap;
+    swap(datum_, rhs.datum_);
   }
 
   /**
@@ -524,7 +593,9 @@ struct Synchronized {
    */
   void swap(T& rhs) {
     LockedPtr guard = operator->();
-    datum_.swap(rhs);
+
+    using std::swap;
+    swap(datum_, rhs);
   }
 
   /**