Skip to content

Commit f90bed0

Browse files
authored
Change API to work with wasmtime-wasi (#467)
* Change API to work with wasmtime-wasi * Use 33.0.0 for Rust crate version
1 parent c3b9846 commit f90bed0

File tree

13 files changed

+269
-370
lines changed

13 files changed

+269
-370
lines changed

Cargo.lock

Lines changed: 3 additions & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/linking.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@
99
mod1 = Wasmtime::Module.from_file(engine, "examples/linking1.wat")
1010
mod2 = Wasmtime::Module.from_file(engine, "examples/linking2.wat")
1111

12-
wasi_ctx_builder = Wasmtime::WasiCtxBuilder.new
12+
wasi_config = Wasmtime::WasiConfig.new
1313
.inherit_stdin
1414
.inherit_stdout
15-
.build
1615

17-
store = Wasmtime::Store.new(engine, wasi_ctx: wasi_ctx_builder)
16+
store = Wasmtime::Store.new(engine, wasi_config: wasi_config)
1817

1918
# Instantiate `mod2` which only uses WASI, then register
2019
# that instance with the linker so `mod1` can use it.

examples/wasi.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55

66
linker = Wasmtime::Linker.new(engine, wasi: true)
77

8-
wasi_ctx = Wasmtime::WasiCtxBuilder.new
8+
wasi_config = Wasmtime::WasiConfig.new
99
.set_stdin_string("hi!")
1010
.inherit_stdout
1111
.inherit_stderr
1212
.set_argv(ARGV)
1313
.set_env(ENV)
14-
.build
15-
store = Wasmtime::Store.new(engine, wasi_ctx: wasi_ctx)
14+
store = Wasmtime::Store.new(engine, wasi_config: wasi_config)
1615

1716
instance = linker.instantiate(store, mod)
1817
instance.invoke("_start")

ext/Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "wasmtime-rb"
3-
version = "9.0.4"
3+
version = "33.0.0"
44
edition = "2021"
55
authors = ["The Wasmtime Project Developers"]
66
license = "Apache-2.0"
@@ -17,14 +17,15 @@ all-arch = ["wasmtime/all-arch"]
1717
winch = ["wasmtime/winch"]
1818

1919
[dependencies]
20+
async-trait = "*" # Needed for `OutputLimitedBuffer`. Use wasmtime's version.
21+
bytes = "*" # Needed for `OutputLimitedBuffer`. Use wasmtime's version.
2022
lazy_static = "1.5.0"
2123
magnus = { version = "0.7", features = ["rb-sys"] }
2224
rb-sys = { version = "*", default-features = false, features = [
2325
"stable-api-compiled-fallback",
2426
] }
2527
wasmtime = { version = "=33.0.0", features = ["memory-protection-keys"] }
2628
wasmtime-wasi = "=33.0.0"
27-
wasi-common = "=33.0.0"
2829
cap-std = "3.4.0"
2930
wat = "1.227.1"
3031
tokio = { version = "1.40.0", features = [
@@ -38,7 +39,7 @@ async-timer = { version = "1.0.0-beta.15", features = [
3839
], optional = true }
3940
static_assertions = "1.1.0"
4041
wasmtime-environ = "=33.0.0"
41-
deterministic-wasi-ctx = { version = "=1.2.1", features = ["wasi-common"] }
42+
deterministic-wasi-ctx = { version = "=1.2.1" }
4243

4344
[build-dependencies]
4445
rb-sys-env = "0.2.2"
Lines changed: 75 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,88 @@
1+
use bytes::Bytes;
12
use magnus::{
23
value::{InnerValue, Opaque, ReprValue},
34
RString, Ruby,
45
};
5-
use std::io;
6-
use std::io::ErrorKind;
6+
use std::io::Write;
7+
use std::sync::{Arc, Mutex};
8+
use wasmtime_wasi::p2::{OutputStream, Pollable, StdoutStream, StreamError, StreamResult};
79

810
/// A buffer that limits the number of bytes that can be written to it.
911
/// If the buffer is full, it will truncate the data.
10-
/// Is used in the buffer implementations of stdout and stderr in `WasiCtx` and `WasiCtxBuilder`.
12+
/// Is used in the buffer implementations of stdout and stderr in `WasiP1Ctx` and `WasiCtxBuilder`.
1113
pub struct OutputLimitedBuffer {
14+
inner: Arc<Mutex<OutputLimitedBufferInner>>,
15+
}
16+
17+
impl OutputLimitedBuffer {
18+
/// Creates a new [OutputLimitedBuffer] with the given underlying buffer
19+
/// and capacity.
20+
pub fn new(buffer: Opaque<RString>, capacity: usize) -> Self {
21+
Self {
22+
inner: Arc::new(Mutex::new(OutputLimitedBufferInner::new(buffer, capacity))),
23+
}
24+
}
25+
}
26+
27+
impl Clone for OutputLimitedBuffer {
28+
fn clone(&self) -> Self {
29+
Self {
30+
inner: Arc::clone(&self.inner),
31+
}
32+
}
33+
}
34+
35+
impl StdoutStream for OutputLimitedBuffer {
36+
fn stream(&self) -> Box<dyn OutputStream> {
37+
let cloned = self.clone();
38+
Box::new(cloned)
39+
}
40+
41+
fn isatty(&self) -> bool {
42+
false
43+
}
44+
}
45+
46+
#[async_trait::async_trait]
47+
impl Pollable for OutputLimitedBuffer {
48+
async fn ready(&mut self) {}
49+
}
50+
51+
impl OutputStream for OutputLimitedBuffer {
52+
fn write(&mut self, bytes: Bytes) -> StreamResult<()> {
53+
let mut stream = self.inner.lock().expect("Should be only writer");
54+
stream.write(&bytes)
55+
}
56+
57+
fn flush(&mut self) -> StreamResult<()> {
58+
Ok(())
59+
}
60+
61+
fn check_write(&mut self) -> StreamResult<usize> {
62+
let mut stream = self.inner.lock().expect("Should be only writer");
63+
stream.check_write()
64+
}
65+
}
66+
67+
struct OutputLimitedBufferInner {
1268
buffer: Opaque<RString>,
1369
/// The maximum number of bytes that can be written to the output stream buffer.
1470
capacity: usize,
1571
}
1672

17-
impl OutputLimitedBuffer {
73+
impl OutputLimitedBufferInner {
1874
#[must_use]
1975
pub fn new(buffer: Opaque<RString>, capacity: usize) -> Self {
2076
Self { buffer, capacity }
2177
}
2278
}
2379

24-
impl io::Write for OutputLimitedBuffer {
25-
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
80+
impl OutputLimitedBufferInner {
81+
fn check_write(&mut self) -> StreamResult<usize> {
82+
Ok(usize::MAX)
83+
}
84+
85+
fn write(&mut self, buf: &[u8]) -> StreamResult<()> {
2686
// Append a buffer to the string and truncate when hitting the capacity.
2787
// We return the input buffer size regardless of whether we truncated or not to avoid a panic.
2888
let ruby = Ruby::get().unwrap();
@@ -32,39 +92,28 @@ impl io::Write for OutputLimitedBuffer {
3292
// Handling frozen case here is necessary because magnus does not check if a string is frozen before writing to it.
3393
let is_frozen = inner_buffer.as_value().is_frozen();
3494
if is_frozen {
35-
return Err(io::Error::new(
36-
ErrorKind::WriteZero,
37-
"Cannot write to a frozen buffer.",
38-
));
95+
return Err(StreamError::trap("Cannot write to a frozen buffer."));
3996
}
4097

4198
if buf.is_empty() {
42-
return Ok(0);
99+
return Ok(());
43100
}
44101

45102
if inner_buffer
46103
.len()
47104
.checked_add(buf.len())
48105
.is_some_and(|val| val < self.capacity)
49106
{
50-
let amount_written = inner_buffer.write(buf)?;
51-
if amount_written < buf.len() {
52-
return Ok(amount_written);
53-
}
107+
inner_buffer
108+
.write(buf)
109+
.map_err(|e| StreamError::trap(&e.to_string()))?;
54110
} else {
55111
let portion = self.capacity - inner_buffer.len();
56-
let amount_written = inner_buffer.write(&buf[0..portion])?;
57-
if amount_written < portion {
58-
return Ok(amount_written);
59-
}
112+
inner_buffer
113+
.write(&buf[0..portion])
114+
.map_err(|e| StreamError::trap(&e.to_string()))?;
60115
};
61116

62-
Ok(buf.len())
63-
}
64-
65-
fn flush(&mut self) -> io::Result<()> {
66-
let ruby = Ruby::get().unwrap();
67-
68-
self.buffer.get_inner_with(&ruby).flush()
117+
Ok(())
69118
}
70119
}

ext/src/ruby_api/linker.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,8 @@ impl Linker {
5454

5555
let mut inner: LinkerImpl<StoreData> = LinkerImpl::new(engine.get());
5656
if wasi {
57-
wasi_common::sync::snapshots::preview_1::add_wasi_snapshot_preview1_to_linker(
58-
&mut inner,
59-
|s| s.wasi_ctx_mut(),
60-
)
61-
.map_err(|e| error!("{}", e))?
57+
wasmtime_wasi::preview1::add_to_linker_sync(&mut inner, |s| s.wasi_ctx_mut())
58+
.map_err(|e| error!("{}", e))?
6259
}
6360
Ok(Self {
6461
inner: RefCell::new(inner),
@@ -288,8 +285,8 @@ impl Linker {
288285
"Store is missing WASI configuration.\n\n\
289286
When using `wasi: true`, the Store given to\n\
290287
`Linker#instantiate` must have a WASI configuration.\n\
291-
To fix this, provide the `wasi_ctx` when creating the Store:\n\
292-
Wasmtime::Store.new(engine, wasi_ctx: WasiCtxBuilder.new)"
288+
To fix this, provide the `wasi_config` when creating the Store:\n\
289+
Wasmtime::Store.new(engine, wasi_config: WasiConfig.new)"
293290
);
294291
}
295292

@@ -316,6 +313,15 @@ impl Linker {
316313
.map(|func| Func::from_inner(store.into(), func))
317314
.map_err(|e| error!("{}", e))
318315
}
316+
317+
/// @yard
318+
/// Replaces the `poll_oneoff` and `sched_yield` function implementations
319+
/// with deterministic ones.
320+
/// @return [void]
321+
pub fn use_deterministic_scheduling_functions(&self) -> Result<(), Error> {
322+
let mut inner = self.inner.borrow_mut();
323+
deterministic_wasi_ctx::replace_scheduling_functions(&mut inner).map_err(|e| error!("{e}"))
324+
}
319325
}
320326

321327
pub fn init() -> Result<(), Error> {
@@ -339,6 +345,10 @@ pub fn init() -> Result<(), Error> {
339345
class.define_method("alias_module", method!(Linker::alias_module, 2))?;
340346
class.define_method("instantiate", method!(Linker::instantiate, 2))?;
341347
class.define_method("get_default", method!(Linker::get_default, 2))?;
348+
class.define_method(
349+
"use_deterministic_scheduling_functions",
350+
method!(Linker::use_deterministic_scheduling_functions, 0),
351+
)?;
342352

343353
Ok(())
344354
}

ext/src/ruby_api/mod.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ mod pooling_allocation_config;
2727
mod store;
2828
mod table;
2929
mod trap;
30-
mod wasi_ctx;
31-
mod wasi_ctx_builder;
30+
mod wasi_config;
3231

3332
pub use caller::Caller;
3433
pub use engine::Engine;
@@ -41,8 +40,7 @@ pub use params::Params;
4140
pub use pooling_allocation_config::PoolingAllocationConfig;
4241
pub use store::Store;
4342
pub use trap::Trap;
44-
pub use wasi_ctx::WasiCtx;
45-
pub use wasi_ctx_builder::WasiCtxBuilder;
43+
pub use wasi_config::WasiConfig;
4644

4745
/// The "Wasmtime" Ruby module.
4846
pub fn root() -> RModule {
@@ -85,10 +83,9 @@ pub fn init(ruby: &Ruby) -> Result<(), Error> {
8583
memory::init(ruby)?;
8684
linker::init()?;
8785
externals::init()?;
88-
wasi_ctx_builder::init()?;
86+
wasi_config::init()?;
8987
table::init()?;
9088
global::init()?;
91-
wasi_ctx::init()?;
9289
pooling_allocation_config::init()?;
9390
component::init(ruby)?;
9491

0 commit comments

Comments
 (0)