/*
- * 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.
#include <folly/io/async/DelayedDestruction.h>
#include <boost/intrusive/list.hpp>
+#include <glog/logging.h>
#include <chrono>
#include <cstddef>
* 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;
*/
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.
*
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(
};
/**
- * 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.
*
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.
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
*/
catchupEveryN_ = everyN;
}
+ bool isDetachable() const {
+ return !folly::AsyncTimeout::isScheduled();
+ }
+
using folly::AsyncTimeout::attachEventBase;
using folly::AsyncTimeout::detachEventBase;
using folly::AsyncTimeout::getTimeoutManager;
virtual void timeoutExpired() noexcept;
std::chrono::milliseconds interval_;
+ std::chrono::milliseconds defaultTimeout_;
static constexpr int WHEEL_BUCKETS = 4;
static constexpr int WHEEL_BITS = 8;
uint32_t catchupEveryN_;
uint32_t expirationsSinceCatchup_;
+ bool processingCallbacksGuard_;
};
} // folly