89.12% Lines (213/239) 100.00% Functions (27/27)
TLA Baseline Branch
Line Hits Code Line Hits Code
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/detail/timer.hpp> 14   #include <boost/corosio/detail/timer.hpp>
15   #include <boost/corosio/detail/scheduler.hpp> 15   #include <boost/corosio/detail/scheduler.hpp>
16   #include <boost/corosio/detail/scheduler_op.hpp> 16   #include <boost/corosio/detail/scheduler_op.hpp>
17   #include <boost/corosio/detail/intrusive.hpp> 17   #include <boost/corosio/detail/intrusive.hpp>
18   #include <boost/corosio/detail/thread_local_ptr.hpp> 18   #include <boost/corosio/detail/thread_local_ptr.hpp>
19   #include <boost/capy/error.hpp> 19   #include <boost/capy/error.hpp>
20   #include <boost/capy/ex/execution_context.hpp> 20   #include <boost/capy/ex/execution_context.hpp>
21   #include <boost/capy/ex/executor_ref.hpp> 21   #include <boost/capy/ex/executor_ref.hpp>
22   #include <system_error> 22   #include <system_error>
23   23  
24   #include <atomic> 24   #include <atomic>
25   #include <chrono> 25   #include <chrono>
26   #include <coroutine> 26   #include <coroutine>
27   #include <cstddef> 27   #include <cstddef>
28   #include <limits> 28   #include <limits>
29   #include <mutex> 29   #include <mutex>
30   #include <stop_token> 30   #include <stop_token>
31   #include <utility> 31   #include <utility>
32   #include <vector> 32   #include <vector>
33   33  
34   namespace boost::corosio::detail { 34   namespace boost::corosio::detail {
35   35  
36   struct scheduler; 36   struct scheduler;
37   37  
38   /* 38   /*
39   Timer Service 39   Timer Service
40   ============= 40   =============
41   41  
42   Data Structures 42   Data Structures
43   --------------- 43   ---------------
44   waiter_node (defined in timer.hpp) holds per-waiter state: 44   waiter_node (defined in timer.hpp) holds per-waiter state:
45   coroutine handle, executor, error output, embedded 45   coroutine handle, executor, error output, embedded
46   completion_op. Each concurrent co_await t.wait() embeds one 46   completion_op. Each concurrent co_await t.wait() embeds one
47   waiter_node in the awaitable on the suspended coroutine's 47   waiter_node in the awaitable on the suspended coroutine's
48   frame — waits perform no allocation. 48   frame — waits perform no allocation.
49   49  
50   timer::implementation holds per-timer state: expiry, heap 50   timer::implementation holds per-timer state: expiry, heap
51 - index, and an intrusive_list of waiter_nodes. Multiple 51 + index, and the single published waiter. Each timer holds
52 - coroutines can wait on the same timer simultaneously. 52 + at most one waiter; process_expired's local cross-timer drain
  53 + list still threads waiters through their intrusive hooks when
  54 + collecting several timers' waiters past the lock.
53   55  
54   timer_service owns a min-heap of active timers and a free list 56   timer_service owns a min-heap of active timers and a free list
55   of recycled impls. The heap is ordered by expiry time; the 57   of recycled impls. The heap is ordered by expiry time; the
56   scheduler queries nearest_expiry() to set the epoll/timerfd 58   scheduler queries nearest_expiry() to set the epoll/timerfd
57   timeout. 59   timeout.
58   60  
59   Optimization Strategy 61   Optimization Strategy
60   --------------------- 62   ---------------------
61   1. Deferred heap insertion — expires_after() stores the expiry 63   1. Deferred heap insertion — expires_after() stores the expiry
62   but does not insert into the heap. Insertion happens in wait(). 64   but does not insert into the heap. Insertion happens in wait().
63   2. Thread-local impl cache — single-slot per-thread cache. 65   2. Thread-local impl cache — single-slot per-thread cache.
64   3. Frame-resident waiter_node with embedded completion_op — 66   3. Frame-resident waiter_node with embedded completion_op —
65   eliminates heap allocation per wait/fire/cancel. 67   eliminates heap allocation per wait/fire/cancel.
66   4. Cached nearest expiry — atomic avoids mutex in nearest_expiry(). 68   4. Cached nearest expiry — atomic avoids mutex in nearest_expiry().
67   5. might_have_pending_waits_ flag — skips lock when no wait issued. 69   5. might_have_pending_waits_ flag — skips lock when no wait issued.
68   70  
69   Concurrency 71   Concurrency
70   ----------- 72   -----------
71   stop_token callbacks can fire from any thread. The impl_ 73   stop_token callbacks can fire from any thread. The impl_
72   pointer on waiter_node is used as a "still in list" marker. 74   pointer on waiter_node is used as a "still in list" marker.
73   A waiter_node's storage is the suspended coroutine's frame: 75   A waiter_node's storage is the suspended coroutine's frame:
74   every completion path must finish touching the node before 76   every completion path must finish touching the node before
75   posting the continuation or destroying the handle. 77   posting the continuation or destroying the handle.
76   */ 78   */
77   79  
78   inline void timer_service_invalidate_cache() noexcept; 80   inline void timer_service_invalidate_cache() noexcept;
79   81  
80   // timer_service class body — member function definitions are 82   // timer_service class body — member function definitions are
81   // out-of-class (after implementation and waiter_node are complete) 83   // out-of-class (after implementation and waiter_node are complete)
82   class BOOST_COROSIO_DECL timer_service final 84   class BOOST_COROSIO_DECL timer_service final
83   : public capy::execution_context::service 85   : public capy::execution_context::service
84   , public io_object::io_service 86   , public io_object::io_service
85   { 87   {
86   public: 88   public:
87   using clock_type = std::chrono::steady_clock; 89   using clock_type = std::chrono::steady_clock;
88   using time_point = clock_type::time_point; 90   using time_point = clock_type::time_point;
89   91  
90   /// Type-erased callback for earliest-expiry-changed notifications. 92   /// Type-erased callback for earliest-expiry-changed notifications.
91   class callback 93   class callback
92   { 94   {
93   void* ctx_ = nullptr; 95   void* ctx_ = nullptr;
94   void (*fn_)(void*) = nullptr; 96   void (*fn_)(void*) = nullptr;
95   97  
96   public: 98   public:
97   /// Construct an empty callback. 99   /// Construct an empty callback.
HITCBC 98   1382 callback() = default; 100   1410 callback() = default;
99   101  
100   /// Construct a callback with the given context and function. 102   /// Construct a callback with the given context and function.
HITCBC 101   1382 callback(void* ctx, void (*fn)(void*)) noexcept : ctx_(ctx), fn_(fn) {} 103   1410 callback(void* ctx, void (*fn)(void*)) noexcept : ctx_(ctx), fn_(fn) {}
102   104  
103   /// Return true if the callback is non-empty. 105   /// Return true if the callback is non-empty.
104   explicit operator bool() const noexcept 106   explicit operator bool() const noexcept
105   { 107   {
106   return fn_ != nullptr; 108   return fn_ != nullptr;
107   } 109   }
108   110  
109   /// Invoke the callback. 111   /// Invoke the callback.
HITCBC 110   5939 void operator()() const 112   9873 void operator()() const
111   { 113   {
HITCBC 112   5939 if (fn_) 114   9873 if (fn_)
HITCBC 113   5939 fn_(ctx_); 115   9873 fn_(ctx_);
HITCBC 114   5939 } 116   9873 }
115   }; 117   };
116   118  
117   private: 119   private:
118   struct heap_entry 120   struct heap_entry
119   { 121   {
120   time_point time_; 122   time_point time_;
121   timer::implementation* timer_; 123   timer::implementation* timer_;
122   }; 124   };
123   125  
124   scheduler* sched_ = nullptr; 126   scheduler* sched_ = nullptr;
125   BOOST_COROSIO_MSVC_WARNING_PUSH 127   BOOST_COROSIO_MSVC_WARNING_PUSH
126   BOOST_COROSIO_MSVC_WARNING_DISABLE(4251) // std:: members, dll-interface 128   BOOST_COROSIO_MSVC_WARNING_DISABLE(4251) // std:: members, dll-interface
127   mutable std::mutex mutex_; 129   mutable std::mutex mutex_;
128   std::vector<heap_entry> heap_; 130   std::vector<heap_entry> heap_;
129   timer::implementation* free_list_ = nullptr; 131   timer::implementation* free_list_ = nullptr;
130   callback on_earliest_changed_; 132   callback on_earliest_changed_;
131   bool shutting_down_ = false; 133   bool shutting_down_ = false;
132   // Avoids mutex in nearest_expiry() and empty() 134   // Avoids mutex in nearest_expiry() and empty()
133   mutable std::atomic<std::int64_t> cached_nearest_ns_{ 135   mutable std::atomic<std::int64_t> cached_nearest_ns_{
134   (std::numeric_limits<std::int64_t>::max)()}; 136   (std::numeric_limits<std::int64_t>::max)()};
135   BOOST_COROSIO_MSVC_WARNING_POP 137   BOOST_COROSIO_MSVC_WARNING_POP
136   138  
137   public: 139   public:
138   /// Construct the timer service bound to a scheduler. 140   /// Construct the timer service bound to a scheduler.
HITCBC 139   1382 inline timer_service(capy::execution_context&, scheduler& sched) 141   1410 inline timer_service(capy::execution_context&, scheduler& sched)
HITCBC 140   1382 : sched_(&sched) 142   1410 : sched_(&sched)
141   { 143   {
HITCBC 142   1382 } 144   1410 }
143   145  
144   /// Return the associated scheduler. 146   /// Return the associated scheduler.
HITCBC 145   12108 inline scheduler& get_scheduler() noexcept 147   19944 inline scheduler& get_scheduler() noexcept
146   { 148   {
HITCBC 147   12108 return *sched_; 149   19944 return *sched_;
148   } 150   }
149   151  
150   /// Destroy the timer service. 152   /// Destroy the timer service.
HITCBC 151   2764 ~timer_service() override = default; 153   2820 ~timer_service() override = default;
152   154  
153   timer_service(timer_service const&) = delete; 155   timer_service(timer_service const&) = delete;
154   timer_service& operator=(timer_service const&) = delete; 156   timer_service& operator=(timer_service const&) = delete;
155   157  
156   /// Register a callback invoked when the earliest expiry changes. 158   /// Register a callback invoked when the earliest expiry changes.
HITCBC 157   1382 inline void set_on_earliest_changed(callback cb) 159   1410 inline void set_on_earliest_changed(callback cb)
158   { 160   {
HITCBC 159   1382 on_earliest_changed_ = cb; 161   1410 on_earliest_changed_ = cb;
HITCBC 160   1382 } 162   1410 }
161   163  
162   /// Return true if no timers are in the heap. 164   /// Return true if no timers are in the heap.
163   inline bool empty() const noexcept 165   inline bool empty() const noexcept
164   { 166   {
165   return cached_nearest_ns_.load(std::memory_order_acquire) == 167   return cached_nearest_ns_.load(std::memory_order_acquire) ==
166   (std::numeric_limits<std::int64_t>::max)(); 168   (std::numeric_limits<std::int64_t>::max)();
167   } 169   }
168   170  
169   /// Return the nearest timer expiry without acquiring the mutex. 171   /// Return the nearest timer expiry without acquiring the mutex.
HITCBC 170   193969 inline time_point nearest_expiry() const noexcept 172   294358 inline time_point nearest_expiry() const noexcept
171   { 173   {
HITCBC 172   193969 auto ns = cached_nearest_ns_.load(std::memory_order_acquire); 174   294358 auto ns = cached_nearest_ns_.load(std::memory_order_acquire);
HITCBC 173   193969 return time_point(time_point::duration(ns)); 175   294358 return time_point(time_point::duration(ns));
174   } 176   }
175   177  
176   /// Cancel all pending timers and free cached resources. 178   /// Cancel all pending timers and free cached resources.
177   inline void shutdown() override; 179   inline void shutdown() override;
178   180  
179   /// Construct a new timer implementation. 181   /// Construct a new timer implementation.
180   inline io_object::implementation* construct() override; 182   inline io_object::implementation* construct() override;
181   183  
182   /// Destroy a timer implementation, cancelling pending waiters. 184   /// Destroy a timer implementation, cancelling pending waiters.
183   inline void destroy(io_object::implementation* p) override; 185   inline void destroy(io_object::implementation* p) override;
184   186  
185   /// Cancel and recycle a timer implementation. 187   /// Cancel and recycle a timer implementation.
186   inline void destroy_impl(timer::implementation& impl); 188   inline void destroy_impl(timer::implementation& impl);
187   189  
188 - /// Update the timer expiry, cancelling existing waiters. 190 + /// Publish the timer's waiter and insert the timer into the heap.
189 - inline std::size_t update_timer(  
190 - timer::implementation& impl, time_point new_time);  
191 -  
192 - /// Insert a waiter into the timer's waiter list and the heap.  
193   inline void insert_waiter(timer::implementation& impl, waiter_node* w); 191   inline void insert_waiter(timer::implementation& impl, waiter_node* w);
194   192  
195 - /// Cancel all waiters on a timer. 193 + /// Cancel the timer's published waiter, if any.
196 - inline std::size_t cancel_timer(timer::implementation& impl); 194 + inline void cancel_timer(timer::implementation& impl);
197   195  
198   /// Cancel one specific waiter ( stop_token callback path ). 196   /// Cancel one specific waiter ( stop_token callback path ).
199   inline void cancel_waiter(waiter_node* w); 197   inline void cancel_waiter(waiter_node* w);
200 - /// Cancel the oldest pending waiter on a timer ( FIFO ).  
201 - inline std::size_t cancel_one_waiter(timer::implementation& impl);  
202 -  
203   198  
204   /// Complete all waiters whose timers have expired. 199   /// Complete all waiters whose timers have expired.
205   inline std::size_t process_expired(); 200   inline std::size_t process_expired();
206   201  
207   private: 202   private:
HITCBC 208   222019 inline void refresh_cached_nearest() noexcept 203   333584 inline void refresh_cached_nearest() noexcept
209   { 204   {
HITCBC 210   222019 auto ns = heap_.empty() ? (std::numeric_limits<std::int64_t>::max)() 205   333584 auto ns = heap_.empty() ? (std::numeric_limits<std::int64_t>::max)()
HITCBC 211   218832 : heap_[0].time_.time_since_epoch().count(); 206   330373 : heap_[0].time_.time_since_epoch().count();
HITCBC 212   222019 cached_nearest_ns_.store(ns, std::memory_order_release); 207   333584 cached_nearest_ns_.store(ns, std::memory_order_release);
HITCBC 213   222019 } 208   333584 }
214   209  
215   inline void remove_timer_impl(timer::implementation& impl); 210   inline void remove_timer_impl(timer::implementation& impl);
216   inline void up_heap(std::size_t index); 211   inline void up_heap(std::size_t index);
217   inline void down_heap(std::size_t index); 212   inline void down_heap(std::size_t index);
218   inline void swap_heap(std::size_t i1, std::size_t i2); 213   inline void swap_heap(std::size_t i1, std::size_t i2);
219   }; 214   };
220   215  
221   // Thread-local cache avoids hot-path mutex acquisitions: 216   // Thread-local cache avoids hot-path mutex acquisitions:
222   // single-slot impl cache, validated by comparing svc_. Cleared by 217   // single-slot impl cache, validated by comparing svc_. Cleared by
223   // timer_service_invalidate_cache() during shutdown. 218   // timer_service_invalidate_cache() during shutdown.
224   219  
225   inline thread_local_ptr<timer::implementation> tl_cached_impl; 220   inline thread_local_ptr<timer::implementation> tl_cached_impl;
226   221  
227   // The POD TLS slot above never runs destructors, so a short-lived 222   // The POD TLS slot above never runs destructors, so a short-lived
228   // run() thread would leak its cached impl. Each push arms this 223   // run() thread would leak its cached impl. Each push arms this
229   // owner, whose destructor frees the slot at thread exit. A cached 224   // owner, whose destructor frees the slot at thread exit. A cached
230   // entry is a quiescent heap object (nothing in the heap or free 225   // entry is a quiescent heap object (nothing in the heap or free
231   // list) and deletion touches no service state, so it is safe after 226   // list) and deletion touches no service state, so it is safe after
232   // the owning service is gone (the stale-entry path in 227   // the owning service is gone (the stale-entry path in
233   // try_pop_tl_cache deletes the same way). 228   // try_pop_tl_cache deletes the same way).
234   struct tl_cache_owner 229   struct tl_cache_owner
235   { 230   {
HITCBC 236   38 ~tl_cache_owner() 231   37 ~tl_cache_owner()
237   { 232   {
HITCBC 238   38 delete tl_cached_impl.get(); 233   37 delete tl_cached_impl.get();
HITCBC 239   38 tl_cached_impl.set(nullptr); 234   37 tl_cached_impl.set(nullptr);
HITCBC 240   38 } 235   37 }
241   }; 236   };
242   237  
243   inline void 238   inline void
HITCBC 244   6840 arm_tl_cache_cleanup() noexcept 239   10779 arm_tl_cache_cleanup() noexcept
245   { 240   {
HITCBC 246   6840 thread_local tl_cache_owner owner; 241   10779 thread_local tl_cache_owner owner;
247   (void)owner; 242   (void)owner;
HITCBC 248   6840 } 243   10779 }
249   244  
250   inline timer::implementation* 245   inline timer::implementation*
HITCBC 251   6914 try_pop_tl_cache(timer_service* svc) noexcept 246   10853 try_pop_tl_cache(timer_service* svc) noexcept
252   { 247   {
HITCBC 253   6914 auto* impl = tl_cached_impl.get(); 248   10853 auto* impl = tl_cached_impl.get();
HITCBC 254   6914 if (impl) 249   10853 if (impl)
255   { 250   {
HITCBC 256   6604 tl_cached_impl.set(nullptr); 251   10536 tl_cached_impl.set(nullptr);
HITCBC 257   6604 if (impl->svc_ == svc) 252   10536 if (impl->svc_ == svc)
HITCBC 258   6604 return impl; 253   10536 return impl;
259   // Stale impl from a destroyed service 254   // Stale impl from a destroyed service
MISUBC 260   delete impl; 255   delete impl;
261   } 256   }
HITCBC 262   310 return nullptr; 257   317 return nullptr;
263   } 258   }
264   259  
265   inline bool 260   inline bool
HITCBC 266   6888 try_push_tl_cache(timer::implementation* impl) noexcept 261   10825 try_push_tl_cache(timer::implementation* impl) noexcept
267   { 262   {
HITCBC 268   6888 if (!tl_cached_impl.get()) 263   10825 if (!tl_cached_impl.get())
269   { 264   {
HITCBC 270   6840 arm_tl_cache_cleanup(); 265   10779 arm_tl_cache_cleanup();
HITCBC 271   6840 tl_cached_impl.set(impl); 266   10779 tl_cached_impl.set(impl);
HITCBC 272   6840 return true; 267   10779 return true;
273   } 268   }
HITCBC 274   48 return false; 269   46 return false;
275   } 270   }
276   271  
277   inline void 272   inline void
HITCBC 278   1382 timer_service_invalidate_cache() noexcept 273   1410 timer_service_invalidate_cache() noexcept
279   { 274   {
HITCBC 280   1382 delete tl_cached_impl.get(); 275   1410 delete tl_cached_impl.get();
HITCBC 281   1382 tl_cached_impl.set(nullptr); 276   1410 tl_cached_impl.set(nullptr);
HITCBC 282   1382 } 277   1410 }
283   278  
284   // timer_service out-of-class member function definitions 279   // timer_service out-of-class member function definitions
285   280  
286   inline void 281   inline void
HITCBC 287   1382 timer_service::shutdown() 282   1410 timer_service::shutdown()
288   { 283   {
HITCBC 289   1382 timer_service_invalidate_cache(); 284   1410 timer_service_invalidate_cache();
HITCBC 290   1382 shutting_down_ = true; 285   1410 shutting_down_ = true;
291   286  
292   // Snapshot impls and detach them from the heap so that 287   // Snapshot impls and detach them from the heap so that
293   // coroutine-owned timer destructors (triggered by h.destroy() 288   // coroutine-owned timer destructors (triggered by h.destroy()
294   // below) cannot re-enter remove_timer_impl() and mutate the 289   // below) cannot re-enter remove_timer_impl() and mutate the
295   // vector during iteration. 290   // vector during iteration.
HITCBC 296   1382 std::vector<timer::implementation*> impls; 291   1410 std::vector<timer::implementation*> impls;
HITCBC 297   1382 impls.reserve(heap_.size()); 292   1410 impls.reserve(heap_.size());
HITCBC 298   1408 for (auto& entry : heap_) 293   1438 for (auto& entry : heap_)
299   { 294   {
HITCBC 300   26 entry.timer_->heap_index_.store( 295   28 entry.timer_->heap_index_.store(
301   (std::numeric_limits<std::size_t>::max)(), 296   (std::numeric_limits<std::size_t>::max)(),
302   std::memory_order_relaxed); 297   std::memory_order_relaxed);
HITCBC 303   26 impls.push_back(entry.timer_); 298   28 impls.push_back(entry.timer_);
304   } 299   }
HITCBC 305   1382 heap_.clear(); 300   1410 heap_.clear();
HITCBC 306   1382 cached_nearest_ns_.store( 301   1410 cached_nearest_ns_.store(
307   (std::numeric_limits<std::int64_t>::max)(), std::memory_order_release); 302   (std::numeric_limits<std::int64_t>::max)(), std::memory_order_release);
308   303  
309   // Cancel waiting timers. Each waiter called work_started() 304   // Cancel waiting timers. Each waiter called work_started()
310   // in implementation::wait(). On IOCP the scheduler shutdown 305   // in implementation::wait(). On IOCP the scheduler shutdown
311   // loop exits when outstanding_work_ reaches zero, so we must 306   // loop exits when outstanding_work_ reaches zero, so we must
312   // call work_finished() here to balance it. On other backends 307   // call work_finished() here to balance it. On other backends
313   // this is harmless. 308   // this is harmless.
HITCBC 314   1408 for (auto* impl : impls) 309   1438 for (auto* impl : impls)
315   { 310   {
HITCBC 316 - 52 while (auto* w = impl->waiters_.pop_front()) 311 + 28 if (auto* w = std::exchange(impl->waiter_, nullptr))
317   { 312   {
HITCBC 318   26 w->reset_stop_cb(); 313   28 w->reset_stop_cb();
HITCBC 319   26 auto h = std::exchange(w->h_, {}); 314   28 auto h = std::exchange(w->h_, {});
HITCBC 320   26 sched_->work_finished(); 315   28 sched_->work_finished();
321   // Destroying the frame also ends the node's storage 316   // Destroying the frame also ends the node's storage
HITCBC 322   26 if (h) 317   28 if (h)
HITCBC 323   26 h.destroy(); 318   28 h.destroy();
ECB 324   26 } 319   }
HITCBC 325   26 delete impl; 320   28 delete impl;
326   } 321   }
327   322  
328   // Delete free-listed impls 323   // Delete free-listed impls
HITCBC 329   1430 while (free_list_) 324   1456 while (free_list_)
330   { 325   {
HITCBC 331   48 auto* next = free_list_->next_free_; 326   46 auto* next = free_list_->next_free_;
HITCBC 332   48 delete free_list_; 327   46 delete free_list_;
HITCBC 333   48 free_list_ = next; 328   46 free_list_ = next;
334   } 329   }
HITCBC 335   1382 } 330   1410 }
336   331  
337   inline io_object::implementation* 332   inline io_object::implementation*
HITCBC 338   6914 timer_service::construct() 333   10853 timer_service::construct()
339   { 334   {
HITCBC 340   6914 timer::implementation* impl = try_pop_tl_cache(this); 335   10853 timer::implementation* impl = try_pop_tl_cache(this);
HITCBC 341   6914 if (impl) 336   10853 if (impl)
342   { 337   {
HITCBC 343   6604 impl->svc_ = this; 338   10536 impl->svc_ = this;
344   // Reset expiry_ too: a recycled impl must behave like a fresh 339   // Reset expiry_ too: a recycled impl must behave like a fresh
345   // one, whose default expiry reads as already elapsed 340   // one, whose default expiry reads as already elapsed
HITCBC 346   6604 impl->expiry_ = {}; 341   10536 impl->expiry_ = {};
HITCBC 347   6604 impl->heap_index_.store( 342   10536 impl->heap_index_.store(
348   (std::numeric_limits<std::size_t>::max)(), 343   (std::numeric_limits<std::size_t>::max)(),
349   std::memory_order_relaxed); 344   std::memory_order_relaxed);
HITCBC 350   6604 impl->might_have_pending_waits_.store(false, std::memory_order_relaxed); 345   10536 impl->might_have_pending_waits_.store(false, std::memory_order_relaxed);
HITGNC   346 + 10536 BOOST_COROSIO_ASSERT(impl->waiter_ == nullptr);
HITCBC 351   6604 return impl; 347   10536 return impl;
352   } 348   }
353   349  
HITCBC 354   310 std::lock_guard lock(mutex_); 350   317 std::lock_guard lock(mutex_);
HITCBC 355   310 if (free_list_) 351   317 if (free_list_)
356   { 352   {
MISUBC 357   impl = free_list_; 353   impl = free_list_;
MISUBC 358   free_list_ = impl->next_free_; 354   free_list_ = impl->next_free_;
MISUBC 359   impl->next_free_ = nullptr; 355   impl->next_free_ = nullptr;
MISUBC 360   impl->svc_ = this; 356   impl->svc_ = this;
MISUBC 361   impl->expiry_ = {}; 357   impl->expiry_ = {};
MISUBC 362   impl->heap_index_.store( 358   impl->heap_index_.store(
363   (std::numeric_limits<std::size_t>::max)(), 359   (std::numeric_limits<std::size_t>::max)(),
364   std::memory_order_relaxed); 360   std::memory_order_relaxed);
MISUBC 365   impl->might_have_pending_waits_.store(false, std::memory_order_relaxed); 361   impl->might_have_pending_waits_.store(false, std::memory_order_relaxed);
MISUNC   362 + BOOST_COROSIO_ASSERT(impl->waiter_ == nullptr);
366   } 363   }
367   else 364   else
368   { 365   {
HITCBC 369   310 impl = new timer::implementation(*this); 366   317 impl = new timer::implementation(*this);
370   } 367   }
HITCBC 371   310 return impl; 368   317 return impl;
HITCBC 372   310 } 369   317 }
373   370  
374   inline void 371   inline void
HITCBC 375   6914 timer_service::destroy(io_object::implementation* p) 372   10853 timer_service::destroy(io_object::implementation* p)
376   { 373   {
377   // During shutdown the drain loop owns every impl and deletes 374   // During shutdown the drain loop owns every impl and deletes
378   // them directly. A frame destroyed by that loop can unwind a 375   // them directly. A frame destroyed by that loop can unwind a
379   // handle whose impl was freed in an earlier iteration (a 376   // handle whose impl was freed in an earlier iteration (a
380   // timeout's parent frame owns the timeout timer while 377   // timeout's parent frame owns the timeout timer while
381   // suspended on the inner delay's timer), so bail out before 378   // suspended on the inner delay's timer), so bail out before
382   // even downcasting the pointer. 379   // even downcasting the pointer.
HITCBC 383   6914 if (shutting_down_) 380   10853 if (shutting_down_)
HITCBC 384   26 return; 381   28 return;
HITCBC 385   6888 destroy_impl(static_cast<timer::implementation&>(*p)); 382   10825 destroy_impl(static_cast<timer::implementation&>(*p));
386   } 383   }
387   384  
388   inline void 385   inline void
HITCBC 389   6888 timer_service::destroy_impl(timer::implementation& impl) 386   10825 timer_service::destroy_impl(timer::implementation& impl)
390   { 387   {
391   // During shutdown the impl is owned by the shutdown loop. 388   // During shutdown the impl is owned by the shutdown loop.
392   // Re-entering here (from a coroutine-owned timer destructor 389   // Re-entering here (from a coroutine-owned timer destructor
393   // triggered by h.destroy()) must not modify the heap or 390   // triggered by h.destroy()) must not modify the heap or
394   // recycle the impl — shutdown deletes it directly. 391   // recycle the impl — shutdown deletes it directly.
HITCBC 395   6888 if (shutting_down_) 392   10825 if (shutting_down_)
HITCBC 396   6840 return; 393   10779 return;
397   394  
HITCBC 398   6888 cancel_timer(impl); 395   10825 cancel_timer(impl);
399   396  
HITCBC 400   13776 if (impl.heap_index_.load(std::memory_order_relaxed) != 397   21650 if (impl.heap_index_.load(std::memory_order_relaxed) !=
HITCBC 401   6888 (std::numeric_limits<std::size_t>::max)()) 398   10825 (std::numeric_limits<std::size_t>::max)())
402   { 399   {
MISUBC 403   std::lock_guard lock(mutex_); 400   std::lock_guard lock(mutex_);
MISUBC 404   remove_timer_impl(impl); 401   remove_timer_impl(impl);
MISUBC 405   refresh_cached_nearest(); 402   refresh_cached_nearest();
MISUBC 406   } 403   }
407   404  
HITCBC 408   6888 if (try_push_tl_cache(&impl)) 405   10825 if (try_push_tl_cache(&impl))
HITCBC 409   6840 return; 406   10779 return;
410   407  
HITCBC 411   48 std::lock_guard lock(mutex_); 408   46 std::lock_guard lock(mutex_);
HITCBC 412   48 impl.next_free_ = free_list_; 409   46 impl.next_free_ = free_list_;
HITCBC 413   48 free_list_ = &impl; 410   46 free_list_ = &impl;
HITCBC 414   48 } 411   46 }
415 - inline std::size_t  
416 - timer_service::update_timer(timer::implementation& impl, time_point new_time)  
DUB 417 - {  
418 - // Gate on the flag, not waiters_: reading the non-atomic list  
419 - // here would race a concurrent drain. A false flag is safe to  
420 - // trust pre-lock: wait() stores it true before publishing, and  
421 - // it is cleared only under the mutex when the waiter list is  
422 - // empty, so false implies no published waiters.  
423 - bool in_heap =  
424 - (impl.heap_index_.load(std::memory_order_relaxed) !=  
DUB 425 - (std::numeric_limits<std::size_t>::max)());  
DUB 426 - if (!in_heap &&  
DUB 427 - !impl.might_have_pending_waits_.load(std::memory_order_relaxed))  
DUB 428 - return 0;  
DUB 429 -  
430 - bool notify = false;  
DUB 431 - intrusive_list<waiter_node> canceled;  
DUB 432 -  
433 - {  
434 - std::lock_guard lock(mutex_);  
DUB 435 -  
436 - while (auto* w = impl.waiters_.pop_front())  
DUB 437 - {  
438 - w->impl_ = nullptr;  
DUB 439 - canceled.push_back(w);  
DUB 440 - }  
DUB 441 -  
442 - std::size_t idx = impl.heap_index_.load(std::memory_order_relaxed);  
DUB 443 - if (idx < heap_.size())  
DUB 444 - {  
445 - time_point old_time = heap_[idx].time_;  
DUB 446 - heap_[idx].time_ = new_time;  
DUB 447 -  
448 - if (new_time < old_time)  
DUB 449 - up_heap(idx);  
DUB 450 - else  
451 - down_heap(idx);  
DUB 452 -  
453 - notify =  
DUB 454 - (impl.heap_index_.load(std::memory_order_relaxed) == 0);  
DUB 455 - }  
456 -  
457 - refresh_cached_nearest();  
DUB 458 - }  
DUB 459 -  
460 - std::size_t count = 0;  
DUB 461 - while (auto* w = canceled.pop_front())  
DUB 462 - {  
463 - w->ec_ = make_error_code(capy::error::canceled);  
DUB 464 - sched_->post(&w->op_);  
DUB 465 - ++count;  
DUB 466 - }  
DUB 467 -  
468 - if (notify)  
DUB 469 - on_earliest_changed_();  
DUB 470 -  
471 - return count;  
DUB 472 - }  
473 -  
474   412  
475   inline void 413   inline void
HITCBC 476   6067 timer_service::insert_waiter(timer::implementation& impl, waiter_node* w) 414   9998 timer_service::insert_waiter(timer::implementation& impl, waiter_node* w)
477   { 415   {
HITCBC 478   6067 bool notify = false; 416   9998 bool notify = false;
HITCBC 479   6067 bool lost_cancel = false; 417   9998 bool lost_cancel = false;
480   { 418   {
HITCBC 481   6067 std::lock_guard lock(mutex_); 419   9998 std::lock_guard lock(mutex_);
  420 + // Grow before publishing anything, so the push_back below
  421 + // cannot throw: a failure here leaves the waiter untouched,
  422 + // the strong guarantee rearm_wait's recovery relies on.
HITGNC   423 + 9998 if (impl.heap_index_.load(std::memory_order_relaxed) ==
HITGNC   424 + 19996 (std::numeric_limits<std::size_t>::max)() &&
HITGNC   425 + 9998 heap_.size() == heap_.capacity())
HITGNC   426 + 245 heap_.reserve(
HITGNC   427 + 245 heap_.capacity() == 0 ? 16 : 2 * heap_.capacity());
482   // Publish: from here the waiter is visible to the fire path and 428   // Publish: from here the waiter is visible to the fire path and
483   // to its own stop callback (impl_ non-null enables cancel_waiter). 429   // to its own stop callback (impl_ non-null enables cancel_waiter).
HITCBC 484   6067 w->impl_ = &impl; 430   9998 w->impl_ = &impl;
HITCBC 485   12134 if (impl.heap_index_.load(std::memory_order_relaxed) == 431   19996 if (impl.heap_index_.load(std::memory_order_relaxed) ==
HITCBC 486   6067 (std::numeric_limits<std::size_t>::max)()) 432   9998 (std::numeric_limits<std::size_t>::max)())
487   { 433   {
HITCBC 488   6067 impl.heap_index_.store(heap_.size(), std::memory_order_relaxed); 434   9998 impl.heap_index_.store(heap_.size(), std::memory_order_relaxed);
HITCBC 489   6067 heap_.push_back({impl.expiry_, &impl}); 435   9998 heap_.push_back({impl.expiry_, &impl});
HITCBC 490   6067 up_heap(heap_.size() - 1); 436   9998 up_heap(heap_.size() - 1);
HITCBC 491   6067 notify = 437   9998 notify =
HITCBC 492   6067 (impl.heap_index_.load(std::memory_order_relaxed) == 0); 438   9998 (impl.heap_index_.load(std::memory_order_relaxed) == 0);
HITCBC 493   6067 refresh_cached_nearest(); 439   9998 refresh_cached_nearest();
494   } 440   }
HITCBC 495 - 6067 impl.waiters_.push_back(w); 441 + 9998 BOOST_COROSIO_ASSERT(impl.waiter_ == nullptr);
HITGNC   442 + 9998 impl.waiter_ = w;
496   443  
497   // Lost-cancel re-check: a stop requested after the canceller was 444   // Lost-cancel re-check: a stop requested after the canceller was
498   // armed in wait() but before this publication found impl_ null 445   // armed in wait() but before this publication found impl_ null
499   // and returned a no-op. Observe it now and undo the insertion. 446   // and returned a no-op. Observe it now and undo the insertion.
HITCBC 500   6067 if (w->token_->stop_requested()) 447   9998 if (w->token_->stop_requested())
501   { 448   {
MISLBC 502 - 1 w->impl_ = nullptr; 449 + w->impl_ = nullptr;
MISLBC 503 - 1 impl.waiters_.remove(w); 450 + impl.waiter_ = nullptr;
MISLBC 504 - 1 if (impl.waiters_.empty()) 451 + remove_timer_impl(impl);
MISUIC 505 - { 452 + impl.might_have_pending_waits_.store(
ECB 506 - 1 remove_timer_impl(impl); 453 + false, std::memory_order_relaxed);
DCB 507 - 1 impl.might_have_pending_waits_.store(  
508 - false, std::memory_order_relaxed);  
509 - }  
MISLBC 510   1 refresh_cached_nearest(); 454   refresh_cached_nearest();
MISLBC 511   1 lost_cancel = true; 455   lost_cancel = true;
MISLBC 512   1 notify = false; // insertion undone; nearest unchanged 456   notify = false; // insertion undone; nearest unchanged
513   } 457   }
HITCBC 514   6067 } 458   9998 }
HITCBC 515   6067 if (notify) 459   9998 if (notify)
HITCBC 516   5939 on_earliest_changed_(); 460   9873 on_earliest_changed_();
HITCBC 517   6067 if (lost_cancel) 461   9998 if (lost_cancel)
518   { 462   {
MISLBC 519   1 w->ec_ = make_error_code(capy::error::canceled); 463   w->ec_ = make_error_code(capy::error::canceled);
MISLBC 520   1 sched_->post(&w->op_); 464   sched_->post(&w->op_);
521   } 465   }
HITCBC 522   6067 } 466   9998 }
523   467  
524 - inline std::size_t 468 + inline void
HITCBC 525   6888 timer_service::cancel_timer(timer::implementation& impl) 469   10825 timer_service::cancel_timer(timer::implementation& impl)
526   { 470   {
HITCBC 527   6888 if (!impl.might_have_pending_waits_.load(std::memory_order_relaxed)) 471   10825 if (!impl.might_have_pending_waits_.load(std::memory_order_relaxed))
HITCBC 528 - 6886 return 0; 472 + 10823 return;
529   473  
530   // No unlocked already-done fast-out here: it would need the 474   // No unlocked already-done fast-out here: it would need the
531 - // non-atomic waiters_ (a race with concurrent drains), and an 475 + // non-atomic waiter_ (a race with concurrent drains), and an
532   // index-only check is lifetime-unsafe because npos is stored 476   // index-only check is lifetime-unsafe because npos is stored
533   // before the drain finishes touching the impl. A stale-true 477   // before the drain finishes touching the impl. A stale-true
534   // flag is rare with the stateless API; the locked path below 478   // flag is rare with the stateless API; the locked path below
535   // re-validates. 479   // re-validates.
536   480  
HITCBC 537 - 2 intrusive_list<waiter_node> canceled; 481 + 2 waiter_node* canceled = nullptr;
538   482  
539   { 483   {
HITCBC 540   2 std::lock_guard lock(mutex_); 484   2 std::lock_guard lock(mutex_);
HITCBC 541   2 remove_timer_impl(impl); 485   2 remove_timer_impl(impl);
HITCBC 542 - 4 while (auto* w = impl.waiters_.pop_front()) 486 + 2 canceled = std::exchange(impl.waiter_, nullptr);
HITGIC 543 - { 487 + 2 if (canceled)
HITCBC 544 - 2 w->impl_ = nullptr; 488 + 2 canceled->impl_ = nullptr;
DCB 545 - 2 canceled.push_back(w);  
DCB 546 - 2 }  
547   // Store false as the final touch of the impl under the lock so 489   // Store false as the final touch of the impl under the lock so
548 - // update_timer's pre-lock false-flag trust holds unqualified. 490 + // a pre-lock false-flag check trusts it unqualified.
HITCBC 549   2 impl.might_have_pending_waits_.store(false, std::memory_order_relaxed); 491   2 impl.might_have_pending_waits_.store(false, std::memory_order_relaxed);
HITCBC 550   2 refresh_cached_nearest(); 492   2 refresh_cached_nearest();
HITCBC 551   2 } 493   2 }
552   494  
HITCBC 553 - 2 std::size_t count = 0; 495 + 2 if (canceled)
DCB 554 - 4 while (auto* w = canceled.pop_front())  
555   { 496   {
HITCBC 556 - 2 w->ec_ = make_error_code(capy::error::canceled); 497 + 2 canceled->ec_ = make_error_code(capy::error::canceled);
HITCBC 557 - 2 sched_->post(&w->op_); 498 + 2 sched_->post(&canceled->op_);
DCB 558 - 2 ++count;  
DCB 559 - 2  
560 - return count;  
ECB 561   2 } 499   }
562   } 500   }
563   501  
564   inline void 502   inline void
HITCBC 565   1398 timer_service::cancel_waiter(waiter_node* w) 503   1394 timer_service::cancel_waiter(waiter_node* w)
566   { 504   {
567   { 505   {
HITCBC 568   1398 std::lock_guard lock(mutex_); 506   1394 std::lock_guard lock(mutex_);
569   // Already removed by another drain: cancel_timer, 507   // Already removed by another drain: cancel_timer,
570 - // cancel_one_waiter, update_timer, process_expired, or 508 + // process_expired, or insert_waiter's lost-cancel recheck
571 - // insert_waiter's lost-cancel recheck  
HITCBC 572   1398 if (!w->impl_) 509   1394 if (!w->impl_)
MISLBC 573   2 return; 510   return;
HITCBC 574 - 1396 auto* impl = w->impl_; 511 + 1394 auto* impl = w->impl_;
HITCBC 575 - 1396 w->impl_ = nullptr; 512 + 1394 w->impl_ = nullptr;
HITCBC 576 - 1396 impl->waiters_.remove(w); 513 + 1394 impl->waiter_ = nullptr;
HITCBC 577 - 1396 if (impl->waiters_.empty()) 514 + 1394 remove_timer_impl(*impl);
HITGIC 578 - { 515 + 1394 impl->might_have_pending_waits_.store(
ECB 579 - 1396 remove_timer_impl(*impl); 516 + false, std::memory_order_relaxed);
DCB 580 - 1396 impl->might_have_pending_waits_.store(  
581 - false, std::memory_order_relaxed);  
582 - }  
DCB 583 - 1396 refresh_cached_nearest();  
DCB 584 - 1398 }  
585 -  
DCB 586 - 1396 w->ec_ = make_error_code(capy::error::canceled);  
DCB 587 - 1396 sched_->post(&w->op_);  
588 - }  
589 -  
590 - inline std::size_t  
DUB 591 - timer_service::cancel_one_waiter(timer::implementation& impl)  
592 - {  
DUB 593 - if (!impl.might_have_pending_waits_.load(std::memory_order_relaxed))  
DUB 594 - return 0;  
595 -  
DUB 596 - waiter_node* w = nullptr;  
597 -  
598 - {  
DUB 599 - std::lock_guard lock(mutex_);  
DUB 600 - w = impl.waiters_.pop_front();  
DUB 601 - if (!w)  
DUB 602 - return 0;  
DUB 603 - w->impl_ = nullptr;  
DUB 604 - if (impl.waiters_.empty())  
605 - {  
DUB 606 - remove_timer_impl(impl);  
DUB 607 - impl.might_have_pending_waits_.store(  
608 - false, std::memory_order_relaxed);  
609 - }  
HITGBC 610   refresh_cached_nearest(); 517   1394 refresh_cached_nearest();
HITGBC 611   } 518   1394 }
612   519  
HITGBC 613   w->ec_ = make_error_code(capy::error::canceled); 520   1394 w->ec_ = make_error_code(capy::error::canceled);
DUB 614 - return 1;  
HITGBC 615   sched_->post(&w->op_); 521   1394 sched_->post(&w->op_);
616   } 522   }
617   523  
618   inline std::size_t 524   inline std::size_t
HITCBC 619   214553 timer_service::process_expired() 525   322190 timer_service::process_expired()
620   { 526   {
HITCBC 621   214553 intrusive_list<waiter_node> expired; 527   322190 intrusive_list<waiter_node> expired;
622   528  
623   { 529   {
HITCBC 624   214553 std::lock_guard lock(mutex_); 530   322190 std::lock_guard lock(mutex_);
HITCBC 625   214553 auto now = clock_type::now(); 531   322190 auto now = clock_type::now();
626   532  
HITCBC 627   219195 while (!heap_.empty() && heap_[0].time_ <= now) 533   330764 while (!heap_.empty() && heap_[0].time_ <= now)
628   { 534   {
HITCBC 629   4642 timer::implementation* t = heap_[0].timer_; 535   8574 timer::implementation* t = heap_[0].timer_;
HITCBC 630   4642 remove_timer_impl(*t); 536   8574 remove_timer_impl(*t);
HITCBC 631 - 9284 while (auto* w = t->waiters_.pop_front()) 537 + 8574 if (auto* w = std::exchange(t->waiter_, nullptr))
632   { 538   {
HITCBC 633   4642 w->impl_ = nullptr; 539   8574 w->impl_ = nullptr;
HITCBC 634   4642 w->ec_ = {}; 540   8574 w->ec_ = {};
HITCBC 635   4642 expired.push_back(w); 541   8574 expired.push_back(w);
ECB 636   4642 } 542   }
HITCBC 637   4642 t->might_have_pending_waits_.store( 543   8574 t->might_have_pending_waits_.store(
638   false, std::memory_order_relaxed); 544   false, std::memory_order_relaxed);
639   } 545   }
640   546  
HITCBC 641   214553 refresh_cached_nearest(); 547   322190 refresh_cached_nearest();
HITCBC 642   214553 } 548   322190 }
643   549  
HITCBC 644   214553 std::size_t count = 0; 550   322190 std::size_t count = 0;
HITCBC 645   219195 while (auto* w = expired.pop_front()) 551   330764 while (auto* w = expired.pop_front())
646   { 552   {
HITCBC 647   4642 sched_->post(&w->op_); 553   8574 sched_->post(&w->op_);
HITCBC 648   4642 ++count; 554   8574 ++count;
HITCBC 649   4642 } 555   8574 }
650   556  
HITCBC 651   214553 return count; 557   322190 return count;
652   } 558   }
653   559  
654   inline void 560   inline void
HITCBC 655   6041 timer_service::remove_timer_impl(timer::implementation& impl) 561   9970 timer_service::remove_timer_impl(timer::implementation& impl)
656   { 562   {
HITCBC 657   6041 std::size_t index = impl.heap_index_.load(std::memory_order_relaxed); 563   9970 std::size_t index = impl.heap_index_.load(std::memory_order_relaxed);
HITCBC 658   6041 if (index >= heap_.size()) 564   9970 if (index >= heap_.size())
MISUBC 659   return; // Not in heap 565   return; // Not in heap
660   566  
HITCBC 661   6041 if (index == heap_.size() - 1) 567   9970 if (index == heap_.size() - 1)
662   { 568   {
663   // Last element, just pop 569   // Last element, just pop
HITCBC 664   1645 impl.heap_index_.store( 570   1633 impl.heap_index_.store(
665   (std::numeric_limits<std::size_t>::max)(), 571   (std::numeric_limits<std::size_t>::max)(),
666   std::memory_order_relaxed); 572   std::memory_order_relaxed);
HITCBC 667   1645 heap_.pop_back(); 573   1633 heap_.pop_back();
668   } 574   }
669   else 575   else
670   { 576   {
671   // Swap with last and reheapify 577   // Swap with last and reheapify
HITCBC 672   4396 swap_heap(index, heap_.size() - 1); 578   8337 swap_heap(index, heap_.size() - 1);
HITCBC 673   4396 impl.heap_index_.store( 579   8337 impl.heap_index_.store(
674   (std::numeric_limits<std::size_t>::max)(), 580   (std::numeric_limits<std::size_t>::max)(),
675   std::memory_order_relaxed); 581   std::memory_order_relaxed);
HITCBC 676   4396 heap_.pop_back(); 582   8337 heap_.pop_back();
677   583  
HITCBC 678   4396 if (index > 0 && heap_[index].time_ < heap_[(index - 1) / 2].time_) 584   8337 if (index > 0 && heap_[index].time_ < heap_[(index - 1) / 2].time_)
MISUBC 679   up_heap(index); 585   up_heap(index);
680   else 586   else
HITCBC 681   4396 down_heap(index); 587   8337 down_heap(index);
682   } 588   }
683   } 589   }
684   590  
685   inline void 591   inline void
HITCBC 686   6067 timer_service::up_heap(std::size_t index) 592   9998 timer_service::up_heap(std::size_t index)
687   { 593   {
HITCBC 688   10436 while (index > 0) 594   18305 while (index > 0)
689   { 595   {
HITCBC 690   4496 std::size_t parent = (index - 1) / 2; 596   8432 std::size_t parent = (index - 1) / 2;
HITCBC 691   4496 if (!(heap_[index].time_ < heap_[parent].time_)) 597   8432 if (!(heap_[index].time_ < heap_[parent].time_))
HITCBC 692   127 break; 598   125 break;
HITCBC 693   4369 swap_heap(index, parent); 599   8307 swap_heap(index, parent);
HITCBC 694   4369 index = parent; 600   8307 index = parent;
695   } 601   }
HITCBC 696   6067 } 602   9998 }
697   603  
698   inline void 604   inline void
HITCBC 699   4396 timer_service::down_heap(std::size_t index) 605   8337 timer_service::down_heap(std::size_t index)
700   { 606   {
HITCBC 701   4396 std::size_t child = index * 2 + 1; 607   8337 std::size_t child = index * 2 + 1;
HITCBC 702   4398 while (child < heap_.size()) 608   8339 while (child < heap_.size())
703   { 609   {
HITCBC 704   4 std::size_t min_child = (child + 1 == heap_.size() || 610   4 std::size_t min_child = (child + 1 == heap_.size() ||
MISUBC 705   heap_[child].time_ < heap_[child + 1].time_) 611   heap_[child].time_ < heap_[child + 1].time_)
HITCBC 706   4 ? child 612   4 ? child
HITCBC 707   4 : child + 1; 613   4 : child + 1;
708   614  
HITCBC 709   4 if (heap_[index].time_ < heap_[min_child].time_) 615   4 if (heap_[index].time_ < heap_[min_child].time_)
HITCBC 710   2 break; 616   2 break;
711   617  
HITCBC 712   2 swap_heap(index, min_child); 618   2 swap_heap(index, min_child);
HITCBC 713   2 index = min_child; 619   2 index = min_child;
HITCBC 714   2 child = index * 2 + 1; 620   2 child = index * 2 + 1;
715   } 621   }
HITCBC 716   4396 } 622   8337 }
717   623  
718   inline void 624   inline void
HITCBC 719   8767 timer_service::swap_heap(std::size_t i1, std::size_t i2) 625   16646 timer_service::swap_heap(std::size_t i1, std::size_t i2)
720   { 626   {
HITCBC 721   8767 heap_entry tmp = heap_[i1]; 627   16646 heap_entry tmp = heap_[i1];
HITCBC 722   8767 heap_[i1] = heap_[i2]; 628   16646 heap_[i1] = heap_[i2];
HITCBC 723   8767 heap_[i2] = tmp; 629   16646 heap_[i2] = tmp;
HITCBC 724   8767 heap_[i1].timer_->heap_index_.store(i1, std::memory_order_relaxed); 630   16646 heap_[i1].timer_->heap_index_.store(i1, std::memory_order_relaxed);
HITCBC 725   8767 heap_[i2].timer_->heap_index_.store(i2, std::memory_order_relaxed); 631   16646 heap_[i2].timer_->heap_index_.store(i2, std::memory_order_relaxed);
HITCBC 726   8767 } 632   16646 }
727   633  
728   // waiter_node's completion_op and canceller members are defined in 634   // waiter_node's completion_op and canceller members are defined in
729   // timer.cpp alongside implementation::wait(), for the same reason 635   // timer.cpp alongside implementation::wait(), for the same reason
730   // wait() lives there (see below). 636   // wait() lives there (see below).
731   637  
732   // timer::implementation::wait() is defined in timer.cpp, not here. 638   // timer::implementation::wait() is defined in timer.cpp, not here.
733   // It must be a non-inline definition in a translation unit that is 639   // It must be a non-inline definition in a translation unit that is
734   // always pulled into the link whenever detail::timer is used (every 640   // always pulled into the link whenever detail::timer is used (every
735   // consumer needs timer's constructors from that same object file). 641   // consumer needs timer's constructors from that same object file).
736   // An inline definition in this header would only be emitted in 642   // An inline definition in this header would only be emitted in
737   // translation units that happen to also include this header, which 643   // translation units that happen to also include this header, which
738   // is not guaranteed for every caller of wait_awaitable::await_suspend 644   // is not guaranteed for every caller of wait_awaitable::await_suspend
739   // in timer.hpp (e.g. code that only reaches timer.hpp through 645   // in timer.hpp (e.g. code that only reaches timer.hpp through
740   // delay.hpp, without transitively including a scheduler header). 646   // delay.hpp, without transitively including a scheduler header).
741   647  
742 -  
743 - inline std::size_t  
744 - timer_service_update_expiry(timer::implementation& impl)  
DUB 745 - {  
746 - return impl.svc_->update_timer(impl, impl.expiry_);  
DUB 747 - }  
748 -  
749 - inline std::size_t  
750 - timer_service_cancel(timer::implementation& impl) noexcept  
DUB 751 - {  
752 - return impl.svc_->cancel_timer(impl);  
DUB 753 - }  
754 -  
755 - inline std::size_t  
756 - timer_service_cancel_one(timer::implementation& impl) noexcept  
DUB 757 - {  
758 - return impl.svc_->cancel_one_waiter(impl);  
DUB 759 - }  
760   // Free functions 648   // Free functions
761   649  
762   inline timer_service& 650   inline timer_service&
HITCBC 763   1382 get_timer_service(capy::execution_context& ctx, scheduler& sched) 651   1410 get_timer_service(capy::execution_context& ctx, scheduler& sched)
764   { 652   {
HITCBC 765   1382 return ctx.make_service<timer_service>(sched); 653   1410 return ctx.make_service<timer_service>(sched);
766   } 654   }
767   655  
768   } // namespace boost::corosio::detail 656   } // namespace boost::corosio::detail
769   657  
770   #endif 658   #endif