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

 
28  

22  

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

36  

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

54  

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

 
73 -
void
 
74 -
timer_service::
 
75 -
shutdown()
 
76 -
{
 
77 -
    stop_and_join();
 
78  
        thread_.join();
65  
        thread_.join();
79  
}
66  
}
80  

67  

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

77  

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

83  

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

91  

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

95  

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

99  

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

109  

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