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