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 19x timer_service::
17 19x timer_service(execution_context& ctx)
18 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 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 34x return;
50 8x if(executing_id_ == id)
51 {
52 // Callback is running — wait for it to finish.
53 // run() erases from active_ids_ after execution.
54 6x while(executing_id_ == id)
55 3x cancel_cv_.wait(lock);
56 3x return;
57 }
58 5x active_ids_.erase(id);
59 42x }
60
61 void
62 38x timer_service::
63 stop_and_join()
64 {
65 {
66 38x std::lock_guard lock(mutex_);
67 38x stopped_ = true;
68 38x }
69 38x cv_.notify_one();
70 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 19x std::unique_lock lock(mutex_);
86 for(;;)
87 {
88 205x if(stopped_)
89 19x return;
90
91 186x if(queue_.empty())
92 {
93 19x cv_.wait(lock);
94 60x continue;
95 }
96
97 167x auto deadline = queue_.top().deadline;
98 167x auto now = std::chrono::steady_clock::now();
99 167x if(deadline > now)
100 {
101 39x cv_.wait_until(lock, deadline);
102 39x 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 314x }
121 19x }
122
123 } // detail
124 } // capy
125 } // boost
126