include/boost/capy/ex/detail/timer_service.hpp

100.0% Lines (14/14) 100.0% List of functions (4/4) 100.0% Branches (6/6)
f(x) Functions (4)
Line Branch TLA Hits Source 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 The id is written to @p out while the internal lock is
61 held, so the timer thread cannot fire the callback before
62 the write completes. This is required when the id
63 destination lives in memory that the callback itself may
64 free (e.g. a coroutine frame).
65 */
66 template<typename Rep, typename Period>
67 134x void schedule_after(
68 std::chrono::duration<Rep, Period> dur,
69 std::function<void()> cb,
70 timer_id& out)
71 {
72
3/3
void boost::capy::detail::timer_service::schedule_after<long, std::ratio<1l, 1000000000l> >(std::chrono::duration<long, std::ratio<1l, 1000000000l> >, std::function<void ()>, unsigned long&):
✓ Branch 2 taken 19 times.
void boost::capy::detail::timer_service::schedule_after<long, std::ratio<1l, 1000l> >(std::chrono::duration<long, std::ratio<1l, 1000l> >, std::function<void ()>, unsigned long&):
✓ Branch 2 taken 111 times.
void boost::capy::detail::timer_service::schedule_after<long, std::ratio<1l, 1l> >(std::chrono::duration<long, std::ratio<1l, 1l> >, std::function<void ()>, unsigned long&):
✓ Branch 2 taken 4 times.
134x auto deadline = std::chrono::steady_clock::now() + dur;
73
3/3
void boost::capy::detail::timer_service::schedule_after<long, std::ratio<1l, 1000000000l> >(std::chrono::duration<long, std::ratio<1l, 1000000000l> >, std::function<void ()>, unsigned long&):
✓ Branch 3 taken 19 times.
void boost::capy::detail::timer_service::schedule_after<long, std::ratio<1l, 1000l> >(std::chrono::duration<long, std::ratio<1l, 1000l> >, std::function<void ()>, unsigned long&):
✓ Branch 3 taken 111 times.
void boost::capy::detail::timer_service::schedule_after<long, std::ratio<1l, 1l> >(std::chrono::duration<long, std::ratio<1l, 1l> >, std::function<void ()>, unsigned long&):
✓ Branch 3 taken 4 times.
134x schedule_at(deadline, std::move(cb), out);
74 134x }
75
76 /** Cancel a pending timer.
77
78 After this function returns, the callback is guaranteed
79 not to be running and will never be invoked. If the
80 callback is currently executing on the timer thread,
81 this call blocks until it completes.
82
83 Safe to call with any id, including ids that have
84 already fired, been cancelled, or were never issued.
85 */
86 void cancel(timer_id id);
87
88 protected:
89 void shutdown() override;
90
91 private:
92 void stop_and_join();
93 struct entry
94 {
95 std::chrono::steady_clock::time_point deadline;
96 timer_id id;
97 std::function<void()> callback;
98
99 664x bool operator>(entry const& o) const noexcept
100 {
101 664x return deadline > o.deadline;
102 }
103 };
104
105 void schedule_at(
106 std::chrono::steady_clock::time_point deadline,
107 std::function<void()> cb,
108 timer_id& out);
109
110 void run();
111
112 // warning C4251: std types need to have dll-interface
113 #ifdef _MSC_VER
114 # pragma warning(push)
115 # pragma warning(disable: 4251)
116 #endif
117 std::mutex mutex_;
118 std::condition_variable cv_;
119 std::condition_variable cancel_cv_;
120 std::priority_queue<
121 entry,
122 std::vector<entry>,
123 std::greater<>> queue_;
124 std::unordered_set<timer_id> active_ids_;
125 timer_id next_id_ = 0;
126 timer_id executing_id_ = 0;
127 bool stopped_ = false;
128 std::thread thread_;
129 #ifdef _MSC_VER
130 # pragma warning(pop)
131 #endif
132 };
133
134 } // detail
135 } // capy
136 } // boost
137
138 #endif
139