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

96.4% Lines (27/28) 100.0% List of functions (7/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 190749x bool may_speculate_read() const noexcept
56 {
57 190749x return try_read_.load( std::memory_order_relaxed )
58 190749x && !perma_off_read_.load( std::memory_order_relaxed );
59 }
60
61 /// Return true when speculative write is currently worth trying.
62 190675x bool may_speculate_write() const noexcept
63 {
64 190675x 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 44x void on_read_exhausted() noexcept
71 {
72 44x try_read_.store( false, std::memory_order_relaxed );
73 44x int s = read_eagain_streak_.load( std::memory_order_relaxed );
74 44x if ( s < max_read_failures )
75 {
76 44x ++s;
77 44x read_eagain_streak_.store( s, std::memory_order_relaxed );
78 44x if ( s >= max_read_failures )
79 4x perma_off_read_.store( true, std::memory_order_relaxed );
80 }
81 44x }
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 190430x void on_read_success() noexcept
87 {
88 380860x if ( read_eagain_streak_.load( std::memory_order_relaxed ) != 0 )
89 read_eagain_streak_.store( 0, std::memory_order_relaxed );
90 190430x }
91
92 /// Disable speculative writes (kernel buffer is full).
93 20x void on_write_exhausted() noexcept
94 {
95 20x try_write_.store( false, std::memory_order_relaxed );
96 20x }
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 11350x void on_async_read_ready() noexcept
102 {
103 11350x if ( !perma_off_read_.load( std::memory_order_relaxed ) )
104 11224x try_read_.store( true, std::memory_order_relaxed );
105 11350x }
106
107 /// Restore speculative writes (kernel signalled readiness via CQE).
108 11219x void on_async_write_ready() noexcept
109 {
110 11219x try_write_.store( true, std::memory_order_relaxed );
111 11219x }
112 };
113
114 } // namespace boost::corosio::detail
115
116 #endif
117