include/boost/corosio/native/detail/speculative_state.hpp

85.7% Lines (24/28) 85.7% List of functions (6/7)
speculative_state.hpp
f(x) Functions (7)
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