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 -
    // Calls shutdown() to join the background thread.
 
50 -
    // Handles the discard path in use_service_impl where
 
51 -
    // a duplicate service is deleted without shutdown().
 
52 -
    ~timer_service();
 
53 -

 
54  

49  

55  
    /** Schedule a callback to fire after a duration.
50  
    /** Schedule a callback to fire after a duration.
56  

51  

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

54  

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

65  

71  
    /** Cancel a pending timer.
66  
    /** Cancel a pending timer.
72  

67  

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

72  

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

77  

83  
protected:
78  
protected:
84  
    void shutdown() override;
79  
    void shutdown() override;
85  

80  

86 -
    void stop_and_join();
 
87  
private:
81  
private:
88  
    struct entry
82  
    struct entry
89  
    {
83  
    {
90  
        std::chrono::steady_clock::time_point deadline;
84  
        std::chrono::steady_clock::time_point deadline;
91  
        timer_id id;
85  
        timer_id id;
92  
        std::function<void()> callback;
86  
        std::function<void()> callback;
93  

87  

94  
        bool operator>(entry const& o) const noexcept
88  
        bool operator>(entry const& o) const noexcept
95  
        {
89  
        {
96  
            return deadline > o.deadline;
90  
            return deadline > o.deadline;
97  
        }
91  
        }
98  
    };
92  
    };
99  

93  

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

97  

104  
    void run();
98  
    void run();
105  

99  

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

121  

128  
} // detail
122  
} // detail
129  
} // capy
123  
} // capy
130  
} // boost
124  
} // boost
131  

125  

132  
#endif
126  
#endif