src/ex/detail/timer_service.cpp

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