src/ex/detail/timer_service.cpp

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