From 1b237200a9f5e65c80a09f995a29120c4341ec50 Mon Sep 17 00:00:00 2001 From: Jeeyong Um Date: Sat, 2 Jul 2022 16:38:08 +0800 Subject: [PATCH] feat(time): add missing methods to ticker/timer --- CMakeLists.txt | 2 ++ examples/CMakeLists.txt | 2 ++ examples/default-selection.cpp | 8 ++--- examples/tickers.cpp | 59 ++++++++++++++++++++++++++++++++++ examples/timers.cpp | 49 ++++++++++++++++++++++++++++ src/eo/CMakeLists.txt | 1 + src/eo/fmt.h | 13 ++++++++ src/eo/main.cpp | 1 + src/eo/time/ticker.h | 35 ++++++++++++++------ src/eo/time/timer.h | 40 ++++++++++++++++++----- 10 files changed, 189 insertions(+), 21 deletions(-) create mode 100644 examples/tickers.cpp create mode 100644 examples/timers.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b03eae4..9ec4860 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ endif() include(conan) set(CONAN_PACKAGES + date/3.0.1 fmt/8.1.1 scope-lite/0.2.0 ) @@ -66,6 +67,7 @@ if(APPLE) endif() find_package(Boost REQUIRED) +find_package(date REQUIRED) find_package(fmt REQUIRED) find_package(scope-lite REQUIRED) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e437754..152d220 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,6 +5,8 @@ add_executable(channels channels.cpp) add_executable(channel-directions channel-directions.cpp) add_executable(select select.cpp) add_executable(non-blocking-channel-operations non-blocking-channel-operations.cpp) +add_executable(timers timers.cpp) +add_executable(tickers tickers.cpp) add_executable(waitgroups waitgroups.cpp) add_executable(mutexes mutexes.cpp) add_executable(defer defer.cpp) diff --git a/examples/default-selection.cpp b/examples/default-selection.cpp index 8262edf..8fa1e01 100644 --- a/examples/default-selection.cpp +++ b/examples/default-selection.cpp @@ -30,11 +30,11 @@ using namespace eo; func<> eo_main() { - auto tick = time::Ticker(std::chrono::milliseconds(100)); - auto boom = time::Timer(std::chrono::milliseconds(500)); + auto tick = time::new_ticker(std::chrono::milliseconds(100)); + auto boom = time::new_timer(std::chrono::milliseconds(500)); - auto select = Select{ *tick.ch, *boom.ch, CaseDefault() }; - // auto select = Select{ *tick.ch, *boom.ch }; + auto select = Select{ *tick->c, *boom->c, CaseDefault() }; + // auto select = Select{ *tick.c, *boom.c }; for (;;) { switch (co_await select.index()) { diff --git a/examples/tickers.cpp b/examples/tickers.cpp new file mode 100644 index 0000000..b5edd08 --- /dev/null +++ b/examples/tickers.cpp @@ -0,0 +1,59 @@ +// https://gobyexample.com/tickers +// +// package main +// +// import ( +// "fmt" +// "time" +// ) +// +// func main() { +// ticker := time.NewTicker(500 * time.Millisecond) +// done := make(chan bool) +// +// go func() { +// for { +// select { +// case <-done: +// return +// case t := <-ticker.C: +// fmt.Println("Tick at", t) +// } +// } +// }() +// +// time.Sleep(1600 * time.Millisecond) +// ticker.Stop() +// done <- true +// fmt.Println("Ticker stopped") +// } + +#include +#include + +using namespace eo; + +func<> eo_main() { + auto ticker = time::new_ticker(std::chrono::milliseconds(500)); + auto done = make_chan(); + + go([&]() -> func<> { + auto select = Select{*done, *ticker->c}; + for (;;) { + switch (co_await select.index()) { + case 0: + co_await select.process<0>(); + co_return; + case 1: + auto t = co_await select.process<1>(); + fmt::println("Tick at {}", t); + break; + } + } + }); + + co_await time::sleep(std::chrono::milliseconds(1600)); + ticker->stop(); + co_await *(done << true); + fmt::println("Ticker stopped"); +} diff --git a/examples/timers.cpp b/examples/timers.cpp new file mode 100644 index 0000000..bbcf284 --- /dev/null +++ b/examples/timers.cpp @@ -0,0 +1,49 @@ +// package main +// +// import ( +// "fmt" +// "time" +// ) +// +// func main() { +// timer1 := time.NewTimer(2 * time.Second) +// +// <-timer1.C +// fmt.Println("Timer 1 fired") +// +// timer2 := time.NewTimer(time.Second) +// go func() { +// <-timer2.C +// fmt.Println("Timer 2 fired") +// }() +// stop2 := timer2.Stop() +// if stop2 { +// fmt.Println("Timer 2 stopped") +// } +// +// time.Sleep(2 * time.Second) +// } + +#include +#include + +using namespace eo; + +func<> eo_main() { + auto timer1 = time::new_timer(std::chrono::seconds(2)); + + co_await **timer1->c; + fmt::println("Timer 1 fired"); + + auto timer2 = time::new_timer(std::chrono::seconds(1)); + go([=]() -> func<> { + co_await **timer2->c; + fmt::println("Timer 2 fired"); + }); + auto stop2 = timer2->stop(); + if (stop2) { + fmt::println("Timer 2 stopped"); + } + + co_await time::sleep(std::chrono::seconds(2)); +} diff --git a/src/eo/CMakeLists.txt b/src/eo/CMakeLists.txt index b244ee7..8bdca91 100644 --- a/src/eo/CMakeLists.txt +++ b/src/eo/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(eo STATIC ) target_include_directories(eo PUBLIC ${PROJECT_SOURCE_DIR}/src ${Boost_INCLUDE_DIR}) target_link_libraries(eo + date::date fmt::fmt nonstd::scope-lite ) diff --git a/src/eo/fmt.h b/src/eo/fmt.h index 8df9a60..d50c36c 100644 --- a/src/eo/fmt.h +++ b/src/eo/fmt.h @@ -4,6 +4,8 @@ #pragma once #include +#include +#include #include #include @@ -55,6 +57,17 @@ struct formatter> { } }; +template +struct formatter> { + constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + return ctx.end(); + } + template + auto format(const std::chrono::time_point& p, FormatContext& ctx) { + return format_to(ctx.out(), date::format("%F %T %z %Z", date::make_zoned(date::current_zone(), p))); + } +}; + } // namespace fmt namespace eo { diff --git a/src/eo/main.cpp b/src/eo/main.cpp index 84dcc9a..6ff7b69 100644 --- a/src/eo/main.cpp +++ b/src/eo/main.cpp @@ -14,6 +14,7 @@ int main(int argc, char** argv) { std::exception_ptr _eptr{}; boost::asio::co_spawn(runtime::executor, eo_main(), [&](std::exception_ptr eptr) { _eptr = eptr; + runtime::executor.stop(); }); runtime::executor.join(); if (_eptr) { diff --git a/src/eo/time/ticker.h b/src/eo/time/ticker.h index 09657fc..f7fd40a 100644 --- a/src/eo/time/ticker.h +++ b/src/eo/time/ticker.h @@ -7,19 +7,30 @@ namespace eo::time { -struct Ticker { +struct Ticker : public std::enable_shared_from_this { + using time_point = std::chrono::system_clock::time_point; + boost::asio::steady_timer timer{runtime::executor}; - chan<> ch = make_chan(1); + chan c = make_chan(1); - Ticker(const std::chrono::steady_clock::duration& d) { - reset(d); + static auto create(const std::chrono::steady_clock::duration& d) -> std::shared_ptr { + auto timer = std::make_shared(); + timer->reset(d); + return timer; } template - Ticker(const std::chrono::steady_clock::duration& d, Executor& executor): timer(executor) { - reset(d); + static auto create_with_executor(const std::chrono::steady_clock::duration& d, Executor& executor) -> std::shared_ptr { + auto timer = std::make_shared(executor); + timer->reset(d); + return timer; } + Ticker() = default; + + template + Ticker(Executor& executor): timer(executor) {} + ~Ticker() { timer.cancel(); } @@ -27,13 +38,19 @@ struct Ticker { void reset(const std::chrono::steady_clock::duration& d) { timer.cancel(); timer.expires_after(d); - timer.async_wait([this, d{d}](boost::system::error_code ec) { + timer.async_wait([=, self{shared_from_this()}](boost::system::error_code ec) { if (ec) return; - ch.try_send(boost::system::error_code{}, std::monostate{}); - reset(d); + self->c.try_send(boost::system::error_code{}, std::chrono::system_clock::now()); + self->reset(d); }); } + + void stop() { + timer.cancel(); + } }; +const auto new_ticker = Ticker::create; + } // namespace eo::time diff --git a/src/eo/time/timer.h b/src/eo/time/timer.h index bba73c4..318040e 100644 --- a/src/eo/time/timer.h +++ b/src/eo/time/timer.h @@ -7,19 +7,31 @@ namespace eo::time { -struct Timer { +struct Timer : public std::enable_shared_from_this { + using time_point = std::chrono::system_clock::time_point; + boost::asio::steady_timer timer{runtime::executor}; - chan<> ch = make_chan(1); + chan c = make_chan(1); + bool expired; - Timer(const std::chrono::steady_clock::duration& d) { - reset(d); + static auto create(const std::chrono::steady_clock::duration& d) -> std::shared_ptr { + auto timer = std::make_shared(); + timer->reset(d); + return timer; } template - Timer(const std::chrono::steady_clock::duration& d, Executor& executor): timer(executor) { - reset(d); + static auto create_with_executor(const std::chrono::steady_clock::duration& d, Executor& executor) -> std::shared_ptr { + auto timer = std::make_shared(executor); + timer->reset(d); + return timer; } + Timer() = default; + + template + Timer(Executor& executor): timer(executor) {} + ~Timer() { timer.cancel(); } @@ -27,12 +39,24 @@ struct Timer { void reset(const std::chrono::steady_clock::duration& d) { timer.cancel(); timer.expires_after(d); - timer.async_wait([this, d{d}](boost::system::error_code ec) { + expired = false; + timer.async_wait([=, self{shared_from_this()}](boost::system::error_code ec) { + self->expired = true; if (ec) return; - ch.try_send(boost::system::error_code{}, std::monostate{}); + self->c.try_send(boost::system::error_code{}, std::chrono::system_clock::now()); }); } + + auto stop() -> bool { + if (expired) { + return false; + } + timer.cancel(); + return !std::exchange(expired, true); + } }; +const auto new_timer = Timer::create; + } // namespace eo::time