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