#define MS_CLASS "BackoffTimerHandle"
#include "handles/BackoffTimerHandle.hpp"
#include "Logger.hpp"
#include "MediaSoupErrors.hpp"
#include <algorithm>
BackoffTimerHandle::BackoffTimerHandle(BackoffTimerHandleOptions options)
: listener(options.listener),
label(std::move(options.label)),
baseTimeoutMs(options.baseTimeoutMs),
backoffAlgorithm(options.backoffAlgorithm),
maxBackoffTimeoutMs(options.maxBackoffTimeoutMs),
maxRestarts(options.maxRestarts)
{
MS_TRACE();
if (!this->listener)
{
MS_THROW_TYPE_ERROR("options.listener must be given");
}
if (this->label.empty())
{
MS_THROW_TYPE_ERROR("options.label must be given");
}
if (this->baseTimeoutMs > BackoffTimerHandleInterface::MaxTimeoutMs)
{
MS_THROW_ERROR(
"[%s] base timeout (%" PRIu64 " ms) cannot be greater than %" PRIu64 " ms",
this->label.c_str(),
this->baseTimeoutMs,
BackoffTimerHandleInterface::MaxTimeoutMs);
}
this->timer = new TimerHandle(this);
}
BackoffTimerHandle::~BackoffTimerHandle()
{
MS_TRACE();
delete this->timer;
this->timer = nullptr;
}
void BackoffTimerHandle::Start()
{
MS_TRACE();
this->timer->Start(this->baseTimeoutMs);
this->running = true;
this->expirationCount = 0;
}
void BackoffTimerHandle::Stop()
{
MS_TRACE();
this->timer->Stop();
this->running = false;
this->expirationCount = 0;
}
void BackoffTimerHandle::SetBaseTimeoutMs(uint64_t baseTimeoutMs)
{
MS_TRACE();
if (baseTimeoutMs > BackoffTimerHandleInterface::MaxTimeoutMs)
{
MS_THROW_ERROR(
"[%s] base timeout (%" PRIu64 " ms) cannot be greater than %" PRIu64 " ms",
this->label.c_str(),
baseTimeoutMs,
BackoffTimerHandleInterface::MaxTimeoutMs);
}
this->baseTimeoutMs = baseTimeoutMs;
}
uint64_t BackoffTimerHandle::ComputeNextTimeoutMs() const
{
MS_TRACE();
auto expirationCount = this->expirationCount;
switch (this->backoffAlgorithm)
{
case BackoffAlgorithm::FIXED:
{
return this->baseTimeoutMs;
}
case BackoffAlgorithm::EXPONENTIAL:
{
auto timeoutMs = this->baseTimeoutMs;
while (expirationCount > 0 && timeoutMs < BackoffTimerHandleInterface::MaxTimeoutMs)
{
timeoutMs *= 2;
--expirationCount;
if (this->maxBackoffTimeoutMs.has_value() && timeoutMs > this->maxBackoffTimeoutMs.value())
{
return this->maxBackoffTimeoutMs.value();
}
}
return std::min<uint64_t>(timeoutMs, BackoffTimerHandleInterface::MaxTimeoutMs);
}
NO_DEFAULT_GCC();
}
}
void BackoffTimerHandle::OnTimer(TimerHandleInterface* )
{
MS_TRACE();
this->expirationCount++;
this->running =
!this->maxRestarts.has_value() || this->expirationCount <= this->maxRestarts.value();
uint64_t baseTimeoutMs{ this->baseTimeoutMs };
bool stop{ false };
this->listener->OnBackoffTimer(this, baseTimeoutMs, stop);
if (stop)
{
return;
}
SetBaseTimeoutMs(baseTimeoutMs);
if (this->running)
{
auto nextTimeoutMs = ComputeNextTimeoutMs();
this->timer->Start(nextTimeoutMs);
}
}