src/ex/detail/timer_service.cpp

100.0% Lines (62/62) 100.0% List of functions (8/8)
f(x) Functions (8)
Line 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 20x timer_service::
17 20x timer_service(execution_context& ctx)
18 40x : thread_([this] { run(); })
19 {
20 (void)ctx;
21 20x }
22
23 40x timer_service::
24 20x ~timer_service()
25 {
26 20x stop_and_join();
27 40x }
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 134x std::lock_guard lock(mutex_);
36 134x auto id = ++next_id_;
37 134x active_ids_.insert(id);
38 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 42x std::unique_lock lock(mutex_);
48 42x if(!active_ids_.contains(id))
49 32x return;
50 10x if(executing_id_ == id)
51 {
52 // Callback is running — wait for it to finish.
53 // run() erases from active_ids_ after execution.
54 10x while(executing_id_ == id)
55 5x cancel_cv_.wait(lock);
56 5x return;
57 }
58 5x active_ids_.erase(id);
59 42x }
60
61 void
62 39x timer_service::
63 stop_and_join()
64 {
65 {
66 39x std::lock_guard lock(mutex_);
67 39x stopped_ = true;
68 39x }
69 39x cv_.notify_one();
70 39x if(thread_.joinable())
71 20x thread_.join();
72 39x }
73
74 void
75 19x timer_service::
76 shutdown()
77 {
78 19x stop_and_join();
79 19x }
80
81 void
82 20x timer_service::
83 run()
84 {
85 20x std::unique_lock lock(mutex_);
86 for(;;)
87 {
88 209x if(stopped_)
89 20x return;
90
91 189x if(queue_.empty())
92 {
93 17x cv_.wait(lock);
94 63x continue;
95 }
96
97 172x auto deadline = queue_.top().deadline;
98 172x auto now = std::chrono::steady_clock::now();
99 172x if(deadline > now)
100 {
101 44x cv_.wait_until(lock, deadline);
102 44x 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 128x queue_.pop();
108
109 // Skip if cancelled (no longer in active set)
110 128x if(!active_ids_.contains(e.id))
111 2x continue;
112
113 126x executing_id_ = e.id;
114 126x lock.unlock();
115 126x e.callback();
116 126x lock.lock();
117 126x active_ids_.erase(e.id);
118 126x executing_id_ = 0;
119 126x cancel_cv_.notify_all();
120 317x }
121 20x }
122
123 } // detail
124 } // capy
125 } // boost
126