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