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

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