diff --git a/Makefile b/Makefile index 9afd6587..637289a2 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,8 @@ ringloop.o: ringloop.cpp ringloop.h g++ $(CXXFLAGS) -c -o $@ $< timerfd_interval.o: timerfd_interval.cpp timerfd_interval.h ringloop.h g++ $(CXXFLAGS) -c -o $@ $< +timerfd_manager.o: timerfd_manager.cpp timerfd_manager.h ringloop.h + g++ $(CXXFLAGS) -c -o $@ $< %.o: %.cpp allocator.h blockstore_flush.h blockstore.h blockstore_impl.h blockstore_init.h blockstore_journal.h crc32c.h ringloop.h timerfd_interval.h object_id.h g++ $(CXXFLAGS) -c -o $@ $< diff --git a/timerfd_interval.h b/timerfd_interval.h index 84d9587c..96e60ec7 100644 --- a/timerfd_interval.h +++ b/timerfd_interval.h @@ -6,7 +6,6 @@ class timerfd_interval { int wait_state; int timerfd; - int status; ring_loop_t *ringloop; ring_consumer_t consumer; std::function callback; diff --git a/timerfd_manager.cpp b/timerfd_manager.cpp new file mode 100644 index 00000000..3d68fb93 --- /dev/null +++ b/timerfd_manager.cpp @@ -0,0 +1,160 @@ +#include +#include +#include +#include "timerfd_manager.h" + +timerfd_manager_t::timerfd_manager_t(ring_loop_t *ringloop) +{ + wait_state = 0; + timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); + if (timerfd < 0) + { + throw std::runtime_error(std::string("timerfd_create: ") + strerror(errno)); + } + consumer.loop = [this]() { loop(); }; + ringloop->register_consumer(&consumer); + this->ringloop = ringloop; +} + +timerfd_manager_t::~timerfd_manager_t() +{ + ringloop->unregister_consumer(&consumer); + close(timerfd); +} + +void timerfd_manager_t::inc_timer(timerfd_timer_t & t) +{ + t.next.tv_sec += t.millis/1000; + t.next.tv_nsec += (t.millis%1000)*1000000; + if (t.next.tv_nsec > 1000000000) + { + t.next.tv_sec++; + t.next.tv_nsec -= 1000000000; + } +} + +int timerfd_manager_t::set_timer(uint64_t millis, bool repeat, std::function callback) +{ + timespec start; + clock_gettime(CLOCK_MONOTONIC, &start); + timers.push_back({ + .id = id++, + .millis = millis, + .start = start, + .next = start, + .repeat = repeat, + .callback = callback, + }); + inc_timer(timers[timers.size()-1]); + set_nearest(); + set_wait(); + return id; +} + +void timerfd_manager_t::clear_timer(int timer_id) +{ + for (int i = 0; i < timers.size(); i++) + { + if (timers[i].id == timer_id) + { + timers.erase(timers.begin()+i, timers.begin()+i+1); + if (nearest == i) + { + nearest = -1; + wait_state = wait_state & ~1; + } + else if (nearest > i) + { + nearest--; + } + set_nearest(); + set_wait(); + break; + } + } +} + +void timerfd_manager_t::set_nearest() +{ + if (!timers.size()) + { + nearest = -1; + itimerspec exp = { 0 }; + if (timerfd_settime(timerfd, 0, &exp, NULL)) + { + throw std::runtime_error(std::string("timerfd_settime: ") + strerror(errno)); + } + wait_state = wait_state & ~1; + } + else + { + nearest = 0; + for (int i = 1; i < timers.size(); i++) + { + if (timers[i].next.tv_sec < timers[nearest].next.tv_sec || + timers[i].next.tv_sec == timers[nearest].next.tv_sec && + timers[i].next.tv_nsec < timers[nearest].next.tv_nsec) + { + nearest = i; + } + } + itimerspec exp = { + .it_interval = { 0 }, + .it_value = timers[nearest].next, + }; + if (timerfd_settime(timerfd, 0, &exp, NULL)) + { + throw std::runtime_error(std::string("timerfd_settime: ") + strerror(errno)); + } + wait_state = wait_state | 1; + } +} + +void timerfd_manager_t::loop() +{ + if (!(wait_state & 1) && timers.size()) + { + set_nearest(); + } + set_wait(); +} + +void timerfd_manager_t::set_wait() +{ + if ((wait_state & 3) == 1) + { + io_uring_sqe *sqe = ringloop->get_sqe(); + if (!sqe) + { + return; + } + ring_data_t *data = ((ring_data_t*)sqe->user_data); + my_uring_prep_poll_add(sqe, timerfd, POLLIN); + data->callback = [&](ring_data_t *data) + { + if (data->res < 0) + { + throw std::runtime_error(std::string("waiting for timer failed: ") + strerror(-data->res)); + } + uint64_t n; + read(timerfd, &n, 8); + if (nearest >= 0) + { + timers[nearest].callback(timers[nearest].id); + if (!timers[nearest].repeat) + { + timers.erase(timers.begin()+nearest, timers.begin()+nearest+1); + } + else + { + inc_timer(timers[nearest]); + } + nearest = -1; + } + wait_state = 0; + set_nearest(); + set_wait(); + }; + wait_state = 3; + } +} diff --git a/timerfd_manager.h b/timerfd_manager.h new file mode 100644 index 00000000..1ca5a232 --- /dev/null +++ b/timerfd_manager.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include "ringloop.h" + +struct timerfd_timer_t +{ + int id; + uint64_t millis; + timespec start, next; + bool repeat; + std::function callback; +}; + +class timerfd_manager_t +{ + int wait_state = 0; + int timerfd; + int nearest = -1; + int id = 1; + std::vector timers; + ring_loop_t *ringloop; + ring_consumer_t consumer; + + void inc_timer(timerfd_timer_t & t); + void set_nearest(); + void set_wait(); + void loop(); +public: + timerfd_manager_t(ring_loop_t *ringloop); + ~timerfd_manager_t(); + int set_timer(uint64_t millis, bool repeat, std::function callback); + void clear_timer(int timer_id); +};