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::
 
24 +
~timer_service()
 
25 +
{
 
26 +
    stop_and_join();
 
27 +
}
 
28 +

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

42  

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

60  

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

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

80  

68  
void
81  
void
69  
timer_service::
82  
timer_service::
70  
run()
83  
run()
71  
{
84  
{
72  
    std::unique_lock lock(mutex_);
85  
    std::unique_lock lock(mutex_);
73  
    for(;;)
86  
    for(;;)
74  
    {
87  
    {
75  
        if(stopped_)
88  
        if(stopped_)
76  
            return;
89  
            return;
77  

90  

78  
        if(queue_.empty())
91  
        if(queue_.empty())
79  
        {
92  
        {
80  
            cv_.wait(lock);
93  
            cv_.wait(lock);
81  
            continue;
94  
            continue;
82  
        }
95  
        }
83  

96  

84  
        auto deadline = queue_.top().deadline;
97  
        auto deadline = queue_.top().deadline;
85  
        auto now = std::chrono::steady_clock::now();
98  
        auto now = std::chrono::steady_clock::now();
86  
        if(deadline > now)
99  
        if(deadline > now)
87  
        {
100  
        {
88  
            cv_.wait_until(lock, deadline);
101  
            cv_.wait_until(lock, deadline);
89  
            continue;
102  
            continue;
90  
        }
103  
        }
91  

104  

92  
        // 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)
93  
        auto e = std::move(const_cast<entry&>(queue_.top()));
106  
        auto e = std::move(const_cast<entry&>(queue_.top()));
94  
        queue_.pop();
107  
        queue_.pop();
95  

108  

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

112  

100  
        executing_id_ = e.id;
113  
        executing_id_ = e.id;
101  
        lock.unlock();
114  
        lock.unlock();
102  
        e.callback();
115  
        e.callback();
103  
        lock.lock();
116  
        lock.lock();
104  
        active_ids_.erase(e.id);
117  
        active_ids_.erase(e.id);
105  
        executing_id_ = 0;
118  
        executing_id_ = 0;
106  
        cancel_cv_.notify_all();
119  
        cancel_cv_.notify_all();
107  
    }
120  
    }
108  
}
121  
}
109  

122  

110  
} // detail
123  
} // detail
111  
} // capy
124  
} // capy
112  
} // boost
125  
} // boost