summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/net/dcsctp/timer/timer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/net/dcsctp/timer/timer.cc')
-rw-r--r--third_party/libwebrtc/net/dcsctp/timer/timer.cc156
1 files changed, 156 insertions, 0 deletions
diff --git a/third_party/libwebrtc/net/dcsctp/timer/timer.cc b/third_party/libwebrtc/net/dcsctp/timer/timer.cc
new file mode 100644
index 0000000000..bde07638a5
--- /dev/null
+++ b/third_party/libwebrtc/net/dcsctp/timer/timer.cc
@@ -0,0 +1,156 @@
+/*
+ * 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 <algorithm>
+#include <cstdint>
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "absl/memory/memory.h"
+#include "absl/strings/string_view.h"
+#include "net/dcsctp/public/timeout.h"
+#include "rtc_base/checks.h"
+
+namespace dcsctp {
+namespace {
+TimeoutID MakeTimeoutId(TimerID timer_id, TimerGeneration generation) {
+ return TimeoutID(static_cast<uint64_t>(*timer_id) << 32 | *generation);
+}
+
+DurationMs GetBackoffDuration(const TimerOptions& options,
+ DurationMs base_duration,
+ int expiration_count) {
+ switch (options.backoff_algorithm) {
+ case TimerBackoffAlgorithm::kFixed:
+ return base_duration;
+ case TimerBackoffAlgorithm::kExponential: {
+ int32_t duration_ms = *base_duration;
+
+ while (expiration_count > 0 && duration_ms < *Timer::kMaxTimerDuration) {
+ duration_ms *= 2;
+ --expiration_count;
+
+ if (options.max_backoff_duration.has_value() &&
+ duration_ms > **options.max_backoff_duration) {
+ return *options.max_backoff_duration;
+ }
+ }
+
+ return DurationMs(std::min(duration_ms, *Timer::kMaxTimerDuration));
+ }
+ }
+}
+} // namespace
+
+constexpr DurationMs Timer::kMaxTimerDuration;
+
+Timer::Timer(TimerID 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;
+ generation_ = TimerGeneration(*generation_ + 1);
+ timeout_->Start(duration_, MakeTimeoutId(id_, generation_));
+ } else {
+ // Timer was running - stop and restart it, to make it expire in `duration_`
+ // from now.
+ generation_ = TimerGeneration(*generation_ + 1);
+ timeout_->Restart(duration_, MakeTimeoutId(id_, generation_));
+ }
+}
+
+void Timer::Stop() {
+ if (is_running()) {
+ timeout_->Stop();
+ expiration_count_ = 0;
+ is_running_ = false;
+ }
+}
+
+void Timer::Trigger(TimerGeneration generation) {
+ if (is_running_ && generation == generation_) {
+ ++expiration_count_;
+ is_running_ = false;
+ if (!options_.max_restarts.has_value() ||
+ expiration_count_ <= *options_.max_restarts) {
+ // The timer should still be running after this triggers. Start a new
+ // timer. Note that it might be very quickly restarted again, if the
+ // `on_expired_` callback returns a new duration.
+ is_running_ = true;
+ DurationMs duration =
+ GetBackoffDuration(options_, duration_, expiration_count_);
+ generation_ = TimerGeneration(*generation_ + 1);
+ timeout_->Start(duration, MakeTimeoutId(id_, generation_));
+ }
+
+ absl::optional<DurationMs> new_duration = on_expired_();
+ if (new_duration.has_value() && new_duration != duration_) {
+ duration_ = new_duration.value();
+ if (is_running_) {
+ // Restart it with new duration.
+ timeout_->Stop();
+
+ DurationMs duration =
+ GetBackoffDuration(options_, duration_, expiration_count_);
+ generation_ = TimerGeneration(*generation_ + 1);
+ timeout_->Start(duration, MakeTimeoutId(id_, generation_));
+ }
+ }
+ }
+}
+
+void TimerManager::HandleTimeout(TimeoutID timeout_id) {
+ TimerID timer_id(*timeout_id >> 32);
+ TimerGeneration 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) {
+ next_id_ = TimerID(*next_id_ + 1);
+ TimerID id = next_id_;
+ // This would overflow after 4 billion timers created, which in SCTP would be
+ // after 800 million reconnections on a single socket. Ensure this will never
+ // happen.
+ RTC_CHECK_NE(*id, std::numeric_limits<uint32_t>::max());
+ std::unique_ptr<Timeout> timeout = create_timeout_(options.precision);
+ RTC_CHECK(timeout != nullptr);
+ auto timer = absl::WrapUnique(new Timer(
+ id, name, std::move(on_expired), [this, id]() { timers_.erase(id); },
+ std::move(timeout), options));
+ timers_[id] = timer.get();
+ return timer;
+}
+
+} // namespace dcsctp