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