Skip to content

Commit

Permalink
Update Timer: support level_down
Browse files Browse the repository at this point in the history
  • Loading branch information
8sileus committed Mar 20, 2024
1 parent c8dffd6 commit bc0b6cf
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 81 deletions.
9 changes: 8 additions & 1 deletion tests/timeout_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,14 @@ auto test() -> Task<void> {
LOG_INFO("co_await a passed time");
}

auto main_test() -> Task<void> {
co_await test();
co_await time::sleep(5s);
LOG_INFO("interval {}", 5s);
co_await test();
}

auto main() -> int {
SET_LOG_LEVEL(zedio::log::LogLevel::TRACE);
return Runtime::options().scheduler().set_num_workers(1).build().block_on(test());
return Runtime::options().scheduler().set_num_workers(1).build().block_on(main_test());
}
102 changes: 68 additions & 34 deletions zedio/runtime/timer/timer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,28 @@ class Timer {
public:
auto add_entry(std::chrono::steady_clock::time_point expiration_time,
std::coroutine_handle<> handle) -> Result<std::weak_ptr<Entry>> {
if (expiration_time <= std::chrono::steady_clock::now()) [[unlikely]] {
auto now = std::chrono::steady_clock::now();
if (expiration_time <= now) [[unlikely]] {
return std::unexpected{make_zedio_error(Error::PassedTime)};
}
if (expiration_time >= start_ + std::chrono::milliseconds{MAX_MS}) [[unlikely]] {
return std::unexpected{make_zedio_error(Error::TooLongTime)};
}
std::size_t interval
= std::chrono::duration_cast<std::chrono::milliseconds>(expiration_time - start_)
.count();

auto [entry, result] = Entry::make(expiration_time, handle);

std::visit(
[this, interval, &entry]<typename T>(T &wheel) {
[this, now, &entry]<typename T>(T &wheel) {
if constexpr (std::is_same_v<T, std::monostate>) {
auto wheel = std::make_unique<Wheel<0>>();
this->level_up_and_add_entry(std::move(wheel), std::move(entry), interval);
start_ = now;
// build main wheel
build_wheel_and_add_entry(std::move(entry),
time_since_start(entry->expiration_time_));
} else {
this->level_up_and_add_entry(std::move(wheel), std::move(entry), interval);
// level up
level_up_and_add_entry(std::move(wheel),
std::move(entry),
time_since_start(entry->expiration_time_));
}
},
root_wheel_);
Expand All @@ -58,38 +61,37 @@ class Timer {
}

void remove_entry(std::shared_ptr<Entry> &&entry) {
auto interval = std::chrono::duration_cast<std::chrono::milliseconds>(
entry->expiration_time_ - start_)
.count();
assert(root_wheel_.index() != 0 && num_entries_ != 0);
std::visit(
[this, interval, &entry]<typename T>(T &wheel) {
[this, &entry]<typename T>(T &wheel) {
if constexpr (std::is_same_v<T, std::monostate>) {
std::unreachable();
LOG_ERROR("no entries");
} else {
wheel->remove_entry(std::move(entry), interval);
wheel->remove_entry(std::move(entry),
time_since_start(entry->expiration_time_));
num_entries_ -= 1;
if (num_entries_ == 0) {
root_wheel_ = std::monostate{};
}
// maybe optimize
// else {
// level_down_if_needed(std::move(wheel));
// }
}
},
root_wheel_);
num_entries_ -= 1;
if (num_entries_ == 0) {
root_wheel_ = std::monostate{};
}
// TODO consider level_downs
}

[[nodiscard]]
auto next_expiration_time() -> std::optional<uint64_t> {
return std::visit(
[this]<typename T>(T &ptr) -> std::optional<uint64_t> {
[this]<typename T>(T &wheel) -> std::optional<uint64_t> {
if constexpr (std::is_same_v<T, std::monostate>) {
return std::nullopt;
} else {
return ptr->next_expiration_time()
- std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start_)
.count();
auto now = std::chrono::steady_clock::now();
return wheel->next_expiration_time() - time_since_start(now);
}
},
root_wheel_);
Expand Down Expand Up @@ -120,36 +122,38 @@ class Timer {
count,
static_cast<std::size_t>(interval));
num_entries_ -= count;
change_state(wheel, interval);
maintenance(wheel, interval);
}
},
root_wheel_);
return count;
}

private:
template <typename T>
void change_state(T &wheel, std::size_t interval) {
template <std::size_t N>
void maintenance(std::unique_ptr<Wheel<N>> &wheel, std::size_t interval) {
if (wheel->empty()) {
assert(num_entries_ == 0);
root_wheel_ = std::monostate{};
return;
}
// TODO consider level_down
auto n = interval / wheel->ms_per_slot();
auto n = interval / Wheel<N>::MS_PER_SLOT;
if (n > 0) {
wheel->rotate(n);
}
start_ += std::chrono::milliseconds{wheel->ms_per_slot() * n};
start_ += std::chrono::milliseconds{Wheel<N>::MS_PER_SLOT * n};
level_down_if_needed(std::move(wheel));
}

template <class T>
void level_up_and_add_entry(T &&wheel, std::shared_ptr<Entry> &&entry, std::size_t interval) {
if constexpr (std::is_same_v<T, std::unique_ptr<Wheel<MAX_LEVEL + 1uz>>>) {
template <std::size_t N>
void level_up_and_add_entry(std::unique_ptr<Wheel<N>> &&wheel,
std::shared_ptr<Entry> &&entry,
std::size_t interval) {
if constexpr (N == MAX_LEVEL + 1) {
assert(false);
std::unreachable();
} else {
if (interval >= wheel->max_ms()) {
if (interval >= Wheel<N>::MAX_MS) {
level_up_and_add_entry(wheel->level_up(std::move(wheel)),
std::move(entry),
interval);
Expand All @@ -160,9 +164,39 @@ class Timer {
}
}

template <std::size_t N = MAX_LEVEL>
void build_wheel_and_add_entry(std::shared_ptr<Entry> &&entry, std::size_t interval) {
if constexpr (N > 0uz) {
if (!(Wheel<N>::MS_PER_SLOT <= interval && interval < Wheel<N>::MAX_MS)) {
build_wheel_and_add_entry<N - 1>(std::move(entry), interval);
return;
}
}
auto wheel = std::make_unique<Wheel<N>>();
wheel->add_entry(std::move(entry), interval);
root_wheel_ = std::move(wheel);
}

template <class T>
void level_down_if_needed(T &&wheel) {
if constexpr (!std::is_same_v<T, std::unique_ptr<Wheel<0>>>) {
if (wheel->can_level_down()) {
level_down_if_needed(wheel->level_down());
return;
}
}
root_wheel_ = std::move(wheel);
}

[[nodiscard]]
auto time_since_start(std::chrono::steady_clock::time_point expiration_time) -> std::size_t {
return std::chrono::duration_cast<std::chrono::milliseconds>(expiration_time - start_)
.count();
}

private:
//~ 139 years
static constexpr std::size_t MAX_MS{util::static_pow(SLOT_SIZE, MAX_LEVEL + 1)};
static constexpr std::size_t MAX_MS{util::static_pow(SLOT_SIZE, MAX_LEVEL + 1uz)};

private:
std::chrono::steady_clock::time_point start_{std::chrono::steady_clock::now()};
Expand Down
81 changes: 35 additions & 46 deletions zedio/runtime/timer/wheel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,33 @@

namespace zedio::runtime::detail {

template <std::size_t level>
template <std::size_t LEVEL>
class Wheel {
private:
using FatherWheel = Wheel<level + 1>;
public:
using FatherWheel = Wheel<LEVEL + 1>;
using FatherWheelPtr = std::unique_ptr<FatherWheel>;
using ChildWheel = Wheel<level - 1>;
using ChildWheel = Wheel<LEVEL - 1>;
using ChildWheelPtr = std::unique_ptr<ChildWheel>;

public:
Wheel() {
// LOG_DEBUG("build Wheel {}", level);
LOG_DEBUG("build Wheel {}", LEVEL);
}

Wheel(ChildWheelPtr &&child)
: bitmap_{1uz}
, slots_{std::move(child)} {
// LOG_DEBUG("level up from {} to {}", level - 1, level);
LOG_DEBUG("level up from {} to {}", LEVEL - 1, LEVEL);
}

~Wheel() {
// LOG_DEBUG("desc Wheel {}", level);
LOG_DEBUG("desc Wheel {}", LEVEL);
}

public:
void add_entry(std::shared_ptr<Entry> &&entry, std::size_t interval) {
std::size_t index = get_index(interval);
LOG_DEBUG("{} {}", index, interval);
if (slots_[index] == nullptr) {
bitmap_ |= 1uz << index;
slots_[index] = std::make_unique<ChildWheel>();
Expand All @@ -57,11 +58,6 @@ class Wheel {
}
}

[[nodiscard]]
auto empty() const noexcept -> bool {
return bitmap_ == 0;
}

void handle_expired_entries(runtime::detail::LocalQueue &local_queue,
runtime::detail::GlobalQueue &global_queue,
std::size_t &count,
Expand Down Expand Up @@ -101,35 +97,36 @@ class Wheel {
}

[[nodiscard]]
constexpr auto ms_per_slot() const noexcept -> std::size_t {
return MS_PER_SLOT;
auto level_up(std::unique_ptr<Wheel> &&me) -> FatherWheelPtr {
return std::make_unique<FatherWheel>(std::move(me));
}

[[nodiscard]]
constexpr auto max_ms() const noexcept -> std::size_t {
return MS_PER_SLOT * SLOT_SIZE;
auto level_down() -> ChildWheelPtr {
return std::move(slots_[0]);
}

[[nodiscard]]
auto level_up(std::unique_ptr<Wheel> &&me) -> FatherWheelPtr {
return std::make_unique<FatherWheel>(std::move(me));
auto can_level_down() const noexcept -> bool {
return bitmap_ == 1uz;
}

// TODO
// auto level_down() -> ChildWheelPtr {
// return std::move(slots_[0]);
// }
[[nodiscard]]
auto empty() const noexcept -> bool {
return bitmap_ == 0;
}

private:
[[nodiscard]]
auto get_index(std::size_t interval) -> std::size_t {
return (interval & MASK) >> SHIFT;
}

private:
static constexpr std::size_t SHIFT{level * 6uz};
public:
static constexpr std::size_t SHIFT{LEVEL * 6uz};
static constexpr std::size_t MASK{(SLOT_SIZE - 1uz) << SHIFT};
static constexpr std::size_t MS_PER_SLOT{util::static_pow(SLOT_SIZE, level)};
static constexpr std::size_t MS_PER_SLOT{util::static_pow(SLOT_SIZE, LEVEL)};
static constexpr std::size_t MAX_MS{MS_PER_SLOT * SLOT_SIZE};

private:
uint64_t bitmap_{0};
Expand All @@ -138,22 +135,23 @@ class Wheel {

template <>
class Wheel<0uz> {
private:
public:
using FatherWheel = Wheel<1>;
using FatherWheelPtr = std::unique_ptr<FatherWheel>;

public:
Wheel() {
// LOG_DEBUG("build Wheel {}", 0);
LOG_DEBUG("build Wheel {}", 0);
}

~Wheel() {
// LOG_DEBUG("desc Wheel {}", 0);
LOG_DEBUG("desc Wheel {}", 0);
}

public:
void add_entry(std::shared_ptr<Entry> &&entry, std::size_t interval) {
auto index = get_index(interval);
LOG_DEBUG("{} {}", index, interval);
entry->next_ = std::move(slots_[index]);
bitmap_ |= 1uz << index;
slots_[index] = std::move(entry);
Expand Down Expand Up @@ -204,21 +202,6 @@ class Wheel<0uz> {
return std::countr_zero(bitmap_);
}

[[nodiscard]]
auto empty() const noexcept -> bool {
return bitmap_ == 0;
}

[[nodiscard]]
constexpr auto ms_per_slot() const noexcept -> std::size_t {
return MS_PER_SLOT;
}

[[nodiscard]]
constexpr auto max_ms() const noexcept -> std::size_t {
return MS_PER_SLOT * SLOT_SIZE;
}

void rotate(std::size_t start) {
assert(start < slots_.size());
for (auto i = start; i < slots_.size(); i += 1) {
Expand All @@ -233,15 +216,21 @@ class Wheel<0uz> {
return std::make_unique<FatherWheel>(std::move(me));
}

[[nodiscard]]
auto empty() const noexcept -> bool {
return bitmap_ == 0;
}

private:
[[nodiscard]]
auto get_index(std::size_t interval) -> std::size_t {
return interval & MASK;
}

private:
static constexpr std::size_t MASK{SLOT_SIZE - 1};
static constexpr std::size_t MS_PER_SLOT{1};
public:
static constexpr std::size_t MASK{SLOT_SIZE - 1uz};
static constexpr std::size_t MS_PER_SLOT{1uz};
static constexpr std::size_t MAX_MS{MS_PER_SLOT * SLOT_SIZE};

private:
uint64_t bitmap_{0};
Expand Down

0 comments on commit bc0b6cf

Please sign in to comment.