Skip to content

Commit cbc5f40

Browse files
committed
Add forward
1 parent 324be89 commit cbc5f40

File tree

2 files changed

+220
-2
lines changed

2 files changed

+220
-2
lines changed

imports.md

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,16 @@ use the <code>subscribe</code> function to obtain a <a href="#pollable"><code>po
132132
for using <code>wasi:io/poll</code>.</p>
133133
<h4><a name="output_stream"><code>resource output-stream</code></a></h4>
134134
<p>An output bytestream.</p>
135-
<h2><a href="#output_stream"><code>output-stream</code></a>s are <em>non-blocking</em> to the extent practical on
135+
<p><a href="#output_stream"><code>output-stream</code></a>s are <em>non-blocking</em> to the extent practical on
136136
underlying platforms. Except where specified otherwise, I/O operations also
137137
always return promptly, after the number of bytes that can be written
138138
promptly, which could even be zero. To wait for the stream to be ready to
139139
accept data, the <code>subscribe</code> function to obtain a <a href="#pollable"><code>pollable</code></a> which can be
140-
polled for using <code>wasi:io/poll</code>.</h2>
140+
polled for using <code>wasi:io/poll</code>.</p>
141+
<h4><a name="future_forward_result"><code>resource future-forward-result</code></a></h4>
142+
<p>Represents a future which will eventually return the forward result.</p>
143+
<h2>Dropping this future while it's still pending may trap. Use <code>cancel</code> to
144+
cancel the operation. A future is &quot;pending&quot; while <code>get</code> would return <code>none</code>.</h2>
141145
<h3>Functions</h3>
142146
<h4><a name="method_input_stream.read"><code>[method]input-stream.read: func</code></a></h4>
143147
<p>Perform a non-blocking read from the stream.</p>
@@ -207,6 +211,81 @@ can be skipped. Except for blocking behavior, identical to <code>skip</code>.</p
207211
<ul>
208212
<li><a name="method_input_stream.blocking_skip.0"></a> result&lt;<code>u64</code>, <a href="#stream_error"><a href="#stream_error"><code>stream-error</code></a></a>&gt;</li>
209213
</ul>
214+
<h4><a name="method_input_stream.forward"><code>[method]input-stream.forward: func</code></a></h4>
215+
<p>Completely drain the input stream into the provided output stream on
216+
a background task. The returned future resolves when either the input
217+
stream has been fully drained or when an error occurred while reading
218+
or writing.</p>
219+
<p>The <code>flush-on-block</code> parameter controls whether the output stream
220+
should be automatically flushed whenever the input stream reports
221+
that it has no data at that moment. When the future resolves it is
222+
possible for there to be data written to the output stream that
223+
hasn't been flushed yet because the last read didn't block.</p>
224+
<p>If you need to be sure that all data has been flushed at the end of
225+
the forward, call <code>flush</code> yourself afterwards or use
226+
<code>forward-and-drop</code> instead.</p>
227+
<p>Even though this function only borrows its parameters, it requires
228+
exclusive access to them for as long as the forward is in progress.
229+
Any attempt to access or drop the streams in the meantime will trap.</p>
230+
<p>This method is equivalent to spawning a background task running the
231+
following pseudo-code:</p>
232+
<pre><code class="language-text">let src-pollable = src.subscribe();
233+
let dst-pollable = dst.subscribe();
234+
235+
loop { // Error &amp; cancellation checking omitted for brevity.
236+
let len = src.splice(dst);
237+
if len == 0 { // No data available at the moment
238+
if flush-on-block {
239+
dst.flush();
240+
}
241+
src-pollable.block();
242+
dst-pollable.block();
243+
}
244+
}
245+
</code></pre>
246+
<h5>Params</h5>
247+
<ul>
248+
<li><a name="method_input_stream.forward.self"><code>self</code></a>: borrow&lt;<a href="#input_stream"><a href="#input_stream"><code>input-stream</code></a></a>&gt;</li>
249+
<li><a name="method_input_stream.forward.dst"><code>dst</code></a>: borrow&lt;<a href="#output_stream"><a href="#output_stream"><code>output-stream</code></a></a>&gt;</li>
250+
<li><a name="method_input_stream.forward.flush_on_block"><code>flush-on-block</code></a>: <code>bool</code></li>
251+
</ul>
252+
<h5>Return values</h5>
253+
<ul>
254+
<li><a name="method_input_stream.forward.0"></a> own&lt;<a href="#future_forward_result"><a href="#future_forward_result"><code>future-forward-result</code></a></a>&gt;</li>
255+
</ul>
256+
<h4><a name="static_input_stream.forward_and_drop"><code>[static]input-stream.forward-and-drop: func</code></a></h4>
257+
<p>Functionally similar to <code>forward</code> except that this function also:</p>
258+
<ul>
259+
<li>automatically performs a final flush, and</li>
260+
<li>drops the stream when it's done.</li>
261+
</ul>
262+
<p>Control over the streams is handed over to the host. This may enable
263+
implementations to perform additional optimizations not possible otherwise.</p>
264+
<p>The streams remain children and their respective parents (if any).
265+
If those parents place any lifetimes restrictions on the streams,
266+
those continue to apply. In practice this typically means that the
267+
returned future should not outlive the stream's parents.
268+
Implementations may trap if the the streams themselves still have
269+
any active child resources (pollables) at the time of calling this
270+
function.</p>
271+
<p>This method is equivalent to spawning a background task running the
272+
following pseudo-code:</p>
273+
<pre><code class="language-text">// Error &amp; cancellation checking omitted for brevity.
274+
src.forward(dst, flush-on-block).subscribe().block();
275+
dst.blocking-flush();
276+
drop(src);
277+
drop(dst);
278+
</code></pre>
279+
<h5>Params</h5>
280+
<ul>
281+
<li><a name="static_input_stream.forward_and_drop.src"><code>src</code></a>: own&lt;<a href="#input_stream"><a href="#input_stream"><code>input-stream</code></a></a>&gt;</li>
282+
<li><a name="static_input_stream.forward_and_drop.dst"><code>dst</code></a>: own&lt;<a href="#output_stream"><a href="#output_stream"><code>output-stream</code></a></a>&gt;</li>
283+
<li><a name="static_input_stream.forward_and_drop.flush_on_block"><code>flush-on-block</code></a>: <code>bool</code></li>
284+
</ul>
285+
<h5>Return values</h5>
286+
<ul>
287+
<li><a name="static_input_stream.forward_and_drop.0"></a> own&lt;<a href="#future_forward_result"><a href="#future_forward_result"><code>future-forward-result</code></a></a>&gt;</li>
288+
</ul>
210289
<h4><a name="method_input_stream.subscribe"><code>[method]input-stream.subscribe: func</code></a></h4>
211290
<p>Create a <a href="#pollable"><code>pollable</code></a> which will resolve once either the specified stream
212291
has bytes available to read or the other end of the stream has been
@@ -418,3 +497,45 @@ is ready for reading, before performing the <code>splice</code>.</p>
418497
<ul>
419498
<li><a name="method_output_stream.blocking_splice.0"></a> result&lt;<code>u64</code>, <a href="#stream_error"><a href="#stream_error"><code>stream-error</code></a></a>&gt;</li>
420499
</ul>
500+
<h4><a name="method_future_forward_result.subscribe"><code>[method]future-forward-result.subscribe: func</code></a></h4>
501+
<p>Returns a pollable which becomes ready when either the operation has
502+
succeeded, failed or has been canceled. When this pollable is ready,
503+
the <code>get</code> method will return <code>some</code>.</p>
504+
<h5>Params</h5>
505+
<ul>
506+
<li><a name="method_future_forward_result.subscribe.self"><code>self</code></a>: borrow&lt;<a href="#future_forward_result"><a href="#future_forward_result"><code>future-forward-result</code></a></a>&gt;</li>
507+
</ul>
508+
<h5>Return values</h5>
509+
<ul>
510+
<li><a name="method_future_forward_result.subscribe.0"></a> own&lt;<a href="#pollable"><a href="#pollable"><code>pollable</code></a></a>&gt;</li>
511+
</ul>
512+
<h4><a name="method_future_forward_result.cancel"><code>[method]future-forward-result.cancel: func</code></a></h4>
513+
<p>Initiate cancellation of the task. This is an asynchronous operation.
514+
Use <code>subscribe</code> to wait for the cancallation to finish.</p>
515+
<p>Dropping the future while the cancellation is in progress may trap.</p>
516+
<h5>Params</h5>
517+
<ul>
518+
<li><a name="method_future_forward_result.cancel.self"><code>self</code></a>: borrow&lt;<a href="#future_forward_result"><a href="#future_forward_result"><code>future-forward-result</code></a></a>&gt;</li>
519+
</ul>
520+
<h4><a name="method_future_forward_result.get"><code>[method]future-forward-result.get: func</code></a></h4>
521+
<p>Returns the result of the forward operation once the future is ready.</p>
522+
<p>The outer <code>option</code> represents future readiness. Users can wait on this
523+
<code>option</code> to become <code>some</code> using the <code>subscribe</code> method.</p>
524+
<p>The outer <code>result</code> will be <a href="#error"><code>error</code></a> if the future was canceled or the
525+
inner result has already been retrieved from the future in a previous
526+
call.</p>
527+
<p>The inner <code>result</code> represents the result of the actual forward
528+
operation. This will be:</p>
529+
<ul>
530+
<li><code>ok</code> when the source stream was successfully read until the end,</li>
531+
<li><a href="#error"><code>error</code></a> when destination stream was closed before the source stream ended,</li>
532+
<li><a href="#error"><code>error</code></a> when either the source or destination stream returned an error.</li>
533+
</ul>
534+
<h5>Params</h5>
535+
<ul>
536+
<li><a name="method_future_forward_result.get.self"><code>self</code></a>: borrow&lt;<a href="#future_forward_result"><a href="#future_forward_result"><code>future-forward-result</code></a></a>&gt;</li>
537+
</ul>
538+
<h5>Return values</h5>
539+
<ul>
540+
<li><a name="method_future_forward_result.get.0"></a> option&lt;result&lt;result&lt;_, <a href="#stream_error"><a href="#stream_error"><code>stream-error</code></a></a>&gt;&gt;&gt;</li>
541+
</ul>

wit/streams.wit

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,70 @@ interface streams {
8484
len: u64,
8585
) -> result<u64, stream-error>;
8686

87+
/// Completely drain the input stream into the provided output stream on
88+
/// a background task. The returned future resolves when either the input
89+
/// stream has been fully drained or when an error occurred while reading
90+
/// or writing.
91+
///
92+
/// The `flush-on-block` parameter controls whether the output stream
93+
/// should be automatically flushed whenever the input stream reports
94+
/// that it has no data at that moment. When the future resolves it is
95+
/// possible for there to be data written to the output stream that
96+
/// hasn't been flushed yet because the last read didn't block.
97+
///
98+
/// If you need to be sure that all data has been flushed at the end of
99+
/// the forward, call `flush` yourself afterwards or use
100+
/// `forward-and-drop` instead.
101+
///
102+
/// Even though this function only borrows its parameters, it requires
103+
/// exclusive access to them for as long as the forward is in progress.
104+
/// Any attempt to access or drop the streams in the meantime will trap.
105+
///
106+
/// This method is equivalent to spawning a background task running the
107+
/// following pseudo-code:
108+
/// ```text
109+
/// let src-pollable = src.subscribe();
110+
/// let dst-pollable = dst.subscribe();
111+
///
112+
/// loop { // Error & cancellation checking omitted for brevity.
113+
/// let len = src.splice(dst);
114+
/// if len == 0 { // No data available at the moment
115+
/// if flush-on-block {
116+
/// dst.flush();
117+
/// }
118+
/// src-pollable.block();
119+
/// dst-pollable.block();
120+
/// }
121+
/// }
122+
/// ```
123+
forward: func(dst: borrow<output-stream>, flush-on-block: bool) -> future-forward-result;
124+
125+
/// Functionally similar to `forward` except that this function also:
126+
/// - automatically performs a final flush, and
127+
/// - drops the stream when it's done.
128+
///
129+
/// Control over the streams is handed over to the host. This may enable
130+
/// implementations to perform additional optimizations not possible otherwise.
131+
///
132+
/// The streams remain children and their respective parents (if any).
133+
/// If those parents place any lifetimes restrictions on the streams,
134+
/// those continue to apply. In practice this typically means that the
135+
/// returned future should not outlive the stream's parents.
136+
/// Implementations may trap if the the streams themselves still have
137+
/// any active child resources (pollables) at the time of calling this
138+
/// function.
139+
///
140+
/// This method is equivalent to spawning a background task running the
141+
/// following pseudo-code:
142+
/// ```text
143+
/// // Error & cancellation checking omitted for brevity.
144+
/// src.forward(dst, flush-on-block).subscribe().block();
145+
/// dst.blocking-flush();
146+
/// drop(src);
147+
/// drop(dst);
148+
/// ```
149+
forward-and-drop: static func(src: input-stream, dst: output-stream, flush-on-block: bool) -> future-forward-result;
150+
87151
/// Create a `pollable` which will resolve once either the specified stream
88152
/// has bytes available to read or the other end of the stream has been
89153
/// closed.
@@ -259,4 +323,37 @@ interface streams {
259323
len: u64,
260324
) -> result<u64, stream-error>;
261325
}
326+
327+
/// Represents a future which will eventually return the forward result.
328+
///
329+
/// Dropping this future while it's still pending may trap. Use `cancel` to
330+
/// cancel the operation. A future is "pending" while `get` would return `none`.
331+
resource future-forward-result {
332+
/// Returns a pollable which becomes ready when either the operation has
333+
/// succeeded, failed or has been canceled. When this pollable is ready,
334+
/// the `get` method will return `some`.
335+
subscribe: func() -> pollable;
336+
337+
/// Initiate cancellation of the task. This is an asynchronous operation.
338+
/// Use `subscribe` to wait for the cancallation to finish.
339+
///
340+
/// Dropping the future while the cancellation is in progress may trap.
341+
cancel: func();
342+
343+
/// Returns the result of the forward operation once the future is ready.
344+
///
345+
/// The outer `option` represents future readiness. Users can wait on this
346+
/// `option` to become `some` using the `subscribe` method.
347+
///
348+
/// The outer `result` will be `error` if the future was canceled or the
349+
/// inner result has already been retrieved from the future in a previous
350+
/// call.
351+
///
352+
/// The inner `result` represents the result of the actual forward
353+
/// operation. This will be:
354+
/// - `ok` when the source stream was successfully read until the end,
355+
/// - `error` when destination stream was closed before the source stream ended,
356+
/// - `error` when either the source or destination stream returned an error.
357+
get: func() -> option<result<result<_, stream-error>>>;
358+
}
262359
}

0 commit comments

Comments
 (0)