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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
|
//! [`SometimesUnboundedSink`]
use std::collections::VecDeque;
use std::pin::Pin;
use std::task::{Context, Poll, Poll::*, ready};
use futures::{Sink, future};
use pin_project::pin_project;
/// Wraps a [`Sink`], providing an only-sometimes-used unbounded buffer
///
/// For example, consider `SometimesUnboundedSink<T, mpsc::Receiver>`.
/// The `Receiver` is not always ready for writing:
/// if the capacity is exceeded, `send` will block.
///
/// `SometimesUnboundedSink`'s `Sink` implementation works the same way.
/// But there are also two methods
/// [`pollish_send_unbounded`](SometimesUnboundedSink::pollish_send_unbounded)
/// and
/// [`send_unbounded`](SometimesUnboundedSink::send_unbounded)
/// which will always succeed immediately.
/// Items which the underlying sink `S` is not ready to accept are queued,
/// and will be delivered to `S` when possible.
///
/// ### You must poll this type
///
/// For queued items to be delivered,
/// `SometimesUnboundedSink` must be polled,
/// even if you don't have an item to send.
///
/// You can use [`Sink::poll_ready`] for this.
/// Any [`Context`]-taking methods is suitable.
///
/// (This is a difference between [`SometimesUnboundedSink`]
/// and [`mpsc::UnboundedSender`](futures::channel::mpsc::UnboundedSender):
/// [`UnboundedSender::unbounded_send`](futures::channel::mpsc::UnboundedSender::unbounded_send)
/// does not require a flush operation.
/// In this way, [`SometimesUnboundedSink::send_unbounded`] behaves more like
/// [`Sink::start_send`], which _does_ require a subsequent flush.)
//
/// ### Error handling
///
/// Errors from the underlying sink may not be reported immediately,
/// due to the buffering in `SometimesUnboundedSink`.
///
/// However, if the sink reports errors from `poll_ready`
/// these will surface in a timely fashion.
///
/// After an error has been reported, there may still be buffered data,
/// which will only be delivered if `SometimesUnboundedSink` is polled again
/// (and the error in the underlying sink was transient).
#[pin_project]
pub(crate) struct SometimesUnboundedSink<T, S> {
/// Things we couldn't send_unbounded right away
///
/// Invariants:
///
/// * Everything here must be fed to `inner` before any further user data
/// (unbounded user data may be appended).
///
/// * If this is nonempty, the executor knows to wake this task.
/// This is achieved as follows:
/// If this is nonempty, `inner.poll_ready()` has been called.
buf: VecDeque<T>,
/// The actual sink
///
/// This also has the relevant `Waker`.
///
/// # Waker invariant
///
/// Whenever either
///
/// * The last call to any of our public methods returned `Pending`, or
/// * `buf` is nonempty,
///
/// the last method call `inner` *also* returned `Pending`.
/// (Or, we have reported an error.)
///
/// So, in those situations, this task has been recorded for wakeup
/// by `inner` (specifically, its other end, if it's a channel)
/// when `inner` becomes readable.
///
/// Therefore this task will be woken up, and, if the caller actually
/// polls us again (as is usual and is required by our docs),
/// we'll drain any queued data.
#[pin]
inner: S,
}
impl<T, S: Sink<T>> SometimesUnboundedSink<T, S> {
/// Wrap an inner `Sink` with a `SometimesUnboundedSink`
//
// There is no method for unwrapping. If we make this type more public,
// there should be, but that method will need `where S: Unpin`.
pub(crate) fn new(inner: S) -> Self {
SometimesUnboundedSink {
buf: VecDeque::new(),
inner,
}
}
/// Return the number of T queued in this sink.
pub(crate) fn n_queued(&self) -> usize {
self.buf.len()
}
/// Return an iterator over the items queued in this sink.
///
/// (Used by circuit padding to see whether we have a cell queued for a given hop.)
#[cfg(feature = "circ-padding")]
pub(crate) fn iter_queue(&self) -> impl Iterator<Item = &T> + '_ {
self.buf.iter()
}
/// Hand `item` to the inner Sink if possible, or queue it otherwise
///
/// Like a `poll_...` method in that it takes a `Context`.
/// That's needed to make sure we get polled again
/// when the underlying sink can accept items.
///
/// But unlike a `poll_...` method in that it doesn't return `Poll`,
/// since completion is always immediate.
pub(crate) fn pollish_send_unbounded(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
item: T,
) -> Result<(), S::Error> {
match self.as_mut().poll_ready(cx) {
// Waker invariant: poll_ready only returns Ready(Ok(())) if `buf` is empty
Ready(Ok(())) => self.as_mut().start_send(item),
// Waker invariant: if we report an error, we're then allowed to expect polling again
Ready(Err(e)) => Err(e),
Pending => {
// Waker invariant: poll_ready() returned Pending,
// so the task has indeed already been recorded.
self.as_mut().project().buf.push_back(item);
Ok(())
}
}
}
/// Hand `item` to the inner Sink if possible, or queue it otherwise (async fn)
///
/// You must `.await` this, but it will never block.
/// (Its future is always `Ready`.)
pub(crate) async fn send_unbounded(mut self: Pin<&mut Self>, item: T) -> Result<(), S::Error> {
// Waker invariant: this is just a wrapper around `pollish_send_unbounded`
let mut item = Some(item);
future::poll_fn(move |cx| {
let item = item.take().expect("polled after Ready");
Ready(self.as_mut().pollish_send_unbounded(cx, item))
})
.await
}
/// Flush the buffer. On a `Ready(())` return, it's empty.
///
/// This satisfies the Waker invariant as if it were a public method.
fn flush_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), S::Error>> {
let mut self_ = self.as_mut().project();
while !self_.buf.is_empty() {
// Waker invariant:
// if inner gave Pending, we give Pending too: ok
// if inner gave Err, we're allowed to want polling again
ready!(self_.inner.as_mut().poll_ready(cx))?;
let item = self_.buf.pop_front().expect("suddenly empty!");
// Waker invariant: returning Err
self_.inner.as_mut().start_send(item)?;
}
// Waker invariant: buffer is empty, and we're not about to return Pending
Ready(Ok(()))
}
/// Obtain a reference to the inner `Sink`, `S`
///
/// This method should be used with a little care, since it bypasses the wrapper.
/// For example, if `S` has interior mutability, and this method is used to
/// modify it, the `SometimesUnboundedSink` may malfunction.
pub(crate) fn as_inner(&self) -> &S {
&self.inner
}
/// Obtain a mutable reference to the inner `Sink`, `S`
///
/// This method should be used with extra care, since it bypasses the wrapper.
/// Before you call this method,
/// make sure you understand the internal invariants for `SometimesUnboundedSink`,
/// and make sure that you are not violating them.
/// In particular, do not queue anything onto the resulting `Sink` directly.
pub(crate) fn as_inner_mut(&mut self) -> &mut S {
&mut self.inner
}
}
// Waker invariant for all these impls:
// returning Err or Pending from flush_buf: OK, flush_buf ensures the condition holds
// returning from the inner method: trivially OK
impl<T, S: Sink<T>> Sink<T> for SometimesUnboundedSink<T, S> {
type Error = S::Error;
// Only returns `Ready(Ok(()))` if `buf` is empty
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), S::Error>> {
ready!(self.as_mut().flush_buf(cx))?;
self.project().inner.poll_ready(cx)
}
fn start_send(self: Pin<&mut Self>, item: T) -> Result<(), S::Error> {
assert!(self.buf.is_empty(), "start_send without poll_ready");
self.project().inner.start_send(item)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), S::Error>> {
ready!(self.as_mut().flush_buf(cx))?;
self.project().inner.poll_flush(cx)
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), S::Error>> {
ready!(self.as_mut().flush_buf(cx))?;
self.project().inner.poll_close(cx)
}
}
#[cfg(test)]
mod test {
// @@ begin test lint list maintained by maint/add_warning @@
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
#![allow(clippy::string_slice)] // See arti#2571
//! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
use super::*;
use futures::channel::mpsc;
use futures::{SinkExt as _, StreamExt as _};
use std::pin::pin;
use tor_rtmock::MockRuntime;
#[test]
fn cases() {
// `test_with_various` runs with both LIFO and FIFO scheduling policies,
// so should interleave the sending and receiving tasks
// in ways that exercise the corner cases we're interested in.
MockRuntime::test_with_various(|runtime| async move {
let (tx, rx) = mpsc::channel(1);
let tx = SometimesUnboundedSink::new(tx);
runtime.spawn_identified("sender", async move {
let mut tx = pin!(tx);
let mut n = 0..;
let mut n = move || n.next().unwrap();
// unbounded when we can send right away
tx.as_mut().send_unbounded(n()).await.unwrap();
tx.as_mut().send(n()).await.unwrap();
tx.as_mut().send(n()).await.unwrap();
tx.as_mut().send(n()).await.unwrap();
// unbounded when we maybe can't and might queue
tx.as_mut().send_unbounded(n()).await.unwrap();
tx.as_mut().send_unbounded(n()).await.unwrap();
tx.as_mut().send_unbounded(n()).await.unwrap();
// some interleaving
tx.as_mut().send(n()).await.unwrap();
tx.as_mut().send_unbounded(n()).await.unwrap();
// flush
tx.as_mut().flush().await.unwrap();
// close
tx.as_mut().close().await.unwrap();
});
runtime.spawn_identified("receiver", async move {
let mut rx = pin!(rx);
let mut exp = 0..;
while let Some(n) = rx.next().await {
assert_eq!(n, exp.next().unwrap());
}
assert_eq!(exp.next().unwrap(), 9);
});
runtime.progress_until_stalled().await;
});
}
}
|