From b9e6b1260b2c569562f679c3dbf876160fba1c1c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 12 Sep 2023 15:47:46 -0700 Subject: [PATCH] Convert to Resources, and other API cleanups. - Convert to resources. Use resources instead of `u32`s, remove drop functions, `this` arguments, and rename `subscribe-to-*` to just `subscribe`, as discussed in WebAssembly/wasi-poll#21. - Merge wasi-poll into wasi-io. These two proposals are closely related to each other, so it makes sense to have them together. - While here, tidy up the poll API, incorporating ideas discussed in WebAssembly/wasi-poll#22: - Rename `poll-oneoff` to `poll-list`, and add a `poll-one`. - Change `poll-oneoff`'s return type from `list` to `list`, because in the common case, this should allow it to create much smaller allocations. --- example-world.md | 417 ----------------------------------- imports.md | 386 +++++++++++++++++++++++++++++++++ wit/deps.lock | 4 - wit/deps.toml | 1 - wit/deps/poll/poll.wit | 49 ----- wit/deps/poll/world.wit | 5 - wit/poll.wit | 34 +++ wit/streams.wit | 465 +++++++++++++++++++--------------------- wit/world.wit | 1 + 9 files changed, 646 insertions(+), 716 deletions(-) delete mode 100644 example-world.md create mode 100644 imports.md delete mode 100644 wit/deps.lock delete mode 100644 wit/deps.toml delete mode 100644 wit/deps/poll/poll.wit delete mode 100644 wit/deps/poll/world.wit create mode 100644 wit/poll.wit diff --git a/example-world.md b/example-world.md deleted file mode 100644 index cef732d..0000000 --- a/example-world.md +++ /dev/null @@ -1,417 +0,0 @@ -

World example-world

- -

Import interface wasi:poll/poll

-

A poll API intended to let users wait for I/O events on multiple handles -at once.

-
-

Types

-

type pollable

-

u32

-

A "pollable" handle. -

This is conceptually represents a stream<_, _>, or in other words, -a stream that one can wait on, repeatedly, but which does not itself -produce any data. It's temporary scaffolding until component-model's -async features are ready.

-

And at present, it is a u32 instead of being an actual handle, until -the wit-bindgen implementation of handles and resources is ready.

-

pollable lifetimes are not automatically managed. Users must ensure -that they do not outlive the resource they reference.

-

This represents a resource.

-
-

Functions

-

drop-pollable: func

-

Dispose of the specified pollable, after which it may no longer -be used.

-
Params
- -

poll-oneoff: func

-

Poll for completion on a set of pollables.

-

This function takes a list of pollables, which identify I/O sources of -interest, and waits until one or more of the events is ready for I/O.

-

The result list<bool> is the same length as the argument -list<pollable>, and indicates the readiness of each corresponding -element in that list, with true indicating ready. A single call can -return multiple true elements.

-

A timeout can be implemented by adding a pollable from the -wasi-clocks API to the list.

-

This function does not return a result; polling in itself does not -do any I/O so it doesn't fail. If any of the I/O sources identified by -the pollables has an error, it is indicated by marking the source as -ready in the list<bool>.

-

The "oneoff" in the name refers to the fact that this function must do a -linear scan through the entire list of subscriptions, which may be -inefficient if the number is large and the same subscriptions are used -many times. In the future, this is expected to be obsoleted by the -component model async proposal, which will include a scalable waiting -facility.

-
Params
- -
Return values
-
    -
  • list<bool>
  • -
-

Import interface wasi:io/streams

-

WASI I/O is an I/O abstraction API which is currently focused on providing -stream types.

-

In the future, the component model is expected to add built-in stream types; -when it does, they are expected to subsume this API.

-
-

Types

-

type pollable

-

pollable

-

-#### `enum write-error` -

An error for output-stream operations.

-

Contrary to input-streams, a closed output-stream is reported using -an error.

-
Enum Cases
-
    -
  • -

    last-operation-failed

    -

    The last operation (a write or flush) failed before completion. -

  • -
  • -

    closed

    -

    The stream is closed: no more input will be accepted by the -stream. A closed output-stream will return this error on all -future operations. -

  • -
-

enum stream-status

-

Streams provide a sequence of data and then end; once they end, they -no longer provide any further data.

-

For example, a stream reading from a file ends when the stream reaches -the end of the file. For another example, a stream reading from a -socket ends when the socket is closed.

-
Enum Cases
-
    -
  • -

    open

    -

    The stream is open and may produce further data. -

  • -
  • -

    ended

    -

    When reading, this indicates that the stream will not produce -further data. -When writing, this indicates that the stream will no longer be read. -Further writes are still permitted. -

  • -
-

type output-stream

-

u32

-

An output bytestream. In the future, this will be replaced by handle -types. -

output-streams are non-blocking to the extent practical on -underlying platforms. Except where specified otherwise, I/O operations also -always return promptly, after the number of bytes that can be written -promptly, which could even be zero. To wait for the stream to be ready to -accept data, the subscribe-to-output-stream function to obtain a -pollable which can be polled for using wasi:poll.

-

And at present, it is a u32 instead of being an actual handle, until -the wit-bindgen implementation of handles and resources is ready.

-

This represents a resource.

-

type input-stream

-

u32

-

An input bytestream. In the future, this will be replaced by handle -types. -

input-streams are non-blocking to the extent practical on underlying -platforms. I/O operations always return promptly; if fewer bytes are -promptly available than requested, they return the number of bytes promptly -available, which could even be zero. To wait for data to be available, -use the subscribe-to-input-stream function to obtain a pollable which -can be polled for using wasi:poll/poll.poll_oneoff.

-

And at present, it is a u32 instead of being an actual handle, until -the wit-bindgen implementation of handles and resources is ready.

-

This represents a resource.

-
-

Functions

-

read: func

-

Perform a non-blocking read from the stream.

-

This function returns a list of bytes containing the data that was -read, along with a stream-status which, indicates whether further -reads are expected to produce data. The returned list will contain up to -len bytes; it may return fewer than requested, but not more. An -empty list and stream-status:open indicates no more data is -available at this time, and that the pollable given by -subscribe-to-input-stream will be ready when more data is available.

-

Once a stream has reached the end, subsequent calls to read or -skip will always report stream-status:ended rather than producing more -data.

-

When the caller gives a len of 0, it represents a request to read 0 -bytes. This read should always succeed and return an empty list and -the current stream-status.

-

The len parameter is a u64, which could represent a list of u8 which -is not possible to allocate in wasm32, or not desirable to allocate as -as a return value by the callee. The callee may return a list of bytes -less than len in size while more bytes are available for reading.

-
Params
- -
Return values
- -

blocking-read: func

-

Read bytes from a stream, after blocking until at least one byte can -be read. Except for blocking, identical to read.

-
Params
- -
Return values
- -

skip: func

-

Skip bytes from a stream.

-

This is similar to the read function, but avoids copying the -bytes into the instance.

-

Once a stream has reached the end, subsequent calls to read or -skip will always report end-of-stream rather than producing more -data.

-

This function returns the number of bytes skipped, along with a -stream-status indicating whether the end of the stream was -reached. The returned value will be at most len; it may be less.

-
Params
- -
Return values
- -

blocking-skip: func

-

Skip bytes from a stream, after blocking until at least one byte -can be skipped. Except for blocking behavior, identical to skip.

-
Params
- -
Return values
- -

subscribe-to-input-stream: func

-

Create a pollable which will resolve once either the specified stream -has bytes available to read or the other end of the stream has been -closed. -The created pollable is a child resource of the input-stream. -Implementations may trap if the input-stream is dropped before -all derived pollables created with this function are dropped.

-
Params
- -
Return values
- -

drop-input-stream: func

-

Dispose of the specified input-stream, after which it may no longer -be used. -Implementations may trap if this input-stream is dropped while child -pollable resources are still alive. -After this input-stream is dropped, implementations may report any -corresponding output-stream has stream-state.closed.

-
Params
- -

check-write: func

-

Check readiness for writing. This function never blocks.

-

Returns the number of bytes permitted for the next call to write, -or an error. Calling write with more bytes than this function has -permitted will trap.

-

When this function returns 0 bytes, the subscribe-to-output-stream -pollable will become ready when this function will report at least -1 byte, or an error.

-
Params
- -
Return values
- -

write: func

-

Perform a write. This function never blocks.

-

Precondition: check-write gave permit of Ok(n) and contents has a -length of less than or equal to n. Otherwise, this function will trap.

-

returns Err(closed) without writing if the stream has closed since -the last call to check-write provided a permit.

-
Params
- -
Return values
- -

blocking-write-and-flush: func

-

Perform a write of up to 4096 bytes, and then flush the stream. Block -until all of these operations are complete, or an error occurs.

-

This is a convenience wrapper around the use of check-write, -subscribe-to-output-stream, write, and flush, and is implemented -with the following pseudo-code:

-
let pollable = subscribe-to-output-stream(this);
-while !contents.is_empty() {
-  // Wait for the stream to become writable
-  poll-oneoff(pollable);
-  let Ok(n) = check-write(this); // eliding error handling
-  let len = min(n, contents.len());
-  let (chunk, rest) = contents.split_at(len);
-  write(this, chunk);            // eliding error handling
-  contents = rest;
-}
-flush(this);
-// Wait for completion of `flush`
-poll-oneoff(pollable);
-// Check for any errors that arose during `flush`
-let _ = check-write(this);       // eliding error handling
-
-
Params
- -
Return values
- -

flush: func

-

Request to flush buffered output. This function never blocks.

-

This tells the output-stream that the caller intends any buffered -output to be flushed. the output which is expected to be flushed -is all that has been passed to write prior to this call.

-

Upon calling this function, the output-stream will not accept any -writes (check-write will return ok(0)) until the flush has -completed. The subscribe-to-output-stream pollable will become ready -when the flush has completed and the stream can accept more writes.

-
Params
- -
Return values
- -

blocking-flush: func

-

Request to flush buffered output, and block until flush completes -and stream is ready for writing again.

-
Params
- -
Return values
- -

subscribe-to-output-stream: func

-

Create a pollable which will resolve once the output-stream -is ready for more writing, or an error has occured. When this -pollable is ready, check-write will return ok(n) with n>0, or an -error.

-

If the stream is closed, this pollable is always ready immediately.

-

The created pollable is a child resource of the output-stream. -Implementations may trap if the output-stream is dropped before -all derived pollables created with this function are dropped.

-
Params
- -
Return values
- -

write-zeroes: func

-

Write zeroes to a stream.

-

this should be used precisely like write with the exact same -preconditions (must use check-write first), but instead of -passing a list of bytes, you simply pass the number of zero-bytes -that should be written.

-
Params
- -
Return values
- -

splice: func

-

Read from one stream and write to another.

-

This function returns the number of bytes transferred; it may be less -than len.

-

Unlike other I/O functions, this function blocks until all the data -read from the input stream has been written to the output stream.

-
Params
- -
Return values
- -

blocking-splice: func

-

Read from one stream and write to another, with blocking.

-

This is similar to splice, except that it blocks until at least -one byte can be read.

-
Params
- -
Return values
- -

forward: func

-

Forward the entire contents of an input stream to an output stream.

-

This function repeatedly reads from the input stream and writes -the data to the output stream, until the end of the input stream -is reached, or an error is encountered.

-

Unlike other I/O functions, this function blocks until the end -of the input stream is seen and all the data has been written to -the output stream.

-

This function returns the number of bytes transferred, and the status of -the output stream.

-
Params
- -
Return values
- -

drop-output-stream: func

-

Dispose of the specified output-stream, after which it may no longer -be used. -Implementations may trap if this output-stream is dropped while -child pollable resources are still alive. -After this output-stream is dropped, implementations may report any -corresponding input-stream has stream-state.closed.

-
Params
- diff --git a/imports.md b/imports.md new file mode 100644 index 0000000..4b44bf2 --- /dev/null +++ b/imports.md @@ -0,0 +1,386 @@ +

World imports

+ +

Import interface wasi:io/poll

+

A poll API intended to let users wait for I/O events on multiple handles +at once.

+
+

Types

+

resource pollable

+
+

Functions

+

poll-list: func

+

Poll for completion on a set of pollables.

+

This function takes a list of pollables, which identify I/O sources of +interest, and waits until one or more of the events is ready for I/O.

+

The result list<u32> contains one or more indices of handles in the +argument list that is ready for I/O.

+

If the list contains more elements than can be indexed with a u32 +value, this function traps.

+

A timeout can be implemented by adding a pollable from the +wasi-clocks API to the list.

+

This function does not return a result; polling in itself does not +do any I/O so it doesn't fail. If any of the I/O sources identified by +the pollables has an error, it is indicated by marking the source as +being reaedy for I/O.

+
Params
+ +
Return values
+
    +
  • list<u32>
  • +
+

poll-one: func

+

Poll for completion on a single pollable.

+

This function is similar to poll-list, but operates on only a single +pollable. When it returns, the handle is ready for I/O.

+
Params
+ +

Import interface wasi:io/streams

+

WASI I/O is an I/O abstraction API which is currently focused on providing +stream types.

+

In the future, the component model is expected to add built-in stream types; +when it does, they are expected to subsume this API.

+
+

Types

+

type pollable

+

pollable

+

+#### `enum stream-status` +

Streams provide a sequence of data and then end; once they end, they +no longer provide any further data.

+

For example, a stream reading from a file ends when the stream reaches +the end of the file. For another example, a stream reading from a +socket ends when the socket is closed.

+
Enum Cases
+
    +
  • +

    open

    +

    The stream is open and may produce further data. +

  • +
  • +

    ended

    +

    When reading, this indicates that the stream will not produce +further data. +When writing, this indicates that the stream will no longer be read. +Further writes are still permitted. +

  • +
+

resource input-stream

+

enum write-error

+

An error for output-stream operations.

+

Contrary to input-streams, a closed output-stream is reported using +an error.

+
Enum Cases
+
    +
  • +

    last-operation-failed

    +

    The last operation (a write or flush) failed before completion. +

  • +
  • +

    closed

    +

    The stream is closed: no more input will be accepted by the +stream. A closed output-stream will return this error on all +future operations. +

  • +
+

resource output-stream

+
+

Functions

+

[method]input-stream.read: func

+

Perform a non-blocking read from the stream.

+

This function returns a list of bytes containing the data that was +read, along with a stream-status which, indicates whether further +reads are expected to produce data. The returned list will contain up to +len bytes; it may return fewer than requested, but not more. An +empty list and stream-status:open indicates no more data is +available at this time, and that the pollable given by subscribe +will be ready when more data is available.

+

Once a stream has reached the end, subsequent calls to read or +skip will always report stream-status:ended rather than producing more +data.

+

When the caller gives a len of 0, it represents a request to read 0 +bytes. This read should always succeed and return an empty list and +the current stream-status.

+

The len parameter is a u64, which could represent a list of u8 which +is not possible to allocate in wasm32, or not desirable to allocate as +as a return value by the callee. The callee may return a list of bytes +less than len in size while more bytes are available for reading.

+
Params
+ +
Return values
+ +

[method]input-stream.blocking-read: func

+

Read bytes from a stream, after blocking until at least one byte can +be read. Except for blocking, identical to read.

+
Params
+ +
Return values
+ +

[method]input-stream.skip: func

+

Skip bytes from a stream.

+

This is similar to the read function, but avoids copying the +bytes into the instance.

+

Once a stream has reached the end, subsequent calls to read or +skip will always report end-of-stream rather than producing more +data.

+

This function returns the number of bytes skipped, along with a +stream-status indicating whether the end of the stream was +reached. The returned value will be at most len; it may be less.

+
Params
+ +
Return values
+ +

[method]input-stream.blocking-skip: func

+

Skip bytes from a stream, after blocking until at least one byte +can be skipped. Except for blocking behavior, identical to skip.

+
Params
+ +
Return values
+ +

[method]input-stream.subscribe: func

+

Create a pollable which will resolve once either the specified stream +has bytes available to read or the other end of the stream has been +closed. +The created pollable is a child resource of the input-stream. +Implementations may trap if the input-stream is dropped before +all derived pollables created with this function are dropped.

+
Params
+ +
Return values
+ +

[method]output-stream.check-write: func

+

Check readiness for writing. This function never blocks.

+

Returns the number of bytes permitted for the next call to write, +or an error. Calling write with more bytes than this function has +permitted will trap.

+

When this function returns 0 bytes, the subscribe pollable will +become ready when this function will report at least 1 byte, or an +error.

+
Params
+ +
Return values
+ +

[method]output-stream.write: func

+

Perform a write. This function never blocks.

+

Precondition: check-write gave permit of Ok(n) and contents has a +length of less than or equal to n. Otherwise, this function will trap.

+

returns Err(closed) without writing if the stream has closed since +the last call to check-write provided a permit.

+
Params
+ +
Return values
+ +

[method]output-stream.blocking-write-and-flush: func

+

Perform a write of up to 4096 bytes, and then flush the stream. Block +until all of these operations are complete, or an error occurs.

+

This is a convenience wrapper around the use of check-write, +subscribe, write, and flush, and is implemented with the +following pseudo-code:

+
let pollable = this.subscribe();
+while !contents.is_empty() {
+  // Wait for the stream to become writable
+  poll-one(pollable);
+  let Ok(n) = this.check-write(); // eliding error handling
+  let len = min(n, contents.len());
+  let (chunk, rest) = contents.split_at(len);
+  this.write(chunk  );            // eliding error handling
+  contents = rest;
+}
+this.flush();
+// Wait for completion of `flush`
+poll-oneoff(pollable);
+// Check for any errors that arose during `flush`
+let _ = this.check-write();         // eliding error handling
+
+
Params
+ +
Return values
+ +

[method]output-stream.flush: func

+

Request to flush buffered output. This function never blocks.

+

This tells the output-stream that the caller intends any buffered +output to be flushed. the output which is expected to be flushed +is all that has been passed to write prior to this call.

+

Upon calling this function, the output-stream will not accept any +writes (check-write will return ok(0)) until the flush has +completed. The subscribe pollable will become ready when the +flush has completed and the stream can accept more writes.

+
Params
+ +
Return values
+ +

[method]output-stream.blocking-flush: func

+

Request to flush buffered output, and block until flush completes +and stream is ready for writing again.

+
Params
+ +
Return values
+ +

[method]output-stream.subscribe: func

+

Create a pollable which will resolve once the output-stream +is ready for more writing, or an error has occured. When this +pollable is ready, check-write will return ok(n) with n>0, or an +error.

+

If the stream is closed, this pollable is always ready immediately.

+

The created pollable is a child resource of the output-stream. +Implementations may trap if the output-stream is dropped before +all derived pollables created with this function are dropped.

+
Params
+ +
Return values
+ +

[method]output-stream.write-zeroes: func

+

Write zeroes to a stream.

+

this should be used precisely like write with the exact same +preconditions (must use check-write first), but instead of +passing a list of bytes, you simply pass the number of zero-bytes +that should be written.

+
Params
+ +
Return values
+ +

[method]output-stream.blocking-write-zeroes-and-flush: func

+

Perform a write of up to 4096 zeroes, and then flush the stream. +Block until all of these operations are complete, or an error +occurs.

+

This is a convenience wrapper around the use of check-write, +subscribe, write-zeroes, and flush, and is implemented with +the following pseudo-code:

+
let pollable = this.subscribe();
+while num_zeroes != 0 {
+  // Wait for the stream to become writable
+  poll-oneoff(pollable);
+  let Ok(n) = this.check-write(); // eliding error handling
+  let len = min(n, num_zeroes);
+  this.write-zeroes(len);         // eliding error handling
+  num_zeroes -= len;
+}
+this.flush();
+// Wait for completion of `flush`
+poll-oneoff(pollable);
+// Check for any errors that arose during `flush`
+let _ = this.check-write();         // eliding error handling
+
+
Params
+ +
Return values
+ +

[method]output-stream.splice: func

+

Read from one stream and write to another.

+

This function returns the number of bytes transferred; it may be less +than len.

+

Unlike other I/O functions, this function blocks until all the data +read from the input stream has been written to the output stream.

+
Params
+ +
Return values
+ +

[method]output-stream.blocking-splice: func

+

Read from one stream and write to another, with blocking.

+

This is similar to splice, except that it blocks until at least +one byte can be read.

+
Params
+ +
Return values
+ +

[method]output-stream.forward: func

+

Forward the entire contents of an input stream to an output stream.

+

This function repeatedly reads from the input stream and writes +the data to the output stream, until the end of the input stream +is reached, or an error is encountered.

+

Unlike other I/O functions, this function blocks until the end +of the input stream is seen and all the data has been written to +the output stream.

+

This function returns the number of bytes transferred, and the status of +the output stream.

+
Params
+ +
Return values
+ diff --git a/wit/deps.lock b/wit/deps.lock deleted file mode 100644 index 6babe9f..0000000 --- a/wit/deps.lock +++ /dev/null @@ -1,4 +0,0 @@ -[poll] -url = "https://github.com/WebAssembly/wasi-poll/archive/main.tar.gz" -sha256 = "d4c27124f4c137eb538b5c92ba5858ed9042e11b24a2eef85d14becd0b7f55de" -sha512 = "422c01b273b4b1377ece6f2e4ba0dfc609ca8ef30a3e0be0e172e1303fcf7b3ca4c470f4dea6c51bdf114b0f5c871ebc4934dfe3bf217d66ea689748df2b1e55" diff --git a/wit/deps.toml b/wit/deps.toml deleted file mode 100644 index cefc393..0000000 --- a/wit/deps.toml +++ /dev/null @@ -1 +0,0 @@ -poll = "https://github.com/WebAssembly/wasi-poll/archive/main.tar.gz" diff --git a/wit/deps/poll/poll.wit b/wit/deps/poll/poll.wit deleted file mode 100644 index fa82b60..0000000 --- a/wit/deps/poll/poll.wit +++ /dev/null @@ -1,49 +0,0 @@ -/// A poll API intended to let users wait for I/O events on multiple handles -/// at once. -interface poll { - /// A "pollable" handle. - /// - /// This is conceptually represents a `stream<_, _>`, or in other words, - /// a stream that one can wait on, repeatedly, but which does not itself - /// produce any data. It's temporary scaffolding until component-model's - /// async features are ready. - /// - /// And at present, it is a `u32` instead of being an actual handle, until - /// the wit-bindgen implementation of handles and resources is ready. - /// - /// `pollable` lifetimes are not automatically managed. Users must ensure - /// that they do not outlive the resource they reference. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type pollable = u32 - - /// Dispose of the specified `pollable`, after which it may no longer - /// be used. - drop-pollable: func(this: pollable) - - /// Poll for completion on a set of pollables. - /// - /// This function takes a list of pollables, which identify I/O sources of - /// interest, and waits until one or more of the events is ready for I/O. - /// - /// The result `list` is the same length as the argument - /// `list`, and indicates the readiness of each corresponding - /// element in that list, with true indicating ready. A single call can - /// return multiple true elements. - /// - /// A timeout can be implemented by adding a pollable from the - /// wasi-clocks API to the list. - /// - /// This function does not return a `result`; polling in itself does not - /// do any I/O so it doesn't fail. If any of the I/O sources identified by - /// the pollables has an error, it is indicated by marking the source as - /// ready in the `list`. - /// - /// The "oneoff" in the name refers to the fact that this function must do a - /// linear scan through the entire list of subscriptions, which may be - /// inefficient if the number is large and the same subscriptions are used - /// many times. In the future, this is expected to be obsoleted by the - /// component model async proposal, which will include a scalable waiting - /// facility. - poll-oneoff: func(in: list) -> list -} diff --git a/wit/deps/poll/world.wit b/wit/deps/poll/world.wit deleted file mode 100644 index d08cadc..0000000 --- a/wit/deps/poll/world.wit +++ /dev/null @@ -1,5 +0,0 @@ -package wasi:poll - -world example-world { - import poll -} diff --git a/wit/poll.wit b/wit/poll.wit new file mode 100644 index 0000000..61ab432 --- /dev/null +++ b/wit/poll.wit @@ -0,0 +1,34 @@ +package wasi:io + +/// A poll API intended to let users wait for I/O events on multiple handles +/// at once. +interface poll { + /// A "pollable" handle. + resource pollable + + /// Poll for completion on a set of pollables. + /// + /// This function takes a list of pollables, which identify I/O sources of + /// interest, and waits until one or more of the events is ready for I/O. + /// + /// The result `list` contains one or more indices of handles in the + /// argument list that is ready for I/O. + /// + /// If the list contains more elements than can be indexed with a `u32` + /// value, this function traps. + /// + /// A timeout can be implemented by adding a pollable from the + /// wasi-clocks API to the list. + /// + /// This function does not return a `result`; polling in itself does not + /// do any I/O so it doesn't fail. If any of the I/O sources identified by + /// the pollables has an error, it is indicated by marking the source as + /// being reaedy for I/O. + poll-list: func(in: list) -> list + + /// Poll for completion on a single pollable. + /// + /// This function is similar to `poll-list`, but operates on only a single + /// pollable. When it returns, the handle is ready for I/O. + poll-one: func(in: pollable) +} diff --git a/wit/streams.wit b/wit/streams.wit index e2631f6..f8230ed 100644 --- a/wit/streams.wit +++ b/wit/streams.wit @@ -6,7 +6,7 @@ package wasi:io /// In the future, the component model is expected to add built-in stream types; /// when it does, they are expected to subsume this API. interface streams { - use wasi:poll/poll.{pollable} + use poll.{pollable} /// Streams provide a sequence of data and then end; once they end, they /// no longer provide any further data. @@ -24,115 +24,81 @@ interface streams { ended, } - /// An input bytestream. In the future, this will be replaced by handle - /// types. + /// An input bytestream. /// /// `input-stream`s are *non-blocking* to the extent practical on underlying /// platforms. I/O operations always return promptly; if fewer bytes are /// promptly available than requested, they return the number of bytes promptly /// available, which could even be zero. To wait for data to be available, - /// use the `subscribe-to-input-stream` function to obtain a `pollable` which - /// can be polled for using `wasi:poll/poll.poll_oneoff`. - /// - /// And at present, it is a `u32` instead of being an actual handle, until - /// the wit-bindgen implementation of handles and resources is ready. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type input-stream = u32 - - /// Perform a non-blocking read from the stream. - /// - /// This function returns a list of bytes containing the data that was - /// read, along with a `stream-status` which, indicates whether further - /// reads are expected to produce data. The returned list will contain up to - /// `len` bytes; it may return fewer than requested, but not more. An - /// empty list and `stream-status:open` indicates no more data is - /// available at this time, and that the pollable given by - /// `subscribe-to-input-stream` will be ready when more data is available. - /// - /// Once a stream has reached the end, subsequent calls to `read` or - /// `skip` will always report `stream-status:ended` rather than producing more - /// data. - /// - /// When the caller gives a `len` of 0, it represents a request to read 0 - /// bytes. This read should always succeed and return an empty list and - /// the current `stream-status`. - /// - /// The `len` parameter is a `u64`, which could represent a list of u8 which - /// is not possible to allocate in wasm32, or not desirable to allocate as - /// as a return value by the callee. The callee may return a list of bytes - /// less than `len` in size while more bytes are available for reading. - read: func( - this: input-stream, - /// The maximum number of bytes to read - len: u64 - ) -> result, stream-status>> - - /// Read bytes from a stream, after blocking until at least one byte can - /// be read. Except for blocking, identical to `read`. - blocking-read: func( - this: input-stream, - /// The maximum number of bytes to read - len: u64 - ) -> result, stream-status>> - - /// Skip bytes from a stream. - /// - /// This is similar to the `read` function, but avoids copying the - /// bytes into the instance. - /// - /// Once a stream has reached the end, subsequent calls to read or - /// `skip` will always report end-of-stream rather than producing more - /// data. - /// - /// This function returns the number of bytes skipped, along with a - /// `stream-status` indicating whether the end of the stream was - /// reached. The returned value will be at most `len`; it may be less. - skip: func( - this: input-stream, - /// The maximum number of bytes to skip. - len: u64, - ) -> result> + /// use the `subscribe` function to obtain a `pollable` which can be polled + /// for using `wasi:io/poll`. + resource input-stream { + /// Perform a non-blocking read from the stream. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a `stream-status` which, indicates whether further + /// reads are expected to produce data. The returned list will contain up to + /// `len` bytes; it may return fewer than requested, but not more. An + /// empty list and `stream-status:open` indicates no more data is + /// available at this time, and that the pollable given by `subscribe` + /// will be ready when more data is available. + /// + /// Once a stream has reached the end, subsequent calls to `read` or + /// `skip` will always report `stream-status:ended` rather than producing more + /// data. + /// + /// When the caller gives a `len` of 0, it represents a request to read 0 + /// bytes. This read should always succeed and return an empty list and + /// the current `stream-status`. + /// + /// The `len` parameter is a `u64`, which could represent a list of u8 which + /// is not possible to allocate in wasm32, or not desirable to allocate as + /// as a return value by the callee. The callee may return a list of bytes + /// less than `len` in size while more bytes are available for reading. + read: func( + /// The maximum number of bytes to read + len: u64 + ) -> result, stream-status>> - /// Skip bytes from a stream, after blocking until at least one byte - /// can be skipped. Except for blocking behavior, identical to `skip`. - blocking-skip: func( - this: input-stream, - /// The maximum number of bytes to skip. - len: u64, - ) -> result> + /// Read bytes from a stream, after blocking until at least one byte can + /// be read. Except for blocking, identical to `read`. + blocking-read: func( + /// The maximum number of bytes to read + len: u64 + ) -> result, stream-status>> - /// Create a `pollable` which will resolve once either the specified stream - /// has bytes available to read or the other end of the stream has been - /// closed. - /// The created `pollable` is a child resource of the `input-stream`. - /// Implementations may trap if the `input-stream` is dropped before - /// all derived `pollable`s created with this function are dropped. - subscribe-to-input-stream: func(this: input-stream) -> pollable + /// Skip bytes from a stream. + /// + /// This is similar to the `read` function, but avoids copying the + /// bytes into the instance. + /// + /// Once a stream has reached the end, subsequent calls to read or + /// `skip` will always report end-of-stream rather than producing more + /// data. + /// + /// This function returns the number of bytes skipped, along with a + /// `stream-status` indicating whether the end of the stream was + /// reached. The returned value will be at most `len`; it may be less. + skip: func( + /// The maximum number of bytes to skip. + len: u64, + ) -> result> - /// Dispose of the specified `input-stream`, after which it may no longer - /// be used. - /// Implementations may trap if this `input-stream` is dropped while child - /// `pollable` resources are still alive. - /// After this `input-stream` is dropped, implementations may report any - /// corresponding `output-stream` has `stream-state.closed`. - drop-input-stream: func(this: input-stream) + /// Skip bytes from a stream, after blocking until at least one byte + /// can be skipped. Except for blocking behavior, identical to `skip`. + blocking-skip: func( + /// The maximum number of bytes to skip. + len: u64, + ) -> result> - /// An output bytestream. In the future, this will be replaced by handle - /// types. - /// - /// `output-stream`s are *non-blocking* to the extent practical on - /// underlying platforms. Except where specified otherwise, I/O operations also - /// always return promptly, after the number of bytes that can be written - /// promptly, which could even be zero. To wait for the stream to be ready to - /// accept data, the `subscribe-to-output-stream` function to obtain a - /// `pollable` which can be polled for using `wasi:poll`. - /// - /// And at present, it is a `u32` instead of being an actual handle, until - /// the wit-bindgen implementation of handles and resources is ready. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type output-stream = u32 + /// Create a `pollable` which will resolve once either the specified stream + /// has bytes available to read or the other end of the stream has been + /// closed. + /// The created `pollable` is a child resource of the `input-stream`. + /// Implementations may trap if the `input-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + subscribe: func() -> pollable + } /// An error for output-stream operations. /// @@ -146,155 +112,174 @@ interface streams { /// future operations. closed } - /// Check readiness for writing. This function never blocks. - /// - /// Returns the number of bytes permitted for the next call to `write`, - /// or an error. Calling `write` with more bytes than this function has - /// permitted will trap. - /// - /// When this function returns 0 bytes, the `subscribe-to-output-stream` - /// pollable will become ready when this function will report at least - /// 1 byte, or an error. - check-write: func( - this: output-stream - ) -> result - /// Perform a write. This function never blocks. - /// - /// Precondition: check-write gave permit of Ok(n) and contents has a - /// length of less than or equal to n. Otherwise, this function will trap. + /// An output bytestream. /// - /// returns Err(closed) without writing if the stream has closed since - /// the last call to check-write provided a permit. - write: func( - this: output-stream, - contents: list - ) -> result<_, write-error> + /// `output-stream`s are *non-blocking* to the extent practical on + /// underlying platforms. Except where specified otherwise, I/O operations also + /// always return promptly, after the number of bytes that can be written + /// promptly, which could even be zero. To wait for the stream to be ready to + /// accept data, the `subscribe` function to obtain a `pollable` which can be + /// polled for using `wasi:io/poll`. + resource output-stream { + /// Check readiness for writing. This function never blocks. + /// + /// Returns the number of bytes permitted for the next call to `write`, + /// or an error. Calling `write` with more bytes than this function has + /// permitted will trap. + /// + /// When this function returns 0 bytes, the `subscribe` pollable will + /// become ready when this function will report at least 1 byte, or an + /// error. + check-write: func() -> result - /// Perform a write of up to 4096 bytes, and then flush the stream. Block - /// until all of these operations are complete, or an error occurs. - /// - /// This is a convenience wrapper around the use of `check-write`, - /// `subscribe-to-output-stream`, `write`, and `flush`, and is implemented - /// with the following pseudo-code: - /// - /// ```text - /// let pollable = subscribe-to-output-stream(this); - /// while !contents.is_empty() { - /// // Wait for the stream to become writable - /// poll-oneoff(pollable); - /// let Ok(n) = check-write(this); // eliding error handling - /// let len = min(n, contents.len()); - /// let (chunk, rest) = contents.split_at(len); - /// write(this, chunk); // eliding error handling - /// contents = rest; - /// } - /// flush(this); - /// // Wait for completion of `flush` - /// poll-oneoff(pollable); - /// // Check for any errors that arose during `flush` - /// let _ = check-write(this); // eliding error handling - /// ``` - blocking-write-and-flush: func( - this: output-stream, - contents: list - ) -> result<_, write-error> + /// Perform a write. This function never blocks. + /// + /// Precondition: check-write gave permit of Ok(n) and contents has a + /// length of less than or equal to n. Otherwise, this function will trap. + /// + /// returns Err(closed) without writing if the stream has closed since + /// the last call to check-write provided a permit. + write: func( + contents: list + ) -> result<_, write-error> - /// Request to flush buffered output. This function never blocks. - /// - /// This tells the output-stream that the caller intends any buffered - /// output to be flushed. the output which is expected to be flushed - /// is all that has been passed to `write` prior to this call. - /// - /// Upon calling this function, the `output-stream` will not accept any - /// writes (`check-write` will return `ok(0)`) until the flush has - /// completed. The `subscribe-to-output-stream` pollable will become ready - /// when the flush has completed and the stream can accept more writes. - flush: func( - this: output-stream, - ) -> result<_, write-error> + /// Perform a write of up to 4096 bytes, and then flush the stream. Block + /// until all of these operations are complete, or an error occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write`, and `flush`, and is implemented with the + /// following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while !contents.is_empty() { + /// // Wait for the stream to become writable + /// poll-one(pollable); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, contents.len()); + /// let (chunk, rest) = contents.split_at(len); + /// this.write(chunk ); // eliding error handling + /// contents = rest; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// poll-oneoff(pollable); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + blocking-write-and-flush: func( + contents: list + ) -> result<_, write-error> - /// Request to flush buffered output, and block until flush completes - /// and stream is ready for writing again. - blocking-flush: func( - this: output-stream, - ) -> result<_, write-error> + /// Request to flush buffered output. This function never blocks. + /// + /// This tells the output-stream that the caller intends any buffered + /// output to be flushed. the output which is expected to be flushed + /// is all that has been passed to `write` prior to this call. + /// + /// Upon calling this function, the `output-stream` will not accept any + /// writes (`check-write` will return `ok(0)`) until the flush has + /// completed. The `subscribe` pollable will become ready when the + /// flush has completed and the stream can accept more writes. + flush: func() -> result<_, write-error> - /// Create a `pollable` which will resolve once the output-stream - /// is ready for more writing, or an error has occured. When this - /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an - /// error. - /// - /// If the stream is closed, this pollable is always ready immediately. - /// - /// The created `pollable` is a child resource of the `output-stream`. - /// Implementations may trap if the `output-stream` is dropped before - /// all derived `pollable`s created with this function are dropped. - subscribe-to-output-stream: func(this: output-stream) -> pollable + /// Request to flush buffered output, and block until flush completes + /// and stream is ready for writing again. + blocking-flush: func() -> result<_, write-error> - /// Write zeroes to a stream. - /// - /// this should be used precisely like `write` with the exact same - /// preconditions (must use check-write first), but instead of - /// passing a list of bytes, you simply pass the number of zero-bytes - /// that should be written. - write-zeroes: func( - this: output-stream, - /// The number of zero-bytes to write - len: u64 - ) -> result<_, write-error> + /// Create a `pollable` which will resolve once the output-stream + /// is ready for more writing, or an error has occured. When this + /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an + /// error. + /// + /// If the stream is closed, this pollable is always ready immediately. + /// + /// The created `pollable` is a child resource of the `output-stream`. + /// Implementations may trap if the `output-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + subscribe: func() -> pollable - /// Read from one stream and write to another. - /// - /// This function returns the number of bytes transferred; it may be less - /// than `len`. - /// - /// Unlike other I/O functions, this function blocks until all the data - /// read from the input stream has been written to the output stream. - splice: func( - this: output-stream, - /// The stream to read from - src: input-stream, - /// The number of bytes to splice - len: u64, - ) -> result> + /// Write zeroes to a stream. + /// + /// this should be used precisely like `write` with the exact same + /// preconditions (must use check-write first), but instead of + /// passing a list of bytes, you simply pass the number of zero-bytes + /// that should be written. + write-zeroes: func( + /// The number of zero-bytes to write + len: u64 + ) -> result<_, write-error> - /// Read from one stream and write to another, with blocking. - /// - /// This is similar to `splice`, except that it blocks until at least - /// one byte can be read. - blocking-splice: func( - this: output-stream, - /// The stream to read from - src: input-stream, - /// The number of bytes to splice - len: u64, - ) -> result> + /// Perform a write of up to 4096 zeroes, and then flush the stream. + /// Block until all of these operations are complete, or an error + /// occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with + /// the following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while num_zeroes != 0 { + /// // Wait for the stream to become writable + /// poll-oneoff(pollable); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, num_zeroes); + /// this.write-zeroes(len); // eliding error handling + /// num_zeroes -= len; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// poll-oneoff(pollable); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + blocking-write-zeroes-and-flush: func( + /// The number of zero-bytes to write + len: u64 + ) -> result<_, write-error> - /// Forward the entire contents of an input stream to an output stream. - /// - /// This function repeatedly reads from the input stream and writes - /// the data to the output stream, until the end of the input stream - /// is reached, or an error is encountered. - /// - /// Unlike other I/O functions, this function blocks until the end - /// of the input stream is seen and all the data has been written to - /// the output stream. - /// - /// This function returns the number of bytes transferred, and the status of - /// the output stream. - forward: func( - this: output-stream, - /// The stream to read from - src: input-stream - ) -> result> + /// Read from one stream and write to another. + /// + /// This function returns the number of bytes transferred; it may be less + /// than `len`. + /// + /// Unlike other I/O functions, this function blocks until all the data + /// read from the input stream has been written to the output stream. + splice: func( + /// The stream to read from + src: input-stream, + /// The number of bytes to splice + len: u64, + ) -> result> + /// Read from one stream and write to another, with blocking. + /// + /// This is similar to `splice`, except that it blocks until at least + /// one byte can be read. + blocking-splice: func( + /// The stream to read from + src: input-stream, + /// The number of bytes to splice + len: u64, + ) -> result> - /// Dispose of the specified `output-stream`, after which it may no longer - /// be used. - /// Implementations may trap if this `output-stream` is dropped while - /// child `pollable` resources are still alive. - /// After this `output-stream` is dropped, implementations may report any - /// corresponding `input-stream` has `stream-state.closed`. - drop-output-stream: func(this: output-stream) + /// Forward the entire contents of an input stream to an output stream. + /// + /// This function repeatedly reads from the input stream and writes + /// the data to the output stream, until the end of the input stream + /// is reached, or an error is encountered. + /// + /// Unlike other I/O functions, this function blocks until the end + /// of the input stream is seen and all the data has been written to + /// the output stream. + /// + /// This function returns the number of bytes transferred, and the status of + /// the output stream. + forward: func( + /// The stream to read from + src: input-stream + ) -> result> + } } diff --git a/wit/world.wit b/wit/world.wit index e9ca233..8738dba 100644 --- a/wit/world.wit +++ b/wit/world.wit @@ -2,4 +2,5 @@ package wasi:io world imports { import streams + import poll }