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