100.00% Lines (5/5)
100.00% Functions (2/2)
| TLA | Baseline | Branch | ||||||
|---|---|---|---|---|---|---|---|---|
| Line | Hits | Code | Line | Hits | Code | |||
| 1 | // | 1 | // | |||||
| 2 | // Copyright (c) 2026 Michael Vandeberg | 2 | // Copyright (c) 2026 Michael Vandeberg | |||||
| 3 | // | 3 | // | |||||
| 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |||||
| 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |||||
| 6 | // | 6 | // | |||||
| 7 | // Official repository: https://github.com/cppalliance/capy | 7 | // Official repository: https://github.com/cppalliance/capy | |||||
| 8 | // | 8 | // | |||||
| 9 | 9 | |||||||
| 10 | #ifndef BOOST_CAPY_EX_TIMER_SERVICE_HPP | 10 | #ifndef BOOST_CAPY_EX_TIMER_SERVICE_HPP | |||||
| 11 | #define BOOST_CAPY_EX_TIMER_SERVICE_HPP | 11 | #define BOOST_CAPY_EX_TIMER_SERVICE_HPP | |||||
| 12 | 12 | |||||||
| 13 | #include <boost/capy/detail/config.hpp> | 13 | #include <boost/capy/detail/config.hpp> | |||||
| 14 | #include <boost/capy/ex/execution_context.hpp> | 14 | #include <boost/capy/ex/execution_context.hpp> | |||||
| 15 | 15 | |||||||
| 16 | #include <chrono> | 16 | #include <chrono> | |||||
| 17 | #include <cstdint> | 17 | #include <cstdint> | |||||
| 18 | #include <functional> | 18 | #include <functional> | |||||
| 19 | #include <mutex> | 19 | #include <mutex> | |||||
| 20 | #include <condition_variable> | 20 | #include <condition_variable> | |||||
| 21 | #include <queue> | 21 | #include <queue> | |||||
| 22 | #include <thread> | 22 | #include <thread> | |||||
| 23 | #include <unordered_set> | 23 | #include <unordered_set> | |||||
| 24 | #include <vector> | 24 | #include <vector> | |||||
| 25 | 25 | |||||||
| 26 | namespace boost { | 26 | namespace boost { | |||||
| 27 | namespace capy { | 27 | namespace capy { | |||||
| 28 | namespace detail { | 28 | namespace detail { | |||||
| 29 | 29 | |||||||
| 30 | /* Shared timer thread for an execution_context. | 30 | /* Shared timer thread for an execution_context. | |||||
| 31 | 31 | |||||||
| 32 | One background std::thread per execution_context. All timeouts | 32 | One background std::thread per execution_context. All timeouts | |||||
| 33 | scheduled through this context share the same thread, which sleeps | 33 | scheduled through this context share the same thread, which sleeps | |||||
| 34 | on a condition variable until the next deadline. | 34 | on a condition variable until the next deadline. | |||||
| 35 | 35 | |||||||
| 36 | The timer thread never touches coroutine frames or executors | 36 | The timer thread never touches coroutine frames or executors | |||||
| 37 | directly — callbacks are responsible for posting work through | 37 | directly — callbacks are responsible for posting work through | |||||
| 38 | the appropriate executor. | 38 | the appropriate executor. | |||||
| 39 | */ | 39 | */ | |||||
| 40 | 40 | |||||||
| 41 | class BOOST_CAPY_DECL | 41 | class BOOST_CAPY_DECL | |||||
| 42 | timer_service | 42 | timer_service | |||||
| 43 | : public execution_context::service | 43 | : public execution_context::service | |||||
| 44 | { | 44 | { | |||||
| 45 | public: | 45 | public: | |||||
| 46 | using timer_id = std::uint64_t; | 46 | using timer_id = std::uint64_t; | |||||
| 47 | 47 | |||||||
| 48 | explicit timer_service(execution_context& ctx); | 48 | explicit timer_service(execution_context& ctx); | |||||
| 49 | 49 | |||||||
| 50 | // Calls shutdown() to join the background thread. | 50 | // Calls shutdown() to join the background thread. | |||||
| 51 | // Handles the discard path in use_service_impl where | 51 | // Handles the discard path in use_service_impl where | |||||
| 52 | // a duplicate service is deleted without shutdown(). | 52 | // a duplicate service is deleted without shutdown(). | |||||
| 53 | ~timer_service(); | 53 | ~timer_service(); | |||||
| 54 | 54 | |||||||
| 55 | /** Schedule a callback to fire after a duration. | 55 | /** Schedule a callback to fire after a duration. | |||||
| 56 | 56 | |||||||
| 57 | The callback is invoked on the timer service's background | 57 | The callback is invoked on the timer service's background | |||||
| 58 | thread. It must not block for extended periods. | 58 | thread. It must not block for extended periods. | |||||
| 59 | 59 | |||||||
| 60 | @return An id that can be passed to cancel(). | 60 | @return An id that can be passed to cancel(). | |||||
| 61 | */ | 61 | */ | |||||
| 62 | template<typename Rep, typename Period> | 62 | template<typename Rep, typename Period> | |||||
| HITCBC | 63 | 135 | timer_id schedule_after( | 63 | 135 | timer_id schedule_after( | ||
| 64 | std::chrono::duration<Rep, Period> dur, | 64 | std::chrono::duration<Rep, Period> dur, | |||||
| 65 | std::function<void()> cb) | 65 | std::function<void()> cb) | |||||
| 66 | { | 66 | { | |||||
| HITCBC | 67 | 135 | auto deadline = std::chrono::steady_clock::now() + dur; | 67 | 135 | auto deadline = std::chrono::steady_clock::now() + dur; | ||
| HITCBC | 68 | 135 | return schedule_at(deadline, std::move(cb)); | 68 | 135 | return schedule_at(deadline, std::move(cb)); | ||
| 69 | } | 69 | } | |||||
| 70 | 70 | |||||||
| 71 | /** Cancel a pending timer. | 71 | /** Cancel a pending timer. | |||||
| 72 | 72 | |||||||
| 73 | After this function returns, the callback is guaranteed | 73 | After this function returns, the callback is guaranteed | |||||
| 74 | not to be running and will never be invoked. If the | 74 | not to be running and will never be invoked. If the | |||||
| 75 | callback is currently executing on the timer thread, | 75 | callback is currently executing on the timer thread, | |||||
| 76 | this call blocks until it completes. | 76 | this call blocks until it completes. | |||||
| 77 | 77 | |||||||
| 78 | Safe to call with any id, including ids that have | 78 | Safe to call with any id, including ids that have | |||||
| 79 | already fired, been cancelled, or were never issued. | 79 | already fired, been cancelled, or were never issued. | |||||
| 80 | */ | 80 | */ | |||||
| 81 | void cancel(timer_id id); | 81 | void cancel(timer_id id); | |||||
| 82 | 82 | |||||||
| 83 | protected: | 83 | protected: | |||||
| 84 | void shutdown() override; | 84 | void shutdown() override; | |||||
| 85 | 85 | |||||||
| 86 | private: | 86 | private: | |||||
| 87 | void stop_and_join(); | 87 | void stop_and_join(); | |||||
| 88 | struct entry | 88 | struct entry | |||||
| 89 | { | 89 | { | |||||
| 90 | std::chrono::steady_clock::time_point deadline; | 90 | std::chrono::steady_clock::time_point deadline; | |||||
| 91 | timer_id id; | 91 | timer_id id; | |||||
| 92 | std::function<void()> callback; | 92 | std::function<void()> callback; | |||||
| 93 | 93 | |||||||
| HITCBC | 94 | 664 | bool operator>(entry const& o) const noexcept | 94 | 664 | bool operator>(entry const& o) const noexcept | ||
| 95 | { | 95 | { | |||||
| HITCBC | 96 | 664 | return deadline > o.deadline; | 96 | 664 | return deadline > o.deadline; | ||
| 97 | } | 97 | } | |||||
| 98 | }; | 98 | }; | |||||
| 99 | 99 | |||||||
| 100 | timer_id schedule_at( | 100 | timer_id schedule_at( | |||||
| 101 | std::chrono::steady_clock::time_point deadline, | 101 | std::chrono::steady_clock::time_point deadline, | |||||
| 102 | std::function<void()> cb); | 102 | std::function<void()> cb); | |||||
| 103 | 103 | |||||||
| 104 | void run(); | 104 | void run(); | |||||
| 105 | 105 | |||||||
| 106 | // warning C4251: std types need to have dll-interface | 106 | // warning C4251: std types need to have dll-interface | |||||
| 107 | BOOST_CAPY_MSVC_WARNING_PUSH | 107 | BOOST_CAPY_MSVC_WARNING_PUSH | |||||
| 108 | BOOST_CAPY_MSVC_WARNING_DISABLE(4251) | 108 | BOOST_CAPY_MSVC_WARNING_DISABLE(4251) | |||||
| 109 | std::mutex mutex_; | 109 | std::mutex mutex_; | |||||
| 110 | std::condition_variable cv_; | 110 | std::condition_variable cv_; | |||||
| 111 | std::condition_variable cancel_cv_; | 111 | std::condition_variable cancel_cv_; | |||||
| 112 | std::priority_queue< | 112 | std::priority_queue< | |||||
| 113 | entry, | 113 | entry, | |||||
| 114 | std::vector<entry>, | 114 | std::vector<entry>, | |||||
| 115 | std::greater<>> queue_; | 115 | std::greater<>> queue_; | |||||
| 116 | std::unordered_set<timer_id> active_ids_; | 116 | std::unordered_set<timer_id> active_ids_; | |||||
| 117 | timer_id next_id_ = 0; | 117 | timer_id next_id_ = 0; | |||||
| 118 | timer_id executing_id_ = 0; | 118 | timer_id executing_id_ = 0; | |||||
| 119 | bool stopped_ = false; | 119 | bool stopped_ = false; | |||||
| 120 | std::thread thread_; | 120 | std::thread thread_; | |||||
| 121 | BOOST_CAPY_MSVC_WARNING_POP | 121 | BOOST_CAPY_MSVC_WARNING_POP | |||||
| 122 | }; | 122 | }; | |||||
| 123 | 123 | |||||||
| 124 | } // detail | 124 | } // detail | |||||
| 125 | } // capy | 125 | } // capy | |||||
| 126 | } // boost | 126 | } // boost | |||||
| 127 | 127 | |||||||
| 128 | #endif | 128 | #endif | |||||