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 : #include <boost/capy/ex/detail/timer_service.hpp>
11 :
12 : namespace boost {
13 : namespace capy {
14 : namespace detail {
15 :
16 HIT 19 : timer_service::
17 19 : timer_service(execution_context& ctx)
18 38 : : thread_([this] { run(); })
19 : {
20 : (void)ctx;
21 19 : }
22 :
23 38 : timer_service::
24 19 : ~timer_service()
25 : {
26 19 : stop_and_join();
27 38 : }
28 :
29 : void
30 134 : timer_service::
31 : schedule_at(
32 : std::chrono::steady_clock::time_point deadline,
33 : std::function<void()> cb,
34 : timer_id& out)
35 : {
36 134 : std::lock_guard lock(mutex_);
37 134 : auto id = ++next_id_;
38 134 : out = id;
39 134 : active_ids_.insert(id);
40 134 : queue_.push(entry{deadline, id, std::move(cb)});
41 134 : cv_.notify_one();
42 134 : }
43 :
44 : void
45 42 : timer_service::
46 : cancel(timer_id id)
47 : {
48 42 : std::unique_lock lock(mutex_);
49 42 : if(!active_ids_.contains(id))
50 34 : return;
51 8 : if(executing_id_ == id)
52 : {
53 : // Callback is running — wait for it to finish.
54 : // run() erases from active_ids_ after execution.
55 6 : while(executing_id_ == id)
56 3 : cancel_cv_.wait(lock);
57 3 : return;
58 : }
59 5 : active_ids_.erase(id);
60 42 : }
61 :
62 : void
63 38 : timer_service::
64 : stop_and_join()
65 : {
66 : {
67 38 : std::lock_guard lock(mutex_);
68 38 : stopped_ = true;
69 38 : }
70 38 : cv_.notify_one();
71 38 : if(thread_.joinable())
72 19 : thread_.join();
73 38 : }
74 :
75 : void
76 19 : timer_service::
77 : shutdown()
78 : {
79 19 : stop_and_join();
80 19 : }
81 :
82 : void
83 19 : timer_service::
84 : run()
85 : {
86 19 : std::unique_lock lock(mutex_);
87 : for(;;)
88 : {
89 196 : if(stopped_)
90 19 : return;
91 :
92 177 : if(queue_.empty())
93 : {
94 17 : cv_.wait(lock);
95 51 : continue;
96 : }
97 :
98 160 : auto deadline = queue_.top().deadline;
99 160 : auto now = std::chrono::steady_clock::now();
100 160 : if(deadline > now)
101 : {
102 32 : cv_.wait_until(lock, deadline);
103 32 : continue;
104 : }
105 :
106 : // Pop the entry (const_cast needed because priority_queue::top is const)
107 128 : auto e = std::move(const_cast<entry&>(queue_.top()));
108 128 : queue_.pop();
109 :
110 : // Skip if cancelled (no longer in active set)
111 128 : if(!active_ids_.contains(e.id))
112 2 : continue;
113 :
114 126 : executing_id_ = e.id;
115 126 : lock.unlock();
116 126 : e.callback();
117 126 : lock.lock();
118 126 : active_ids_.erase(e.id);
119 126 : executing_id_ = 0;
120 126 : cancel_cv_.notify_all();
121 305 : }
122 19 : }
123 :
124 : } // detail
125 : } // capy
126 : } // boost
|