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 : 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 HIT 134 : void schedule_after(
68 : std::chrono::duration<Rep, Period> dur,
69 : std::function<void()> cb,
70 : timer_id& out)
71 : {
72 134 : auto deadline = std::chrono::steady_clock::now() + dur;
73 134 : schedule_at(deadline, std::move(cb), out);
74 134 : }
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 664 : bool operator>(entry const& o) const noexcept
100 : {
101 664 : 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
|