1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
//! RAII guard for protecting against partial I/O corruption in async contexts.
//!
//! # The Problem
//!
//! In async Rust, when a Future is dropped (e.g., due to timeout), execution stops
//! immediately. If this happens during a multi-step I/O operation like writing a
//! multipart ZMTP message, the underlying stream is left in an undefined state
//! with potentially half a frame written.
//!
//! # The Solution
//!
//! The `PoisonGuard` uses RAII to track whether a critical I/O section completed
//! successfully:
//!
//! 1. `PoisonGuard::new()` sets a flag to `true` (assume failure)
//! 2. If the Future is dropped before completion, the flag remains `true`
//! 3. Only by calling `disarm()` after successful I/O does the flag reset to `false`
//!
//! # Example
//!
//! ```rust
//! use monocoque_core::poison::PoisonGuard;
//!
//! struct MySocket {
//! is_poisoned: bool,
//! // ... other fields
//! }
//!
//! impl MySocket {
//! async fn send(&mut self, data: &[u8]) -> std::io::Result<()> {
//! // Check health before attempting I/O
//! if self.is_poisoned {
//! return Err(std::io::Error::new(
//! std::io::ErrorKind::BrokenPipe,
//! "Socket poisoned by cancelled I/O"
//! ));
//! }
//!
//! // Arm the guard - if dropped, socket remains poisoned
//! let guard = PoisonGuard::new(&mut self.is_poisoned);
//!
//! // Critical section: if this is cancelled, guard drops
//! // and socket remains poisoned
//! // ... perform I/O operations ...
//!
//! // Success! Disarm the guard
//! guard.disarm();
//! Ok(())
//! }
//! }
//! ```
//!
//! # When to Use
//!
//! Apply this to **every** function that performs non-atomic writes:
//! - Writing multipart messages
//! - Flushing buffered data
//! - Any write operation larger than MTU
//! - Sequential writes that form a logical unit
//!
//! # When NOT to Use
//!
//! Typically don't use for reads (they're usually idempotent), unless:
//! - Reading multipart data where internal state changes
//! - State transitions that can't be rolled back
//!
//! # Critical Rules
//!
//! 1. **Only disarm when the entire logical operation completes**
//! 2. **Never manually reset `is_poisoned` after an error**
//! 3. **Once poisoned, the connection must be dropped and reconnected**
/// A RAII guard that marks a connection as poisoned if dropped before disarmed.
///
/// This is a structural guarantee that protects protocol integrity when async
/// operations are cancelled (e.g., by timeouts).
///
/// # Safety
///
/// The guard must live across the entire critical section. Dropping it early
/// or disarming before all I/O completes defeats its purpose.