Futex::futexWait returns FutexResult
[folly.git] / folly / detail / MemoryIdler.h
index 5898b07f5d6ddd4ea5166c9677764043356a5a4f..caac253e014be4bd99a749f74f51a90a85daa216 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2014-present Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
-#ifndef FOLLY_DETAIL_MEMORYIDLER_H
-#define FOLLY_DETAIL_MEMORYIDLER_H
+#pragma once
 
 #include <atomic>
 #include <chrono>
-#include <folly/AtomicStruct.h>
-#include <folly/Hash.h>
-#include <folly/Traits.h>
-#include "Futex.h"
 
-namespace folly {
-
-// gcc 4.7 doesn't do std::is_trivial correctly, override so we can use
-// AtomicStruct<duration>
-template<>
-struct IsTriviallyCopyable<std::chrono::steady_clock::duration>
-  : std::true_type {};
-
-}
+#include <folly/detail/Futex.h>
+#include <folly/hash/Hash.h>
+#include <folly/synchronization/AtomicStruct.h>
+#include <folly/system/ThreadId.h>
 
 namespace folly { namespace detail {
 
@@ -74,6 +64,31 @@ struct MemoryIdler {
   /// avoid synchronizing their flushes.
   static AtomicStruct<std::chrono::steady_clock::duration> defaultIdleTimeout;
 
+  /// Selects a timeout pseudo-randomly chosen to be between
+  /// idleTimeout and idleTimeout * (1 + timeoutVariationFraction), to
+  /// smooth out the behavior in a bursty system
+  template <typename Clock = std::chrono::steady_clock>
+  static typename Clock::duration getVariationTimeout(
+      typename Clock::duration idleTimeout
+          = defaultIdleTimeout.load(std::memory_order_acquire),
+      float timeoutVariationFrac = 0.5) {
+    if (idleTimeout.count() > 0 && timeoutVariationFrac > 0) {
+      // hash the pthread_t and the time to get the adjustment.
+      // Standard hash func isn't very good, so bit mix the result
+      auto pr = std::make_pair(getCurrentThreadID(),
+                               Clock::now().time_since_epoch().count());
+      std::hash<decltype(pr)> hash_fn;
+      uint64_t h = folly::hash::twang_mix64(hash_fn(pr));
+
+      // multiplying the duration by a floating point doesn't work, grr..
+      auto extraFrac =
+        timeoutVariationFrac / std::numeric_limits<uint64_t>::max() * h;
+      auto tics = uint64_t(idleTimeout.count() * (1 + extraFrac));
+      idleTimeout = typename Clock::duration(tics);
+    }
+
+    return idleTimeout;
+  }
 
   /// Equivalent to fut.futexWait(expected, waitMask), but calls
   /// flushLocalMallocCaches() and unmapUnusedStack(stackToRetain)
@@ -84,42 +99,27 @@ struct MemoryIdler {
   /// (1 + timeoutVariationFraction), to smooth out the behavior in a
   /// system with bursty requests.  The default is to wait up to 50%
   /// extra, so on average 25% extra
-  template <template <typename> class Atom,
-            typename Clock = std::chrono::steady_clock>
-  static bool futexWait(
+  template <
+      template <typename> class Atom,
+      typename Clock = std::chrono::steady_clock>
+  static FutexResult futexWait(
       Futex<Atom>& fut,
       uint32_t expected,
       uint32_t waitMask = -1,
-      typename Clock::duration idleTimeout
-          defaultIdleTimeout.load(std::memory_order_acquire),
+      typename Clock::duration idleTimeout =
+          defaultIdleTimeout.load(std::memory_order_acquire),
       size_t stackToRetain = kDefaultStackToRetain,
       float timeoutVariationFrac = 0.5) {
-
     if (idleTimeout == Clock::duration::max()) {
       // no need to use futexWaitUntil if no timeout is possible
       return fut.futexWait(expected, waitMask);
     }
 
+    idleTimeout = getVariationTimeout(idleTimeout, timeoutVariationFrac);
     if (idleTimeout.count() > 0) {
-      auto begin = Clock::now();
-
-      if (timeoutVariationFrac > 0) {
-        // hash the pthread_t and the time to get the adjustment.
-        // Standard hash func isn't very good, so bit mix the result
-        auto pr = std::make_pair(pthread_self(),
-                                 begin.time_since_epoch().count());
-        std::hash<decltype(pr)> hash_fn;
-        uint64_t h = folly::hash::twang_mix64(hash_fn(pr));
-
-        // multiplying the duration by a floating point doesn't work, grr..
-        auto extraFrac =
-            timeoutVariationFrac / std::numeric_limits<uint64_t>::max() * h;
-        uint64_t tics = idleTimeout.count() * (1 + extraFrac);
-        idleTimeout = typename Clock::duration(tics);
-      }
-
       while (true) {
-        auto rv = fut.futexWaitUntil(expected, begin + idleTimeout, waitMask);
+        auto rv = fut.futexWaitUntil(
+          expected, Clock::now() + idleTimeout, waitMask);
         if (rv == FutexResult::TIMEDOUT) {
           // timeout is over
           break;
@@ -127,7 +127,7 @@ struct MemoryIdler {
         // finished before timeout hit, no flush
         assert(rv == FutexResult::VALUE_CHANGED || rv == FutexResult::AWOKEN ||
                rv == FutexResult::INTERRUPTED);
-        return rv == FutexResult::AWOKEN;
+        return rv;
       }
     }
 
@@ -138,6 +138,5 @@ struct MemoryIdler {
   }
 };
 
-}} // namespace folly::detail
-
-#endif
+} // namespace detail
+} // namespace folly