Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wasi-common/src/pipe.rs
3073 views
1
//! Virtual pipes.
2
//!
3
//! These types provide easy implementations of `WasiFile` that mimic much of the behavior of Unix
4
//! pipes. These are particularly helpful for redirecting WASI stdio handles to destinations other
5
//! than OS files.
6
//!
7
//! Some convenience constructors are included for common backing types like `Vec<u8>` and `String`,
8
//! but the virtual pipes can be instantiated with any `Read` or `Write` type.
9
//!
10
use crate::Error;
11
use crate::file::{FdFlags, FileType, WasiFile};
12
use std::any::Any;
13
use std::io::{self, Read, Write};
14
use std::sync::{Arc, RwLock};
15
16
/// A virtual pipe read end.
17
///
18
/// A variety of `From` impls are provided so that common pipe types are easy to create. For example:
19
///
20
/// ```rust
21
/// use wasi_common::{pipe::ReadPipe, WasiCtx, Table};
22
/// let stdin = ReadPipe::from("hello from stdin!");
23
/// // Bring these instances from elsewhere (e.g. wasi-cap-std-sync or wasi-cap-std-tokio):
24
/// use wasi_common::sync::{random_ctx, clocks_ctx, sched_ctx};
25
/// let random = random_ctx();
26
/// let clocks = clocks_ctx();
27
/// let sched = sched_ctx();
28
/// let table = Table::new();
29
/// let mut ctx = WasiCtx::new(random, clocks, sched, table);
30
/// ctx.set_stdin(Box::new(stdin.clone()));
31
/// ```
32
#[derive(Debug)]
33
pub struct ReadPipe<R: Read> {
34
reader: Arc<RwLock<R>>,
35
}
36
37
impl<R: Read> Clone for ReadPipe<R> {
38
fn clone(&self) -> Self {
39
Self {
40
reader: self.reader.clone(),
41
}
42
}
43
}
44
45
impl<R: Read> ReadPipe<R> {
46
/// Create a new pipe from a `Read` type.
47
///
48
/// All `Handle` read operations delegate to reading from this underlying reader.
49
pub fn new(r: R) -> Self {
50
Self::from_shared(Arc::new(RwLock::new(r)))
51
}
52
53
/// Create a new pipe from a shareable `Read` type.
54
///
55
/// All `Handle` read operations delegate to reading from this underlying reader.
56
pub fn from_shared(reader: Arc<RwLock<R>>) -> Self {
57
Self { reader }
58
}
59
60
/// Try to convert this `ReadPipe<R>` back to the underlying `R` type.
61
///
62
/// This will fail with `Err(self)` if multiple references to the underlying `R` exist.
63
pub fn try_into_inner(mut self) -> Result<R, Self> {
64
match Arc::try_unwrap(self.reader) {
65
Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),
66
Err(reader) => {
67
self.reader = reader;
68
Err(self)
69
}
70
}
71
}
72
fn borrow(&self) -> std::sync::RwLockWriteGuard<'_, R> {
73
RwLock::write(&self.reader).unwrap()
74
}
75
}
76
77
impl From<Vec<u8>> for ReadPipe<io::Cursor<Vec<u8>>> {
78
fn from(r: Vec<u8>) -> Self {
79
Self::new(io::Cursor::new(r))
80
}
81
}
82
83
impl From<&[u8]> for ReadPipe<io::Cursor<Vec<u8>>> {
84
fn from(r: &[u8]) -> Self {
85
Self::from(r.to_vec())
86
}
87
}
88
89
impl From<String> for ReadPipe<io::Cursor<String>> {
90
fn from(r: String) -> Self {
91
Self::new(io::Cursor::new(r))
92
}
93
}
94
95
impl From<&str> for ReadPipe<io::Cursor<String>> {
96
fn from(r: &str) -> Self {
97
Self::from(r.to_string())
98
}
99
}
100
101
#[async_trait::async_trait]
102
impl<R: Read + Any + Send + Sync> WasiFile for ReadPipe<R> {
103
fn as_any(&self) -> &dyn Any {
104
self
105
}
106
async fn get_filetype(&self) -> Result<FileType, Error> {
107
Ok(FileType::Pipe)
108
}
109
async fn read_vectored<'a>(&self, bufs: &mut [io::IoSliceMut<'a>]) -> Result<u64, Error> {
110
let n = self.borrow().read_vectored(bufs)?;
111
Ok(n.try_into()?)
112
}
113
}
114
115
/// A virtual pipe write end.
116
///
117
/// ```rust
118
/// use wasi_common::{pipe::WritePipe, WasiCtx, Table};
119
/// let stdout = WritePipe::new_in_memory();
120
/// // Bring these instances from elsewhere (e.g. wasi-cap-std-sync or wasi-cap-std-tokio):
121
/// use wasi_common::sync::{random_ctx, clocks_ctx, sched_ctx};
122
/// let random = random_ctx();
123
/// let clocks = clocks_ctx();
124
/// let sched = sched_ctx();
125
/// let table = Table::new();
126
/// let mut ctx = WasiCtx::new(random, clocks, sched, table);
127
/// ctx.set_stdout(Box::new(stdout.clone()));
128
/// // use ctx in an instance, then make sure it is dropped:
129
/// drop(ctx);
130
/// let contents: Vec<u8> = stdout.try_into_inner().expect("sole remaining reference to WritePipe").into_inner();
131
/// println!("contents of stdout: {:?}", contents);
132
/// ```
133
#[derive(Debug)]
134
pub struct WritePipe<W: Write> {
135
writer: Arc<RwLock<W>>,
136
}
137
138
impl<W: Write> Clone for WritePipe<W> {
139
fn clone(&self) -> Self {
140
Self {
141
writer: self.writer.clone(),
142
}
143
}
144
}
145
146
impl<W: Write> WritePipe<W> {
147
/// Create a new pipe from a `Write` type.
148
///
149
/// All `Handle` write operations delegate to writing to this underlying writer.
150
pub fn new(w: W) -> Self {
151
Self::from_shared(Arc::new(RwLock::new(w)))
152
}
153
154
/// Create a new pipe from a shareable `Write` type.
155
///
156
/// All `Handle` write operations delegate to writing to this underlying writer.
157
pub fn from_shared(writer: Arc<RwLock<W>>) -> Self {
158
Self { writer }
159
}
160
161
/// Try to convert this `WritePipe<W>` back to the underlying `W` type.
162
///
163
/// This will fail with `Err(self)` if multiple references to the underlying `W` exist.
164
pub fn try_into_inner(mut self) -> Result<W, Self> {
165
match Arc::try_unwrap(self.writer) {
166
Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),
167
Err(writer) => {
168
self.writer = writer;
169
Err(self)
170
}
171
}
172
}
173
174
fn borrow(&self) -> std::sync::RwLockWriteGuard<'_, W> {
175
RwLock::write(&self.writer).unwrap()
176
}
177
}
178
179
impl WritePipe<io::Cursor<Vec<u8>>> {
180
/// Create a new writable virtual pipe backed by a `Vec<u8>` buffer.
181
pub fn new_in_memory() -> Self {
182
Self::new(io::Cursor::new(vec![]))
183
}
184
}
185
186
#[async_trait::async_trait]
187
impl<W: Write + Any + Send + Sync> WasiFile for WritePipe<W> {
188
fn as_any(&self) -> &dyn Any {
189
self
190
}
191
async fn get_filetype(&self) -> Result<FileType, Error> {
192
Ok(FileType::Pipe)
193
}
194
async fn get_fdflags(&self) -> Result<FdFlags, Error> {
195
Ok(FdFlags::APPEND)
196
}
197
async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
198
let n = self.borrow().write_vectored(bufs)?;
199
Ok(n.try_into()?)
200
}
201
}
202
203