1  
//
1  
//
2  
// Copyright (c) 2026 Michael Vandeberg
2  
// Copyright (c) 2026 Michael Vandeberg
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/capy
7  
// Official repository: https://github.com/cppalliance/capy
8  
//
8  
//
9  

9  

10  
#include <boost/capy/ex/detail/timer_service.hpp>
10  
#include <boost/capy/ex/detail/timer_service.hpp>
11  

11  

12  
namespace boost {
12  
namespace boost {
13  
namespace capy {
13  
namespace capy {
14  
namespace detail {
14  
namespace detail {
15  

15  

16  
timer_service::
16  
timer_service::
17  
timer_service(execution_context& ctx)
17  
timer_service(execution_context& ctx)
18  
    : thread_([this] { run(); })
18  
    : thread_([this] { run(); })
19  
{
19  
{
20  
    (void)ctx;
20  
    (void)ctx;
21  
}
21  
}
22  

22  

23  
timer_service::
23  
timer_service::
24  
~timer_service()
24  
~timer_service()
25  
{
25  
{
26  
    stop_and_join();
26  
    stop_and_join();
27  
}
27  
}
28  

28  

29  
timer_service::timer_id
29  
timer_service::timer_id
30  
timer_service::
30  
timer_service::
31  
schedule_at(
31  
schedule_at(
32  
    std::chrono::steady_clock::time_point deadline,
32  
    std::chrono::steady_clock::time_point deadline,
33  
    std::function<void()> cb)
33  
    std::function<void()> cb)
34  
{
34  
{
35  
    std::lock_guard lock(mutex_);
35  
    std::lock_guard lock(mutex_);
36  
    auto id = ++next_id_;
36  
    auto id = ++next_id_;
37  
    active_ids_.insert(id);
37  
    active_ids_.insert(id);
38  
    queue_.push(entry{deadline, id, std::move(cb)});
38  
    queue_.push(entry{deadline, id, std::move(cb)});
39  
    cv_.notify_one();
39  
    cv_.notify_one();
40  
    return id;
40  
    return id;
41  
}
41  
}
42  

42  

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

60  

61  
void
61  
void
62  
timer_service::
62  
timer_service::
63  
stop_and_join()
63  
stop_and_join()
64  
{
64  
{
65  
    {
65  
    {
66  
        std::lock_guard lock(mutex_);
66  
        std::lock_guard lock(mutex_);
67  
        stopped_ = true;
67  
        stopped_ = true;
68  
    }
68  
    }
69  
    cv_.notify_one();
69  
    cv_.notify_one();
70  
    if(thread_.joinable())
70  
    if(thread_.joinable())
71  
        thread_.join();
71  
        thread_.join();
72  
}
72  
}
73  

73  

74  
void
74  
void
75  
timer_service::
75  
timer_service::
76  
shutdown()
76  
shutdown()
77  
{
77  
{
78  
    stop_and_join();
78  
    stop_and_join();
79  
}
79  
}
80  

80  

81  
void
81  
void
82  
timer_service::
82  
timer_service::
83  
run()
83  
run()
84  
{
84  
{
85  
    std::unique_lock lock(mutex_);
85  
    std::unique_lock lock(mutex_);
86  
    for(;;)
86  
    for(;;)
87  
    {
87  
    {
88  
        if(stopped_)
88  
        if(stopped_)
89  
            return;
89  
            return;
90  

90  

91  
        if(queue_.empty())
91  
        if(queue_.empty())
92  
        {
92  
        {
93  
            cv_.wait(lock);
93  
            cv_.wait(lock);
94  
            continue;
94  
            continue;
95  
        }
95  
        }
96  

96  

97  
        auto deadline = queue_.top().deadline;
97  
        auto deadline = queue_.top().deadline;
98  
        auto now = std::chrono::steady_clock::now();
98  
        auto now = std::chrono::steady_clock::now();
99  
        if(deadline > now)
99  
        if(deadline > now)
100  
        {
100  
        {
101  
            cv_.wait_until(lock, deadline);
101  
            cv_.wait_until(lock, deadline);
102  
            continue;
102  
            continue;
103  
        }
103  
        }
104  

104  

105  
        // Pop the entry (const_cast needed because priority_queue::top is const)
105  
        // Pop the entry (const_cast needed because priority_queue::top is const)
106  
        auto e = std::move(const_cast<entry&>(queue_.top()));
106  
        auto e = std::move(const_cast<entry&>(queue_.top()));
107  
        queue_.pop();
107  
        queue_.pop();
108  

108  

109  
        // Skip if cancelled (no longer in active set)
109  
        // Skip if cancelled (no longer in active set)
110  
        if(!active_ids_.contains(e.id))
110  
        if(!active_ids_.contains(e.id))
111  
            continue;
111  
            continue;
112  

112  

113  
        executing_id_ = e.id;
113  
        executing_id_ = e.id;
114  
        lock.unlock();
114  
        lock.unlock();
115  
        e.callback();
115  
        e.callback();
116  
        lock.lock();
116  
        lock.lock();
117  
        active_ids_.erase(e.id);
117  
        active_ids_.erase(e.id);
118  
        executing_id_ = 0;
118  
        executing_id_ = 0;
119  
        cancel_cv_.notify_all();
119  
        cancel_cv_.notify_all();
120  
    }
120  
    }
121  
}
121  
}
122  

122  

123  
} // detail
123  
} // detail
124  
} // capy
124  
} // capy
125  
} // boost
125  
} // boost