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 +
void
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 +
    timer_id& out)
34  
{
35  
{
35  
    std::lock_guard lock(mutex_);
36  
    std::lock_guard lock(mutex_);
36  
    auto id = ++next_id_;
37  
    auto id = ++next_id_;
 
38 +
    out = id;
37  
    active_ids_.insert(id);
39  
    active_ids_.insert(id);
38  
    queue_.push(entry{deadline, id, std::move(cb)});
40  
    queue_.push(entry{deadline, id, std::move(cb)});
39 -
    return id;
 
40  
    cv_.notify_one();
41  
    cv_.notify_one();
41  
}
42  
}
42  

43  

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

61  

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

74  

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

81  

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

91  

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

97  

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

105  

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

109  

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

113  

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

123  

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