feat add timer
All checks were successful
linux-x64-gcc / linux-gcc (Debug) (push) Successful in 38s
linux-x64-gcc / linux-gcc (Release) (push) Successful in 48s

This commit is contained in:
tqcq 2024-03-07 21:10:13 +08:00
parent f6b9cc5aee
commit b94e31983b
7 changed files with 289 additions and 0 deletions

View File

@ -49,6 +49,8 @@ target_sources(
src/system/thread.cc
src/task_queue/pending_task_safety_flag.cc
src/task_queue/task_queue_base.cc
src/timer/task_queue_timeout.cc
src/timer/timer.cc
src/units/time_delta.cc
src/units/timestamp.cc
src/operations_chain.cc

View File

@ -53,6 +53,22 @@ public:
PostDelayedTaskImpl(std::move(task), delay, traits, location);
}
void
PostDelayedTaskWithPrecision(DelayPrecision precision,
std::function<void()> &&task,
TimeDelta delay,
const Location &location = Location::Current())
{
switch (precision) {
case DelayPrecision::kLow:
PostDelayedTask(std::move(task), delay, location);
break;
case DelayPrecision::kHigh:
PostDelayedHighPrecisionTask(std::move(task), delay, location);
break;
}
}
static TaskQueueBase *Current();
bool IsCurrent() const { return Current() == this; };

View File

@ -0,0 +1,54 @@
#pragma once
#ifndef SLED_TIMER_QUEUE_TIMEOUT_H
#define SLED_TIMER_QUEUE_TIMEOUT_H
#include "sled/task_queue/task_queue_base.h"
#include "sled/timer/timeout.h"
#include <limits>
#include <memory>
namespace sled {
typedef uint64_t TimeMs;
class TaskQueueTimeoutFactory {
public:
TaskQueueTimeoutFactory(
sled::TaskQueueBase &task_queue,
std::function<TimeMs()> get_time,
std::function<void(TimeoutID timeout_id)> on_expired)
: task_queue_(task_queue),
get_time_(get_time),
on_expired_(on_expired)
{}
std::unique_ptr<Timeout>
CreateTimeout(sled::TaskQueueBase::DelayPrecision precision =
sled::TaskQueueBase::DelayPrecision::kHigh)
{
return std::unique_ptr<TaskQueueTimeout>(
new TaskQueueTimeout(*this, precision));
}
private:
class TaskQueueTimeout : public Timeout {
public:
TaskQueueTimeout(TaskQueueTimeoutFactory &parent,
sled::TaskQueueBase::DelayPrecision precision);
~TaskQueueTimeout() override;
void Start(DurationMs duration, TimeoutID timeout_id) override;
void Stop() override;
private:
TaskQueueTimeoutFactory &parent_;
const sled::TaskQueueBase::DelayPrecision precision_;
TimeMs posted_task_expiration_ = std::numeric_limits<TimeMs>::max();
TimeMs timeout_expiration_ = std::numeric_limits<TimeMs>::max();
TimeoutID timeout_id_ = TimeoutID(0);
};
sled::TaskQueueBase &task_queue_;
const std::function<TimeMs()> get_time_;
const std::function<void(TimeoutID)> on_expired_;
};
}// namespace sled
#endif// SLED_TIMER_QUEUE_TIMEOUT_H

View File

@ -0,0 +1,25 @@
#pragma once
#ifndef SLED_TIMER_TIMEOUT_H
#define SLED_TIMER_TIMEOUT_H
#include <stdint.h>
namespace sled {
typedef uint32_t DurationMs;
typedef uint64_t TimeoutID;
class Timeout {
public:
virtual ~Timeout() = default;
virtual void Start(DurationMs duration, TimeoutID timeout_id) = 0;
virtual void Stop() = 0;
virtual void Restart(DurationMs duration, TimeoutID timeout_id)
{
Stop();
Start(duration, timeout_id);
}
};
}// namespace sled
//
#endif// SLED_TIMER_TIMEOUT_H

View File

@ -0,0 +1,74 @@
#pragma once
#ifndef SLED_TIMER_TIMER_H
#define SLED_TIMER_TIMER_H
#include "timeout.h"
#include <map>
#include <memory>
#include <sled/optional.h>
#include <sled/task_queue/task_queue_base.h>
#include <stdint.h>
namespace sled {
typedef uint64_t TimerID;
typedef uint32_t TimerGeneration;
class Timer {
public:
using OnExpired = std::function<sled::optional<DurationMs>()>;
Timer(const Timer &) = delete;
Timer &operator=(const Timer &) = delete;
~Timer();
void Start();
void Stop();
void set_duration(DurationMs duration) { duration_ = duration; }
const DurationMs &duration() const { return duration_; }
int expireation_count() const { return expiration_count_; }
bool is_running() const { return is_running_; }
private:
friend class TimerManager;
using UnregisterHandler = std::function<void()>;
Timer(TimerID id,
const std::string &name,
OnExpired on_expired,
UnregisterHandler unregister_handler,
std::unique_ptr<Timeout> timeout);
const TimerID id_;
const std::string name_;
const OnExpired on_expired_;
const UnregisterHandler unregister_handler_;
std::unique_ptr<Timeout> timeout_;
DurationMs duration_;
TimerGeneration generation_ = TimerGeneration(0);
bool is_running_ = false;
int expiration_count_ = 0;
};
class TimerManager {
using TimeoutCreator = std::function<std::unique_ptr<Timeout>(
sled::TaskQueueBase::DelayPrecision)>;
public:
explicit TimerManager(TimeoutCreator timeout_creator)
: timeout_creator_(timeout_creator)
{}
std::unique_ptr<Timer> CreateTimer(const std::string &name,
Timer::OnExpired on_expired);
void HandleTimeout(TimeoutID timeout_id);
private:
const TimeoutCreator timeout_creator_;
std::map<TimerID, Timer *> timers_;
TimerID next_id_ = TimerID(0);
};
}// namespace sled
#endif// SLED_TIMER_TIMER_H

View File

@ -0,0 +1,58 @@
#include "sled/timer/task_queue_timeout.h"
#include "sled/log/log.h"
#include "sled/units/time_delta.h"
namespace sled {
TaskQueueTimeoutFactory::TaskQueueTimeout::TaskQueueTimeout(
TaskQueueTimeoutFactory &parent,
sled::TaskQueueBase::DelayPrecision precision)
: parent_(parent),
precision_(precision)
{}
TaskQueueTimeoutFactory::TaskQueueTimeout::~TaskQueueTimeout() {}
void
TaskQueueTimeoutFactory::TaskQueueTimeout::Start(DurationMs duration_ms,
TimeoutID timeout_id)
{
timeout_expiration_ = parent_.get_time_() + duration_ms;
timeout_id_ = timeout_id;
if (timeout_expiration_ >= posted_task_expiration_) { return; }
if (posted_task_expiration_ != std::numeric_limits<TimeMs>::max()) {
LOGV("timer",
"New timeout duration is less than scheduled - "
"ghosting old delayed task");
}
posted_task_expiration_ = timeout_expiration_;
parent_.task_queue_.PostDelayedTaskWithPrecision(
precision_,
[timeout_id, this]() {
posted_task_expiration_ = std::numeric_limits<TimeMs>::max();
if (timeout_expiration_ == std::numeric_limits<TimeMs>::max()) {
// cancelled timer
// do nothing
} else {
DurationMs remaining =
timeout_expiration_ - parent_.get_time_();
timeout_expiration_ = std::numeric_limits<TimeMs>::max();
if (remaining > 0) {
Start(remaining, timeout_id);
} else {
LOGD("", "Timeout Triggered: {}", timeout_id);
parent_.on_expired_(timeout_id_);
}
}
},
sled::TimeDelta::Millis(duration_ms));
}
void
TaskQueueTimeoutFactory::TaskQueueTimeout::Stop()
{
timeout_expiration_ = std::numeric_limits<TimeMs>::max();
}
}// namespace sled

60
src/timer/timer.cc Normal file
View File

@ -0,0 +1,60 @@
#include "sled/timer/timer.h"
namespace sled {
namespace {
TimeoutID
MakeTimeoutId(TimerID timer_id, TimerGeneration generation)
{
return TimeoutID(static_cast<uint64_t>((timer_id << 32) | generation));
}
}// namespace
Timer::Timer(TimerID id,
const std::string &name,
OnExpired on_expired,
UnregisterHandler unregister_handler,
std::unique_ptr<Timeout> timeout)
: id_(id),
name_(name),
on_expired_(on_expired),
unregister_handler_(unregister_handler),
timeout_(std::move(timeout))
{}
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 {
generation_ = TimerGeneration(generation_ + 1);
timeout_->Restart(duration_, MakeTimeoutId(id_, generation_));
}
}
std::unique_ptr<Timer>
TimerManager::CreateTimer(const std::string &name, Timer::OnExpired on_expired)
{
next_id_ = TimerID(next_id_ + 1);
TimerID id = next_id_;
std::unique_ptr<Timeout> timeout =
timeout_creator_(sled::TaskQueueBase::DelayPrecision::kHigh);
auto timer = std::unique_ptr<Timer>(new Timer(
id, name, std::move(on_expired),
/* ungrgister_handler=*/[this, id]() { timers_.erase(id); },
std::move(timeout)));
timers_[id] = timer.get();
return timer;
}
}// namespace sled