| /* |
| * 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 <utility> |
| #include <vector> |
| |
| #include "absl/types/optional.h" |
| #include "api/task_queue/task_queue_base.h" |
| #include "net/dcsctp/public/timeout.h" |
| #include "net/dcsctp/public/types.h" |
| #include "rtc_base/checks.h" |
| #include "rtc_base/containers/flat_set.h" |
| |
| namespace dcsctp { |
| |
| // A timeout used in tests. |
| class FakeTimeout : public Timeout { |
| public: |
| 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 { |
| RTC_DCHECK(expiry_ == TimeMs::InfiniteFuture()); |
| timeout_id_ = timeout_id; |
| expiry_ = get_time_() + duration_ms; |
| } |
| void Stop() override { |
| RTC_DCHECK(expiry_ != TimeMs::InfiniteFuture()); |
| expiry_ = TimeMs::InfiniteFuture(); |
| } |
| |
| bool EvaluateHasExpired(TimeMs now) { |
| if (now >= expiry_) { |
| expiry_ = TimeMs::InfiniteFuture(); |
| return true; |
| } |
| return false; |
| } |
| |
| TimeoutID timeout_id() const { return timeout_id_; } |
| TimeMs expiry() const { return expiry_; } |
| |
| private: |
| const std::function<TimeMs()> get_time_; |
| const std::function<void(FakeTimeout*)> on_delete_; |
| |
| TimeoutID timeout_id_ = TimeoutID(0); |
| TimeMs expiry_ = TimeMs::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<FakeTimeout> CreateTimeout() { |
| auto timer = std::make_unique<FakeTimeout>( |
| get_time_, [this](FakeTimeout* timer) { timers_.erase(timer); }); |
| timers_.insert(timer.get()); |
| return timer; |
| } |
| std::unique_ptr<FakeTimeout> CreateTimeout( |
| webrtc::TaskQueueBase::DelayPrecision precision) { |
| // FakeTimeout does not support implement |precision|. |
| return CreateTimeout(); |
| } |
| |
| // NOTE: This can't return a vector, as calling EvaluateHasExpired requires |
| // calling socket->HandleTimeout directly afterwards, as the owning Timer |
| // still believes it's running, and it needs to be updated to set |
| // Timer::is_running_ to false before you operate on the Timer or Timeout |
| // again. |
| absl::optional<TimeoutID> GetNextExpiredTimeout() { |
| TimeMs now = get_time_(); |
| std::vector<TimeoutID> expired_timers; |
| for (auto& timer : timers_) { |
| if (timer->EvaluateHasExpired(now)) { |
| return timer->timeout_id(); |
| } |
| } |
| return absl::nullopt; |
| } |
| |
| DurationMs GetTimeToNextTimeout() const { |
| TimeMs next_expiry = TimeMs::InfiniteFuture(); |
| for (const FakeTimeout* timer : timers_) { |
| if (timer->expiry() < next_expiry) { |
| next_expiry = timer->expiry(); |
| } |
| } |
| TimeMs now = get_time_(); |
| return next_expiry != TimeMs::InfiniteFuture() && next_expiry >= now |
| ? next_expiry - now |
| : DurationMs::InfiniteDuration(); |
| } |
| |
| private: |
| const std::function<TimeMs()> get_time_; |
| webrtc::flat_set<FakeTimeout*> timers_; |
| }; |
| |
| } // namespace dcsctp |
| |
| #endif // NET_DCSCTP_TIMER_FAKE_TIMEOUT_H_ |