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

54  

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

56  

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

59  

60 -
        @return An id that can be passed to cancel().
60 +
        The id is written to @p out while the internal lock is
 
61 +
        held, so the timer thread cannot fire the callback before
 
62 +
        the write completes. This is required when the id
 
63 +
        destination lives in memory that the callback itself may
 
64 +
        free (e.g. a coroutine frame).
61  
    */
65  
    */
62  
    template<typename Rep, typename Period>
66  
    template<typename Rep, typename Period>
63 -
    timer_id schedule_after(
67 +
    void schedule_after(
64  
        std::chrono::duration<Rep, Period> dur,
68  
        std::chrono::duration<Rep, Period> dur,
65 -
        std::function<void()> cb)
69 +
        std::function<void()> cb,
 
70 +
        timer_id& out)
66  
    {
71  
    {
67  
        auto deadline = std::chrono::steady_clock::now() + dur;
72  
        auto deadline = std::chrono::steady_clock::now() + dur;
68 -
        return schedule_at(deadline, std::move(cb));
73 +
        schedule_at(deadline, std::move(cb), out);
69  
    }
74  
    }
70  

75  

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

77  

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

82  

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

87  

83  
protected:
88  
protected:
84  
    void shutdown() override;
89  
    void shutdown() override;
85  

90  

86  
private:
91  
private:
87  
    void stop_and_join();
92  
    void stop_and_join();
88  
    struct entry
93  
    struct entry
89  
    {
94  
    {
90  
        std::chrono::steady_clock::time_point deadline;
95  
        std::chrono::steady_clock::time_point deadline;
91  
        timer_id id;
96  
        timer_id id;
92  
        std::function<void()> callback;
97  
        std::function<void()> callback;
93  

98  

94  
        bool operator>(entry const& o) const noexcept
99  
        bool operator>(entry const& o) const noexcept
95  
        {
100  
        {
96  
            return deadline > o.deadline;
101  
            return deadline > o.deadline;
97  
        }
102  
        }
98  
    };
103  
    };
99  

104  

100 -
    timer_id schedule_at(
105 +
    void schedule_at(
101  
        std::chrono::steady_clock::time_point deadline,
106  
        std::chrono::steady_clock::time_point deadline,
102 -
        std::function<void()> cb);
107 +
        std::function<void()> cb,
 
108 +
        timer_id& out);
103  

109  

104  
    void run();
110  
    void run();
105  

111  

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

133  

124  
} // detail
134  
} // detail
125  
} // capy
135  
} // capy
126  
} // boost
136  
} // boost
127  

137  

128  
#endif
138  
#endif