From 3150cf0bd30a98d51db94c1554f9ea071c121122 Mon Sep 17 00:00:00 2001 From: Steve Gerbino Date: Thu, 12 Feb 2026 20:44:45 +0100 Subject: [PATCH] Inline timer fast paths to eliminate cross-TU calls on common operations Move expiry_, heap_index_, and might_have_pending_waits_ from the private timer_impl to the public base class so cancel(), expires_at(), expires_after(), expiry(), and await_suspend can check for no-op conditions inline. Use time_point::min() as an already-expired sentinel to skip clock_gettime in expires_after() and wait() for zero-delay timers. --- include/boost/corosio/timer.hpp | 71 ++++++++++++++++++++++-- src/corosio/src/detail/timer_service.cpp | 24 +------- src/corosio/src/timer.cpp | 26 ++------- 3 files changed, 73 insertions(+), 48 deletions(-) diff --git a/include/boost/corosio/timer.hpp b/include/boost/corosio/timer.hpp index d9952b1c..1c06459b 100644 --- a/include/boost/corosio/timer.hpp +++ b/include/boost/corosio/timer.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace boost::corosio { @@ -79,13 +80,32 @@ class BOOST_COROSIO_DECL timer : public io_object capy::io_env const* env) -> std::coroutine_handle<> { token_ = env->stop_token; - return t_.get().wait(h, env->executor, token_, &ec_); + auto& impl = t_.get(); + // Inline fast path: already expired and not in the heap + if (impl.heap_index_ == timer_impl::npos && + (impl.expiry_ == (time_point::min)() || + impl.expiry_ <= clock_type::now())) + { + ec_ = {}; + auto d = env->executor; + d.post(h); + return std::noop_coroutine(); + } + return impl.wait( + h, env->executor, std::move(token_), &ec_); } }; public: struct timer_impl : io_object_impl { + static constexpr std::size_t npos = + (std::numeric_limits::max)(); + + std::chrono::steady_clock::time_point expiry_{}; + std::size_t heap_index_ = npos; + bool might_have_pending_waits_ = false; + virtual std::coroutine_handle<> wait( std::coroutine_handle<>, capy::executor_ref, @@ -167,7 +187,12 @@ class BOOST_COROSIO_DECL timer : public io_object @return The number of operations that were cancelled. */ - std::size_t cancel(); + std::size_t cancel() + { + if (!get().might_have_pending_waits_) + return 0; + return do_cancel(); + } /** Cancel one pending asynchronous wait operation. @@ -177,14 +202,22 @@ class BOOST_COROSIO_DECL timer : public io_object @return The number of operations that were cancelled (0 or 1). */ - std::size_t cancel_one(); + std::size_t cancel_one() + { + if (!get().might_have_pending_waits_) + return 0; + return do_cancel_one(); + } /** Return the timer's expiry time as an absolute time. @return The expiry time point. If no expiry has been set, returns a default-constructed time_point. */ - time_point expiry() const; + time_point expiry() const noexcept + { + return get().expiry_; + } /** Set the timer's expiry time as an absolute time. @@ -194,7 +227,15 @@ class BOOST_COROSIO_DECL timer : public io_object @return The number of pending operations that were cancelled. */ - std::size_t expires_at(time_point t); + std::size_t expires_at(time_point t) + { + auto& impl = get(); + impl.expiry_ = t; + if (impl.heap_index_ == timer_impl::npos && + !impl.might_have_pending_waits_) + return 0; + return do_update_expiry(); + } /** Set the timer's expiry time relative to now. @@ -204,7 +245,18 @@ class BOOST_COROSIO_DECL timer : public io_object @return The number of pending operations that were cancelled. */ - std::size_t expires_after(duration d); + std::size_t expires_after(duration d) + { + auto& impl = get(); + if (d <= duration::zero()) + impl.expiry_ = (time_point::min)(); + else + impl.expiry_ = clock_type::now() + d; + if (impl.heap_index_ == timer_impl::npos && + !impl.might_have_pending_waits_) + return 0; + return do_update_expiry(); + } /** Set the timer's expiry time relative to now. @@ -266,6 +318,13 @@ class BOOST_COROSIO_DECL timer : public io_object } private: + // Out-of-line cancel/expiry when inline fast-path + // conditions (no waiters, not in heap) are not met. + std::size_t do_cancel(); + std::size_t do_cancel_one(); + std::size_t do_update_expiry(); + + /// Return the underlying implementation. timer_impl& get() const noexcept { return *static_cast(impl_); diff --git a/src/corosio/src/detail/timer_service.cpp b/src/corosio/src/detail/timer_service.cpp index 1a611526..2d821390 100644 --- a/src/corosio/src/detail/timer_service.cpp +++ b/src/corosio/src/detail/timer_service.cpp @@ -172,10 +172,6 @@ struct timer_impl using duration = clock_type::duration; timer_service_impl* svc_ = nullptr; - time_point expiry_; - std::size_t heap_index_ = (std::numeric_limits::max)(); - // Lets cancel_timer() skip the lock when no wait() was ever issued - bool might_have_pending_waits_ = false; intrusive_list waiters_; // Free list linkage (reused when impl is on free_list) @@ -702,7 +698,8 @@ wait( // scheduler, allowing other queued work to run. if (heap_index_ == (std::numeric_limits::max)()) { - if (expiry_ <= clock_type::now()) + if (expiry_ == (time_point::min)() || + expiry_ <= clock_type::now()) { if (ec) *ec = {}; @@ -832,25 +829,10 @@ timer_service_destroy(timer::timer_impl& base) noexcept static_cast(base).release(); } -timer::time_point -timer_service_expiry(timer::timer_impl& base) noexcept -{ - return static_cast(base).expiry_; -} - -std::size_t -timer_service_expires_at(timer::timer_impl& base, timer::time_point t) -{ - auto& impl = static_cast(base); - impl.expiry_ = t; - return impl.svc_->update_timer(impl, t); -} - std::size_t -timer_service_expires_after(timer::timer_impl& base, timer::duration d) +timer_service_update_expiry(timer::timer_impl& base) { auto& impl = static_cast(base); - impl.expiry_ = timer::clock_type::now() + d; return impl.svc_->update_timer(impl, impl.expiry_); } diff --git a/src/corosio/src/timer.cpp b/src/corosio/src/timer.cpp index 68bd51a5..4ca70114 100644 --- a/src/corosio/src/timer.cpp +++ b/src/corosio/src/timer.cpp @@ -19,9 +19,7 @@ namespace detail { // Defined in timer_service.cpp extern timer::timer_impl* timer_service_create(capy::execution_context&); extern void timer_service_destroy(timer::timer_impl&) noexcept; -extern timer::time_point timer_service_expiry(timer::timer_impl&) noexcept; -extern std::size_t timer_service_expires_at(timer::timer_impl&, timer::time_point); -extern std::size_t timer_service_expires_after(timer::timer_impl&, timer::duration); +extern std::size_t timer_service_update_expiry(timer::timer_impl&); extern std::size_t timer_service_cancel(timer::timer_impl&) noexcept; extern std::size_t timer_service_cancel_one(timer::timer_impl&) noexcept; @@ -75,37 +73,23 @@ operator=(timer&& other) std::size_t timer:: -cancel() +do_cancel() { return detail::timer_service_cancel(get()); } std::size_t timer:: -cancel_one() +do_cancel_one() { return detail::timer_service_cancel_one(get()); } -timer::time_point -timer:: -expiry() const -{ - return detail::timer_service_expiry(get()); -} - -std::size_t -timer:: -expires_at(time_point t) -{ - return detail::timer_service_expires_at(get(), t); -} - std::size_t timer:: -expires_after(duration d) +do_update_expiry() { - return detail::timer_service_expires_after(get(), d); + return detail::timer_service_update_expiry(get()); } } // namespace boost::corosio