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 +
timer_service::
 
17 +
timer_service(execution_context& ctx)
 
18 +
    : thread_([this] { run(); })
 
19 +
{
 
20 +
    (void)ctx;
 
21 +
}
 
22 +

 
23 +
timer_service::timer_id
 
24 +
timer_service::
 
25 +
schedule_at(
 
26 +
    std::chrono::steady_clock::time_point deadline,
 
27 +
    std::function<void()> cb)
 
28 +
{
 
29 +
    std::lock_guard lock(mutex_);
 
30 +
    auto id = ++next_id_;
 
31 +
    active_ids_.insert(id);
 
32 +
    queue_.push(entry{deadline, id, std::move(cb)});
 
33 +
    cv_.notify_one();
 
34 +
    return id;
 
35 +
}
 
36 +

 
37 +
void
 
38 +
timer_service::
 
39 +
cancel(timer_id id)
 
40 +
{
 
41 +
    std::unique_lock lock(mutex_);
 
42 +
    if(!active_ids_.contains(id))
 
43 +
        return;
 
44 +
    if(executing_id_ == id)
 
45 +
    {
 
46 +
        // Callback is running — wait for it to finish.
 
47 +
        // run() erases from active_ids_ after execution.
 
48 +
        while(executing_id_ == id)
 
49 +
            cancel_cv_.wait(lock);
 
50 +
        return;
 
51 +
    }
 
52 +
    active_ids_.erase(id);
 
53 +
}
 
54 +

 
55 +
void
 
56 +
timer_service::
 
57 +
shutdown()
 
58 +
{
 
59 +
    {
 
60 +
        std::lock_guard lock(mutex_);
 
61 +
        stopped_ = true;
 
62 +
    }
 
63 +
    cv_.notify_one();
 
64 +
    if(thread_.joinable())
 
65 +
        thread_.join();
 
66 +
}
 
67 +

 
68 +
void
 
69 +
timer_service::
 
70 +
run()
 
71 +
{
 
72 +
    std::unique_lock lock(mutex_);
 
73 +
    for(;;)
 
74 +
    {
 
75 +
        if(stopped_)
 
76 +
            return;
 
77 +

 
78 +
        if(queue_.empty())
 
79 +
        {
 
80 +
            cv_.wait(lock);
 
81 +
            continue;
 
82 +
        }
 
83 +

 
84 +
        auto deadline = queue_.top().deadline;
 
85 +
        auto now = std::chrono::steady_clock::now();
 
86 +
        if(deadline > now)
 
87 +
        {
 
88 +
            cv_.wait_until(lock, deadline);
 
89 +
            continue;
 
90 +
        }
 
91 +

 
92 +
        // Pop the entry (const_cast needed because priority_queue::top is const)
 
93 +
        auto e = std::move(const_cast<entry&>(queue_.top()));
 
94 +
        queue_.pop();
 
95 +

 
96 +
        // Skip if cancelled (no longer in active set)
 
97 +
        if(!active_ids_.contains(e.id))
 
98 +
            continue;
 
99 +

 
100 +
        executing_id_ = e.id;
 
101 +
        lock.unlock();
 
102 +
        e.callback();
 
103 +
        lock.lock();
 
104 +
        active_ids_.erase(e.id);
 
105 +
        executing_id_ = 0;
 
106 +
        cancel_cv_.notify_all();
 
107 +
    }
 
108 +
}
 
109 +

 
110 +
} // detail
 
111 +
} // capy
 
112 +
} // boost