include/boost/corosio/native/detail/speculative_state.hpp
85.7% Lines (24/28)
85.7% List of functions (6/7)
Functions (7)
Function
Calls
Lines
Blocks
boost::corosio::detail::speculative_state::may_speculate_read() const
:55
287032x
100.0%
100.0%
boost::corosio::detail::speculative_state::may_speculate_write() const
:62
286923x
100.0%
100.0%
boost::corosio::detail::speculative_state::on_read_exhausted()
:70
22x
100.0%
67.0%
boost::corosio::detail::speculative_state::on_read_success()
:86
286790x
75.0%
30.0%
boost::corosio::detail::speculative_state::on_write_exhausted()
:93
0
0.0%
0.0%
boost::corosio::detail::speculative_state::on_async_read_ready()
:101
16970x
100.0%
100.0%
boost::corosio::detail::speculative_state::on_async_write_ready()
:108
16868x
100.0%
100.0%
| Line | TLA | Hits | Source Code |
|---|---|---|---|
| 1 | // | ||
| 2 | // Copyright (c) 2026 Steve Gerbino | ||
| 3 | // | ||
| 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
| 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
| 6 | // | ||
| 7 | // Official repository: https://github.com/cppalliance/corosio | ||
| 8 | // | ||
| 9 | |||
| 10 | #ifndef BOOST_COROSIO_NATIVE_DETAIL_SPECULATIVE_STATE_HPP | ||
| 11 | #define BOOST_COROSIO_NATIVE_DETAIL_SPECULATIVE_STATE_HPP | ||
| 12 | |||
| 13 | #include <atomic> | ||
| 14 | |||
| 15 | namespace boost::corosio::detail { | ||
| 16 | |||
| 17 | /** Per-socket per-op-type speculative-attempt hint. | ||
| 18 | |||
| 19 | Tracks whether a speculative non-blocking syscall is worth trying | ||
| 20 | for read and write paths. The flag is set false when speculation | ||
| 21 | discovers an exhausted buffer (EAGAIN) and restored when the async | ||
| 22 | completion path observes a kernel readiness signal. | ||
| 23 | |||
| 24 | Atomics are relaxed because the flag is a hint, not an invariant: | ||
| 25 | a stale read causes at most one wasted or skipped speculation, never | ||
| 26 | a correctness failure. | ||
| 27 | |||
| 28 | @par Thread Safety | ||
| 29 | Distinct objects: Safe. | ||
| 30 | Shared objects: Safe. | ||
| 31 | */ | ||
| 32 | class speculative_state | ||
| 33 | { | ||
| 34 | std::atomic< bool > try_read_ { true }; | ||
| 35 | std::atomic< bool > try_write_{ true }; | ||
| 36 | |||
| 37 | // Failure-streak counter for the read path. Increments on every | ||
| 38 | // speculative-read EAGAIN; resets to 0 whenever a speculative read | ||
| 39 | // succeeds. When it reaches max_read_failures the socket gives up | ||
| 40 | // on speculative reads permanently — perma_off_read_ latches and | ||
| 41 | // may_speculate_read() returns false regardless of any subsequent | ||
| 42 | // async-read re-arm signal. | ||
| 43 | // | ||
| 44 | // Distinguishes "transient EAGAIN under heavy success" (e.g. | ||
| 45 | // socket_throughput streaming: 1 EAGAIN per ~100 successes -> | ||
| 46 | // streak resets, perma-off never triggers) from "structural EAGAIN | ||
| 47 | // pattern" (e.g. fan_out:nested/16: every speculation EAGAINs -> | ||
| 48 | // streak hits max_read_failures and we stop wasting syscalls). | ||
| 49 | static constexpr int max_read_failures = 4; | ||
| 50 | std::atomic< int > read_eagain_streak_ { 0 }; | ||
| 51 | std::atomic< bool > perma_off_read_ { false }; | ||
| 52 | |||
| 53 | public: | ||
| 54 | /// Return true when speculative read is currently worth trying. | ||
| 55 | 287032x | bool may_speculate_read() const noexcept | |
| 56 | { | ||
| 57 | 287032x | return try_read_.load( std::memory_order_relaxed ) | |
| 58 | 287032x | && !perma_off_read_.load( std::memory_order_relaxed ); | |
| 59 | } | ||
| 60 | |||
| 61 | /// Return true when speculative write is currently worth trying. | ||
| 62 | 286923x | bool may_speculate_write() const noexcept | |
| 63 | { | ||
| 64 | 286923x | return try_write_.load( std::memory_order_relaxed ); | |
| 65 | } | ||
| 66 | |||
| 67 | /// Disable speculative reads (kernel buffer is empty). | ||
| 68 | /// Tracks the failure streak; permanently disables speculation | ||
| 69 | /// for this socket once the streak hits max_read_failures. | ||
| 70 | 22x | void on_read_exhausted() noexcept | |
| 71 | { | ||
| 72 | 22x | try_read_.store( false, std::memory_order_relaxed ); | |
| 73 | 22x | int s = read_eagain_streak_.load( std::memory_order_relaxed ); | |
| 74 | 22x | if ( s < max_read_failures ) | |
| 75 | { | ||
| 76 | 22x | ++s; | |
| 77 | 22x | read_eagain_streak_.store( s, std::memory_order_relaxed ); | |
| 78 | 22x | if ( s >= max_read_failures ) | |
| 79 | 2x | perma_off_read_.store( true, std::memory_order_relaxed ); | |
| 80 | } | ||
| 81 | 22x | } | |
| 82 | |||
| 83 | /// Reset the failure streak on a successful speculative read. The | ||
| 84 | /// successful syscall is proof that the workload pattern *does* | ||
| 85 | /// hit speculation often enough to be worth the occasional EAGAIN. | ||
| 86 | 286790x | void on_read_success() noexcept | |
| 87 | { | ||
| 88 | 573580x | if ( read_eagain_streak_.load( std::memory_order_relaxed ) != 0 ) | |
| 89 | ✗ | read_eagain_streak_.store( 0, std::memory_order_relaxed ); | |
| 90 | 286790x | } | |
| 91 | |||
| 92 | /// Disable speculative writes (kernel buffer is full). | ||
| 93 | ✗ | void on_write_exhausted() noexcept | |
| 94 | { | ||
| 95 | ✗ | try_write_.store( false, std::memory_order_relaxed ); | |
| 96 | ✗ | } | |
| 97 | |||
| 98 | /// Restore speculative reads (kernel signalled readiness via CQE). | ||
| 99 | /// If the socket has hit perma_off_read_ the re-arm is suppressed | ||
| 100 | /// — the strike-counter / perma-off latch overrides this signal. | ||
| 101 | 16970x | void on_async_read_ready() noexcept | |
| 102 | { | ||
| 103 | 16970x | if ( !perma_off_read_.load( std::memory_order_relaxed ) ) | |
| 104 | 16877x | try_read_.store( true, std::memory_order_relaxed ); | |
| 105 | 16970x | } | |
| 106 | |||
| 107 | /// Restore speculative writes (kernel signalled readiness via CQE). | ||
| 108 | 16868x | void on_async_write_ready() noexcept | |
| 109 | { | ||
| 110 | 16868x | try_write_.store( true, std::memory_order_relaxed ); | |
| 111 | 16868x | } | |
| 112 | }; | ||
| 113 | |||
| 114 | } // namespace boost::corosio::detail | ||
| 115 | |||
| 116 | #endif | ||
| 117 |