dcsctp: Add Timer and TimerManager

Timer is a high-level timer (in contrast to the low-level `Timeout`
class). Timers are started and can be stopped or restarted. When a timer
expires, the provided callback will be triggered.

Timers can be configured to do e.g. exponential backoff when they expire
and how many times they should be automatically restarted.

Bug: webrtc:12614
Change-Id: Id5eddd58dd0af62184b10dd1f98e3e886e3f1d50
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/213350
Reviewed-by: Tommi <tommi@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33666}
diff --git a/net/dcsctp/BUILD.gn b/net/dcsctp/BUILD.gn
index 8510f42..ff93f7e 100644
--- a/net/dcsctp/BUILD.gn
+++ b/net/dcsctp/BUILD.gn
@@ -16,6 +16,7 @@
       "common:dcsctp_common_unittests",
       "packet:dcsctp_packet_unittests",
       "public:dcsctp_public_unittests",
+      "timer:dcsctp_timer_unittests",
     ]
   }
 }
diff --git a/net/dcsctp/timer/BUILD.gn b/net/dcsctp/timer/BUILD.gn
new file mode 100644
index 0000000..845504e
--- /dev/null
+++ b/net/dcsctp/timer/BUILD.gn
@@ -0,0 +1,41 @@
+# Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS.  All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+import("../../../webrtc.gni")
+
+rtc_library("timer") {
+  deps = [
+    "../public:types",
+    "//api:array_view",
+    "//rtc_base",
+    "//rtc_base:checks",
+    "//rtc_base:rtc_base_approved",
+  ]
+  sources = [
+    "fake_timeout.h",
+    "timer.cc",
+    "timer.h",
+  ]
+}
+
+if (rtc_include_tests) {
+  rtc_library("dcsctp_timer_unittests") {
+    testonly = true
+
+    defines = []
+    deps = [
+      ":timer",
+      "//api:array_view",
+      "//rtc_base:checks",
+      "//rtc_base:gunit_helpers",
+      "//rtc_base:rtc_base_approved",
+      "//test:test_support",
+    ]
+    sources = [ "timer_test.cc" ]
+  }
+}
diff --git a/net/dcsctp/timer/fake_timeout.h b/net/dcsctp/timer/fake_timeout.h
new file mode 100644
index 0000000..06e3085
--- /dev/null
+++ b/net/dcsctp/timer/fake_timeout.h
@@ -0,0 +1,94 @@
+/*
+ *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef NET_DCSCTP_TIMER_FAKE_TIMEOUT_H_
+#define NET_DCSCTP_TIMER_FAKE_TIMEOUT_H_
+
+#include <cstdint>
+#include <functional>
+#include <limits>
+#include <memory>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "net/dcsctp/public/timeout.h"
+
+namespace dcsctp {
+
+// A timeout used in tests.
+class FakeTimeout : public Timeout {
+ public:
+  explicit FakeTimeout(std::function<TimeMs()> get_time,
+                       std::function<void(FakeTimeout*)> on_delete)
+      : get_time_(std::move(get_time)), on_delete_(std::move(on_delete)) {}
+
+  ~FakeTimeout() override { on_delete_(this); }
+
+  void Start(DurationMs duration_ms, TimeoutID timeout_id) override {
+    timeout_id_ = timeout_id;
+    expiry_ = TimeMs(*get_time_() + *duration_ms);
+  }
+  void Stop() override { expiry_ = InfiniteFuture(); }
+
+  bool EvaluateHasExpired(TimeMs now) {
+    if (now >= expiry_) {
+      expiry_ = InfiniteFuture();
+      return true;
+    }
+    return false;
+  }
+
+  TimeoutID timeout_id() const { return timeout_id_; }
+
+ private:
+  static constexpr TimeMs InfiniteFuture() {
+    return TimeMs(std::numeric_limits<TimeMs::UnderlyingType>::max());
+  }
+
+  const std::function<TimeMs()> get_time_;
+  const std::function<void(FakeTimeout*)> on_delete_;
+
+  TimeoutID timeout_id_ = TimeoutID(0);
+  TimeMs expiry_ = InfiniteFuture();
+};
+
+class FakeTimeoutManager {
+ public:
+  // The `get_time` function must return the current time, relative to any
+  // epoch.
+  explicit FakeTimeoutManager(std::function<TimeMs()> get_time)
+      : get_time_(std::move(get_time)) {}
+
+  std::unique_ptr<Timeout> CreateTimeout() {
+    auto timer = std::make_unique<FakeTimeout>(
+        get_time_, [this](FakeTimeout* timer) { timers_.erase(timer); });
+    timers_.insert(timer.get());
+    return timer;
+  }
+
+  std::vector<TimeoutID> RunTimers() {
+    TimeMs now = get_time_();
+    std::vector<TimeoutID> expired_timers;
+    for (auto& timer : timers_) {
+      if (timer->EvaluateHasExpired(now)) {
+        expired_timers.push_back(timer->timeout_id());
+      }
+    }
+    return expired_timers;
+  }
+
+ private:
+  const std::function<TimeMs()> get_time_;
+  std::unordered_set<FakeTimeout*> timers_;
+};
+
+}  // namespace dcsctp
+
+#endif  // NET_DCSCTP_TIMER_FAKE_TIMEOUT_H_
diff --git a/net/dcsctp/timer/timer.cc b/net/dcsctp/timer/timer.cc
new file mode 100644
index 0000000..2376e7a
--- /dev/null
+++ b/net/dcsctp/timer/timer.cc
@@ -0,0 +1,120 @@
+/*
+ *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include "net/dcsctp/timer/timer.h"
+
+#include <cstdint>
+#include <memory>
+#include <unordered_map>
+#include <utility>
+
+#include "absl/memory/memory.h"
+#include "absl/strings/string_view.h"
+#include "net/dcsctp/public/timeout.h"
+
+namespace dcsctp {
+namespace {
+TimeoutID MakeTimeoutId(uint32_t timer_id, uint32_t generation) {
+  return TimeoutID(static_cast<uint64_t>(timer_id) << 32 | generation);
+}
+
+DurationMs GetBackoffDuration(TimerBackoffAlgorithm algorithm,
+                              DurationMs base_duration,
+                              int expiration_count) {
+  switch (algorithm) {
+    case TimerBackoffAlgorithm::kFixed:
+      return base_duration;
+    case TimerBackoffAlgorithm::kExponential:
+      return DurationMs(*base_duration * (1 << expiration_count));
+  }
+}
+}  // namespace
+
+Timer::Timer(uint32_t id,
+             absl::string_view name,
+             OnExpired on_expired,
+             UnregisterHandler unregister_handler,
+             std::unique_ptr<Timeout> timeout,
+             const TimerOptions& options)
+    : id_(id),
+      name_(name),
+      options_(options),
+      on_expired_(std::move(on_expired)),
+      unregister_handler_(std::move(unregister_handler)),
+      timeout_(std::move(timeout)),
+      duration_(options.duration) {}
+
+Timer::~Timer() {
+  Stop();
+  unregister_handler_();
+}
+
+void Timer::Start() {
+  expiration_count_ = 0;
+  if (!is_running()) {
+    is_running_ = true;
+    timeout_->Start(duration_, MakeTimeoutId(id_, ++generation_));
+  } else {
+    // Timer was running - stop and restart it, to make it expire in `duration_`
+    // from now.
+    timeout_->Restart(duration_, MakeTimeoutId(id_, ++generation_));
+  }
+}
+
+void Timer::Stop() {
+  if (is_running()) {
+    timeout_->Stop();
+    expiration_count_ = 0;
+    is_running_ = false;
+  }
+}
+
+void Timer::Trigger(uint32_t generation) {
+  if (is_running_ && generation == generation_) {
+    ++expiration_count_;
+    if (options_.max_restarts >= 0 &&
+        expiration_count_ > options_.max_restarts) {
+      is_running_ = false;
+    }
+
+    absl::optional<DurationMs> new_duration = on_expired_();
+    if (new_duration.has_value()) {
+      duration_ = new_duration.value();
+    }
+
+    if (is_running_) {
+      // Restart it with new duration.
+      DurationMs duration = GetBackoffDuration(options_.backoff_algorithm,
+                                               duration_, expiration_count_);
+      timeout_->Start(duration, MakeTimeoutId(id_, ++generation_));
+    }
+  }
+}
+
+void TimerManager::HandleTimeout(TimeoutID timeout_id) {
+  uint32_t timer_id = *timeout_id >> 32;
+  uint32_t generation = *timeout_id;
+  auto it = timers_.find(timer_id);
+  if (it != timers_.end()) {
+    it->second->Trigger(generation);
+  }
+}
+
+std::unique_ptr<Timer> TimerManager::CreateTimer(absl::string_view name,
+                                                 Timer::OnExpired on_expired,
+                                                 const TimerOptions& options) {
+  uint32_t id = ++next_id_;
+  auto timer = absl::WrapUnique(new Timer(
+      id, name, std::move(on_expired), [this, id]() { timers_.erase(id); },
+      create_timeout_(), options));
+  timers_[id] = timer.get();
+  return timer;
+}
+
+}  // namespace dcsctp
diff --git a/net/dcsctp/timer/timer.h b/net/dcsctp/timer/timer.h
new file mode 100644
index 0000000..6b68c98
--- /dev/null
+++ b/net/dcsctp/timer/timer.h
@@ -0,0 +1,167 @@
+/*
+ *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef NET_DCSCTP_TIMER_TIMER_H_
+#define NET_DCSCTP_TIMER_TIMER_H_
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "net/dcsctp/public/timeout.h"
+
+namespace dcsctp {
+
+enum class TimerBackoffAlgorithm {
+  // The base duration will be used for any restart.
+  kFixed,
+  // An exponential backoff is used for restarts, with a 2x multiplier, meaning
+  // that every restart will use a duration that is twice as long as the
+  // previous.
+  kExponential,
+};
+
+struct TimerOptions {
+  explicit TimerOptions(DurationMs duration)
+      : TimerOptions(duration, TimerBackoffAlgorithm::kExponential) {}
+  TimerOptions(DurationMs duration, TimerBackoffAlgorithm backoff_algorithm)
+      : TimerOptions(duration, backoff_algorithm, -1) {}
+  TimerOptions(DurationMs duration,
+               TimerBackoffAlgorithm backoff_algorithm,
+               int max_restarts)
+      : duration(duration),
+        backoff_algorithm(backoff_algorithm),
+        max_restarts(max_restarts) {}
+
+  // The initial timer duration. Can be overridden with `set_duration`.
+  const DurationMs duration;
+  // If the duration should be increased (using exponential backoff) when it is
+  // restarted. If not set, the same duration will be used.
+  const TimerBackoffAlgorithm backoff_algorithm;
+  // The maximum number of times that the timer will be automatically restarted.
+  const int max_restarts;
+};
+
+// A high-level timer (in contrast to the low-level `Timeout` class).
+//
+// Timers are started and can be stopped or restarted. When a timer expires,
+// the provided `on_expired` callback will be triggered. A timer is
+// automatically restarted, as long as the number of restarts is below the
+// configurable `max_restarts` parameter. The `is_running` property can be
+// queried to know if it's still running after having expired.
+//
+// When a timer is restarted, it will use a configurable `backoff_algorithm` to
+// possibly adjust the duration of the next expiry. It is also possible to
+// return a new base duration (which is the duration before it's adjusted by the
+// backoff algorithm).
+class Timer {
+ public:
+  // When expired, the timer handler can optionally return a new duration which
+  // will be set as `duration` and used as base duration when the timer is
+  // restarted and as input to the backoff algorithm.
+  using OnExpired = std::function<absl::optional<DurationMs>()>;
+
+  // TimerManager will have pointers to these instances, so they must not move.
+  Timer(const Timer&) = delete;
+  Timer& operator=(const Timer&) = delete;
+
+  ~Timer();
+
+  // Starts the timer if it's stopped or restarts the timer if it's already
+  // running. The `expiration_count` will be reset.
+  void Start();
+
+  // Stops the timer. This can also be called when the timer is already stopped.
+  // The `expiration_count` will be reset.
+  void Stop();
+
+  // Sets the base duration. The actual timer duration may be larger depending
+  // on the backoff algorithm.
+  void set_duration(DurationMs duration) { duration_ = duration; }
+
+  // Retrieves the base duration. The actual timer duration may be larger
+  // depending on the backoff algorithm.
+  DurationMs duration() const { return duration_; }
+
+  // Returns the number of times the timer has expired.
+  int expiration_count() const { return expiration_count_; }
+
+  // Returns the timer's options.
+  const TimerOptions& options() const { return options_; }
+
+  // Returns the name of the timer.
+  absl::string_view name() const { return name_; }
+
+  // Indicates if this timer is currently running.
+  bool is_running() const { return is_running_; }
+
+ private:
+  friend class TimerManager;
+  using UnregisterHandler = std::function<void()>;
+  Timer(uint32_t id,
+        absl::string_view name,
+        OnExpired on_expired,
+        UnregisterHandler unregister,
+        std::unique_ptr<Timeout> timeout,
+        const TimerOptions& options);
+
+  // Called by TimerManager. Will trigger the callback and increment
+  // `expiration_count`. The timer will automatically be restarted at the
+  // duration as decided by the backoff algorithm, unless the
+  // `TimerOptions::max_restarts` has been reached and then it will be stopped
+  // and `is_running()` will return false.
+  void Trigger(uint32_t generation);
+
+  const uint32_t id_;
+  const std::string name_;
+  const TimerOptions options_;
+  const OnExpired on_expired_;
+  const UnregisterHandler unregister_handler_;
+  const std::unique_ptr<Timeout> timeout_;
+
+  DurationMs duration_;
+
+  // Increased on each start, and is matched on Trigger, to avoid races.
+  uint32_t generation_ = 0;
+  bool is_running_ = false;
+  // Incremented each time time has expired and reset when stopped or restarted.
+  int expiration_count_ = 0;
+};
+
+// Creates and manages timers.
+class TimerManager {
+ public:
+  explicit TimerManager(
+      std::function<std::unique_ptr<Timeout>()> create_timeout)
+      : create_timeout_(std::move(create_timeout)) {}
+
+  // Creates a timer with name `name` that will expire (when started) after
+  // `options.duration` and call `on_expired`. There are more `options` that
+  // affects the behavior. Note that timers are created initially stopped.
+  std::unique_ptr<Timer> CreateTimer(absl::string_view name,
+                                     Timer::OnExpired on_expired,
+                                     const TimerOptions& options);
+
+  void HandleTimeout(TimeoutID timeout_id);
+
+ private:
+  const std::function<std::unique_ptr<Timeout>()> create_timeout_;
+  std::unordered_map<int, Timer*> timers_;
+  uint32_t next_id_ = 0;
+};
+
+}  // namespace dcsctp
+
+#endif  // NET_DCSCTP_TIMER_TIMER_H_
diff --git a/net/dcsctp/timer/timer_test.cc b/net/dcsctp/timer/timer_test.cc
new file mode 100644
index 0000000..263f535
--- /dev/null
+++ b/net/dcsctp/timer/timer_test.cc
@@ -0,0 +1,314 @@
+/*
+ *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include "net/dcsctp/timer/timer.h"
+
+#include <memory>
+
+#include "absl/types/optional.h"
+#include "net/dcsctp/public/timeout.h"
+#include "net/dcsctp/timer/fake_timeout.h"
+#include "rtc_base/gunit.h"
+#include "test/gmock.h"
+
+namespace dcsctp {
+namespace {
+using ::testing::Return;
+
+class TimerTest : public testing::Test {
+ protected:
+  TimerTest()
+      : timeout_manager_([this]() { return now_; }),
+        manager_([this]() { return timeout_manager_.CreateTimeout(); }) {
+    ON_CALL(on_expired_, Call).WillByDefault(Return(absl::nullopt));
+  }
+
+  void AdvanceTimeAndRunTimers(DurationMs duration) {
+    now_ = TimeMs(*now_ + *duration);
+
+    for (TimeoutID timeout_id : timeout_manager_.RunTimers()) {
+      manager_.HandleTimeout(timeout_id);
+    }
+  }
+
+  TimeMs now_ = TimeMs(0);
+  FakeTimeoutManager timeout_manager_;
+  TimerManager manager_;
+  testing::MockFunction<absl::optional<DurationMs>()> on_expired_;
+};
+
+TEST_F(TimerTest, TimerIsInitiallyStopped) {
+  std::unique_ptr<Timer> t1 = manager_.CreateTimer(
+      "t1", on_expired_.AsStdFunction(),
+      TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed));
+
+  EXPECT_FALSE(t1->is_running());
+}
+
+TEST_F(TimerTest, TimerExpiresAtGivenTime) {
+  std::unique_ptr<Timer> t1 = manager_.CreateTimer(
+      "t1", on_expired_.AsStdFunction(),
+      TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed));
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  t1->Start();
+  EXPECT_TRUE(t1->is_running());
+
+  AdvanceTimeAndRunTimers(DurationMs(4000));
+
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+}
+
+TEST_F(TimerTest, TimerReschedulesAfterExpiredWithFixedBackoff) {
+  std::unique_ptr<Timer> t1 = manager_.CreateTimer(
+      "t1", on_expired_.AsStdFunction(),
+      TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed));
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  t1->Start();
+  EXPECT_EQ(t1->expiration_count(), 0);
+
+  AdvanceTimeAndRunTimers(DurationMs(4000));
+
+  // Fire first time
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+  EXPECT_TRUE(t1->is_running());
+  EXPECT_EQ(t1->expiration_count(), 1);
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(4000));
+
+  // Second time
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+  EXPECT_TRUE(t1->is_running());
+  EXPECT_EQ(t1->expiration_count(), 2);
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(4000));
+
+  // Third time
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+  EXPECT_TRUE(t1->is_running());
+  EXPECT_EQ(t1->expiration_count(), 3);
+}
+
+TEST_F(TimerTest, TimerWithNoRestarts) {
+  std::unique_ptr<Timer> t1 = manager_.CreateTimer(
+      "t1", on_expired_.AsStdFunction(),
+      TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed,
+                   /*max_restart=*/0));
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  t1->Start();
+  AdvanceTimeAndRunTimers(DurationMs(4000));
+
+  // Fire first time
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+
+  EXPECT_FALSE(t1->is_running());
+
+  // Second time - shouldn't fire
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(5000));
+  EXPECT_FALSE(t1->is_running());
+}
+
+TEST_F(TimerTest, TimerWithOneRestart) {
+  std::unique_ptr<Timer> t1 = manager_.CreateTimer(
+      "t1", on_expired_.AsStdFunction(),
+      TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed,
+                   /*max_restart=*/1));
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  t1->Start();
+  AdvanceTimeAndRunTimers(DurationMs(4000));
+
+  // Fire first time
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+  EXPECT_TRUE(t1->is_running());
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(4000));
+
+  // Second time - max restart limit reached.
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+  EXPECT_FALSE(t1->is_running());
+
+  // Third time - should not fire.
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(5000));
+  EXPECT_FALSE(t1->is_running());
+}
+
+TEST_F(TimerTest, TimerWithTwoRestart) {
+  std::unique_ptr<Timer> t1 = manager_.CreateTimer(
+      "t1", on_expired_.AsStdFunction(),
+      TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed,
+                   /*max_restart=*/2));
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  t1->Start();
+  AdvanceTimeAndRunTimers(DurationMs(4000));
+
+  // Fire first time
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+  EXPECT_TRUE(t1->is_running());
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(4000));
+
+  // Second time
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+  EXPECT_TRUE(t1->is_running());
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(4000));
+
+  // Third time
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+  EXPECT_FALSE(t1->is_running());
+}
+
+TEST_F(TimerTest, TimerWithExponentialBackoff) {
+  std::unique_ptr<Timer> t1 = manager_.CreateTimer(
+      "t1", on_expired_.AsStdFunction(),
+      TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kExponential));
+
+  t1->Start();
+
+  // Fire first time at 5 seconds
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(5000));
+
+  // Second time at 5*2^1 = 10 seconds later.
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(9000));
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+
+  // Third time at 5*2^2 = 20 seconds later.
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(19000));
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+
+  // Fourth time at 5*2^3 = 40 seconds later.
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(39000));
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+}
+
+TEST_F(TimerTest, StartTimerWillStopAndStart) {
+  std::unique_ptr<Timer> t1 = manager_.CreateTimer(
+      "t1", on_expired_.AsStdFunction(),
+      TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kExponential));
+
+  t1->Start();
+
+  AdvanceTimeAndRunTimers(DurationMs(3000));
+
+  t1->Start();
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(2000));
+
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(3000));
+}
+
+TEST_F(TimerTest, ExpirationCounterWillResetIfStopped) {
+  std::unique_ptr<Timer> t1 = manager_.CreateTimer(
+      "t1", on_expired_.AsStdFunction(),
+      TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kExponential));
+
+  t1->Start();
+
+  // Fire first time at 5 seconds
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(5000));
+  EXPECT_EQ(t1->expiration_count(), 1);
+
+  // Second time at 5*2^1 = 10 seconds later.
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(9000));
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+  EXPECT_EQ(t1->expiration_count(), 2);
+
+  t1->Start();
+  EXPECT_EQ(t1->expiration_count(), 0);
+
+  // Third time at 5*2^0 = 5 seconds later.
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(4000));
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+  EXPECT_EQ(t1->expiration_count(), 1);
+}
+
+TEST_F(TimerTest, StopTimerWillMakeItNotExpire) {
+  std::unique_ptr<Timer> t1 = manager_.CreateTimer(
+      "t1", on_expired_.AsStdFunction(),
+      TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kExponential));
+
+  t1->Start();
+  EXPECT_TRUE(t1->is_running());
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(4000));
+  t1->Stop();
+  EXPECT_FALSE(t1->is_running());
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+}
+
+TEST_F(TimerTest, ReturningNewDurationWhenExpired) {
+  std::unique_ptr<Timer> t1 = manager_.CreateTimer(
+      "t1", on_expired_.AsStdFunction(),
+      TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed));
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  t1->Start();
+  EXPECT_EQ(t1->duration(), DurationMs(5000));
+
+  AdvanceTimeAndRunTimers(DurationMs(4000));
+
+  // Fire first time
+  EXPECT_CALL(on_expired_, Call).WillOnce(Return(DurationMs(2000)));
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+  EXPECT_EQ(t1->duration(), DurationMs(2000));
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+
+  // Second time
+  EXPECT_CALL(on_expired_, Call).WillOnce(Return(DurationMs(10000)));
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+  EXPECT_EQ(t1->duration(), DurationMs(10000));
+
+  EXPECT_CALL(on_expired_, Call).Times(0);
+  AdvanceTimeAndRunTimers(DurationMs(9000));
+  EXPECT_CALL(on_expired_, Call).Times(1);
+  AdvanceTimeAndRunTimers(DurationMs(1000));
+}
+
+}  // namespace
+}  // namespace dcsctp