Add a default timeout parameter to HHWheelTimer.
[folly.git] / folly / io / async / HHWheelTimer.h
index b55d88578b0dc28cfd88d819d55d5b1b1c0540d1..0424ac59793b769fa95006a5b5e00c4265c4cfb3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 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.
@@ -20,6 +20,7 @@
 #include <folly/io/async/DelayedDestruction.h>
 
 #include <boost/intrusive/list.hpp>
+#include <glog/logging.h>
 
 #include <chrono>
 #include <cstddef>
@@ -54,7 +55,7 @@ namespace folly {
  * maintaining time and timers, provided that we can maintain
  * a consistent rate of ticks.
  */
-class HHWheelTimer : protected folly::AsyncTimeout,
+class HHWheelTimer : private folly::AsyncTimeout,
                      public folly::DelayedDestruction {
  public:
   typedef std::unique_ptr<HHWheelTimer, Destructor> UniquePtr;
@@ -75,6 +76,13 @@ class HHWheelTimer : protected folly::AsyncTimeout,
      */
     virtual void timeoutExpired() noexcept = 0;
 
+    /// This callback was canceled. The default implementation is to just
+    /// proxy to `timeoutExpired` but if you care about the difference between
+    /// the timeout finishing or being canceled you can override this.
+    virtual void callbackCanceled() noexcept {
+      timeoutExpired();
+    }
+
     /**
      * Cancel the timeout, if it is running.
      *
@@ -95,6 +103,16 @@ class HHWheelTimer : protected folly::AsyncTimeout,
       return wheel_ != nullptr;
     }
 
+   protected:
+    /**
+     * Don't override this unless you're doing a test. This is mainly here so
+     * that we can override it to simulate lag in steady_clock.
+     */
+    virtual std::chrono::milliseconds getCurTime() {
+      return std::chrono::duration_cast<std::chrono::milliseconds>(
+        std::chrono::steady_clock::now().time_since_epoch());
+    }
+
    private:
     // Get the time remaining until this timeout expires
     std::chrono::milliseconds getTimeRemaining(
@@ -130,21 +148,39 @@ class HHWheelTimer : protected folly::AsyncTimeout,
   };
 
   /**
-   * Create a new HHWheelTimer with the specified interval.
+   * Create a new HHWheelTimer with the specified interval and the
+   * default timeout value set.
+   *
+   * Objects created using this version of constructor can be used
+   * to schedule both variable interval timeouts using
+   * scheduleTimeout(callback, timeout) method, and default
+   * interval timeouts using scheduleTimeout(callback) method.
    */
   static int DEFAULT_TICK_INTERVAL;
   explicit HHWheelTimer(folly::EventBase* eventBase,
                         std::chrono::milliseconds intervalMS =
-                        std::chrono::milliseconds(DEFAULT_TICK_INTERVAL));
+                        std::chrono::milliseconds(DEFAULT_TICK_INTERVAL),
+                        AsyncTimeout::InternalEnum internal =
+                        AsyncTimeout::InternalEnum::NORMAL,
+                        std::chrono::milliseconds defaultTimeoutMS =
+                        std::chrono::milliseconds(-1));
 
   /**
    * Destroy the HHWheelTimer.
    *
    * A HHWheelTimer should only be destroyed when there are no more
-   * callbacks pending in the set.
+   * callbacks pending in the set. (If it helps you may use cancelAll() to
+   * cancel all pending timeouts explicitly before calling this.)
    */
   virtual void destroy();
 
+  /**
+   * Cancel all outstanding timeouts
+   *
+   * @returns the number of timeouts that were cancelled.
+   */
+  size_t cancelAll();
+
   /**
    * Get the tick interval for this HHWheelTimer.
    *
@@ -154,6 +190,15 @@ class HHWheelTimer : protected folly::AsyncTimeout,
     return interval_;
   }
 
+  /**
+   * Get the default timeout interval for this HHWheelTimer.
+   *
+   * Returns the timeout interval in milliseconds.
+   */
+  std::chrono::milliseconds getDefaultTimeout() const {
+    return defaultTimeout_;
+  }
+
   /**
    * Schedule the specified Callback to be invoked after the
    * specified timeout interval.
@@ -166,6 +211,39 @@ class HHWheelTimer : protected folly::AsyncTimeout,
   void scheduleTimeoutImpl(Callback* callback,
                        std::chrono::milliseconds timeout);
 
+  /**
+   * Schedule the specified Callback to be invoked after the
+   * fefault timeout interval.
+   *
+   * If the callback is already scheduled, this cancels the existing timeout
+   * before scheduling the new timeout.
+   *
+   * This method uses CHECK() to make sure that the default timeout was
+   * specified on the object initialization.
+   */
+  void scheduleTimeout(Callback* callback);
+
+  template <class F>
+  void scheduleTimeoutFn(F fn, std::chrono::milliseconds timeout) {
+    struct Wrapper : Callback {
+      Wrapper(F f) : fn_(std::move(f)) {}
+      void timeoutExpired() noexcept override {
+        try {
+          fn_();
+        } catch (std::exception const& e) {
+          LOG(ERROR) << "HHWheelTimer timeout callback threw an exception: "
+            << e.what();
+        } catch (...) {
+          LOG(ERROR) << "HHWheelTimer timeout callback threw a non-exception.";
+        }
+        delete this;
+      }
+      F fn_;
+    };
+    Wrapper* w = new Wrapper(std::move(fn));
+    scheduleTimeout(w, timeout);
+  }
+
   /**
    * Return the number of currently pending timeouts
    */
@@ -187,6 +265,10 @@ class HHWheelTimer : protected folly::AsyncTimeout,
     catchupEveryN_ = everyN;
   }
 
+  bool isDetachable() const {
+    return !folly::AsyncTimeout::isScheduled();
+  }
+
   using folly::AsyncTimeout::attachEventBase;
   using folly::AsyncTimeout::detachEventBase;
   using folly::AsyncTimeout::getTimeoutManager;
@@ -209,6 +291,7 @@ class HHWheelTimer : protected folly::AsyncTimeout,
   virtual void timeoutExpired() noexcept;
 
   std::chrono::milliseconds interval_;
+  std::chrono::milliseconds defaultTimeout_;
 
   static constexpr int WHEEL_BUCKETS = 4;
   static constexpr int WHEEL_BITS = 8;
@@ -232,6 +315,7 @@ class HHWheelTimer : protected folly::AsyncTimeout,
 
   uint32_t catchupEveryN_;
   uint32_t expirationsSinceCatchup_;
+  bool processingCallbacksGuard_;
 };
 
 } // folly