TLA Line data 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 : @return An id that can be passed to cancel().
61 : */
62 : template<typename Rep, typename Period>
63 HIT 134 : timer_id schedule_after(
64 : std::chrono::duration<Rep, Period> dur,
65 : std::function<void()> cb)
66 : {
67 134 : auto deadline = std::chrono::steady_clock::now() + dur;
68 134 : 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 :
94 664 : bool operator>(entry const& o) const noexcept
95 : {
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
|