1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
// Copyright (c) 2026 Steve Gerbino
3  
// Copyright (c) 2026 Steve Gerbino
4  
//
4  
//
5  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
6  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  
//
7  
//
8  
// Official repository: https://github.com/cppalliance/corosio
8  
// Official repository: https://github.com/cppalliance/corosio
9  
//
9  
//
10  

10  

11  
#ifndef BOOST_COROSIO_DETAIL_TIMER_SERVICE_HPP
11  
#ifndef BOOST_COROSIO_DETAIL_TIMER_SERVICE_HPP
12  
#define BOOST_COROSIO_DETAIL_TIMER_SERVICE_HPP
12  
#define BOOST_COROSIO_DETAIL_TIMER_SERVICE_HPP
13  

13  

14  
#include <boost/corosio/timer.hpp>
14  
#include <boost/corosio/timer.hpp>
15  
#include <boost/corosio/io_context.hpp>
15  
#include <boost/corosio/io_context.hpp>
16  
#include <boost/corosio/detail/scheduler_op.hpp>
16  
#include <boost/corosio/detail/scheduler_op.hpp>
17  
#include <boost/corosio/native/native_scheduler.hpp>
17  
#include <boost/corosio/native/native_scheduler.hpp>
18  
#include <boost/corosio/detail/intrusive.hpp>
18  
#include <boost/corosio/detail/intrusive.hpp>
19  
#include <boost/corosio/detail/thread_local_ptr.hpp>
19  
#include <boost/corosio/detail/thread_local_ptr.hpp>
20  
#include <boost/capy/error.hpp>
20  
#include <boost/capy/error.hpp>
21  
#include <boost/capy/ex/execution_context.hpp>
21  
#include <boost/capy/ex/execution_context.hpp>
22  
#include <boost/capy/ex/executor_ref.hpp>
22  
#include <boost/capy/ex/executor_ref.hpp>
23  
#include <system_error>
23  
#include <system_error>
24  

24  

25  
#include <atomic>
25  
#include <atomic>
26  
#include <chrono>
26  
#include <chrono>
27  
#include <coroutine>
27  
#include <coroutine>
28  
#include <cstddef>
28  
#include <cstddef>
29  
#include <limits>
29  
#include <limits>
30  
#include <mutex>
30  
#include <mutex>
31  
#include <optional>
31  
#include <optional>
32  
#include <stop_token>
32  
#include <stop_token>
33  
#include <vector>
33  
#include <vector>
34  

34  

35  
namespace boost::corosio::detail {
35  
namespace boost::corosio::detail {
36  

36  

37  
struct scheduler;
37  
struct scheduler;
38  

38  

39  
/*
39  
/*
40  
    Timer Service
40  
    Timer Service
41  
    =============
41  
    =============
42  

42  

43  
    Data Structures
43  
    Data Structures
44  
    ---------------
44  
    ---------------
45  
    waiter_node holds per-waiter state: coroutine handle, executor,
45  
    waiter_node holds per-waiter state: coroutine handle, executor,
46  
    error output, stop_token, embedded completion_op. Each concurrent
46  
    error output, stop_token, embedded completion_op. Each concurrent
47  
    co_await t.wait() allocates one waiter_node.
47  
    co_await t.wait() allocates one waiter_node.
48  

48  

49  
    timer_service::implementation holds per-timer state: expiry,
49  
    timer_service::implementation holds per-timer state: expiry,
50  
    heap index, and an intrusive_list of waiter_nodes. Multiple
50  
    heap index, and an intrusive_list of waiter_nodes. Multiple
51  
    coroutines can wait on the same timer simultaneously.
51  
    coroutines can wait on the same timer simultaneously.
52  

52  

53  
    timer_service owns a min-heap of active timers, a free list
53  
    timer_service owns a min-heap of active timers, a free list
54  
    of recycled impls, and a free list of recycled waiter_nodes. The
54  
    of recycled impls, and a free list of recycled waiter_nodes. The
55  
    heap is ordered by expiry time; the scheduler queries
55  
    heap is ordered by expiry time; the scheduler queries
56  
    nearest_expiry() to set the epoll/timerfd timeout.
56  
    nearest_expiry() to set the epoll/timerfd timeout.
57  

57  

58  
    Optimization Strategy
58  
    Optimization Strategy
59  
    ---------------------
59  
    ---------------------
60  
    1. Deferred heap insertion — expires_after() stores the expiry
60  
    1. Deferred heap insertion — expires_after() stores the expiry
61  
       but does not insert into the heap. Insertion happens in wait().
61  
       but does not insert into the heap. Insertion happens in wait().
62  
    2. Thread-local impl cache — single-slot per-thread cache.
62  
    2. Thread-local impl cache — single-slot per-thread cache.
63  
    3. Embedded completion_op — eliminates heap allocation per fire/cancel.
63  
    3. Embedded completion_op — eliminates heap allocation per fire/cancel.
64  
    4. Cached nearest expiry — atomic avoids mutex in nearest_expiry().
64  
    4. Cached nearest expiry — atomic avoids mutex in nearest_expiry().
65  
    5. might_have_pending_waits_ flag — skips lock when no wait issued.
65  
    5. might_have_pending_waits_ flag — skips lock when no wait issued.
66  
    6. Thread-local waiter cache — single-slot per-thread cache.
66  
    6. Thread-local waiter cache — single-slot per-thread cache.
67  

67  

68  
    Concurrency
68  
    Concurrency
69  
    -----------
69  
    -----------
70  
    stop_token callbacks can fire from any thread. The impl_
70  
    stop_token callbacks can fire from any thread. The impl_
71  
    pointer on waiter_node is used as a "still in list" marker.
71  
    pointer on waiter_node is used as a "still in list" marker.
72  
*/
72  
*/
73  

73  

74  
struct BOOST_COROSIO_SYMBOL_VISIBLE waiter_node;
74  
struct BOOST_COROSIO_SYMBOL_VISIBLE waiter_node;
75  

75  

76  
inline void timer_service_invalidate_cache() noexcept;
76  
inline void timer_service_invalidate_cache() noexcept;
77  

77  

78  
// timer_service class body — member function definitions are
78  
// timer_service class body — member function definitions are
79  
// out-of-class (after implementation and waiter_node are complete)
79  
// out-of-class (after implementation and waiter_node are complete)
80  
class BOOST_COROSIO_DECL timer_service final
80  
class BOOST_COROSIO_DECL timer_service final
81  
    : public capy::execution_context::service
81  
    : public capy::execution_context::service
82  
    , public io_object::io_service
82  
    , public io_object::io_service
83  
{
83  
{
84  
public:
84  
public:
85  
    using clock_type = std::chrono::steady_clock;
85  
    using clock_type = std::chrono::steady_clock;
86  
    using time_point = clock_type::time_point;
86  
    using time_point = clock_type::time_point;
87  

87  

88  
    class callback
88  
    class callback
89  
    {
89  
    {
90  
        void* ctx_         = nullptr;
90  
        void* ctx_         = nullptr;
91  
        void (*fn_)(void*) = nullptr;
91  
        void (*fn_)(void*) = nullptr;
92  

92  

93  
    public:
93  
    public:
94  
        callback() = default;
94  
        callback() = default;
95  
        callback(void* ctx, void (*fn)(void*)) noexcept : ctx_(ctx), fn_(fn) {}
95  
        callback(void* ctx, void (*fn)(void*)) noexcept : ctx_(ctx), fn_(fn) {}
96  

96  

97  
        explicit operator bool() const noexcept
97  
        explicit operator bool() const noexcept
98  
        {
98  
        {
99  
            return fn_ != nullptr;
99  
            return fn_ != nullptr;
100  
        }
100  
        }
101  
        void operator()() const
101  
        void operator()() const
102  
        {
102  
        {
103  
            if (fn_)
103  
            if (fn_)
104  
                fn_(ctx_);
104  
                fn_(ctx_);
105  
        }
105  
        }
106  
    };
106  
    };
107  

107  

108  
    struct implementation;
108  
    struct implementation;
109  

109  

110  
private:
110  
private:
111  
    struct heap_entry
111  
    struct heap_entry
112  
    {
112  
    {
113  
        time_point time_;
113  
        time_point time_;
114  
        implementation* timer_;
114  
        implementation* timer_;
115  
    };
115  
    };
116  

116  

117  
    scheduler* sched_ = nullptr;
117  
    scheduler* sched_ = nullptr;
118  
    mutable std::mutex mutex_;
118  
    mutable std::mutex mutex_;
119  
    std::vector<heap_entry> heap_;
119  
    std::vector<heap_entry> heap_;
120  
    implementation* free_list_     = nullptr;
120  
    implementation* free_list_     = nullptr;
121  
    waiter_node* waiter_free_list_ = nullptr;
121  
    waiter_node* waiter_free_list_ = nullptr;
122  
    callback on_earliest_changed_;
122  
    callback on_earliest_changed_;
123  
    // Avoids mutex in nearest_expiry() and empty()
123  
    // Avoids mutex in nearest_expiry() and empty()
124  
    mutable std::atomic<std::int64_t> cached_nearest_ns_{
124  
    mutable std::atomic<std::int64_t> cached_nearest_ns_{
125  
        (std::numeric_limits<std::int64_t>::max)()};
125  
        (std::numeric_limits<std::int64_t>::max)()};
126  

126  

127  
public:
127  
public:
128  
    inline timer_service(capy::execution_context&, scheduler& sched)
128  
    inline timer_service(capy::execution_context&, scheduler& sched)
129  
        : sched_(&sched)
129  
        : sched_(&sched)
130  
    {
130  
    {
131  
    }
131  
    }
132  

132  

133  
    inline scheduler& get_scheduler() noexcept
133  
    inline scheduler& get_scheduler() noexcept
134  
    {
134  
    {
135  
        return *sched_;
135  
        return *sched_;
136  
    }
136  
    }
137  

137  

138  
    ~timer_service() override = default;
138  
    ~timer_service() override = default;
139  

139  

140  
    timer_service(timer_service const&)            = delete;
140  
    timer_service(timer_service const&)            = delete;
141  
    timer_service& operator=(timer_service const&) = delete;
141  
    timer_service& operator=(timer_service const&) = delete;
142  

142  

143  
    inline void set_on_earliest_changed(callback cb)
143  
    inline void set_on_earliest_changed(callback cb)
144  
    {
144  
    {
145  
        on_earliest_changed_ = cb;
145  
        on_earliest_changed_ = cb;
146  
    }
146  
    }
147  

147  

148  
    inline bool empty() const noexcept
148  
    inline bool empty() const noexcept
149  
    {
149  
    {
150  
        return cached_nearest_ns_.load(std::memory_order_acquire) ==
150  
        return cached_nearest_ns_.load(std::memory_order_acquire) ==
151  
            (std::numeric_limits<std::int64_t>::max)();
151  
            (std::numeric_limits<std::int64_t>::max)();
152  
    }
152  
    }
153  

153  

154  
    inline time_point nearest_expiry() const noexcept
154  
    inline time_point nearest_expiry() const noexcept
155  
    {
155  
    {
156  
        auto ns = cached_nearest_ns_.load(std::memory_order_acquire);
156  
        auto ns = cached_nearest_ns_.load(std::memory_order_acquire);
157  
        return time_point(time_point::duration(ns));
157  
        return time_point(time_point::duration(ns));
158  
    }
158  
    }
159  

159  

160  
    inline void shutdown() override;
160  
    inline void shutdown() override;
161  
    inline io_object::implementation* construct() override;
161  
    inline io_object::implementation* construct() override;
162  
    inline void destroy(io_object::implementation* p) override;
162  
    inline void destroy(io_object::implementation* p) override;
163  
    inline void destroy_impl(implementation& impl);
163  
    inline void destroy_impl(implementation& impl);
164  
    inline waiter_node* create_waiter();
164  
    inline waiter_node* create_waiter();
165  
    inline void destroy_waiter(waiter_node* w);
165  
    inline void destroy_waiter(waiter_node* w);
166  
    inline std::size_t update_timer(implementation& impl, time_point new_time);
166  
    inline std::size_t update_timer(implementation& impl, time_point new_time);
167  
    inline void insert_waiter(implementation& impl, waiter_node* w);
167  
    inline void insert_waiter(implementation& impl, waiter_node* w);
168  
    inline std::size_t cancel_timer(implementation& impl);
168  
    inline std::size_t cancel_timer(implementation& impl);
169  
    inline void cancel_waiter(waiter_node* w);
169  
    inline void cancel_waiter(waiter_node* w);
170  
    inline std::size_t cancel_one_waiter(implementation& impl);
170  
    inline std::size_t cancel_one_waiter(implementation& impl);
171  
    inline std::size_t process_expired();
171  
    inline std::size_t process_expired();
172  

172  

173  
private:
173  
private:
174  
    inline void refresh_cached_nearest() noexcept
174  
    inline void refresh_cached_nearest() noexcept
175  
    {
175  
    {
176  
        auto ns = heap_.empty() ? (std::numeric_limits<std::int64_t>::max)()
176  
        auto ns = heap_.empty() ? (std::numeric_limits<std::int64_t>::max)()
177  
                                : heap_[0].time_.time_since_epoch().count();
177  
                                : heap_[0].time_.time_since_epoch().count();
178  
        cached_nearest_ns_.store(ns, std::memory_order_release);
178  
        cached_nearest_ns_.store(ns, std::memory_order_release);
179  
    }
179  
    }
180  

180  

181  
    inline void remove_timer_impl(implementation& impl);
181  
    inline void remove_timer_impl(implementation& impl);
182  
    inline void up_heap(std::size_t index);
182  
    inline void up_heap(std::size_t index);
183  
    inline void down_heap(std::size_t index);
183  
    inline void down_heap(std::size_t index);
184  
    inline void swap_heap(std::size_t i1, std::size_t i2);
184  
    inline void swap_heap(std::size_t i1, std::size_t i2);
185  
};
185  
};
186  

186  

187  
struct BOOST_COROSIO_SYMBOL_VISIBLE waiter_node
187  
struct BOOST_COROSIO_SYMBOL_VISIBLE waiter_node
188  
    : intrusive_list<waiter_node>::node
188  
    : intrusive_list<waiter_node>::node
189  
{
189  
{
190  
    // Embedded completion op — avoids heap allocation per fire/cancel
190  
    // Embedded completion op — avoids heap allocation per fire/cancel
191  
    struct completion_op final : scheduler_op
191  
    struct completion_op final : scheduler_op
192  
    {
192  
    {
193  
        waiter_node* waiter_ = nullptr;
193  
        waiter_node* waiter_ = nullptr;
194  

194  

195  
        static void do_complete(
195  
        static void do_complete(
196  
            void* owner, scheduler_op* base, std::uint32_t, std::uint32_t);
196  
            void* owner, scheduler_op* base, std::uint32_t, std::uint32_t);
197  

197  

198  
        completion_op() noexcept : scheduler_op(&do_complete) {}
198  
        completion_op() noexcept : scheduler_op(&do_complete) {}
199  

199  

200  
        void operator()() override;
200  
        void operator()() override;
201  
        // No-op — lifetime owned by waiter_node, not the scheduler queue
201  
        // No-op — lifetime owned by waiter_node, not the scheduler queue
202  
        void destroy() override {}
202  
        void destroy() override {}
203  
    };
203  
    };
204  

204  

205  
    // Per-waiter stop_token cancellation
205  
    // Per-waiter stop_token cancellation
206  
    struct canceller
206  
    struct canceller
207  
    {
207  
    {
208  
        waiter_node* waiter_;
208  
        waiter_node* waiter_;
209  
        void operator()() const;
209  
        void operator()() const;
210  
    };
210  
    };
211  

211  

212  
    // nullptr once removed from timer's waiter list (concurrency marker)
212  
    // nullptr once removed from timer's waiter list (concurrency marker)
213  
    timer_service::implementation* impl_ = nullptr;
213  
    timer_service::implementation* impl_ = nullptr;
214  
    timer_service* svc_                  = nullptr;
214  
    timer_service* svc_                  = nullptr;
215  
    std::coroutine_handle<> h_;
215  
    std::coroutine_handle<> h_;
216  
    capy::executor_ref d_;
216  
    capy::executor_ref d_;
217  
    std::error_code* ec_out_ = nullptr;
217  
    std::error_code* ec_out_ = nullptr;
218  
    std::stop_token token_;
218  
    std::stop_token token_;
219  
    std::optional<std::stop_callback<canceller>> stop_cb_;
219  
    std::optional<std::stop_callback<canceller>> stop_cb_;
220  
    completion_op op_;
220  
    completion_op op_;
221  
    std::error_code ec_value_;
221  
    std::error_code ec_value_;
222  
    waiter_node* next_free_ = nullptr;
222  
    waiter_node* next_free_ = nullptr;
223  

223  

224  
    waiter_node() noexcept
224  
    waiter_node() noexcept
225  
    {
225  
    {
226  
        op_.waiter_ = this;
226  
        op_.waiter_ = this;
227  
    }
227  
    }
228  
};
228  
};
229  

229  

230  
struct timer_service::implementation final : timer::implementation
230  
struct timer_service::implementation final : timer::implementation
231  
{
231  
{
232  
    using clock_type = std::chrono::steady_clock;
232  
    using clock_type = std::chrono::steady_clock;
233  
    using time_point = clock_type::time_point;
233  
    using time_point = clock_type::time_point;
234  
    using duration   = clock_type::duration;
234  
    using duration   = clock_type::duration;
235  

235  

236  
    timer_service* svc_ = nullptr;
236  
    timer_service* svc_ = nullptr;
237  
    intrusive_list<waiter_node> waiters_;
237  
    intrusive_list<waiter_node> waiters_;
238  

238  

239  
    // Free list linkage (reused when impl is on free_list)
239  
    // Free list linkage (reused when impl is on free_list)
240  
    implementation* next_free_ = nullptr;
240  
    implementation* next_free_ = nullptr;
241  

241  

242  
    inline explicit implementation(timer_service& svc) noexcept;
242  
    inline explicit implementation(timer_service& svc) noexcept;
243  

243  

244  
    inline std::coroutine_handle<> wait(
244  
    inline std::coroutine_handle<> wait(
245  
        std::coroutine_handle<>,
245  
        std::coroutine_handle<>,
246  
        capy::executor_ref,
246  
        capy::executor_ref,
247  
        std::stop_token,
247  
        std::stop_token,
248  
        std::error_code*) override;
248  
        std::error_code*) override;
249  
};
249  
};
250  

250  

251  
// Thread-local caches avoid hot-path mutex acquisitions:
251  
// Thread-local caches avoid hot-path mutex acquisitions:
252  
// 1. Impl cache — single-slot, validated by comparing svc_
252  
// 1. Impl cache — single-slot, validated by comparing svc_
253  
// 2. Waiter cache — single-slot, no service affinity
253  
// 2. Waiter cache — single-slot, no service affinity
254  
// All caches are cleared by timer_service_invalidate_cache() during shutdown.
254  
// All caches are cleared by timer_service_invalidate_cache() during shutdown.
255  

255  

256  
inline thread_local_ptr<timer_service::implementation> tl_cached_impl;
256  
inline thread_local_ptr<timer_service::implementation> tl_cached_impl;
257  
inline thread_local_ptr<waiter_node> tl_cached_waiter;
257  
inline thread_local_ptr<waiter_node> tl_cached_waiter;
258  

258  

259  
inline timer_service::implementation*
259  
inline timer_service::implementation*
260  
try_pop_tl_cache(timer_service* svc) noexcept
260  
try_pop_tl_cache(timer_service* svc) noexcept
261  
{
261  
{
262  
    auto* impl = tl_cached_impl.get();
262  
    auto* impl = tl_cached_impl.get();
263  
    if (impl)
263  
    if (impl)
264  
    {
264  
    {
265  
        tl_cached_impl.set(nullptr);
265  
        tl_cached_impl.set(nullptr);
266  
        if (impl->svc_ == svc)
266  
        if (impl->svc_ == svc)
267  
            return impl;
267  
            return impl;
268  
        // Stale impl from a destroyed service
268  
        // Stale impl from a destroyed service
269  
        delete impl;
269  
        delete impl;
270  
    }
270  
    }
271  
    return nullptr;
271  
    return nullptr;
272  
}
272  
}
273  

273  

274  
inline bool
274  
inline bool
275  
try_push_tl_cache(timer_service::implementation* impl) noexcept
275  
try_push_tl_cache(timer_service::implementation* impl) noexcept
276  
{
276  
{
277  
    if (!tl_cached_impl.get())
277  
    if (!tl_cached_impl.get())
278  
    {
278  
    {
279  
        tl_cached_impl.set(impl);
279  
        tl_cached_impl.set(impl);
280  
        return true;
280  
        return true;
281  
    }
281  
    }
282  
    return false;
282  
    return false;
283  
}
283  
}
284  

284  

285  
inline waiter_node*
285  
inline waiter_node*
286  
try_pop_waiter_tl_cache() noexcept
286  
try_pop_waiter_tl_cache() noexcept
287  
{
287  
{
288  
    auto* w = tl_cached_waiter.get();
288  
    auto* w = tl_cached_waiter.get();
289  
    if (w)
289  
    if (w)
290  
    {
290  
    {
291  
        tl_cached_waiter.set(nullptr);
291  
        tl_cached_waiter.set(nullptr);
292  
        return w;
292  
        return w;
293  
    }
293  
    }
294  
    return nullptr;
294  
    return nullptr;
295  
}
295  
}
296  

296  

297  
inline bool
297  
inline bool
298  
try_push_waiter_tl_cache(waiter_node* w) noexcept
298  
try_push_waiter_tl_cache(waiter_node* w) noexcept
299  
{
299  
{
300  
    if (!tl_cached_waiter.get())
300  
    if (!tl_cached_waiter.get())
301  
    {
301  
    {
302  
        tl_cached_waiter.set(w);
302  
        tl_cached_waiter.set(w);
303  
        return true;
303  
        return true;
304  
    }
304  
    }
305  
    return false;
305  
    return false;
306  
}
306  
}
307  

307  

308  
inline void
308  
inline void
309  
timer_service_invalidate_cache() noexcept
309  
timer_service_invalidate_cache() noexcept
310  
{
310  
{
311  
    delete tl_cached_impl.get();
311  
    delete tl_cached_impl.get();
312  
    tl_cached_impl.set(nullptr);
312  
    tl_cached_impl.set(nullptr);
313  

313  

314  
    delete tl_cached_waiter.get();
314  
    delete tl_cached_waiter.get();
315  
    tl_cached_waiter.set(nullptr);
315  
    tl_cached_waiter.set(nullptr);
316  
}
316  
}
317  

317  

318  
// timer_service out-of-class member function definitions
318  
// timer_service out-of-class member function definitions
319  

319  

320  
inline timer_service::implementation::implementation(
320  
inline timer_service::implementation::implementation(
321  
    timer_service& svc) noexcept
321  
    timer_service& svc) noexcept
322  
    : svc_(&svc)
322  
    : svc_(&svc)
323  
{
323  
{
324  
}
324  
}
325  

325  

326  
inline void
326  
inline void
327  
timer_service::shutdown()
327  
timer_service::shutdown()
328  
{
328  
{
329  
    timer_service_invalidate_cache();
329  
    timer_service_invalidate_cache();
330  

330  

331  
    // Cancel waiting timers still in the heap
331  
    // Cancel waiting timers still in the heap
332  
    for (auto& entry : heap_)
332  
    for (auto& entry : heap_)
333  
    {
333  
    {
334  
        auto* impl = entry.timer_;
334  
        auto* impl = entry.timer_;
335  
        while (auto* w = impl->waiters_.pop_front())
335  
        while (auto* w = impl->waiters_.pop_front())
336  
        {
336  
        {
337  
            w->stop_cb_.reset();
337  
            w->stop_cb_.reset();
338  
            w->h_.destroy();
338  
            w->h_.destroy();
339  
            sched_->work_finished();
339  
            sched_->work_finished();
340  
            delete w;
340  
            delete w;
341  
        }
341  
        }
342  
        impl->heap_index_ = (std::numeric_limits<std::size_t>::max)();
342  
        impl->heap_index_ = (std::numeric_limits<std::size_t>::max)();
343  
        delete impl;
343  
        delete impl;
344  
    }
344  
    }
345  
    heap_.clear();
345  
    heap_.clear();
346  
    cached_nearest_ns_.store(
346  
    cached_nearest_ns_.store(
347  
        (std::numeric_limits<std::int64_t>::max)(), std::memory_order_release);
347  
        (std::numeric_limits<std::int64_t>::max)(), std::memory_order_release);
348  

348  

349  
    // Delete free-listed impls
349  
    // Delete free-listed impls
350  
    while (free_list_)
350  
    while (free_list_)
351  
    {
351  
    {
352  
        auto* next = free_list_->next_free_;
352  
        auto* next = free_list_->next_free_;
353  
        delete free_list_;
353  
        delete free_list_;
354  
        free_list_ = next;
354  
        free_list_ = next;
355  
    }
355  
    }
356  

356  

357  
    // Delete free-listed waiters
357  
    // Delete free-listed waiters
358  
    while (waiter_free_list_)
358  
    while (waiter_free_list_)
359  
    {
359  
    {
360  
        auto* next = waiter_free_list_->next_free_;
360  
        auto* next = waiter_free_list_->next_free_;
361  
        delete waiter_free_list_;
361  
        delete waiter_free_list_;
362  
        waiter_free_list_ = next;
362  
        waiter_free_list_ = next;
363  
    }
363  
    }
364  
}
364  
}
365  

365  

366  
inline io_object::implementation*
366  
inline io_object::implementation*
367  
timer_service::construct()
367  
timer_service::construct()
368  
{
368  
{
369  
    implementation* impl = try_pop_tl_cache(this);
369  
    implementation* impl = try_pop_tl_cache(this);
370  
    if (impl)
370  
    if (impl)
371  
    {
371  
    {
372  
        impl->svc_        = this;
372  
        impl->svc_        = this;
373  
        impl->heap_index_ = (std::numeric_limits<std::size_t>::max)();
373  
        impl->heap_index_ = (std::numeric_limits<std::size_t>::max)();
374  
        impl->might_have_pending_waits_ = false;
374  
        impl->might_have_pending_waits_ = false;
375  
        return impl;
375  
        return impl;
376  
    }
376  
    }
377  

377  

378  
    std::lock_guard lock(mutex_);
378  
    std::lock_guard lock(mutex_);
379  
    if (free_list_)
379  
    if (free_list_)
380  
    {
380  
    {
381  
        impl              = free_list_;
381  
        impl              = free_list_;
382  
        free_list_        = impl->next_free_;
382  
        free_list_        = impl->next_free_;
383  
        impl->next_free_  = nullptr;
383  
        impl->next_free_  = nullptr;
384  
        impl->svc_        = this;
384  
        impl->svc_        = this;
385  
        impl->heap_index_ = (std::numeric_limits<std::size_t>::max)();
385  
        impl->heap_index_ = (std::numeric_limits<std::size_t>::max)();
386  
        impl->might_have_pending_waits_ = false;
386  
        impl->might_have_pending_waits_ = false;
387  
    }
387  
    }
388  
    else
388  
    else
389  
    {
389  
    {
390  
        impl = new implementation(*this);
390  
        impl = new implementation(*this);
391  
    }
391  
    }
392  
    return impl;
392  
    return impl;
393  
}
393  
}
394  

394  

395  
inline void
395  
inline void
396  
timer_service::destroy(io_object::implementation* p)
396  
timer_service::destroy(io_object::implementation* p)
397  
{
397  
{
398  
    destroy_impl(static_cast<implementation&>(*p));
398  
    destroy_impl(static_cast<implementation&>(*p));
399  
}
399  
}
400  

400  

401  
inline void
401  
inline void
402  
timer_service::destroy_impl(implementation& impl)
402  
timer_service::destroy_impl(implementation& impl)
403  
{
403  
{
404  
    cancel_timer(impl);
404  
    cancel_timer(impl);
405  

405  

406  
    if (impl.heap_index_ != (std::numeric_limits<std::size_t>::max)())
406  
    if (impl.heap_index_ != (std::numeric_limits<std::size_t>::max)())
407  
    {
407  
    {
408  
        std::lock_guard lock(mutex_);
408  
        std::lock_guard lock(mutex_);
409  
        remove_timer_impl(impl);
409  
        remove_timer_impl(impl);
410  
        refresh_cached_nearest();
410  
        refresh_cached_nearest();
411  
    }
411  
    }
412  

412  

413  
    if (try_push_tl_cache(&impl))
413  
    if (try_push_tl_cache(&impl))
414  
        return;
414  
        return;
415  

415  

416  
    std::lock_guard lock(mutex_);
416  
    std::lock_guard lock(mutex_);
417  
    impl.next_free_ = free_list_;
417  
    impl.next_free_ = free_list_;
418  
    free_list_      = &impl;
418  
    free_list_      = &impl;
419  
}
419  
}
420  

420  

421  
inline waiter_node*
421  
inline waiter_node*
422  
timer_service::create_waiter()
422  
timer_service::create_waiter()
423  
{
423  
{
424  
    if (auto* w = try_pop_waiter_tl_cache())
424  
    if (auto* w = try_pop_waiter_tl_cache())
425  
        return w;
425  
        return w;
426  

426  

427  
    std::lock_guard lock(mutex_);
427  
    std::lock_guard lock(mutex_);
428  
    if (waiter_free_list_)
428  
    if (waiter_free_list_)
429  
    {
429  
    {
430  
        auto* w           = waiter_free_list_;
430  
        auto* w           = waiter_free_list_;
431  
        waiter_free_list_ = w->next_free_;
431  
        waiter_free_list_ = w->next_free_;
432  
        w->next_free_     = nullptr;
432  
        w->next_free_     = nullptr;
433  
        return w;
433  
        return w;
434  
    }
434  
    }
435  

435  

436  
    return new waiter_node();
436  
    return new waiter_node();
437  
}
437  
}
438  

438  

439  
inline void
439  
inline void
440  
timer_service::destroy_waiter(waiter_node* w)
440  
timer_service::destroy_waiter(waiter_node* w)
441  
{
441  
{
442  
    if (try_push_waiter_tl_cache(w))
442  
    if (try_push_waiter_tl_cache(w))
443  
        return;
443  
        return;
444  

444  

445  
    std::lock_guard lock(mutex_);
445  
    std::lock_guard lock(mutex_);
446  
    w->next_free_     = waiter_free_list_;
446  
    w->next_free_     = waiter_free_list_;
447  
    waiter_free_list_ = w;
447  
    waiter_free_list_ = w;
448  
}
448  
}
449  

449  

450  
inline std::size_t
450  
inline std::size_t
451  
timer_service::update_timer(implementation& impl, time_point new_time)
451  
timer_service::update_timer(implementation& impl, time_point new_time)
452  
{
452  
{
453  
    bool in_heap =
453  
    bool in_heap =
454  
        (impl.heap_index_ != (std::numeric_limits<std::size_t>::max)());
454  
        (impl.heap_index_ != (std::numeric_limits<std::size_t>::max)());
455  
    if (!in_heap && impl.waiters_.empty())
455  
    if (!in_heap && impl.waiters_.empty())
456  
        return 0;
456  
        return 0;
457  

457  

458  
    bool notify = false;
458  
    bool notify = false;
459  
    intrusive_list<waiter_node> canceled;
459  
    intrusive_list<waiter_node> canceled;
460  

460  

461  
    {
461  
    {
462  
        std::lock_guard lock(mutex_);
462  
        std::lock_guard lock(mutex_);
463  

463  

464  
        while (auto* w = impl.waiters_.pop_front())
464  
        while (auto* w = impl.waiters_.pop_front())
465  
        {
465  
        {
466  
            w->impl_ = nullptr;
466  
            w->impl_ = nullptr;
467  
            canceled.push_back(w);
467  
            canceled.push_back(w);
468  
        }
468  
        }
469  

469  

470  
        if (impl.heap_index_ < heap_.size())
470  
        if (impl.heap_index_ < heap_.size())
471  
        {
471  
        {
472  
            time_point old_time           = heap_[impl.heap_index_].time_;
472  
            time_point old_time           = heap_[impl.heap_index_].time_;
473  
            heap_[impl.heap_index_].time_ = new_time;
473  
            heap_[impl.heap_index_].time_ = new_time;
474  

474  

475  
            if (new_time < old_time)
475  
            if (new_time < old_time)
476  
                up_heap(impl.heap_index_);
476  
                up_heap(impl.heap_index_);
477  
            else
477  
            else
478  
                down_heap(impl.heap_index_);
478  
                down_heap(impl.heap_index_);
479  

479  

480  
            notify = (impl.heap_index_ == 0);
480  
            notify = (impl.heap_index_ == 0);
481  
        }
481  
        }
482  

482  

483  
        refresh_cached_nearest();
483  
        refresh_cached_nearest();
484  
    }
484  
    }
485  

485  

486  
    std::size_t count = 0;
486  
    std::size_t count = 0;
487  
    while (auto* w = canceled.pop_front())
487  
    while (auto* w = canceled.pop_front())
488  
    {
488  
    {
489  
        w->ec_value_ = make_error_code(capy::error::canceled);
489  
        w->ec_value_ = make_error_code(capy::error::canceled);
490  
        sched_->post(&w->op_);
490  
        sched_->post(&w->op_);
491  
        ++count;
491  
        ++count;
492  
    }
492  
    }
493  

493  

494  
    if (notify)
494  
    if (notify)
495  
        on_earliest_changed_();
495  
        on_earliest_changed_();
496  

496  

497  
    return count;
497  
    return count;
498  
}
498  
}
499  

499  

500  
inline void
500  
inline void
501  
timer_service::insert_waiter(implementation& impl, waiter_node* w)
501  
timer_service::insert_waiter(implementation& impl, waiter_node* w)
502  
{
502  
{
503  
    bool notify = false;
503  
    bool notify = false;
504  
    {
504  
    {
505  
        std::lock_guard lock(mutex_);
505  
        std::lock_guard lock(mutex_);
506  
        if (impl.heap_index_ == (std::numeric_limits<std::size_t>::max)())
506  
        if (impl.heap_index_ == (std::numeric_limits<std::size_t>::max)())
507  
        {
507  
        {
508  
            impl.heap_index_ = heap_.size();
508  
            impl.heap_index_ = heap_.size();
509  
            heap_.push_back({impl.expiry_, &impl});
509  
            heap_.push_back({impl.expiry_, &impl});
510  
            up_heap(heap_.size() - 1);
510  
            up_heap(heap_.size() - 1);
511  
            notify = (impl.heap_index_ == 0);
511  
            notify = (impl.heap_index_ == 0);
512  
            refresh_cached_nearest();
512  
            refresh_cached_nearest();
513  
        }
513  
        }
514  
        impl.waiters_.push_back(w);
514  
        impl.waiters_.push_back(w);
515  
    }
515  
    }
516  
    if (notify)
516  
    if (notify)
517  
        on_earliest_changed_();
517  
        on_earliest_changed_();
518  
}
518  
}
519  

519  

520  
inline std::size_t
520  
inline std::size_t
521  
timer_service::cancel_timer(implementation& impl)
521  
timer_service::cancel_timer(implementation& impl)
522  
{
522  
{
523  
    if (!impl.might_have_pending_waits_)
523  
    if (!impl.might_have_pending_waits_)
524  
        return 0;
524  
        return 0;
525  

525  

526  
    // Not in heap and no waiters — just clear the flag
526  
    // Not in heap and no waiters — just clear the flag
527  
    if (impl.heap_index_ == (std::numeric_limits<std::size_t>::max)() &&
527  
    if (impl.heap_index_ == (std::numeric_limits<std::size_t>::max)() &&
528  
        impl.waiters_.empty())
528  
        impl.waiters_.empty())
529  
    {
529  
    {
530  
        impl.might_have_pending_waits_ = false;
530  
        impl.might_have_pending_waits_ = false;
531  
        return 0;
531  
        return 0;
532  
    }
532  
    }
533  

533  

534  
    intrusive_list<waiter_node> canceled;
534  
    intrusive_list<waiter_node> canceled;
535  

535  

536  
    {
536  
    {
537  
        std::lock_guard lock(mutex_);
537  
        std::lock_guard lock(mutex_);
538  
        remove_timer_impl(impl);
538  
        remove_timer_impl(impl);
539  
        while (auto* w = impl.waiters_.pop_front())
539  
        while (auto* w = impl.waiters_.pop_front())
540  
        {
540  
        {
541  
            w->impl_ = nullptr;
541  
            w->impl_ = nullptr;
542  
            canceled.push_back(w);
542  
            canceled.push_back(w);
543  
        }
543  
        }
544  
        refresh_cached_nearest();
544  
        refresh_cached_nearest();
545  
    }
545  
    }
546  

546  

547  
    impl.might_have_pending_waits_ = false;
547  
    impl.might_have_pending_waits_ = false;
548  

548  

549  
    std::size_t count = 0;
549  
    std::size_t count = 0;
550  
    while (auto* w = canceled.pop_front())
550  
    while (auto* w = canceled.pop_front())
551  
    {
551  
    {
552  
        w->ec_value_ = make_error_code(capy::error::canceled);
552  
        w->ec_value_ = make_error_code(capy::error::canceled);
553  
        sched_->post(&w->op_);
553  
        sched_->post(&w->op_);
554  
        ++count;
554  
        ++count;
555  
    }
555  
    }
556  

556  

557  
    return count;
557  
    return count;
558  
}
558  
}
559  

559  

560  
inline void
560  
inline void
561  
timer_service::cancel_waiter(waiter_node* w)
561  
timer_service::cancel_waiter(waiter_node* w)
562  
{
562  
{
563  
    {
563  
    {
564  
        std::lock_guard lock(mutex_);
564  
        std::lock_guard lock(mutex_);
565  
        // Already removed by cancel_timer or process_expired
565  
        // Already removed by cancel_timer or process_expired
566  
        if (!w->impl_)
566  
        if (!w->impl_)
567  
            return;
567  
            return;
568  
        auto* impl = w->impl_;
568  
        auto* impl = w->impl_;
569  
        w->impl_   = nullptr;
569  
        w->impl_   = nullptr;
570  
        impl->waiters_.remove(w);
570  
        impl->waiters_.remove(w);
571  
        if (impl->waiters_.empty())
571  
        if (impl->waiters_.empty())
572  
        {
572  
        {
573  
            remove_timer_impl(*impl);
573  
            remove_timer_impl(*impl);
574  
            impl->might_have_pending_waits_ = false;
574  
            impl->might_have_pending_waits_ = false;
575  
        }
575  
        }
576  
        refresh_cached_nearest();
576  
        refresh_cached_nearest();
577  
    }
577  
    }
578  

578  

579  
    w->ec_value_ = make_error_code(capy::error::canceled);
579  
    w->ec_value_ = make_error_code(capy::error::canceled);
580  
    sched_->post(&w->op_);
580  
    sched_->post(&w->op_);
581  
}
581  
}
582  

582  

583  
inline std::size_t
583  
inline std::size_t
584  
timer_service::cancel_one_waiter(implementation& impl)
584  
timer_service::cancel_one_waiter(implementation& impl)
585  
{
585  
{
586  
    if (!impl.might_have_pending_waits_)
586  
    if (!impl.might_have_pending_waits_)
587  
        return 0;
587  
        return 0;
588  

588  

589  
    waiter_node* w = nullptr;
589  
    waiter_node* w = nullptr;
590  

590  

591  
    {
591  
    {
592  
        std::lock_guard lock(mutex_);
592  
        std::lock_guard lock(mutex_);
593  
        w = impl.waiters_.pop_front();
593  
        w = impl.waiters_.pop_front();
594  
        if (!w)
594  
        if (!w)
595  
            return 0;
595  
            return 0;
596  
        w->impl_ = nullptr;
596  
        w->impl_ = nullptr;
597  
        if (impl.waiters_.empty())
597  
        if (impl.waiters_.empty())
598  
        {
598  
        {
599  
            remove_timer_impl(impl);
599  
            remove_timer_impl(impl);
600  
            impl.might_have_pending_waits_ = false;
600  
            impl.might_have_pending_waits_ = false;
601  
        }
601  
        }
602  
        refresh_cached_nearest();
602  
        refresh_cached_nearest();
603  
    }
603  
    }
604  

604  

605  
    w->ec_value_ = make_error_code(capy::error::canceled);
605  
    w->ec_value_ = make_error_code(capy::error::canceled);
606  
    sched_->post(&w->op_);
606  
    sched_->post(&w->op_);
607  
    return 1;
607  
    return 1;
608  
}
608  
}
609  

609  

610  
inline std::size_t
610  
inline std::size_t
611  
timer_service::process_expired()
611  
timer_service::process_expired()
612  
{
612  
{
613  
    intrusive_list<waiter_node> expired;
613  
    intrusive_list<waiter_node> expired;
614  

614  

615  
    {
615  
    {
616  
        std::lock_guard lock(mutex_);
616  
        std::lock_guard lock(mutex_);
617  
        auto now = clock_type::now();
617  
        auto now = clock_type::now();
618  

618  

619  
        while (!heap_.empty() && heap_[0].time_ <= now)
619  
        while (!heap_.empty() && heap_[0].time_ <= now)
620  
        {
620  
        {
621  
            implementation* t = heap_[0].timer_;
621  
            implementation* t = heap_[0].timer_;
622  
            remove_timer_impl(*t);
622  
            remove_timer_impl(*t);
623  
            while (auto* w = t->waiters_.pop_front())
623  
            while (auto* w = t->waiters_.pop_front())
624  
            {
624  
            {
625  
                w->impl_     = nullptr;
625  
                w->impl_     = nullptr;
626  
                w->ec_value_ = {};
626  
                w->ec_value_ = {};
627  
                expired.push_back(w);
627  
                expired.push_back(w);
628  
            }
628  
            }
629  
            t->might_have_pending_waits_ = false;
629  
            t->might_have_pending_waits_ = false;
630  
        }
630  
        }
631  

631  

632  
        refresh_cached_nearest();
632  
        refresh_cached_nearest();
633  
    }
633  
    }
634  

634  

635  
    std::size_t count = 0;
635  
    std::size_t count = 0;
636  
    while (auto* w = expired.pop_front())
636  
    while (auto* w = expired.pop_front())
637  
    {
637  
    {
638  
        sched_->post(&w->op_);
638  
        sched_->post(&w->op_);
639  
        ++count;
639  
        ++count;
640  
    }
640  
    }
641  

641  

642  
    return count;
642  
    return count;
643  
}
643  
}
644  

644  

645  
inline void
645  
inline void
646  
timer_service::remove_timer_impl(implementation& impl)
646  
timer_service::remove_timer_impl(implementation& impl)
647  
{
647  
{
648  
    std::size_t index = impl.heap_index_;
648  
    std::size_t index = impl.heap_index_;
649  
    if (index >= heap_.size())
649  
    if (index >= heap_.size())
650  
        return; // Not in heap
650  
        return; // Not in heap
651  

651  

652  
    if (index == heap_.size() - 1)
652  
    if (index == heap_.size() - 1)
653  
    {
653  
    {
654  
        // Last element, just pop
654  
        // Last element, just pop
655  
        impl.heap_index_ = (std::numeric_limits<std::size_t>::max)();
655  
        impl.heap_index_ = (std::numeric_limits<std::size_t>::max)();
656  
        heap_.pop_back();
656  
        heap_.pop_back();
657  
    }
657  
    }
658  
    else
658  
    else
659  
    {
659  
    {
660  
        // Swap with last and reheapify
660  
        // Swap with last and reheapify
661  
        swap_heap(index, heap_.size() - 1);
661  
        swap_heap(index, heap_.size() - 1);
662  
        impl.heap_index_ = (std::numeric_limits<std::size_t>::max)();
662  
        impl.heap_index_ = (std::numeric_limits<std::size_t>::max)();
663  
        heap_.pop_back();
663  
        heap_.pop_back();
664  

664  

665  
        if (index > 0 && heap_[index].time_ < heap_[(index - 1) / 2].time_)
665  
        if (index > 0 && heap_[index].time_ < heap_[(index - 1) / 2].time_)
666  
            up_heap(index);
666  
            up_heap(index);
667  
        else
667  
        else
668  
            down_heap(index);
668  
            down_heap(index);
669  
    }
669  
    }
670  
}
670  
}
671  

671  

672  
inline void
672  
inline void
673  
timer_service::up_heap(std::size_t index)
673  
timer_service::up_heap(std::size_t index)
674  
{
674  
{
675  
    while (index > 0)
675  
    while (index > 0)
676  
    {
676  
    {
677  
        std::size_t parent = (index - 1) / 2;
677  
        std::size_t parent = (index - 1) / 2;
678  
        if (!(heap_[index].time_ < heap_[parent].time_))
678  
        if (!(heap_[index].time_ < heap_[parent].time_))
679  
            break;
679  
            break;
680  
        swap_heap(index, parent);
680  
        swap_heap(index, parent);
681  
        index = parent;
681  
        index = parent;
682  
    }
682  
    }
683  
}
683  
}
684  

684  

685  
inline void
685  
inline void
686  
timer_service::down_heap(std::size_t index)
686  
timer_service::down_heap(std::size_t index)
687  
{
687  
{
688  
    std::size_t child = index * 2 + 1;
688  
    std::size_t child = index * 2 + 1;
689  
    while (child < heap_.size())
689  
    while (child < heap_.size())
690  
    {
690  
    {
691  
        std::size_t min_child = (child + 1 == heap_.size() ||
691  
        std::size_t min_child = (child + 1 == heap_.size() ||
692  
                                 heap_[child].time_ < heap_[child + 1].time_)
692  
                                 heap_[child].time_ < heap_[child + 1].time_)
693  
            ? child
693  
            ? child
694  
            : child + 1;
694  
            : child + 1;
695  

695  

696  
        if (heap_[index].time_ < heap_[min_child].time_)
696  
        if (heap_[index].time_ < heap_[min_child].time_)
697  
            break;
697  
            break;
698  

698  

699  
        swap_heap(index, min_child);
699  
        swap_heap(index, min_child);
700  
        index = min_child;
700  
        index = min_child;
701  
        child = index * 2 + 1;
701  
        child = index * 2 + 1;
702  
    }
702  
    }
703  
}
703  
}
704  

704  

705  
inline void
705  
inline void
706  
timer_service::swap_heap(std::size_t i1, std::size_t i2)
706  
timer_service::swap_heap(std::size_t i1, std::size_t i2)
707  
{
707  
{
708  
    heap_entry tmp                = heap_[i1];
708  
    heap_entry tmp                = heap_[i1];
709  
    heap_[i1]                     = heap_[i2];
709  
    heap_[i1]                     = heap_[i2];
710  
    heap_[i2]                     = tmp;
710  
    heap_[i2]                     = tmp;
711  
    heap_[i1].timer_->heap_index_ = i1;
711  
    heap_[i1].timer_->heap_index_ = i1;
712  
    heap_[i2].timer_->heap_index_ = i2;
712  
    heap_[i2].timer_->heap_index_ = i2;
713  
}
713  
}
714  

714  

715  
// waiter_node out-of-class member function definitions
715  
// waiter_node out-of-class member function definitions
716  

716  

717  
inline void
717  
inline void
718  
waiter_node::canceller::operator()() const
718  
waiter_node::canceller::operator()() const
719  
{
719  
{
720  
    waiter_->svc_->cancel_waiter(waiter_);
720  
    waiter_->svc_->cancel_waiter(waiter_);
721  
}
721  
}
722  

722  

723  
inline void
723  
inline void
724  
waiter_node::completion_op::do_complete(
724  
waiter_node::completion_op::do_complete(
725  
    void* owner, scheduler_op* base, std::uint32_t, std::uint32_t)
725  
    void* owner, scheduler_op* base, std::uint32_t, std::uint32_t)
726  
{
726  
{
727  
    if (!owner)
727  
    if (!owner)
728  
        return;
728  
        return;
729  
    static_cast<completion_op*>(base)->operator()();
729  
    static_cast<completion_op*>(base)->operator()();
730  
}
730  
}
731  

731  

732  
inline void
732  
inline void
733  
waiter_node::completion_op::operator()()
733  
waiter_node::completion_op::operator()()
734  
{
734  
{
735  
    auto* w = waiter_;
735  
    auto* w = waiter_;
736  
    w->stop_cb_.reset();
736  
    w->stop_cb_.reset();
737  
    if (w->ec_out_)
737  
    if (w->ec_out_)
738  
        *w->ec_out_ = w->ec_value_;
738  
        *w->ec_out_ = w->ec_value_;
739  

739  

740  
    auto h      = w->h_;
740  
    auto h      = w->h_;
741  
    auto d      = w->d_;
741  
    auto d      = w->d_;
742  
    auto* svc   = w->svc_;
742  
    auto* svc   = w->svc_;
743  
    auto& sched = svc->get_scheduler();
743  
    auto& sched = svc->get_scheduler();
744  

744  

745  
    svc->destroy_waiter(w);
745  
    svc->destroy_waiter(w);
746  

746  

747  
    d.post(h);
747  
    d.post(h);
748  
    sched.work_finished();
748  
    sched.work_finished();
749  
}
749  
}
750  

750  

751  
inline std::coroutine_handle<>
751  
inline std::coroutine_handle<>
752  
timer_service::implementation::wait(
752  
timer_service::implementation::wait(
753  
    std::coroutine_handle<> h,
753  
    std::coroutine_handle<> h,
754  
    capy::executor_ref d,
754  
    capy::executor_ref d,
755  
    std::stop_token token,
755  
    std::stop_token token,
756  
    std::error_code* ec)
756  
    std::error_code* ec)
757  
{
757  
{
758  
    // Already-expired fast path — no waiter_node, no mutex.
758  
    // Already-expired fast path — no waiter_node, no mutex.
759  
    // Post instead of dispatch so the coroutine yields to the
759  
    // Post instead of dispatch so the coroutine yields to the
760  
    // scheduler, allowing other queued work to run.
760  
    // scheduler, allowing other queued work to run.
761  
    if (heap_index_ == (std::numeric_limits<std::size_t>::max)())
761  
    if (heap_index_ == (std::numeric_limits<std::size_t>::max)())
762  
    {
762  
    {
763  
        if (expiry_ == (time_point::min)() || expiry_ <= clock_type::now())
763  
        if (expiry_ == (time_point::min)() || expiry_ <= clock_type::now())
764  
        {
764  
        {
765  
            if (ec)
765  
            if (ec)
766  
                *ec = {};
766  
                *ec = {};
767  
            d.post(h);
767  
            d.post(h);
768  
            return std::noop_coroutine();
768  
            return std::noop_coroutine();
769  
        }
769  
        }
770  
    }
770  
    }
771  

771  

772  
    auto* w    = svc_->create_waiter();
772  
    auto* w    = svc_->create_waiter();
773  
    w->impl_   = this;
773  
    w->impl_   = this;
774  
    w->svc_    = svc_;
774  
    w->svc_    = svc_;
775  
    w->h_      = h;
775  
    w->h_      = h;
776  
    w->d_      = d;
776  
    w->d_      = d;
777  
    w->token_  = std::move(token);
777  
    w->token_  = std::move(token);
778  
    w->ec_out_ = ec;
778  
    w->ec_out_ = ec;
779  

779  

780  
    svc_->insert_waiter(*this, w);
780  
    svc_->insert_waiter(*this, w);
781  
    might_have_pending_waits_ = true;
781  
    might_have_pending_waits_ = true;
782  
    svc_->get_scheduler().work_started();
782  
    svc_->get_scheduler().work_started();
783  

783  

784  
    if (w->token_.stop_possible())
784  
    if (w->token_.stop_possible())
785  
        w->stop_cb_.emplace(w->token_, waiter_node::canceller{w});
785  
        w->stop_cb_.emplace(w->token_, waiter_node::canceller{w});
786  

786  

787  
    return std::noop_coroutine();
787  
    return std::noop_coroutine();
788  
}
788  
}
789  

789  

790  
// Free functions
790  
// Free functions
791  

791  

792  
struct timer_service_access
792  
struct timer_service_access
793  
{
793  
{
794  
    static native_scheduler& get_scheduler(io_context& ctx) noexcept
794  
    static native_scheduler& get_scheduler(io_context& ctx) noexcept
795  
    {
795  
    {
796  
        return static_cast<native_scheduler&>(*ctx.sched_);
796  
        return static_cast<native_scheduler&>(*ctx.sched_);
797  
    }
797  
    }
798  
};
798  
};
799  

799  

800  
// Bypass find_service() mutex by reading the scheduler's cached pointer
800  
// Bypass find_service() mutex by reading the scheduler's cached pointer
801  
inline io_object::io_service&
801  
inline io_object::io_service&
802  
timer_service_direct(capy::execution_context& ctx) noexcept
802  
timer_service_direct(capy::execution_context& ctx) noexcept
803  
{
803  
{
804  
    return *timer_service_access::get_scheduler(static_cast<io_context&>(ctx))
804  
    return *timer_service_access::get_scheduler(static_cast<io_context&>(ctx))
805  
                .timer_svc_;
805  
                .timer_svc_;
806  
}
806  
}
807  

807  

808  
inline std::size_t
808  
inline std::size_t
809  
timer_service_update_expiry(timer::implementation& base)
809  
timer_service_update_expiry(timer::implementation& base)
810  
{
810  
{
811  
    auto& impl = static_cast<timer_service::implementation&>(base);
811  
    auto& impl = static_cast<timer_service::implementation&>(base);
812  
    return impl.svc_->update_timer(impl, impl.expiry_);
812  
    return impl.svc_->update_timer(impl, impl.expiry_);
813  
}
813  
}
814  

814  

815  
inline std::size_t
815  
inline std::size_t
816  
timer_service_cancel(timer::implementation& base) noexcept
816  
timer_service_cancel(timer::implementation& base) noexcept
817  
{
817  
{
818  
    auto& impl = static_cast<timer_service::implementation&>(base);
818  
    auto& impl = static_cast<timer_service::implementation&>(base);
819  
    return impl.svc_->cancel_timer(impl);
819  
    return impl.svc_->cancel_timer(impl);
820  
}
820  
}
821  

821  

822  
inline std::size_t
822  
inline std::size_t
823  
timer_service_cancel_one(timer::implementation& base) noexcept
823  
timer_service_cancel_one(timer::implementation& base) noexcept
824  
{
824  
{
825  
    auto& impl = static_cast<timer_service::implementation&>(base);
825  
    auto& impl = static_cast<timer_service::implementation&>(base);
826  
    return impl.svc_->cancel_one_waiter(impl);
826  
    return impl.svc_->cancel_one_waiter(impl);
827  
}
827  
}
828  

828  

829  
inline timer_service&
829  
inline timer_service&
830  
get_timer_service(capy::execution_context& ctx, scheduler& sched)
830  
get_timer_service(capy::execution_context& ctx, scheduler& sched)
831  
{
831  
{
832  
    return ctx.make_service<timer_service>(sched);
832  
    return ctx.make_service<timer_service>(sched);
833  
}
833  
}
834  

834  

835  
} // namespace boost::corosio::detail
835  
} // namespace boost::corosio::detail
836  

836  

837  
#endif
837  
#endif