Browse Source
* better top matter * eliminate wasi-common deprecations from wasmtime-wasi * wasmtime-wasi: preview2 feature always enabled, so just eliminate it * rename preview1-on-preview2 feature to just preview1 * wasi-http fix * dep fixes. change some log::debug! to tracing::debug! * mv preview2 up to root and `sed -i 's/crate::preview2/crate/g' **/*.rs` * fix tests too * fixes throughout wasmtime-wasi-http * cli: s/wasmtime_wasi::preview2/wasmtime_wasi * rustfmt * fix wasi-async example * fix docs build (needs wasi-common built to compile examples) * c-api: use wasi-common directly i guess we didnt build the c-api in CI with deprecation warnings denied prtest:full * fix example required-features * fix examples CMakeLists buildpull/7661/head
Pat Hickey
9 months ago
committed by
GitHub
61 changed files with 689 additions and 840 deletions
@ -0,0 +1,122 @@ |
|||
use crate::WasiView; |
|||
|
|||
wasmtime::component::bindgen!({ |
|||
world: "wasi:cli/command", |
|||
tracing: true, |
|||
async: true, |
|||
with: { |
|||
"wasi:filesystem/types": crate::bindings::filesystem::types, |
|||
"wasi:filesystem/preopens": crate::bindings::filesystem::preopens, |
|||
"wasi:sockets/tcp": crate::bindings::sockets::tcp, |
|||
"wasi:clocks/monotonic_clock": crate::bindings::clocks::monotonic_clock, |
|||
"wasi:io/poll": crate::bindings::io::poll, |
|||
"wasi:io/streams": crate::bindings::io::streams, |
|||
"wasi:clocks/wall_clock": crate::bindings::clocks::wall_clock, |
|||
"wasi:random/random": crate::bindings::random::random, |
|||
"wasi:cli/environment": crate::bindings::cli::environment, |
|||
"wasi:cli/exit": crate::bindings::cli::exit, |
|||
"wasi:cli/stdin": crate::bindings::cli::stdin, |
|||
"wasi:cli/stdout": crate::bindings::cli::stdout, |
|||
"wasi:cli/stderr": crate::bindings::cli::stderr, |
|||
"wasi:cli/terminal-input": crate::bindings::cli::terminal_input, |
|||
"wasi:cli/terminal-output": crate::bindings::cli::terminal_output, |
|||
"wasi:cli/terminal-stdin": crate::bindings::cli::terminal_stdin, |
|||
"wasi:cli/terminal-stdout": crate::bindings::cli::terminal_stdout, |
|||
"wasi:cli/terminal-stderr": crate::bindings::cli::terminal_stderr, |
|||
}, |
|||
}); |
|||
|
|||
pub fn add_to_linker<T: WasiView>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Result<()> { |
|||
crate::bindings::clocks::wall_clock::add_to_linker(l, |t| t)?; |
|||
crate::bindings::clocks::monotonic_clock::add_to_linker(l, |t| t)?; |
|||
crate::bindings::filesystem::types::add_to_linker(l, |t| t)?; |
|||
crate::bindings::filesystem::preopens::add_to_linker(l, |t| t)?; |
|||
crate::bindings::io::error::add_to_linker(l, |t| t)?; |
|||
crate::bindings::io::poll::add_to_linker(l, |t| t)?; |
|||
crate::bindings::io::streams::add_to_linker(l, |t| t)?; |
|||
crate::bindings::random::random::add_to_linker(l, |t| t)?; |
|||
crate::bindings::random::insecure::add_to_linker(l, |t| t)?; |
|||
crate::bindings::random::insecure_seed::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::exit::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::environment::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::stdin::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::stdout::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::stderr::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::terminal_input::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::terminal_output::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::terminal_stdin::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::terminal_stdout::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::terminal_stderr::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sockets::tcp::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sockets::tcp_create_socket::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sockets::udp::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sockets::udp_create_socket::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sockets::instance_network::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sockets::network::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sockets::ip_name_lookup::add_to_linker(l, |t| t)?; |
|||
Ok(()) |
|||
} |
|||
|
|||
pub mod sync { |
|||
use crate::WasiView; |
|||
|
|||
wasmtime::component::bindgen!({ |
|||
world: "wasi:cli/command", |
|||
tracing: true, |
|||
async: false, |
|||
with: { |
|||
"wasi:filesystem/types": crate::bindings::sync_io::filesystem::types, |
|||
"wasi:filesystem/preopens": crate::bindings::filesystem::preopens, |
|||
"wasi:sockets/tcp": crate::bindings::sockets::tcp, |
|||
"wasi:sockets/udp": crate::bindings::sockets::udp, |
|||
"wasi:clocks/monotonic_clock": crate::bindings::clocks::monotonic_clock, |
|||
"wasi:io/poll": crate::bindings::sync_io::io::poll, |
|||
"wasi:io/streams": crate::bindings::sync_io::io::streams, |
|||
"wasi:clocks/wall_clock": crate::bindings::clocks::wall_clock, |
|||
"wasi:random/random": crate::bindings::random::random, |
|||
"wasi:cli/environment": crate::bindings::cli::environment, |
|||
"wasi:cli/exit": crate::bindings::cli::exit, |
|||
"wasi:cli/stdin": crate::bindings::cli::stdin, |
|||
"wasi:cli/stdout": crate::bindings::cli::stdout, |
|||
"wasi:cli/stderr": crate::bindings::cli::stderr, |
|||
"wasi:cli/terminal-input": crate::bindings::cli::terminal_input, |
|||
"wasi:cli/terminal-output": crate::bindings::cli::terminal_output, |
|||
"wasi:cli/terminal-stdin": crate::bindings::cli::terminal_stdin, |
|||
"wasi:cli/terminal-stdout": crate::bindings::cli::terminal_stdout, |
|||
"wasi:cli/terminal-stderr": crate::bindings::cli::terminal_stderr, |
|||
}, |
|||
}); |
|||
|
|||
pub fn add_to_linker<T: WasiView>( |
|||
l: &mut wasmtime::component::Linker<T>, |
|||
) -> anyhow::Result<()> { |
|||
crate::bindings::clocks::wall_clock::add_to_linker(l, |t| t)?; |
|||
crate::bindings::clocks::monotonic_clock::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sync_io::filesystem::types::add_to_linker(l, |t| t)?; |
|||
crate::bindings::filesystem::preopens::add_to_linker(l, |t| t)?; |
|||
crate::bindings::io::error::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sync_io::io::poll::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sync_io::io::streams::add_to_linker(l, |t| t)?; |
|||
crate::bindings::random::random::add_to_linker(l, |t| t)?; |
|||
crate::bindings::random::insecure::add_to_linker(l, |t| t)?; |
|||
crate::bindings::random::insecure_seed::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::exit::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::environment::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::stdin::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::stdout::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::stderr::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::terminal_input::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::terminal_output::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::terminal_stdin::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::terminal_stdout::add_to_linker(l, |t| t)?; |
|||
crate::bindings::cli::terminal_stderr::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sockets::tcp::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sockets::tcp_create_socket::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sockets::udp::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sockets::udp_create_socket::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sockets::instance_network::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sockets::network::add_to_linker(l, |t| t)?; |
|||
crate::bindings::sockets::ip_name_lookup::add_to_linker(l, |t| t)?; |
|||
Ok(()) |
|||
} |
|||
} |
@ -1,4 +1,4 @@ |
|||
use crate::preview2::{ |
|||
use crate::{ |
|||
clocks::{ |
|||
host::{monotonic_clock, wall_clock}, |
|||
HostMonotonicClock, HostWallClock, |
@ -1,11 +1,11 @@ |
|||
#![allow(unused_variables)] |
|||
|
|||
use crate::preview2::bindings::{ |
|||
use crate::bindings::{ |
|||
clocks::monotonic_clock::{self, Duration as WasiDuration, Instant}, |
|||
clocks::wall_clock::{self, Datetime}, |
|||
}; |
|||
use crate::preview2::poll::{subscribe, Subscribe}; |
|||
use crate::preview2::{Pollable, WasiView}; |
|||
use crate::poll::{subscribe, Subscribe}; |
|||
use crate::{Pollable, WasiView}; |
|||
use cap_std::time::SystemTime; |
|||
use std::time::Duration; |
|||
use wasmtime::component::Resource; |
@ -1,5 +1,5 @@ |
|||
use crate::preview2::bindings::cli::environment; |
|||
use crate::preview2::WasiView; |
|||
use crate::bindings::cli::environment; |
|||
use crate::WasiView; |
|||
|
|||
impl<T: WasiView> environment::Host for T { |
|||
fn get_environment(&mut self) -> anyhow::Result<Vec<(String, String)>> { |
@ -1,4 +1,4 @@ |
|||
use crate::preview2::{bindings::cli::exit, I32Exit, WasiView}; |
|||
use crate::{bindings::cli::exit, I32Exit, WasiView}; |
|||
|
|||
impl<T: WasiView> exit::Host for T { |
|||
fn exit(&mut self, status: Result<(), ()>) -> anyhow::Result<()> { |
@ -1,7 +1,7 @@ |
|||
use crate::preview2::bindings::filesystem::types as async_filesystem; |
|||
use crate::preview2::bindings::sync_io::filesystem::types as sync_filesystem; |
|||
use crate::preview2::bindings::sync_io::io::streams; |
|||
use crate::preview2::{in_tokio, FsError, FsResult}; |
|||
use crate::bindings::filesystem::types as async_filesystem; |
|||
use crate::bindings::sync_io::filesystem::types as sync_filesystem; |
|||
use crate::bindings::sync_io::io::streams; |
|||
use crate::{in_tokio, FsError, FsResult}; |
|||
use wasmtime::component::Resource; |
|||
|
|||
impl<T: async_filesystem::Host> sync_filesystem::Host for T { |
@ -1,6 +1,6 @@ |
|||
use crate::preview2::bindings::sockets::instance_network; |
|||
use crate::preview2::network::Network; |
|||
use crate::preview2::WasiView; |
|||
use crate::bindings::sockets::instance_network; |
|||
use crate::network::Network; |
|||
use crate::WasiView; |
|||
use wasmtime::component::Resource; |
|||
|
|||
impl<T: WasiView> instance_network::Host for T { |
@ -1,5 +1,5 @@ |
|||
use crate::preview2::bindings::random::{insecure, insecure_seed, random}; |
|||
use crate::preview2::WasiView; |
|||
use crate::bindings::random::{insecure, insecure_seed, random}; |
|||
use crate::WasiView; |
|||
use cap_rand::{distributions::Standard, Rng}; |
|||
|
|||
impl<T: WasiView> random::Host for T { |
@ -1,6 +1,6 @@ |
|||
use crate::preview2::bindings::{sockets::network::IpAddressFamily, sockets::tcp_create_socket}; |
|||
use crate::preview2::tcp::TcpSocket; |
|||
use crate::preview2::{SocketResult, WasiView}; |
|||
use crate::bindings::{sockets::network::IpAddressFamily, sockets::tcp_create_socket}; |
|||
use crate::tcp::TcpSocket; |
|||
use crate::{SocketResult, WasiView}; |
|||
use wasmtime::component::Resource; |
|||
|
|||
impl<T: WasiView> tcp_create_socket::Host for T { |
@ -1,6 +1,6 @@ |
|||
use crate::preview2::bindings::{sockets::network::IpAddressFamily, sockets::udp_create_socket}; |
|||
use crate::preview2::udp::UdpSocket; |
|||
use crate::preview2::{SocketResult, WasiView}; |
|||
use crate::bindings::{sockets::network::IpAddressFamily, sockets::udp_create_socket}; |
|||
use crate::udp::UdpSocket; |
|||
use crate::{SocketResult, WasiView}; |
|||
use wasmtime::component::Resource; |
|||
|
|||
impl<T: WasiView> udp_create_socket::Host for T { |
@ -1,103 +1,318 @@ |
|||
//! `wasmtime-wasi` now supports using multiple snapshots to interface to the
|
|||
//! same `WasiCtx`!
|
|||
//! # Wasmtime's WASI Implementation
|
|||
//!
|
|||
//! This crate provides a Wasmtime host implementation of WASI 0.2 (aka
|
|||
//! Preview 2), and a compatibility shim that provides an implementation of
|
|||
//! WASI 0.1 (aka Preview 1).
|
|||
//!
|
|||
//! `wasmtime_wasi::Wasi::new(&Store, WasiCtx)` is a struct which owns your
|
|||
//! `WasiCtx` and provides linkage to every available snapshot.
|
|||
//!
|
|||
//! Individual snapshots are available through
|
|||
//! `wasmtime_wasi::snapshots::preview_{0, 1}::Wasi::new(&Store, Rc<RefCell<WasiCtx>>)`.
|
|||
|
|||
#![warn(clippy::cast_sign_loss)] |
|||
use std::future::Future; |
|||
use std::pin::Pin; |
|||
use std::task::{Context, Poll}; |
|||
|
|||
#[cfg(feature = "preview2")] |
|||
pub mod preview2; |
|||
mod clocks; |
|||
pub mod command; |
|||
mod ctx; |
|||
mod error; |
|||
mod filesystem; |
|||
mod host; |
|||
mod ip_name_lookup; |
|||
mod network; |
|||
pub mod pipe; |
|||
mod poll; |
|||
#[cfg(feature = "preview1")] |
|||
pub mod preview0; |
|||
#[cfg(feature = "preview1")] |
|||
pub mod preview1; |
|||
mod random; |
|||
mod stdio; |
|||
mod stream; |
|||
mod tcp; |
|||
mod udp; |
|||
mod write_stream; |
|||
|
|||
#[cfg(feature = "wasi-common-deprecations")] |
|||
#[deprecated(
|
|||
since = "18.0.0", |
|||
note = "The wasmtime_wasi::sync module's functionalty has been moved to |
|||
wasi_common::sync. This re-export will be removed from wasmtime_wasi in |
|||
19.0" |
|||
)] |
|||
pub mod sync { |
|||
pub use wasi_common::sync::*; |
|||
} |
|||
pub use self::clocks::{HostMonotonicClock, HostWallClock}; |
|||
pub use self::ctx::{WasiCtx, WasiCtxBuilder, WasiView}; |
|||
pub use self::error::{I32Exit, TrappableError}; |
|||
pub use self::filesystem::{DirPerms, FilePerms, FsError, FsResult}; |
|||
pub use self::network::{Network, SocketError, SocketResult}; |
|||
pub use self::poll::{subscribe, ClosureFuture, MakeFuture, Pollable, PollableFuture, Subscribe}; |
|||
pub use self::random::{thread_rng, Deterministic}; |
|||
pub use self::stdio::{ |
|||
stderr, stdin, stdout, IsATTY, Stderr, Stdin, StdinStream, Stdout, StdoutStream, |
|||
}; |
|||
pub use self::stream::{ |
|||
HostInputStream, HostOutputStream, InputStream, OutputStream, StreamError, StreamResult, |
|||
}; |
|||
pub use cap_fs_ext::SystemTimeSpec; |
|||
pub use cap_rand::RngCore; |
|||
pub use wasmtime::component::{ResourceTable, ResourceTableError}; |
|||
|
|||
pub mod bindings { |
|||
// Generate traits for synchronous bindings.
|
|||
//
|
|||
// Note that this is only done for interfaces which can block, or those which
|
|||
// have some functions in `only_imports` below for being async.
|
|||
pub mod sync_io { |
|||
pub(crate) mod _internal { |
|||
use crate::{FsError, StreamError}; |
|||
|
|||
wasmtime::component::bindgen!({ |
|||
path: "wit", |
|||
interfaces: " |
|||
import wasi:io/poll@0.2.0; |
|||
import wasi:io/streams@0.2.0; |
|||
import wasi:filesystem/types@0.2.0; |
|||
", |
|||
tracing: true, |
|||
trappable_error_type: { |
|||
"wasi:io/streams/stream-error" => StreamError, |
|||
"wasi:filesystem/types/error-code" => FsError, |
|||
}, |
|||
with: { |
|||
"wasi:clocks/wall-clock": crate::bindings::clocks::wall_clock, |
|||
"wasi:filesystem/types/descriptor": super::super::filesystem::types::Descriptor, |
|||
"wasi:filesystem/types/directory-entry-stream": super::super::filesystem::types::DirectoryEntryStream, |
|||
"wasi:io/poll/pollable": super::super::io::poll::Pollable, |
|||
"wasi:io/streams/input-stream": super::super::io::streams::InputStream, |
|||
"wasi:io/streams/output-stream": super::super::io::streams::OutputStream, |
|||
"wasi:io/error/error": super::super::io::error::Error, |
|||
} |
|||
}); |
|||
} |
|||
pub use self::_internal::wasi::{filesystem, io}; |
|||
} |
|||
|
|||
#[cfg(feature = "wasi-common-deprecations")] |
|||
#[allow(deprecated)] // Satisfy linter locally
|
|||
#[deprecated(
|
|||
since = "18.0.0", |
|||
note = "The wasmtime_wasi module's root export of wasmtime_wasi::sync has |
|||
been moved to wasi_common::sync. This re-export will be removed from |
|||
wasmtime_wasi in 19.0" |
|||
)] |
|||
pub use sync::*; |
|||
wasmtime::component::bindgen!({ |
|||
path: "wit", |
|||
world: "wasi:cli/imports", |
|||
tracing: true, |
|||
async: { |
|||
// Only these functions are `async` and everything else is sync
|
|||
// meaning that it basically doesn't need to block. These functions
|
|||
// are the only ones that need to block.
|
|||
//
|
|||
// Note that at this time `only_imports` works on function names
|
|||
// which in theory can be shared across interfaces, so this may
|
|||
// need fancier syntax in the future.
|
|||
only_imports: [ |
|||
"[method]descriptor.access-at", |
|||
"[method]descriptor.advise", |
|||
"[method]descriptor.change-directory-permissions-at", |
|||
"[method]descriptor.change-file-permissions-at", |
|||
"[method]descriptor.create-directory-at", |
|||
"[method]descriptor.get-flags", |
|||
"[method]descriptor.get-type", |
|||
"[method]descriptor.is-same-object", |
|||
"[method]descriptor.link-at", |
|||
"[method]descriptor.lock-exclusive", |
|||
"[method]descriptor.lock-shared", |
|||
"[method]descriptor.metadata-hash", |
|||
"[method]descriptor.metadata-hash-at", |
|||
"[method]descriptor.open-at", |
|||
"[method]descriptor.read", |
|||
"[method]descriptor.read-directory", |
|||
"[method]descriptor.readlink-at", |
|||
"[method]descriptor.remove-directory-at", |
|||
"[method]descriptor.rename-at", |
|||
"[method]descriptor.set-size", |
|||
"[method]descriptor.set-times", |
|||
"[method]descriptor.set-times-at", |
|||
"[method]descriptor.stat", |
|||
"[method]descriptor.stat-at", |
|||
"[method]descriptor.symlink-at", |
|||
"[method]descriptor.sync", |
|||
"[method]descriptor.sync-data", |
|||
"[method]descriptor.try-lock-exclusive", |
|||
"[method]descriptor.try-lock-shared", |
|||
"[method]descriptor.unlink-file-at", |
|||
"[method]descriptor.unlock", |
|||
"[method]descriptor.write", |
|||
"[method]input-stream.read", |
|||
"[method]input-stream.blocking-read", |
|||
"[method]input-stream.blocking-skip", |
|||
"[method]input-stream.skip", |
|||
"[method]output-stream.forward", |
|||
"[method]output-stream.splice", |
|||
"[method]output-stream.blocking-splice", |
|||
"[method]output-stream.blocking-flush", |
|||
"[method]output-stream.blocking-write", |
|||
"[method]output-stream.blocking-write-and-flush", |
|||
"[method]output-stream.blocking-write-zeroes-and-flush", |
|||
"[method]directory-entry-stream.read-directory-entry", |
|||
"poll", |
|||
"[method]pollable.block", |
|||
"[method]pollable.ready", |
|||
], |
|||
}, |
|||
trappable_error_type: { |
|||
"wasi:io/streams/stream-error" => crate::StreamError, |
|||
"wasi:filesystem/types/error-code" => crate::FsError, |
|||
"wasi:sockets/network/error-code" => crate::SocketError, |
|||
}, |
|||
with: { |
|||
"wasi:sockets/network/network": super::network::Network, |
|||
"wasi:sockets/tcp/tcp-socket": super::tcp::TcpSocket, |
|||
"wasi:sockets/udp/udp-socket": super::udp::UdpSocket, |
|||
"wasi:sockets/udp/incoming-datagram-stream": super::udp::IncomingDatagramStream, |
|||
"wasi:sockets/udp/outgoing-datagram-stream": super::udp::OutgoingDatagramStream, |
|||
"wasi:sockets/ip-name-lookup/resolve-address-stream": super::ip_name_lookup::ResolveAddressStream, |
|||
"wasi:filesystem/types/directory-entry-stream": super::filesystem::ReaddirIterator, |
|||
"wasi:filesystem/types/descriptor": super::filesystem::Descriptor, |
|||
"wasi:io/streams/input-stream": super::stream::InputStream, |
|||
"wasi:io/streams/output-stream": super::stream::OutputStream, |
|||
"wasi:io/error/error": super::stream::Error, |
|||
"wasi:io/poll/pollable": super::poll::Pollable, |
|||
"wasi:cli/terminal-input/terminal-input": super::stdio::TerminalInput, |
|||
"wasi:cli/terminal-output/terminal-output": super::stdio::TerminalOutput, |
|||
}, |
|||
}); |
|||
|
|||
#[cfg(feature = "tokio")] |
|||
#[deprecated(
|
|||
since = "18.0.0", |
|||
note = "The wasmtime_wasi::tokio module's functionalty has been moved to |
|||
wasi_common::tokio. This re-export will be removed from wasmtime_wasi in |
|||
19.0" |
|||
)] |
|||
pub mod tokio { |
|||
pub use wasi_common::tokio::*; |
|||
pub use wasi::*; |
|||
} |
|||
|
|||
/// Exit the process with a conventional OS error code as long as Wasmtime
|
|||
/// understands the error. If the error is not an `I32Exit` or `Trap`, return
|
|||
/// the error back to the caller for it to decide what to do.
|
|||
///
|
|||
/// Note: this function is designed for usage where it is acceptable for
|
|||
/// Wasmtime failures to terminate the parent process, such as in the Wasmtime
|
|||
/// CLI; this would not be suitable for use in multi-tenant embeddings.
|
|||
#[cfg(feature = "exit")] |
|||
#[deprecated(
|
|||
since = "18.0.0", |
|||
note = "This legacy functionality is migrated to the wasi-common crate, and will be removed in 19.0." |
|||
)] |
|||
pub fn maybe_exit_on_error(e: anyhow::Error) -> anyhow::Error { |
|||
use std::process; |
|||
use wasmtime::Trap; |
|||
pub(crate) static RUNTIME: once_cell::sync::Lazy<tokio::runtime::Runtime> = |
|||
once_cell::sync::Lazy::new(|| { |
|||
tokio::runtime::Builder::new_current_thread() |
|||
.enable_time() |
|||
.enable_io() |
|||
.build() |
|||
.unwrap() |
|||
}); |
|||
|
|||
if let Some(exit) = e |
|||
.downcast_ref::<preview2::I32Exit>() |
|||
.map(|exit| exit.process_exit_code()) |
|||
{ |
|||
process::exit(exit) |
|||
pub struct AbortOnDropJoinHandle<T>(tokio::task::JoinHandle<T>); |
|||
impl<T> Drop for AbortOnDropJoinHandle<T> { |
|||
fn drop(&mut self) { |
|||
self.0.abort() |
|||
} |
|||
|
|||
// If a specific WASI error code was requested then that's
|
|||
// forwarded through to the process here without printing any
|
|||
// extra error information.
|
|||
let code = e.downcast_ref::<wasi_common::I32Exit>().map(|e| e.0); |
|||
if let Some(exit) = code { |
|||
// Print the error message in the usual way.
|
|||
// On Windows, exit status 3 indicates an abort (see below),
|
|||
// so return 1 indicating a non-zero status to avoid ambiguity.
|
|||
if cfg!(windows) && exit >= 3 { |
|||
process::exit(1); |
|||
} |
|||
impl<T> std::ops::Deref for AbortOnDropJoinHandle<T> { |
|||
type Target = tokio::task::JoinHandle<T>; |
|||
fn deref(&self) -> &Self::Target { |
|||
&self.0 |
|||
} |
|||
} |
|||
impl<T> std::ops::DerefMut for AbortOnDropJoinHandle<T> { |
|||
fn deref_mut(&mut self) -> &mut tokio::task::JoinHandle<T> { |
|||
&mut self.0 |
|||
} |
|||
} |
|||
impl<T> From<tokio::task::JoinHandle<T>> for AbortOnDropJoinHandle<T> { |
|||
fn from(jh: tokio::task::JoinHandle<T>) -> Self { |
|||
AbortOnDropJoinHandle(jh) |
|||
} |
|||
} |
|||
impl<T> Future for AbortOnDropJoinHandle<T> { |
|||
type Output = T; |
|||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
|||
match Pin::new(&mut self.as_mut().0).poll(cx) { |
|||
Poll::Pending => Poll::Pending, |
|||
Poll::Ready(r) => Poll::Ready(r.expect("child task panicked")), |
|||
} |
|||
process::exit(exit); |
|||
} |
|||
} |
|||
|
|||
pub fn spawn<F>(f: F) -> AbortOnDropJoinHandle<F::Output> |
|||
where |
|||
F: Future + Send + 'static, |
|||
F::Output: Send + 'static, |
|||
{ |
|||
let j = with_ambient_tokio_runtime(|| tokio::task::spawn(f)); |
|||
AbortOnDropJoinHandle(j) |
|||
} |
|||
|
|||
// If the program exited because of a trap, return an error code
|
|||
// to the outside environment indicating a more severe problem
|
|||
// than a simple failure.
|
|||
if e.is::<Trap>() { |
|||
eprintln!("Error: {:?}", e); |
|||
pub fn spawn_blocking<F, R>(f: F) -> AbortOnDropJoinHandle<R> |
|||
where |
|||
F: FnOnce() -> R + Send + 'static, |
|||
R: Send + 'static, |
|||
{ |
|||
let j = with_ambient_tokio_runtime(|| tokio::task::spawn_blocking(f)); |
|||
AbortOnDropJoinHandle(j) |
|||
} |
|||
|
|||
pub fn in_tokio<F: Future>(f: F) -> F::Output { |
|||
match tokio::runtime::Handle::try_current() { |
|||
Ok(h) => { |
|||
let _enter = h.enter(); |
|||
h.block_on(f) |
|||
} |
|||
// The `yield_now` here is non-obvious and if you're reading this
|
|||
// you're likely curious about why it's here. This is currently required
|
|||
// to get some features of "sync mode" working correctly, such as with
|
|||
// the CLI. To illustrate why this is required, consider a program
|
|||
// organized as:
|
|||
//
|
|||
// * A program has a `pollable` that it's waiting on.
|
|||
// * This `pollable` is always ready .
|
|||
// * Actually making the corresponding operation ready, however,
|
|||
// requires some background work on Tokio's part.
|
|||
// * The program is looping on "wait for readiness" coupled with
|
|||
// performing the operation.
|
|||
//
|
|||
// In this situation this program ends up infinitely looping in waiting
|
|||
// for pollables. The reason appears to be that when we enter the tokio
|
|||
// runtime here it doesn't necessary yield to background work because
|
|||
// the provided future `f` is ready immediately. The future `f` will run
|
|||
// through the list of pollables and determine one of them is ready.
|
|||
//
|
|||
// Historically this happened with UDP sockets. A test send a datagram
|
|||
// from one socket to another and the other socket infinitely didn't
|
|||
// receive the data. This appeared to be because the server socket was
|
|||
// waiting on `READABLE | WRITABLE` (which is itself a bug but ignore
|
|||
// that) and the socket was currently in the "writable" state but never
|
|||
// ended up receiving a notification for the "readable" state. Moving
|
|||
// the socket to "readable" would require Tokio to perform some
|
|||
// background work via epoll/kqueue/handle events but if the future
|
|||
// provided here is always ready, then that never happened.
|
|||
//
|
|||
// Thus the `yield_now()` is an attempt to force Tokio to go do some
|
|||
// background work eventually and look at new interest masks for
|
|||
// example. This is a bit of a kludge but everything's already a bit
|
|||
// wonky in synchronous mode anyway. Note that this is hypothesized to
|
|||
// not be an issue in async mode because async mode typically has the
|
|||
// Tokio runtime in a separate thread or otherwise participating in a
|
|||
// larger application, it's only here in synchronous mode where we
|
|||
// effectively own the runtime that we need some special care.
|
|||
Err(_) => { |
|||
let _enter = RUNTIME.enter(); |
|||
RUNTIME.block_on(async move { |
|||
tokio::task::yield_now().await; |
|||
f.await |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
|
|||
cfg_if::cfg_if! { |
|||
if #[cfg(unix)] { |
|||
// On Unix, return the error code of an abort.
|
|||
process::exit(rustix::process::EXIT_SIGNALED_SIGABRT); |
|||
} else if #[cfg(windows)] { |
|||
// On Windows, return 3.
|
|||
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/abort?view=vs-2019
|
|||
process::exit(3); |
|||
} |
|||
/// Executes the closure `f` with an "ambient Tokio runtime" which basically
|
|||
/// means that if code in `f` tries to get a runtime `Handle` it'll succeed.
|
|||
///
|
|||
/// If a `Handle` is already available, e.g. in async contexts, then `f` is run
|
|||
/// immediately. Otherwise for synchronous contexts this crate's fallback
|
|||
/// runtime is configured and then `f` is executed.
|
|||
pub fn with_ambient_tokio_runtime<R>(f: impl FnOnce() -> R) -> R { |
|||
match tokio::runtime::Handle::try_current() { |
|||
Ok(_) => f(), |
|||
Err(_) => { |
|||
let _enter = RUNTIME.enter(); |
|||
f() |
|||
} |
|||
} |
|||
} |
|||
|
|||
e |
|||
/// Attempts to get the result of a `future`.
|
|||
///
|
|||
/// This function does not block and will poll the provided future once. If the
|
|||
/// result is here then `Some` is returned, otherwise `None` is returned.
|
|||
///
|
|||
/// Note that by polling `future` this means that `future` must be re-polled
|
|||
/// later if it's to wake up a task.
|
|||
pub fn poll_noop<F>(future: Pin<&mut F>) -> Option<F::Output> |
|||
where |
|||
F: Future, |
|||
{ |
|||
let mut task = Context::from_waker(futures::task::noop_waker_ref()); |
|||
match future.poll(&mut task) { |
|||
Poll::Ready(result) => Some(result), |
|||
Poll::Pending => None, |
|||
} |
|||
} |
|||
|
@ -1,6 +1,6 @@ |
|||
use crate::preview2::bindings::sockets::network::{Ipv4Address, Ipv6Address}; |
|||
use crate::preview2::bindings::wasi::sockets::network::ErrorCode; |
|||
use crate::preview2::TrappableError; |
|||
use crate::bindings::sockets::network::{Ipv4Address, Ipv6Address}; |
|||
use crate::bindings::wasi::sockets::network::ErrorCode; |
|||
use crate::TrappableError; |
|||
use std::net::SocketAddr; |
|||
use std::sync::Arc; |
|||
|
@ -1,122 +0,0 @@ |
|||
use crate::preview2::WasiView; |
|||
|
|||
wasmtime::component::bindgen!({ |
|||
world: "wasi:cli/command", |
|||
tracing: true, |
|||
async: true, |
|||
with: { |
|||
"wasi:filesystem/types": crate::preview2::bindings::filesystem::types, |
|||
"wasi:filesystem/preopens": crate::preview2::bindings::filesystem::preopens, |
|||
"wasi:sockets/tcp": crate::preview2::bindings::sockets::tcp, |
|||
"wasi:clocks/monotonic_clock": crate::preview2::bindings::clocks::monotonic_clock, |
|||
"wasi:io/poll": crate::preview2::bindings::io::poll, |
|||
"wasi:io/streams": crate::preview2::bindings::io::streams, |
|||
"wasi:clocks/wall_clock": crate::preview2::bindings::clocks::wall_clock, |
|||
"wasi:random/random": crate::preview2::bindings::random::random, |
|||
"wasi:cli/environment": crate::preview2::bindings::cli::environment, |
|||
"wasi:cli/exit": crate::preview2::bindings::cli::exit, |
|||
"wasi:cli/stdin": crate::preview2::bindings::cli::stdin, |
|||
"wasi:cli/stdout": crate::preview2::bindings::cli::stdout, |
|||
"wasi:cli/stderr": crate::preview2::bindings::cli::stderr, |
|||
"wasi:cli/terminal-input": crate::preview2::bindings::cli::terminal_input, |
|||
"wasi:cli/terminal-output": crate::preview2::bindings::cli::terminal_output, |
|||
"wasi:cli/terminal-stdin": crate::preview2::bindings::cli::terminal_stdin, |
|||
"wasi:cli/terminal-stdout": crate::preview2::bindings::cli::terminal_stdout, |
|||
"wasi:cli/terminal-stderr": crate::preview2::bindings::cli::terminal_stderr, |
|||
}, |
|||
}); |
|||
|
|||
pub fn add_to_linker<T: WasiView>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Result<()> { |
|||
crate::preview2::bindings::clocks::wall_clock::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::clocks::monotonic_clock::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::filesystem::types::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::filesystem::preopens::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::io::error::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::io::poll::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::io::streams::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::random::random::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::random::insecure::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::random::insecure_seed::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::exit::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::environment::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::stdin::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::stdout::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::stderr::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::terminal_input::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::terminal_output::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::terminal_stdin::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::terminal_stdout::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::terminal_stderr::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sockets::tcp::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sockets::tcp_create_socket::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sockets::udp::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sockets::udp_create_socket::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sockets::instance_network::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sockets::network::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sockets::ip_name_lookup::add_to_linker(l, |t| t)?; |
|||
Ok(()) |
|||
} |
|||
|
|||
pub mod sync { |
|||
use crate::preview2::WasiView; |
|||
|
|||
wasmtime::component::bindgen!({ |
|||
world: "wasi:cli/command", |
|||
tracing: true, |
|||
async: false, |
|||
with: { |
|||
"wasi:filesystem/types": crate::preview2::bindings::sync_io::filesystem::types, |
|||
"wasi:filesystem/preopens": crate::preview2::bindings::filesystem::preopens, |
|||
"wasi:sockets/tcp": crate::preview2::bindings::sockets::tcp, |
|||
"wasi:sockets/udp": crate::preview2::bindings::sockets::udp, |
|||
"wasi:clocks/monotonic_clock": crate::preview2::bindings::clocks::monotonic_clock, |
|||
"wasi:io/poll": crate::preview2::bindings::sync_io::io::poll, |
|||
"wasi:io/streams": crate::preview2::bindings::sync_io::io::streams, |
|||
"wasi:clocks/wall_clock": crate::preview2::bindings::clocks::wall_clock, |
|||
"wasi:random/random": crate::preview2::bindings::random::random, |
|||
"wasi:cli/environment": crate::preview2::bindings::cli::environment, |
|||
"wasi:cli/exit": crate::preview2::bindings::cli::exit, |
|||
"wasi:cli/stdin": crate::preview2::bindings::cli::stdin, |
|||
"wasi:cli/stdout": crate::preview2::bindings::cli::stdout, |
|||
"wasi:cli/stderr": crate::preview2::bindings::cli::stderr, |
|||
"wasi:cli/terminal-input": crate::preview2::bindings::cli::terminal_input, |
|||
"wasi:cli/terminal-output": crate::preview2::bindings::cli::terminal_output, |
|||
"wasi:cli/terminal-stdin": crate::preview2::bindings::cli::terminal_stdin, |
|||
"wasi:cli/terminal-stdout": crate::preview2::bindings::cli::terminal_stdout, |
|||
"wasi:cli/terminal-stderr": crate::preview2::bindings::cli::terminal_stderr, |
|||
}, |
|||
}); |
|||
|
|||
pub fn add_to_linker<T: WasiView>( |
|||
l: &mut wasmtime::component::Linker<T>, |
|||
) -> anyhow::Result<()> { |
|||
crate::preview2::bindings::clocks::wall_clock::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::clocks::monotonic_clock::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sync_io::filesystem::types::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::filesystem::preopens::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::io::error::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sync_io::io::poll::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sync_io::io::streams::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::random::random::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::random::insecure::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::random::insecure_seed::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::exit::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::environment::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::stdin::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::stdout::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::stderr::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::terminal_input::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::terminal_output::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::terminal_stdin::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::terminal_stdout::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::cli::terminal_stderr::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sockets::tcp::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sockets::tcp_create_socket::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sockets::udp::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sockets::udp_create_socket::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sockets::instance_network::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sockets::network::add_to_linker(l, |t| t)?; |
|||
crate::preview2::bindings::sockets::ip_name_lookup::add_to_linker(l, |t| t)?; |
|||
Ok(()) |
|||
} |
|||
} |
@ -1,327 +0,0 @@ |
|||
//! # Wasmtime's WASI Preview 2 Implementation
|
|||
//!
|
|||
//! Welcome to the (new!) WASI implementation from the Wasmtime team. The goal
|
|||
//! of this implementation is to support WASI Preview 2 via the Component
|
|||
//! Model, as well as to provide legacy Preview 1 host support with an adapter
|
|||
//! that is implemented in terms of the Preview 2 interfaces.
|
|||
//!
|
|||
//! Presently, this crate is experimental. We don't yet recommend you use it
|
|||
//! in production. Specifically:
|
|||
//! * the wit files in tree describing preview 2 are not faithful to the
|
|||
//! standards repos
|
|||
//!
|
|||
//! Once these issues are resolved, we expect to move this namespace up to the
|
|||
//! root of the wasmtime-wasi crate, and move its other exports underneath a
|
|||
//! `pub mod legacy` with an off-by-default feature flag, and after 2
|
|||
//! releases, retire and remove that code from our tree.
|
|||
|
|||
use std::future::Future; |
|||
use std::pin::Pin; |
|||
use std::task::{Context, Poll}; |
|||
|
|||
mod clocks; |
|||
pub mod command; |
|||
mod ctx; |
|||
mod error; |
|||
mod filesystem; |
|||
mod host; |
|||
mod ip_name_lookup; |
|||
mod network; |
|||
pub mod pipe; |
|||
mod poll; |
|||
#[cfg(feature = "preview1-on-preview2")] |
|||
pub mod preview0; |
|||
#[cfg(feature = "preview1-on-preview2")] |
|||
pub mod preview1; |
|||
mod random; |
|||
mod stdio; |
|||
mod stream; |
|||
mod tcp; |
|||
mod udp; |
|||
mod write_stream; |
|||
|
|||
pub use self::clocks::{HostMonotonicClock, HostWallClock}; |
|||
pub use self::ctx::{WasiCtx, WasiCtxBuilder, WasiView}; |
|||
pub use self::error::{I32Exit, TrappableError}; |
|||
pub use self::filesystem::{DirPerms, FilePerms, FsError, FsResult}; |
|||
pub use self::network::{Network, SocketError, SocketResult}; |
|||
pub use self::poll::{subscribe, ClosureFuture, MakeFuture, Pollable, PollableFuture, Subscribe}; |
|||
pub use self::random::{thread_rng, Deterministic}; |
|||
pub use self::stdio::{ |
|||
stderr, stdin, stdout, IsATTY, Stderr, Stdin, StdinStream, Stdout, StdoutStream, |
|||
}; |
|||
pub use self::stream::{ |
|||
HostInputStream, HostOutputStream, InputStream, OutputStream, StreamError, StreamResult, |
|||
}; |
|||
pub use cap_fs_ext::SystemTimeSpec; |
|||
pub use cap_rand::RngCore; |
|||
pub use wasmtime::component::{ResourceTable, ResourceTableError}; |
|||
|
|||
pub mod bindings { |
|||
// Generate traits for synchronous bindings.
|
|||
//
|
|||
// Note that this is only done for interfaces which can block, or those which
|
|||
// have some functions in `only_imports` below for being async.
|
|||
pub mod sync_io { |
|||
pub(crate) mod _internal { |
|||
use crate::preview2::{FsError, StreamError}; |
|||
|
|||
wasmtime::component::bindgen!({ |
|||
path: "wit", |
|||
interfaces: " |
|||
import wasi:io/poll@0.2.0; |
|||
import wasi:io/streams@0.2.0; |
|||
import wasi:filesystem/types@0.2.0; |
|||
", |
|||
tracing: true, |
|||
trappable_error_type: { |
|||
"wasi:io/streams/stream-error" => StreamError, |
|||
"wasi:filesystem/types/error-code" => FsError, |
|||
}, |
|||
with: { |
|||
"wasi:clocks/wall-clock": crate::preview2::bindings::clocks::wall_clock, |
|||
"wasi:filesystem/types/descriptor": super::super::filesystem::types::Descriptor, |
|||
"wasi:filesystem/types/directory-entry-stream": super::super::filesystem::types::DirectoryEntryStream, |
|||
"wasi:io/poll/pollable": super::super::io::poll::Pollable, |
|||
"wasi:io/streams/input-stream": super::super::io::streams::InputStream, |
|||
"wasi:io/streams/output-stream": super::super::io::streams::OutputStream, |
|||
"wasi:io/error/error": super::super::io::error::Error, |
|||
} |
|||
}); |
|||
} |
|||
pub use self::_internal::wasi::{filesystem, io}; |
|||
} |
|||
|
|||
wasmtime::component::bindgen!({ |
|||
path: "wit", |
|||
world: "wasi:cli/imports", |
|||
tracing: true, |
|||
async: { |
|||
// Only these functions are `async` and everything else is sync
|
|||
// meaning that it basically doesn't need to block. These functions
|
|||
// are the only ones that need to block.
|
|||
//
|
|||
// Note that at this time `only_imports` works on function names
|
|||
// which in theory can be shared across interfaces, so this may
|
|||
// need fancier syntax in the future.
|
|||
only_imports: [ |
|||
"[method]descriptor.access-at", |
|||
"[method]descriptor.advise", |
|||
"[method]descriptor.change-directory-permissions-at", |
|||
"[method]descriptor.change-file-permissions-at", |
|||
"[method]descriptor.create-directory-at", |
|||
"[method]descriptor.get-flags", |
|||
"[method]descriptor.get-type", |
|||
"[method]descriptor.is-same-object", |
|||
"[method]descriptor.link-at", |
|||
"[method]descriptor.lock-exclusive", |
|||
"[method]descriptor.lock-shared", |
|||
"[method]descriptor.metadata-hash", |
|||
"[method]descriptor.metadata-hash-at", |
|||
"[method]descriptor.open-at", |
|||
"[method]descriptor.read", |
|||
"[method]descriptor.read-directory", |
|||
"[method]descriptor.readlink-at", |
|||
"[method]descriptor.remove-directory-at", |
|||
"[method]descriptor.rename-at", |
|||
"[method]descriptor.set-size", |
|||
"[method]descriptor.set-times", |
|||
"[method]descriptor.set-times-at", |
|||
"[method]descriptor.stat", |
|||
"[method]descriptor.stat-at", |
|||
"[method]descriptor.symlink-at", |
|||
"[method]descriptor.sync", |
|||
"[method]descriptor.sync-data", |
|||
"[method]descriptor.try-lock-exclusive", |
|||
"[method]descriptor.try-lock-shared", |
|||
"[method]descriptor.unlink-file-at", |
|||
"[method]descriptor.unlock", |
|||
"[method]descriptor.write", |
|||
"[method]input-stream.read", |
|||
"[method]input-stream.blocking-read", |
|||
"[method]input-stream.blocking-skip", |
|||
"[method]input-stream.skip", |
|||
"[method]output-stream.forward", |
|||
"[method]output-stream.splice", |
|||
"[method]output-stream.blocking-splice", |
|||
"[method]output-stream.blocking-flush", |
|||
"[method]output-stream.blocking-write", |
|||
"[method]output-stream.blocking-write-and-flush", |
|||
"[method]output-stream.blocking-write-zeroes-and-flush", |
|||
"[method]directory-entry-stream.read-directory-entry", |
|||
"poll", |
|||
"[method]pollable.block", |
|||
"[method]pollable.ready", |
|||
], |
|||
}, |
|||
trappable_error_type: { |
|||
"wasi:io/streams/stream-error" => crate::preview2::StreamError, |
|||
"wasi:filesystem/types/error-code" => crate::preview2::FsError, |
|||
"wasi:sockets/network/error-code" => crate::preview2::SocketError, |
|||
}, |
|||
with: { |
|||
"wasi:sockets/network/network": super::network::Network, |
|||
"wasi:sockets/tcp/tcp-socket": super::tcp::TcpSocket, |
|||
"wasi:sockets/udp/udp-socket": super::udp::UdpSocket, |
|||
"wasi:sockets/udp/incoming-datagram-stream": super::udp::IncomingDatagramStream, |
|||
"wasi:sockets/udp/outgoing-datagram-stream": super::udp::OutgoingDatagramStream, |
|||
"wasi:sockets/ip-name-lookup/resolve-address-stream": super::ip_name_lookup::ResolveAddressStream, |
|||
"wasi:filesystem/types/directory-entry-stream": super::filesystem::ReaddirIterator, |
|||
"wasi:filesystem/types/descriptor": super::filesystem::Descriptor, |
|||
"wasi:io/streams/input-stream": super::stream::InputStream, |
|||
"wasi:io/streams/output-stream": super::stream::OutputStream, |
|||
"wasi:io/error/error": super::stream::Error, |
|||
"wasi:io/poll/pollable": super::poll::Pollable, |
|||
"wasi:cli/terminal-input/terminal-input": super::stdio::TerminalInput, |
|||
"wasi:cli/terminal-output/terminal-output": super::stdio::TerminalOutput, |
|||
}, |
|||
}); |
|||
|
|||
pub use wasi::*; |
|||
} |
|||
|
|||
pub(crate) static RUNTIME: once_cell::sync::Lazy<tokio::runtime::Runtime> = |
|||
once_cell::sync::Lazy::new(|| { |
|||
tokio::runtime::Builder::new_current_thread() |
|||
.enable_time() |
|||
.enable_io() |
|||
.build() |
|||
.unwrap() |
|||
}); |
|||
|
|||
pub struct AbortOnDropJoinHandle<T>(tokio::task::JoinHandle<T>); |
|||
impl<T> Drop for AbortOnDropJoinHandle<T> { |
|||
fn drop(&mut self) { |
|||
self.0.abort() |
|||
} |
|||
} |
|||
impl<T> std::ops::Deref for AbortOnDropJoinHandle<T> { |
|||
type Target = tokio::task::JoinHandle<T>; |
|||
fn deref(&self) -> &Self::Target { |
|||
&self.0 |
|||
} |
|||
} |
|||
impl<T> std::ops::DerefMut for AbortOnDropJoinHandle<T> { |
|||
fn deref_mut(&mut self) -> &mut tokio::task::JoinHandle<T> { |
|||
&mut self.0 |
|||
} |
|||
} |
|||
impl<T> From<tokio::task::JoinHandle<T>> for AbortOnDropJoinHandle<T> { |
|||
fn from(jh: tokio::task::JoinHandle<T>) -> Self { |
|||
AbortOnDropJoinHandle(jh) |
|||
} |
|||
} |
|||
impl<T> Future for AbortOnDropJoinHandle<T> { |
|||
type Output = T; |
|||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
|||
match Pin::new(&mut self.as_mut().0).poll(cx) { |
|||
Poll::Pending => Poll::Pending, |
|||
Poll::Ready(r) => Poll::Ready(r.expect("child task panicked")), |
|||
} |
|||
} |
|||
} |
|||
|
|||
pub fn spawn<F>(f: F) -> AbortOnDropJoinHandle<F::Output> |
|||
where |
|||
F: Future + Send + 'static, |
|||
F::Output: Send + 'static, |
|||
{ |
|||
let j = with_ambient_tokio_runtime(|| tokio::task::spawn(f)); |
|||
AbortOnDropJoinHandle(j) |
|||
} |
|||
|
|||
pub fn spawn_blocking<F, R>(f: F) -> AbortOnDropJoinHandle<R> |
|||
where |
|||
F: FnOnce() -> R + Send + 'static, |
|||
R: Send + 'static, |
|||
{ |
|||
let j = with_ambient_tokio_runtime(|| tokio::task::spawn_blocking(f)); |
|||
AbortOnDropJoinHandle(j) |
|||
} |
|||
|
|||
pub fn in_tokio<F: Future>(f: F) -> F::Output { |
|||
match tokio::runtime::Handle::try_current() { |
|||
Ok(h) => { |
|||
let _enter = h.enter(); |
|||
h.block_on(f) |
|||
} |
|||
// The `yield_now` here is non-obvious and if you're reading this
|
|||
// you're likely curious about why it's here. This is currently required
|
|||
// to get some features of "sync mode" working correctly, such as with
|
|||
// the CLI. To illustrate why this is required, consider a program
|
|||
// organized as:
|
|||
//
|
|||
// * A program has a `pollable` that it's waiting on.
|
|||
// * This `pollable` is always ready .
|
|||
// * Actually making the corresponding operation ready, however,
|
|||
// requires some background work on Tokio's part.
|
|||
// * The program is looping on "wait for readiness" coupled with
|
|||
// performing the operation.
|
|||
//
|
|||
// In this situation this program ends up infinitely looping in waiting
|
|||
// for pollables. The reason appears to be that when we enter the tokio
|
|||
// runtime here it doesn't necessary yield to background work because
|
|||
// the provided future `f` is ready immediately. The future `f` will run
|
|||
// through the list of pollables and determine one of them is ready.
|
|||
//
|
|||
// Historically this happened with UDP sockets. A test send a datagram
|
|||
// from one socket to another and the other socket infinitely didn't
|
|||
// receive the data. This appeared to be because the server socket was
|
|||
// waiting on `READABLE | WRITABLE` (which is itself a bug but ignore
|
|||
// that) and the socket was currently in the "writable" state but never
|
|||
// ended up receiving a notification for the "readable" state. Moving
|
|||
// the socket to "readable" would require Tokio to perform some
|
|||
// background work via epoll/kqueue/handle events but if the future
|
|||
// provided here is always ready, then that never happened.
|
|||
//
|
|||
// Thus the `yield_now()` is an attempt to force Tokio to go do some
|
|||
// background work eventually and look at new interest masks for
|
|||
// example. This is a bit of a kludge but everything's already a bit
|
|||
// wonky in synchronous mode anyway. Note that this is hypothesized to
|
|||
// not be an issue in async mode because async mode typically has the
|
|||
// Tokio runtime in a separate thread or otherwise participating in a
|
|||
// larger application, it's only here in synchronous mode where we
|
|||
// effectively own the runtime that we need some special care.
|
|||
Err(_) => { |
|||
let _enter = RUNTIME.enter(); |
|||
RUNTIME.block_on(async move { |
|||
tokio::task::yield_now().await; |
|||
f.await |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// Executes the closure `f` with an "ambient Tokio runtime" which basically
|
|||
/// means that if code in `f` tries to get a runtime `Handle` it'll succeed.
|
|||
///
|
|||
/// If a `Handle` is already available, e.g. in async contexts, then `f` is run
|
|||
/// immediately. Otherwise for synchronous contexts this crate's fallback
|
|||
/// runtime is configured and then `f` is executed.
|
|||
pub fn with_ambient_tokio_runtime<R>(f: impl FnOnce() -> R) -> R { |
|||
match tokio::runtime::Handle::try_current() { |
|||
Ok(_) => f(), |
|||
Err(_) => { |
|||
let _enter = RUNTIME.enter(); |
|||
f() |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// Attempts to get the result of a `future`.
|
|||
///
|
|||
/// This function does not block and will poll the provided future once. If the
|
|||
/// result is here then `Some` is returned, otherwise `None` is returned.
|
|||
///
|
|||
/// Note that by polling `future` this means that `future` must be re-polled
|
|||
/// later if it's to wake up a task.
|
|||
pub fn poll_noop<F>(future: Pin<&mut F>) -> Option<F::Output> |
|||
where |
|||
F: Future, |
|||
{ |
|||
let mut task = Context::from_waker(futures::task::noop_waker_ref()); |
|||
match future.poll(&mut task) { |
|||
Poll::Ready(result) => Some(result), |
|||
Poll::Pending => None, |
|||
} |
|||
} |
@ -1,12 +1,10 @@ |
|||
use crate::preview2::bindings::cli::{ |
|||
use crate::bindings::cli::{ |
|||
stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr, terminal_stdin, |
|||
terminal_stdout, |
|||
}; |
|||
use crate::preview2::bindings::io::streams; |
|||
use crate::preview2::pipe; |
|||
use crate::preview2::{ |
|||
HostInputStream, HostOutputStream, StreamError, StreamResult, Subscribe, WasiView, |
|||
}; |
|||
use crate::bindings::io::streams; |
|||
use crate::pipe; |
|||
use crate::{HostInputStream, HostOutputStream, StreamError, StreamResult, Subscribe, WasiView}; |
|||
use bytes::Bytes; |
|||
use std::io::IsTerminal; |
|||
use wasmtime::component::Resource; |
@ -1,5 +1,5 @@ |
|||
use crate::preview2::filesystem::FileInputStream; |
|||
use crate::preview2::poll::Subscribe; |
|||
use crate::filesystem::FileInputStream; |
|||
use crate::poll::Subscribe; |
|||
use anyhow::Result; |
|||
use bytes::Bytes; |
|||
|
@ -1,6 +1,6 @@ |
|||
use crate::preview2::host::network::util; |
|||
use crate::preview2::poll::Subscribe; |
|||
use crate::preview2::with_ambient_tokio_runtime; |
|||
use crate::host::network::util; |
|||
use crate::poll::Subscribe; |
|||
use crate::with_ambient_tokio_runtime; |
|||
use async_trait::async_trait; |
|||
use cap_net_ext::{AddressFamily, Blocking}; |
|||
use io_lifetimes::raw::{FromRawSocketlike, IntoRawSocketlike}; |
Loading…
Reference in new issue