Skip to content

Commit f75d006

Browse files
abrownalexcrichton
andcommitted
review: work around fd_fdstat_set_flags
In order to make progress with wasi-threads, this change temporarily works around limitations induced by `wasi-common`'s `fd_fdstat_set_flags` to allow `&mut self` use in the implementation. Eventual resolution is tracked in #5643. This change makes several related helper functions (e.g., `set_fdflags`) take `&mut self` as well. Co-authored-by: Alex Crichton <[email protected]>
1 parent a718c5c commit f75d006

File tree

10 files changed

+103
-60
lines changed

10 files changed

+103
-60
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/wasi-common/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ tracing = { workspace = true }
2626
cap-std = { workspace = true }
2727
cap-rand = { workspace = true }
2828
bitflags = { workspace = true }
29+
log = { workspace = true }
2930

3031
[target.'cfg(unix)'.dependencies]
3132
rustix = { workspace = true, features = ["fs"] }

crates/wasi-common/cap-std-sync/src/file.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,15 @@ impl WasiFile for File {
9393
let fdflags = get_fd_flags(&*file)?;
9494
Ok(fdflags)
9595
}
96-
async fn set_fdflags(&self, fdflags: FdFlags) -> Result<(), Error> {
96+
async fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> {
9797
if fdflags.intersects(
9898
wasi_common::file::FdFlags::DSYNC
9999
| wasi_common::file::FdFlags::SYNC
100100
| wasi_common::file::FdFlags::RSYNC,
101101
) {
102102
return Err(Error::invalid_argument().context("cannot set DSYNC, SYNC, or RSYNC flag"));
103103
}
104-
let mut file = self.0.write().unwrap();
104+
let file = self.0.get_mut().unwrap();
105105
let set_fd_flags = (*file).new_set_fd_flags(to_sysif_fdflags(fdflags))?;
106106
(*file).set_fd_flags(set_fd_flags)?;
107107
Ok(())

crates/wasi-common/cap-std-sync/src/net.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ macro_rules! wasi_listen_write_impl {
9898
}
9999
async fn sock_accept(&self, fdflags: FdFlags) -> Result<Box<dyn WasiFile>, Error> {
100100
let (stream, _) = self.0.accept()?;
101-
let stream = <$stream>::from_cap_std(stream);
101+
let mut stream = <$stream>::from_cap_std(stream);
102102
stream.set_fdflags(fdflags).await?;
103103
Ok(Box::new(stream))
104104
}
@@ -110,7 +110,7 @@ macro_rules! wasi_listen_write_impl {
110110
let fdflags = get_fd_flags(&self.0)?;
111111
Ok(fdflags)
112112
}
113-
async fn set_fdflags(&self, fdflags: FdFlags) -> Result<(), Error> {
113+
async fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> {
114114
if fdflags == wasi_common::file::FdFlags::NONBLOCK {
115115
self.0.set_nonblocking(true)?;
116116
} else if fdflags.is_empty() {
@@ -197,7 +197,7 @@ macro_rules! wasi_stream_write_impl {
197197
let fdflags = get_fd_flags(&self.0)?;
198198
Ok(fdflags)
199199
}
200-
async fn set_fdflags(&self, fdflags: FdFlags) -> Result<(), Error> {
200+
async fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> {
201201
if fdflags == wasi_common::file::FdFlags::NONBLOCK {
202202
self.0.set_nonblocking(true)?;
203203
} else if fdflags.is_empty() {

crates/wasi-common/src/ctx.rs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,26 @@ use crate::clocks::WasiClocks;
22
use crate::dir::{DirCaps, DirEntry, WasiDir};
33
use crate::file::{FileCaps, FileEntry, WasiFile};
44
use crate::sched::WasiSched;
5-
use crate::string_array::{StringArray, StringArrayError};
5+
use crate::string_array::StringArray;
66
use crate::table::Table;
7-
use crate::Error;
7+
use crate::{Error, StringArrayError};
88
use cap_rand::RngCore;
9+
use std::ops::Deref;
910
use std::path::{Path, PathBuf};
10-
use std::sync::Arc;
11+
use std::sync::{Arc, Mutex};
1112

12-
pub struct WasiCtx {
13+
/// An `Arc`-wrapper around the wasi-common context to allow mutable access to
14+
/// the file descriptor table. This wrapper is only necessary due to the
15+
/// signature of `fd_fdstat_set_flags`; if that changes, there are a variety of
16+
/// improvements that can be made (TODO:
17+
/// https://github.com/bytecodealliance/wasmtime/issues/5643).
18+
#[derive(Clone)]
19+
pub struct WasiCtx(Arc<WasiCtxInner>);
20+
21+
pub struct WasiCtxInner {
1322
pub args: StringArray,
1423
pub env: StringArray,
15-
pub random: Box<dyn RngCore + Send + Sync>,
24+
pub random: Mutex<Box<dyn RngCore + Send + Sync>>,
1625
pub clocks: WasiClocks,
1726
pub sched: Box<dyn WasiSched>,
1827
pub table: Table,
@@ -25,14 +34,14 @@ impl WasiCtx {
2534
sched: Box<dyn WasiSched>,
2635
table: Table,
2736
) -> Self {
28-
let s = WasiCtx {
37+
let s = WasiCtx(Arc::new(WasiCtxInner {
2938
args: StringArray::new(),
3039
env: StringArray::new(),
31-
random,
40+
random: Mutex::new(random),
3241
clocks,
3342
sched,
3443
table,
35-
};
44+
}));
3645
s.set_stdin(Box::new(crate::pipe::ReadPipe::new(std::io::empty())));
3746
s.set_stdout(Box::new(crate::pipe::WritePipe::new(std::io::sink())));
3847
s.set_stderr(Box::new(crate::pipe::WritePipe::new(std::io::sink())));
@@ -77,12 +86,22 @@ impl WasiCtx {
7786
&self.table
7887
}
7988

89+
pub fn table_mut(&mut self) -> Option<&mut Table> {
90+
Arc::get_mut(&mut self.0).map(|c| &mut c.table)
91+
}
92+
8093
pub fn push_arg(&mut self, arg: &str) -> Result<(), StringArrayError> {
81-
self.args.push(arg.to_owned())
94+
let s = Arc::get_mut(&mut self.0).expect(
95+
"`push_arg` should only be used during initialization before the context is cloned",
96+
);
97+
s.args.push(arg.to_owned())
8298
}
8399

84100
pub fn push_env(&mut self, var: &str, value: &str) -> Result<(), StringArrayError> {
85-
self.env.push(format!("{}={}", var, value))?;
101+
let s = Arc::get_mut(&mut self.0).expect(
102+
"`push_env` should only be used during initialization before the context is cloned",
103+
);
104+
s.env.push(format!("{}={}", var, value))?;
86105
Ok(())
87106
}
88107

@@ -130,3 +149,10 @@ impl WasiCtx {
130149
Ok(())
131150
}
132151
}
152+
153+
impl Deref for WasiCtx {
154+
type Target = WasiCtxInner;
155+
fn deref(&self) -> &Self::Target {
156+
&self.0
157+
}
158+
}

crates/wasi-common/src/file.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ pub trait WasiFile: Send + Sync {
6464
Ok(FdFlags::empty())
6565
}
6666

67-
async fn set_fdflags(&self, _flags: FdFlags) -> Result<(), Error> {
67+
async fn set_fdflags(&mut self, _flags: FdFlags) -> Result<(), Error> {
6868
Err(Error::badf())
6969
}
7070

@@ -217,11 +217,15 @@ pub struct Filestat {
217217

218218
pub(crate) trait TableFileExt {
219219
fn get_file(&self, fd: u32) -> Result<Arc<FileEntry>, Error>;
220+
fn get_file_mut(&mut self, fd: u32) -> Result<&mut FileEntry, Error>;
220221
}
221222
impl TableFileExt for crate::table::Table {
222223
fn get_file(&self, fd: u32) -> Result<Arc<FileEntry>, Error> {
223224
self.get(fd)
224225
}
226+
fn get_file_mut(&mut self, fd: u32) -> Result<&mut FileEntry, Error> {
227+
self.get_mut(fd)
228+
}
225229
}
226230

227231
pub(crate) struct FileEntry {
@@ -272,13 +276,18 @@ impl FileEntry {
272276

273277
pub trait FileEntryExt {
274278
fn get_cap(&self, caps: FileCaps) -> Result<&dyn WasiFile, Error>;
279+
fn get_cap_mut(&mut self, caps: FileCaps) -> Result<&mut dyn WasiFile, Error>;
275280
}
276281

277282
impl FileEntryExt for FileEntry {
278283
fn get_cap(&self, caps: FileCaps) -> Result<&dyn WasiFile, Error> {
279284
self.capable_of(caps)?;
280285
Ok(&*self.file)
281286
}
287+
fn get_cap_mut(&mut self, caps: FileCaps) -> Result<&mut dyn WasiFile, Error> {
288+
self.capable_of(caps)?;
289+
Ok(&mut *self.file)
290+
}
282291
}
283292

284293
bitflags! {

crates/wasi-common/src/snapshots/preview_1.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,16 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
189189
fd: types::Fd,
190190
flags: types::Fdflags,
191191
) -> Result<(), Error> {
192-
self.table()
193-
.get_file(u32::from(fd))?
194-
.get_cap(FileCaps::FDSTAT_SET_FLAGS)?
195-
.set_fdflags(FdFlags::from(flags))
196-
.await
192+
if let Some(table) = self.table_mut() {
193+
table
194+
.get_file_mut(u32::from(fd))?
195+
.get_cap_mut(FileCaps::FDSTAT_SET_FLAGS)?
196+
.set_fdflags(FdFlags::from(flags))
197+
.await
198+
} else {
199+
log::warn!("`fd_fdstat_set_flags` does not work with wasi-threads enabled; see https://github.com/bytecodealliance/wasmtime/issues/5643");
200+
Err(Error::invalid_argument())
201+
}
197202
}
198203

199204
async fn fd_fdstat_set_rights(
@@ -1110,7 +1115,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
11101115
while copied < buf.len() {
11111116
let len = (buf.len() - copied).min(MAX_SHARED_BUFFER_SIZE as u32);
11121117
let mut tmp = vec![0; len as usize];
1113-
self.random.try_fill_bytes(&mut tmp)?;
1118+
self.random.lock().unwrap().try_fill_bytes(&mut tmp)?;
11141119
let dest = buf
11151120
.get_range(copied..copied + len)
11161121
.unwrap()
@@ -1122,7 +1127,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
11221127
// If the Wasm memory is non-shared, copy directly into the linear
11231128
// memory.
11241129
let mem = &mut buf.as_slice_mut()?.unwrap();
1125-
self.random.try_fill_bytes(mem)?;
1130+
self.random.lock().unwrap().try_fill_bytes(mem)?;
11261131
}
11271132
Ok(())
11281133
}

crates/wasi-common/src/table.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,22 @@ impl Table {
7676
}
7777
}
7878

79+
/// Get a mutable reference to a resource of a given type at a given index.
80+
/// Only one such reference can be borrowed at any given time.
81+
pub fn get_mut<T: Any>(&mut self, key: u32) -> Result<&mut T, Error> {
82+
let entry = match self.0.get_mut().unwrap().map.get_mut(&key) {
83+
Some(entry) => entry,
84+
None => return Err(Error::badf().context("key not in table")),
85+
};
86+
let entry = match Arc::get_mut(entry) {
87+
Some(entry) => entry,
88+
None => return Err(Error::badf().context("cannot mutably borrow shared file")),
89+
};
90+
entry
91+
.downcast_mut::<T>()
92+
.ok_or_else(|| Error::badf().context("element is a different type"))
93+
}
94+
7995
/// Remove a resource at a given index from the table. Returns the resource
8096
/// if it was present.
8197
pub fn delete<T: Any + Send + Sync>(&self, key: u32) -> Option<Arc<T>> {

crates/wasi-common/tokio/src/file.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ macro_rules! wasi_file_impl {
116116
async fn get_fdflags(&self) -> Result<FdFlags, Error> {
117117
block_on_dummy_executor(|| self.0.get_fdflags())
118118
}
119-
async fn set_fdflags(&self, fdflags: FdFlags) -> Result<(), Error> {
119+
async fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> {
120120
block_on_dummy_executor(|| self.0.set_fdflags(fdflags))
121121
}
122122
async fn get_filestat(&self) -> Result<Filestat, Error> {

0 commit comments

Comments
 (0)