Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
123
TMessagesProj/jni/voip/webrtc/net/dcsctp/timer/fake_timeout.h
Normal file
123
TMessagesProj/jni/voip/webrtc/net/dcsctp/timer/fake_timeout.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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 "api/units/timestamp.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<webrtc::Timestamp()> 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_.IsPlusInfinity());
|
||||
timeout_id_ = timeout_id;
|
||||
expiry_ = get_time_() + duration_ms.ToTimeDelta();
|
||||
}
|
||||
void Stop() override {
|
||||
RTC_DCHECK(!expiry_.IsPlusInfinity());
|
||||
expiry_ = webrtc::Timestamp::PlusInfinity();
|
||||
}
|
||||
|
||||
bool EvaluateHasExpired(webrtc::Timestamp now) {
|
||||
if (now >= expiry_) {
|
||||
expiry_ = webrtc::Timestamp::PlusInfinity();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TimeoutID timeout_id() const { return timeout_id_; }
|
||||
webrtc::Timestamp expiry() const { return expiry_; }
|
||||
|
||||
private:
|
||||
const std::function<webrtc::Timestamp()> get_time_;
|
||||
const std::function<void(FakeTimeout*)> on_delete_;
|
||||
|
||||
TimeoutID timeout_id_ = TimeoutID(0);
|
||||
webrtc::Timestamp expiry_ = webrtc::Timestamp::PlusInfinity();
|
||||
};
|
||||
|
||||
class FakeTimeoutManager {
|
||||
public:
|
||||
// The `get_time` function must return the current time, relative to any
|
||||
// epoch.
|
||||
explicit FakeTimeoutManager(std::function<webrtc::Timestamp()> 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() {
|
||||
webrtc::Timestamp now = get_time_();
|
||||
std::vector<TimeoutID> expired_timers;
|
||||
for (auto& timer : timers_) {
|
||||
if (timer->EvaluateHasExpired(now)) {
|
||||
return timer->timeout_id();
|
||||
}
|
||||
}
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
webrtc::TimeDelta GetTimeToNextTimeout() const {
|
||||
webrtc::Timestamp next_expiry = webrtc::Timestamp::PlusInfinity();
|
||||
for (const FakeTimeout* timer : timers_) {
|
||||
if (timer->expiry() < next_expiry) {
|
||||
next_expiry = timer->expiry();
|
||||
}
|
||||
}
|
||||
webrtc::Timestamp now = get_time_();
|
||||
return !next_expiry.IsPlusInfinity() && next_expiry >= now
|
||||
? next_expiry - now
|
||||
: webrtc::TimeDelta::PlusInfinity();
|
||||
}
|
||||
|
||||
private:
|
||||
const std::function<webrtc::Timestamp()> get_time_;
|
||||
webrtc::flat_set<FakeTimeout*> timers_;
|
||||
};
|
||||
|
||||
} // namespace dcsctp
|
||||
|
||||
#endif // NET_DCSCTP_TIMER_FAKE_TIMEOUT_H_
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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/task_queue_timeout.h"
|
||||
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace dcsctp {
|
||||
using ::webrtc::TimeDelta;
|
||||
using ::webrtc::Timestamp;
|
||||
|
||||
TaskQueueTimeoutFactory::TaskQueueTimeout::TaskQueueTimeout(
|
||||
TaskQueueTimeoutFactory& parent,
|
||||
webrtc::TaskQueueBase::DelayPrecision precision)
|
||||
: parent_(parent),
|
||||
precision_(precision),
|
||||
pending_task_safety_flag_(webrtc::PendingTaskSafetyFlag::Create()) {}
|
||||
|
||||
TaskQueueTimeoutFactory::TaskQueueTimeout::~TaskQueueTimeout() {
|
||||
RTC_DCHECK_RUN_ON(&parent_.thread_checker_);
|
||||
pending_task_safety_flag_->SetNotAlive();
|
||||
}
|
||||
|
||||
void TaskQueueTimeoutFactory::TaskQueueTimeout::Start(DurationMs duration_ms,
|
||||
TimeoutID timeout_id) {
|
||||
RTC_DCHECK_RUN_ON(&parent_.thread_checker_);
|
||||
RTC_DCHECK(timeout_expiration_.IsPlusInfinity());
|
||||
timeout_expiration_ = parent_.Now() + duration_ms.ToTimeDelta();
|
||||
timeout_id_ = timeout_id;
|
||||
|
||||
if (timeout_expiration_ >= posted_task_expiration_) {
|
||||
// There is already a running task, and it's scheduled to expire sooner than
|
||||
// the new expiration time. Don't do anything; The `timeout_expiration_` has
|
||||
// already been updated and if the delayed task _does_ expire and the timer
|
||||
// hasn't been stopped, that will be noticed in the timeout handler, and the
|
||||
// task will be re-scheduled. Most timers are stopped before they expire.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!posted_task_expiration_.IsPlusInfinity()) {
|
||||
RTC_DLOG(LS_VERBOSE) << "New timeout duration is less than scheduled - "
|
||||
"ghosting old delayed task.";
|
||||
// There is already a scheduled delayed task, but its expiration time is
|
||||
// further away than the new expiration, so it can't be used. It will be
|
||||
// "killed" by replacing the safety flag. This is not expected to happen
|
||||
// especially often; Mainly when a timer did exponential backoff and
|
||||
// later recovered.
|
||||
pending_task_safety_flag_->SetNotAlive();
|
||||
pending_task_safety_flag_ = webrtc::PendingTaskSafetyFlag::Create();
|
||||
}
|
||||
|
||||
posted_task_expiration_ = timeout_expiration_;
|
||||
parent_.task_queue_.PostDelayedTaskWithPrecision(
|
||||
precision_,
|
||||
webrtc::SafeTask(
|
||||
pending_task_safety_flag_,
|
||||
[timeout_id, this]() {
|
||||
RTC_DLOG(LS_VERBOSE) << "Timout expired: " << timeout_id.value();
|
||||
RTC_DCHECK_RUN_ON(&parent_.thread_checker_);
|
||||
RTC_DCHECK(!posted_task_expiration_.IsPlusInfinity());
|
||||
posted_task_expiration_ = Timestamp::PlusInfinity();
|
||||
|
||||
if (timeout_expiration_.IsPlusInfinity()) {
|
||||
// The timeout was stopped before it expired. Very common.
|
||||
} else {
|
||||
// Note that the timeout might have been restarted, which updated
|
||||
// `timeout_expiration_` but left the scheduled task running. So
|
||||
// if it's not quite time to trigger the timeout yet, schedule a
|
||||
// new delayed task with what's remaining and retry at that point
|
||||
// in time.
|
||||
TimeDelta remaining = timeout_expiration_ - parent_.Now();
|
||||
timeout_expiration_ = Timestamp::PlusInfinity();
|
||||
if (remaining > TimeDelta::Zero()) {
|
||||
Start(DurationMs(remaining.ms()), timeout_id_);
|
||||
} else {
|
||||
// It has actually triggered.
|
||||
RTC_DLOG(LS_VERBOSE)
|
||||
<< "Timout triggered: " << timeout_id.value();
|
||||
parent_.on_expired_(timeout_id_);
|
||||
}
|
||||
}
|
||||
}),
|
||||
webrtc::TimeDelta::Millis(duration_ms.value()));
|
||||
}
|
||||
|
||||
void TaskQueueTimeoutFactory::TaskQueueTimeout::Stop() {
|
||||
// As the TaskQueue doesn't support deleting a posted task, just mark the
|
||||
// timeout as not running.
|
||||
RTC_DCHECK_RUN_ON(&parent_.thread_checker_);
|
||||
timeout_expiration_ = Timestamp::PlusInfinity();
|
||||
}
|
||||
|
||||
} // namespace dcsctp
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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_TASK_QUEUE_TIMEOUT_H_
|
||||
#define NET_DCSCTP_TIMER_TASK_QUEUE_TIMEOUT_H_
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "net/dcsctp/public/timeout.h"
|
||||
|
||||
namespace dcsctp {
|
||||
|
||||
// The TaskQueueTimeoutFactory creates `Timeout` instances, which schedules
|
||||
// itself to be triggered on the provided `task_queue`, which may be a thread,
|
||||
// an actual TaskQueue or something else which supports posting a delayed task.
|
||||
//
|
||||
// Note that each `DcSctpSocket` must have its own `TaskQueueTimeoutFactory`,
|
||||
// as the `TimeoutID` are not unique among sockets.
|
||||
//
|
||||
// This class must outlive any created Timeout that it has created. Note that
|
||||
// the `DcSctpSocket` will ensure that all Timeouts are deleted when the socket
|
||||
// is destructed, so this means that this class must outlive the `DcSctpSocket`.
|
||||
//
|
||||
// This class, and the timeouts created it, are not thread safe.
|
||||
class TaskQueueTimeoutFactory {
|
||||
public:
|
||||
// The `get_time` function must return the current time, relative to any
|
||||
// epoch. Whenever a timeout expires, the `on_expired` callback will be
|
||||
// triggered, and then the client should provided `timeout_id` to
|
||||
// `DcSctpSocketInterface::HandleTimeout`.
|
||||
TaskQueueTimeoutFactory(webrtc::TaskQueueBase& task_queue,
|
||||
std::function<TimeMs()> get_time,
|
||||
std::function<void(TimeoutID timeout_id)> on_expired)
|
||||
: task_queue_(task_queue),
|
||||
get_time_(std::move(get_time)),
|
||||
on_expired_(std::move(on_expired)) {}
|
||||
|
||||
// Creates an implementation of `Timeout`.
|
||||
std::unique_ptr<Timeout> CreateTimeout(
|
||||
webrtc::TaskQueueBase::DelayPrecision precision =
|
||||
webrtc::TaskQueueBase::DelayPrecision::kLow) {
|
||||
return std::make_unique<TaskQueueTimeout>(*this, precision);
|
||||
}
|
||||
|
||||
private:
|
||||
class TaskQueueTimeout : public Timeout {
|
||||
public:
|
||||
TaskQueueTimeout(TaskQueueTimeoutFactory& parent,
|
||||
webrtc::TaskQueueBase::DelayPrecision precision);
|
||||
~TaskQueueTimeout();
|
||||
|
||||
void Start(DurationMs duration_ms, TimeoutID timeout_id) override;
|
||||
void Stop() override;
|
||||
|
||||
private:
|
||||
TaskQueueTimeoutFactory& parent_;
|
||||
const webrtc::TaskQueueBase::DelayPrecision precision_;
|
||||
// A safety flag to ensure that posted tasks to the task queue don't
|
||||
// reference these object when they go out of scope. Note that this safety
|
||||
// flag will be re-created if the scheduled-but-not-yet-expired task is not
|
||||
// to be run. This happens when there is a posted delayed task with an
|
||||
// expiration time _further away_ than what is now the expected expiration
|
||||
// time. In this scenario, a new delayed task has to be posted with a
|
||||
// shorter duration and the old task has to be forgotten.
|
||||
rtc::scoped_refptr<webrtc::PendingTaskSafetyFlag> pending_task_safety_flag_;
|
||||
// The time when the posted delayed task is set to expire. Will be set to
|
||||
// the infinite future if there is no such task running.
|
||||
webrtc::Timestamp posted_task_expiration_ =
|
||||
webrtc::Timestamp::PlusInfinity();
|
||||
// The time when the timeout expires. It will be set to the infinite future
|
||||
// if the timeout is not running/not started.
|
||||
webrtc::Timestamp timeout_expiration_ = webrtc::Timestamp::PlusInfinity();
|
||||
// The current timeout ID that will be reported when expired.
|
||||
TimeoutID timeout_id_ = TimeoutID(0);
|
||||
};
|
||||
|
||||
webrtc::Timestamp Now() { return webrtc::Timestamp::Millis(*get_time_()); }
|
||||
|
||||
RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker thread_checker_;
|
||||
webrtc::TaskQueueBase& task_queue_;
|
||||
const std::function<TimeMs()> get_time_;
|
||||
const std::function<void(TimeoutID)> on_expired_;
|
||||
};
|
||||
} // namespace dcsctp
|
||||
|
||||
#endif // NET_DCSCTP_TIMER_TASK_QUEUE_TIMEOUT_H_
|
||||
158
TMessagesProj/jni/voip/webrtc/net/dcsctp/timer/timer.cc
Normal file
158
TMessagesProj/jni/voip/webrtc/net/dcsctp/timer/timer.cc
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* 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 {
|
||||
using ::webrtc::TimeDelta;
|
||||
|
||||
TimeoutID MakeTimeoutId(TimerID timer_id, TimerGeneration generation) {
|
||||
return TimeoutID(static_cast<uint64_t>(*timer_id) << 32 | *generation);
|
||||
}
|
||||
|
||||
TimeDelta GetBackoffDuration(const TimerOptions& options,
|
||||
TimeDelta base_duration,
|
||||
int expiration_count) {
|
||||
switch (options.backoff_algorithm) {
|
||||
case TimerBackoffAlgorithm::kFixed:
|
||||
return base_duration;
|
||||
case TimerBackoffAlgorithm::kExponential: {
|
||||
TimeDelta duration = base_duration;
|
||||
|
||||
while (expiration_count > 0 && duration < Timer::kMaxTimerDuration) {
|
||||
duration = duration * 2;
|
||||
--expiration_count;
|
||||
|
||||
if (duration > options.max_backoff_duration) {
|
||||
return options.max_backoff_duration;
|
||||
}
|
||||
}
|
||||
|
||||
return TimeDelta(std::min(duration, Timer::kMaxTimerDuration));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
constexpr TimeDelta 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(DurationMs(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(DurationMs(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;
|
||||
TimeDelta duration =
|
||||
GetBackoffDuration(options_, duration_, expiration_count_);
|
||||
generation_ = TimerGeneration(*generation_ + 1);
|
||||
timeout_->Start(DurationMs(duration), MakeTimeoutId(id_, generation_));
|
||||
}
|
||||
|
||||
TimeDelta new_duration = on_expired_();
|
||||
RTC_DCHECK(new_duration != TimeDelta::PlusInfinity());
|
||||
if (new_duration > TimeDelta::Zero() && new_duration != duration_) {
|
||||
duration_ = new_duration;
|
||||
if (is_running_) {
|
||||
// Restart it with new duration.
|
||||
timeout_->Stop();
|
||||
|
||||
TimeDelta duration =
|
||||
GetBackoffDuration(options_, duration_, expiration_count_);
|
||||
generation_ = TimerGeneration(*generation_ + 1);
|
||||
timeout_->Start(DurationMs(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
|
||||
218
TMessagesProj/jni/voip/webrtc/net/dcsctp/timer/timer.h
Normal file
218
TMessagesProj/jni/voip/webrtc/net/dcsctp/timer/timer.h
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* 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 <algorithm>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "net/dcsctp/public/timeout.h"
|
||||
#include "rtc_base/strong_alias.h"
|
||||
|
||||
namespace dcsctp {
|
||||
|
||||
using TimerID = webrtc::StrongAlias<class TimerIDTag, uint32_t>;
|
||||
using TimerGeneration = webrtc::StrongAlias<class TimerGenerationTag, uint32_t>;
|
||||
|
||||
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(webrtc::TimeDelta duration)
|
||||
: TimerOptions(duration, TimerBackoffAlgorithm::kExponential) {}
|
||||
TimerOptions(webrtc::TimeDelta duration,
|
||||
TimerBackoffAlgorithm backoff_algorithm)
|
||||
: TimerOptions(duration, backoff_algorithm, absl::nullopt) {}
|
||||
TimerOptions(webrtc::TimeDelta duration,
|
||||
TimerBackoffAlgorithm backoff_algorithm,
|
||||
absl::optional<int> max_restarts)
|
||||
: TimerOptions(duration,
|
||||
backoff_algorithm,
|
||||
max_restarts,
|
||||
webrtc::TimeDelta::PlusInfinity()) {}
|
||||
TimerOptions(webrtc::TimeDelta duration,
|
||||
TimerBackoffAlgorithm backoff_algorithm,
|
||||
absl::optional<int> max_restarts,
|
||||
webrtc::TimeDelta max_backoff_duration)
|
||||
: TimerOptions(duration,
|
||||
backoff_algorithm,
|
||||
max_restarts,
|
||||
max_backoff_duration,
|
||||
webrtc::TaskQueueBase::DelayPrecision::kLow) {}
|
||||
TimerOptions(webrtc::TimeDelta duration,
|
||||
TimerBackoffAlgorithm backoff_algorithm,
|
||||
absl::optional<int> max_restarts,
|
||||
webrtc::TimeDelta max_backoff_duration,
|
||||
webrtc::TaskQueueBase::DelayPrecision precision)
|
||||
: duration(duration),
|
||||
backoff_algorithm(backoff_algorithm),
|
||||
max_restarts(max_restarts),
|
||||
max_backoff_duration(max_backoff_duration),
|
||||
precision(precision) {}
|
||||
|
||||
// The initial timer duration. Can be overridden with `set_duration`.
|
||||
const webrtc::TimeDelta 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,
|
||||
// or absl::nullopt if there is no limit.
|
||||
const absl::optional<int> max_restarts;
|
||||
// The maximum timeout value for exponential backoff.
|
||||
const webrtc::TimeDelta max_backoff_duration;
|
||||
// The precision of the webrtc::TaskQueueBase used for scheduling.
|
||||
const webrtc::TaskQueueBase::DelayPrecision precision;
|
||||
};
|
||||
|
||||
// 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:
|
||||
// The maximum timer duration - one day.
|
||||
static constexpr webrtc::TimeDelta kMaxTimerDuration =
|
||||
webrtc::TimeDelta::Seconds(24 * 3600);
|
||||
|
||||
// When expired, the timer handler can optionally return a new non-zero
|
||||
// duration which will be set as `duration` and used as base duration when the
|
||||
// timer is restarted and as input to the backoff algorithm. If zero is
|
||||
// returned, the current duration is used.
|
||||
using OnExpired = std::function<webrtc::TimeDelta()>;
|
||||
|
||||
// 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(webrtc::TimeDelta duration) {
|
||||
duration_ = std::min(duration, kMaxTimerDuration);
|
||||
}
|
||||
|
||||
// Retrieves the base duration. The actual timer duration may be larger
|
||||
// depending on the backoff algorithm.
|
||||
webrtc::TimeDelta 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(TimerID 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(TimerGeneration generation);
|
||||
|
||||
const TimerID id_;
|
||||
const std::string name_;
|
||||
const TimerOptions options_;
|
||||
const OnExpired on_expired_;
|
||||
const UnregisterHandler unregister_handler_;
|
||||
const std::unique_ptr<Timeout> timeout_;
|
||||
|
||||
webrtc::TimeDelta duration_;
|
||||
|
||||
// Increased on each start, and is matched on Trigger, to avoid races. And by
|
||||
// race, meaning that a timeout - which may be evaluated/expired on a
|
||||
// different thread while this thread has stopped that timer already. Note
|
||||
// that the entire socket is not thread-safe, so `TimerManager::HandleTimeout`
|
||||
// is never executed concurrently with any timer starting/stopping.
|
||||
//
|
||||
// This will wrap around after 4 billion timer restarts, and if it wraps
|
||||
// around, it would just trigger _this_ timer in advance (but it's hard to
|
||||
// restart it 4 billion times within its duration).
|
||||
TimerGeneration generation_ = TimerGeneration(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>(
|
||||
webrtc::TaskQueueBase::DelayPrecision)> 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>(
|
||||
webrtc::TaskQueueBase::DelayPrecision)>
|
||||
create_timeout_;
|
||||
std::map<TimerID, Timer*> timers_;
|
||||
TimerID next_id_ = TimerID(0);
|
||||
};
|
||||
|
||||
} // namespace dcsctp
|
||||
|
||||
#endif // NET_DCSCTP_TIMER_TIMER_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue