Skip to content

Commit ff4e314

Browse files
committed
Use a Notify when ReadPipe is ready
1 parent 8ee7f35 commit ff4e314

File tree

1 file changed

+45
-14
lines changed

1 file changed

+45
-14
lines changed

crates/wasi/src/preview2/pipe.rs

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,14 @@ use system_interface::io::ReadReady;
3232
/// ```
3333
#[derive(Debug)]
3434
pub struct ReadPipe<R: Read + ReadReady> {
35+
notify: Arc<tokio::sync::Notify>,
3536
reader: Arc<RwLock<R>>,
3637
}
3738

3839
impl<R: Read + ReadReady> Clone for ReadPipe<R> {
3940
fn clone(&self) -> Self {
4041
Self {
42+
notify: self.notify.clone(),
4143
reader: self.reader.clone(),
4244
}
4345
}
@@ -55,7 +57,11 @@ impl<R: Read + ReadReady> ReadPipe<R> {
5557
///
5658
/// All `Handle` read operations delegate to reading from this underlying reader.
5759
pub fn from_shared(reader: Arc<RwLock<R>>) -> Self {
58-
Self { reader }
60+
Self {
61+
// TODO(elliottt): should the shared notify be an argument as well?
62+
notify: Arc::new(tokio::sync::Notify::new()),
63+
reader,
64+
}
5965
}
6066

6167
/// Try to convert this `ReadPipe<R>` back to the underlying `R` type.
@@ -119,25 +125,50 @@ impl<R: Read + ReadReady + Any + Send + Sync> HostInputStream for ReadPipe<R> {
119125
}
120126

121127
fn pollable(&self) -> HostPollable {
128+
// This is a standalone function because RwLockReadGuard does not implement Send -- calling
129+
// `reader.read()` from within the async closure below is just not possible.
130+
fn ready<T: Read + ReadReady + Any + Send + Sync>(reader: &RwLock<T>) -> bool {
131+
if let Ok(g) = reader.read() {
132+
if let Ok(n) = g.num_ready_bytes() {
133+
return n > 0;
134+
}
135+
}
136+
137+
// If either read or num_ready_bytes raised an error, we want to consider the pipe
138+
// ready for reading.
139+
true
140+
}
141+
142+
let notify = Arc::clone(&self.notify);
122143
let reader = Arc::clone(&self.reader);
123144
HostPollable::new(move || {
145+
// TODO(elliottt): is it possible to avoid these clones? They're needed because `Arc`
146+
// isn't copy, and we need to move values into the async closure.
147+
let notify = Arc::clone(&notify);
124148
let reader = Arc::clone(&reader);
125149
Box::pin(async move {
126-
loop {
127-
let amount = match reader.read() {
128-
Ok(g) => g.num_ready_bytes()?,
129-
Err(_) => {
130-
// TODO(elliottt): are there any circumstances where we want to clear
131-
// the poisoned state of the pipe?
132-
return Err(anyhow!("pipe has been poisoned"));
150+
{
151+
let reader = reader.clone();
152+
let sender = notify.clone();
153+
tokio::spawn(async move {
154+
while !ready(&reader) {
155+
tokio::task::yield_now().await;
133156
}
134-
};
135-
if amount > 0 {
136-
return Ok(());
137-
}
138157

139-
// TODO(elliottt): is there a better way to wait on the pipe to become ready?
140-
tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
158+
sender.notify_one();
159+
});
160+
}
161+
162+
notify.notified().await;
163+
164+
let g = match reader.read() {
165+
Ok(g) => g,
166+
Err(_) => return Err(anyhow!("pipe has been poisoned")),
167+
};
168+
169+
match g.num_ready_bytes() {
170+
Ok(_) => Ok(()),
171+
Err(e) => Err(anyhow!(e)),
141172
}
142173
})
143174
})

0 commit comments

Comments
 (0)