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  
#ifndef BOOST_CAPY_EX_TIMER_SERVICE_HPP
10  
#ifndef BOOST_CAPY_EX_TIMER_SERVICE_HPP
11  
#define BOOST_CAPY_EX_TIMER_SERVICE_HPP
11  
#define BOOST_CAPY_EX_TIMER_SERVICE_HPP
12  

12  

13  
#include <boost/capy/detail/config.hpp>
13  
#include <boost/capy/detail/config.hpp>
14  
#include <boost/capy/ex/execution_context.hpp>
14  
#include <boost/capy/ex/execution_context.hpp>
15  

15  

16  
#include <chrono>
16  
#include <chrono>
17  
#include <cstdint>
17  
#include <cstdint>
18  
#include <functional>
18  
#include <functional>
19  
#include <mutex>
19  
#include <mutex>
20  
#include <condition_variable>
20  
#include <condition_variable>
21  
#include <queue>
21  
#include <queue>
22  
#include <thread>
22  
#include <thread>
23  
#include <unordered_set>
23  
#include <unordered_set>
24  
#include <vector>
24  
#include <vector>
25  

25  

26  
namespace boost {
26  
namespace boost {
27  
namespace capy {
27  
namespace capy {
28  
namespace detail {
28  
namespace detail {
29  

29  

30  
/* Shared timer thread for an execution_context.
30  
/* Shared timer thread for an execution_context.
31  

31  

32  
   One background std::thread per execution_context. All timeouts
32  
   One background std::thread per execution_context. All timeouts
33  
   scheduled through this context share the same thread, which sleeps
33  
   scheduled through this context share the same thread, which sleeps
34  
   on a condition variable until the next deadline.
34  
   on a condition variable until the next deadline.
35  

35  

36  
   The timer thread never touches coroutine frames or executors
36  
   The timer thread never touches coroutine frames or executors
37  
   directly — callbacks are responsible for posting work through
37  
   directly — callbacks are responsible for posting work through
38  
   the appropriate executor.
38  
   the appropriate executor.
39  
*/
39  
*/
40  

40  

41  
class BOOST_CAPY_DECL
41  
class BOOST_CAPY_DECL
42  
    timer_service
42  
    timer_service
43  
    : public execution_context::service
43  
    : public execution_context::service
44  
{
44  
{
45  
public:
45  
public:
46  
    using timer_id = std::uint64_t;
46  
    using timer_id = std::uint64_t;
47  

47  

48  
    explicit timer_service(execution_context& ctx);
48  
    explicit timer_service(execution_context& ctx);
49  

49  

50  
    /** Schedule a callback to fire after a duration.
50  
    /** Schedule a callback to fire after a duration.
51  

51  

52  
        The callback is invoked on the timer service's background
52  
        The callback is invoked on the timer service's background
53  
        thread. It must not block for extended periods.
53  
        thread. It must not block for extended periods.
54  

54  

55  
        @return An id that can be passed to cancel().
55  
        @return An id that can be passed to cancel().
56  
    */
56  
    */
57  
    template<typename Rep, typename Period>
57  
    template<typename Rep, typename Period>
58  
    timer_id schedule_after(
58  
    timer_id schedule_after(
59  
        std::chrono::duration<Rep, Period> dur,
59  
        std::chrono::duration<Rep, Period> dur,
60  
        std::function<void()> cb)
60  
        std::function<void()> cb)
61  
    {
61  
    {
62  
        auto deadline = std::chrono::steady_clock::now() + dur;
62  
        auto deadline = std::chrono::steady_clock::now() + dur;
63  
        return schedule_at(deadline, std::move(cb));
63  
        return schedule_at(deadline, std::move(cb));
64  
    }
64  
    }
65  

65  

66  
    /** Cancel a pending timer.
66  
    /** Cancel a pending timer.
67  

67  

68  
        After this function returns, the callback is guaranteed
68  
        After this function returns, the callback is guaranteed
69  
        not to be running and will never be invoked. If the
69  
        not to be running and will never be invoked. If the
70  
        callback is currently executing on the timer thread,
70  
        callback is currently executing on the timer thread,
71  
        this call blocks until it completes.
71  
        this call blocks until it completes.
72  

72  

73  
        Safe to call with any id, including ids that have
73  
        Safe to call with any id, including ids that have
74  
        already fired, been cancelled, or were never issued.
74  
        already fired, been cancelled, or were never issued.
75  
    */
75  
    */
76  
    void cancel(timer_id id);
76  
    void cancel(timer_id id);
77  

77  

78  
protected:
78  
protected:
79  
    void shutdown() override;
79  
    void shutdown() override;
80  

80  

81  
private:
81  
private:
82  
    struct entry
82  
    struct entry
83  
    {
83  
    {
84  
        std::chrono::steady_clock::time_point deadline;
84  
        std::chrono::steady_clock::time_point deadline;
85  
        timer_id id;
85  
        timer_id id;
86  
        std::function<void()> callback;
86  
        std::function<void()> callback;
87  

87  

88  
        bool operator>(entry const& o) const noexcept
88  
        bool operator>(entry const& o) const noexcept
89  
        {
89  
        {
90  
            return deadline > o.deadline;
90  
            return deadline > o.deadline;
91  
        }
91  
        }
92  
    };
92  
    };
93  

93  

94  
    timer_id schedule_at(
94  
    timer_id schedule_at(
95  
        std::chrono::steady_clock::time_point deadline,
95  
        std::chrono::steady_clock::time_point deadline,
96  
        std::function<void()> cb);
96  
        std::function<void()> cb);
97  

97  

98  
    void run();
98  
    void run();
99  

99  

100  
// warning C4251: std types need to have dll-interface
100  
// warning C4251: std types need to have dll-interface
101  
#ifdef _MSC_VER
101  
#ifdef _MSC_VER
102  
# pragma warning(push)
102  
# pragma warning(push)
103  
# pragma warning(disable: 4251)
103  
# pragma warning(disable: 4251)
104  
#endif
104  
#endif
105  
    std::mutex mutex_;
105  
    std::mutex mutex_;
106  
    std::condition_variable cv_;
106  
    std::condition_variable cv_;
107  
    std::condition_variable cancel_cv_;
107  
    std::condition_variable cancel_cv_;
108  
    std::priority_queue<
108  
    std::priority_queue<
109  
        entry,
109  
        entry,
110  
        std::vector<entry>,
110  
        std::vector<entry>,
111  
        std::greater<>> queue_;
111  
        std::greater<>> queue_;
112  
    std::unordered_set<timer_id> active_ids_;
112  
    std::unordered_set<timer_id> active_ids_;
113  
    timer_id next_id_ = 0;
113  
    timer_id next_id_ = 0;
114  
    timer_id executing_id_ = 0;
114  
    timer_id executing_id_ = 0;
115  
    bool stopped_ = false;
115  
    bool stopped_ = false;
116  
    std::thread thread_;
116  
    std::thread thread_;
117  
#ifdef _MSC_VER
117  
#ifdef _MSC_VER
118  
# pragma warning(pop)
118  
# pragma warning(pop)
119  
#endif
119  
#endif
120  
};
120  
};
121  

121  

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

125  

126  
#endif
126  
#endif