diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3cba299..5df3460 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,14 +10,12 @@ jobs: name: Check ABI files are up-to-date runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: ensure `./wit/deps` are in sync run: | - curl -Lo 'wit-deps' https://github.com/bytecodealliance/wit-deps/releases/download/v0.3.0/wit-deps-x86_64-unknown-linux-musl + curl -Lo 'wit-deps' https://github.com/bytecodealliance/wit-deps/releases/download/v0.3.3/wit-deps-x86_64-unknown-linux-musl chmod +x wit-deps ./wit-deps lock git add -N wit/deps git diff --exit-code - - uses: WebAssembly/wit-abi-up-to-date@v13 - with: - wit-abi-tag: wit-abi-0.11.0 + - uses: WebAssembly/wit-abi-up-to-date@v15 diff --git a/example-world.md b/imports.md similarity index 74% rename from example-world.md rename to imports.md index f716f89..f82d3c1 100644 --- a/example-world.md +++ b/imports.md @@ -1,60 +1,51 @@ -

World example-world

+

World imports

-

Import interface wasi:poll/poll

+

Import interface wasi:io/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.

+

resource pollable


Functions

-

drop-pollable: func

-

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

-
Params
- -

poll-oneoff: func

+

poll-list: func

Poll for completion on a set of pollables.

-

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.

-

Note that the return type would ideally be list<bool>, but that would -be more difficult to polyfill given the current state of wit-bindgen. -See https://github.com/bytecodealliance/preview2-prototyping/pull/11#issuecomment-1329873061 -for details. For now, we use zero to mean "not ready" and non-zero to -mean "ready".

+

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
+

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:clocks/monotonic-clock

WASI Monotonic Clock is a clock API intended to let users measure elapsed @@ -98,7 +89,7 @@ reached.

Return values

Import interface wasi:clocks/wall-clock

WASI Wall Clock is a clock API intended to let users query the current diff --git a/wit/deps.lock b/wit/deps.lock index 6babe9f..41d5cbb 100644 --- a/wit/deps.lock +++ b/wit/deps.lock @@ -1,4 +1,4 @@ -[poll] -url = "https://github.com/WebAssembly/wasi-poll/archive/main.tar.gz" -sha256 = "d4c27124f4c137eb538b5c92ba5858ed9042e11b24a2eef85d14becd0b7f55de" -sha512 = "422c01b273b4b1377ece6f2e4ba0dfc609ca8ef30a3e0be0e172e1303fcf7b3ca4c470f4dea6c51bdf114b0f5c871ebc4934dfe3bf217d66ea689748df2b1e55" +[io] +url = "https://github.com/WebAssembly/wasi-io/archive/main.tar.gz" +sha256 = "6e20bcf4d4f5466b60c05ea8da7289ca361a7febdd22ab1a531e5ef7e394ab8d" +sha512 = "21f6689bce6ed6d9e3bd96372e5c7ed003a7aefbf8d49b4eea949dfbd265cf57a0d7dc67aa71e3de75d48fcc2c0cfe5f06f7e9e7959a23bc98f77da85f4161b9" diff --git a/wit/deps.toml b/wit/deps.toml index cefc393..f29c61c 100644 --- a/wit/deps.toml +++ b/wit/deps.toml @@ -1 +1,2 @@ -poll = "https://github.com/WebAssembly/wasi-poll/archive/main.tar.gz" +# Temporarily use the resources branch. +io = "https://github.com/WebAssembly/wasi-io/archive/main.tar.gz" diff --git a/wit/deps/io/poll.wit b/wit/deps/io/poll.wit new file mode 100644 index 0000000..e95762b --- /dev/null +++ b/wit/deps/io/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: borrow) +} diff --git a/wit/deps/io/streams.wit b/wit/deps/io/streams.wit new file mode 100644 index 0000000..eeeff50 --- /dev/null +++ b/wit/deps/io/streams.wit @@ -0,0 +1,285 @@ +package wasi:io + +/// 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. +interface streams { + use poll.{pollable} + + /// 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 stream-status { + /// The stream is open and may produce further data. + open, + /// 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. + ended, + } + + /// 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` 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>> + + /// 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>> + + /// 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> + + /// 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> + + /// 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. + /// + /// Contrary to input-streams, a closed output-stream is reported using + /// an error. + enum write-error { + /// The last operation (a write or flush) failed before completion. + last-operation-failed, + /// 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. + closed + } + + /// An output bytestream. + /// + /// `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. 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> + + /// 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-one(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. 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> + + /// Request to flush buffered output, and block until flush completes + /// and stream is ready for writing again. + blocking-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: func() -> pollable + + /// 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> + + /// 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-one(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-one(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> + + /// 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> + + /// 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/deps/io/world.wit b/wit/deps/io/world.wit new file mode 100644 index 0000000..8738dba --- /dev/null +++ b/wit/deps/io/world.wit @@ -0,0 +1,6 @@ +package wasi:io + +world imports { + import streams + import poll +} 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/monotonic-clock.wit b/wit/monotonic-clock.wit index fb8424e..703a5fb 100644 --- a/wit/monotonic-clock.wit +++ b/wit/monotonic-clock.wit @@ -9,7 +9,7 @@ /// /// It is intended for measuring elapsed time. interface monotonic-clock { - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} /// A timestamp in nanoseconds. type instant = u64