Skip to content

Commit c29c9c6

Browse files
committed
wasip2: Deduplicate stream code in read/write/TCP
This commit extends the support in WebAssembly#734 by deduplicating the separate paths for reading a stream shared between `read` and `recvfrom`, for example. The `get_{read,write}_stream` methods now return more metadata and the TCP `recvfrom` implementation delegates to an internal `__wasilibc_read` to perform the actual read. This opens up the door to supporting nonblocking reads/writes on other streams in the future, for example.
1 parent 6016e71 commit c29c9c6

File tree

13 files changed

+321
-326
lines changed

13 files changed

+321
-326
lines changed

expected/wasm32-wasip2/defined-symbols.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ __wasilibc_poll_ready
334334
__wasilibc_populate_preopens
335335
__wasilibc_pthread_self
336336
__wasilibc_random
337-
__wasilibc_read_stream
337+
__wasilibc_read
338338
__wasilibc_rename_newat
339339
__wasilibc_rename_oldat
340340
__wasilibc_reset_preopens
@@ -348,7 +348,7 @@ __wasilibc_unspecified_addr
348348
__wasilibc_utimens
349349
__wasilibc_wasi_family_to_libc
350350
__wasilibc_wasi_to_sockaddr
351-
__wasilibc_write_stream
351+
__wasilibc_write
352352
__wasm_call_dtors
353353
__wcscoll_l
354354
__wcsftime_l

libc-bottom-half/cloudlibc/src/libc/poll/poll.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,10 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) {
218218
if (entry->vtable->get_read_stream) {
219219
streams_borrow_input_stream_t input;
220220
poll_own_pollable_t *pollable;
221-
if (entry->vtable->get_read_stream(entry->data, &input, NULL,
222-
&pollable) < 0)
221+
wasip2_read_t read;
222+
if (entry->vtable->get_read_stream(entry->data, &read) < 0)
223223
return -1;
224-
if (__wasilibc_poll_add_input_stream(&state, input, pollable) < 0)
224+
if (__wasilibc_poll_add_input_stream(&state, read.input, read.pollable) < 0)
225225
return -1;
226226
} else {
227227
errno = EOPNOTSUPP;
@@ -231,12 +231,10 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) {
231231

232232
if (pollfd->events & POLLWRNORM) {
233233
if (entry->vtable->get_write_stream) {
234-
streams_borrow_output_stream_t output;
235-
poll_own_pollable_t *pollable;
236-
if (entry->vtable->get_write_stream(entry->data, &output, NULL,
237-
&pollable) < 0)
234+
wasip2_write_t write;
235+
if (entry->vtable->get_write_stream(entry->data, &write) < 0)
238236
return -1;
239-
if (__wasilibc_poll_add_output_stream(&state, output, pollable) < 0)
237+
if (__wasilibc_poll_add_output_stream(&state, write.output, write.pollable) < 0)
240238
return -1;
241239
} else {
242240
errno = EOPNOTSUPP;

libc-bottom-half/cloudlibc/src/libc/unistd/read.c

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,41 +28,19 @@ ssize_t read(int fildes, void *buf, size_t nbyte) {
2828
}
2929
return bytes_read;
3030
#elif defined(__wasip2__)
31-
// First, check to see if this is a socket, in which case we defer to `recvfrom`:
3231
descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes);
3332
if (!entry)
3433
return -1;
35-
if (entry->vtable->recvfrom != NULL)
34+
if (entry->vtable->get_read_stream) {
35+
wasip2_read_t read;
36+
if (entry->vtable->get_read_stream(entry->data, &read) < 0)
37+
return -1;
38+
return __wasilibc_read(&read, buf, nbyte);
39+
}
40+
if (entry->vtable->recvfrom)
3641
return entry->vtable->recvfrom(entry->data, buf, nbyte, 0, NULL, NULL);
37-
38-
bool ok = false;
39-
40-
// Translate the file descriptor to an internal handle
41-
streams_borrow_input_stream_t input_stream;
42-
off_t *off;
43-
if (__wasilibc_read_stream(fildes, &input_stream, &off, NULL) < 0)
44-
return -1;
45-
46-
// Set up a WASI list of bytes to receive the results
47-
wasip2_list_u8_t contents;
48-
49-
// Read the bytes
50-
streams_stream_error_t stream_error;
51-
ok = streams_method_input_stream_blocking_read(input_stream,
52-
nbyte,
53-
&contents,
54-
&stream_error);
55-
if (!ok)
56-
return wasip2_handle_read_error(stream_error);
57-
58-
// Copy the bytes allocated in the canonical ABI to `buf`
59-
memcpy(buf, contents.ptr, contents.len);
60-
wasip2_list_u8_free(&contents);
61-
62-
// Update the offset
63-
if (off)
64-
*off += contents.len;
65-
return contents.len;
42+
errno = EOPNOTSUPP;
43+
return -1;
6644
#elif defined(__wasip3__)
6745
filesystem_tuple2_stream_u8_future_result_void_error_code_t *stream;
6846
off_t *off;

libc-bottom-half/cloudlibc/src/libc/unistd/write.c

Lines changed: 9 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -29,58 +29,19 @@ ssize_t write(int fildes, const void *buf, size_t nbyte) {
2929
}
3030
return bytes_written;
3131
#elif defined(__wasip2__)
32-
// First, check to see if this is a socket, in which case we defer to `sendto`:
3332
descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes);
3433
if (!entry)
3534
return -1;
36-
if (entry->vtable->sendto != NULL)
37-
return entry->vtable->sendto(entry->data, buf, nbyte, 0, NULL, 0);
38-
39-
streams_borrow_output_stream_t output_stream;
40-
poll_borrow_pollable_t pollable;
41-
bool ok = false;
42-
filesystem_error_code_t error_code;
43-
44-
// Translate the file descriptor to an internal handle
45-
off_t *off;
46-
if (__wasilibc_write_stream(fildes, &output_stream, &off, &pollable) < 0)
47-
return -1;
48-
49-
// Check readiness for writing
50-
uint64_t num_bytes_permitted = 0;
51-
streams_stream_error_t stream_error;
52-
while (num_bytes_permitted == 0) {
53-
ok = streams_method_output_stream_check_write(output_stream,
54-
&num_bytes_permitted,
55-
&stream_error);
56-
if (!ok)
57-
return wasip2_handle_write_error(stream_error);
58-
59-
if (num_bytes_permitted == 0)
60-
poll_method_pollable_block(pollable);
35+
if (entry->vtable->get_write_stream) {
36+
wasip2_write_t write;
37+
if (entry->vtable->get_write_stream(entry->data, &write) < 0)
38+
return -1;
39+
return __wasilibc_write(&write, buf, nbyte);
6140
}
62-
63-
// Convert the buffer to a WASI list of bytes
64-
wasip2_list_u8_t contents;
65-
contents.len = num_bytes_permitted < nbyte ? num_bytes_permitted : nbyte;
66-
contents.ptr = (uint8_t*) buf;
67-
68-
// Write the bytes to the stream
69-
ok = streams_method_output_stream_write(output_stream,
70-
&contents,
71-
&stream_error);
72-
if (!ok)
73-
return wasip2_handle_write_error(stream_error);
74-
75-
ok = streams_method_output_stream_blocking_flush(output_stream,
76-
&stream_error);
77-
if (!ok)
78-
return wasip2_handle_write_error(stream_error);
79-
80-
81-
if (off)
82-
*off += contents.len;
83-
return contents.len;
41+
if (entry->vtable->sendto)
42+
return entry->vtable->sendto(entry->data, buf, nbyte, 0, NULL, 0);
43+
errno = EOPNOTSUPP;
44+
return -1;
8445
#elif defined(__wasip3__)
8546
wasip3_write_t *write_end;
8647
off_t *off;

libc-bottom-half/headers/private/wasi/descriptor_table.h

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,34 @@
88
#include <sys/stat.h>
99
#include <netinet/in.h>
1010

11+
#ifdef __wasip2__
12+
// Metadata for WASI reads which is used to delegate to `__wasilibc_read(...)`
13+
// to perform the actual read of a stream.
14+
typedef struct wasip2_read_t {
15+
// The `wasi:io/streams.input-stream` that this is reading from.
16+
streams_borrow_input_stream_t input;
17+
// An optional pointer to the internal offset of this stream, updated on
18+
// successful reads.
19+
off_t *offset;
20+
// A required pointer to an owned pollable for `input`. This is lazily
21+
// initialized as-necessary.
22+
poll_own_pollable_t *pollable;
23+
// Whether or not this read will use blocking I/O.
24+
bool blocking;
25+
// The timeout, in nanoseconds, for this operation.
26+
monotonic_clock_duration_t timeout;
27+
} wasip2_read_t;
28+
29+
// Same as `wasip2_read_t`, but for writes.
30+
typedef struct wasip2_write_t {
31+
streams_borrow_output_stream_t output;
32+
off_t *offset;
33+
poll_own_pollable_t *pollable;
34+
bool blocking;
35+
monotonic_clock_duration_t timeout;
36+
} wasip2_write_t;
37+
#endif
38+
1139
#ifdef __wasip3__
1240
// create an alias to distinguish the handle type in the API
1341
typedef uint32_t waitable_t;
@@ -44,14 +72,12 @@ typedef struct descriptor_vtable_t {
4472
// Generic I/O
4573

4674
#ifdef __wasip2__
47-
/// Looks up a `wasi:io/streams.input-stream` object and stores it in
48-
/// the first argument. If provide also stores a pointer to the internal
49-
/// `off_t` offset and `pollable` for this object. The returned pointers
50-
/// point within the descriptor itself.
51-
int (*get_read_stream)(void*, streams_borrow_input_stream_t*, off_t**, poll_own_pollable_t**);
52-
75+
/// Looks up metadata to perform a read operation for this stream. This is used
76+
/// to implement the `read` syscall, for example, and is also used with `poll`
77+
/// when waiting for readability.
78+
int (*get_read_stream)(void*, wasip2_read_t*);
5379
/// Same as `get_read_stream`, but for output streams.
54-
int (*get_write_stream)(void*, streams_borrow_output_stream_t*, off_t**, poll_own_pollable_t**);
80+
int (*get_write_stream)(void*, wasip2_write_t*);
5581
#endif
5682
#ifdef __wasip3__
5783
int (*get_read_stream3)(void*, filesystem_tuple2_stream_u8_future_result_void_error_code_t **out, off_t** off);

libc-bottom-half/headers/private/wasi/file_utils.h

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,16 @@ static int fd_to_file_handle(int fd, filesystem_borrow_descriptor_t* result) {
6565
#endif
6666

6767
#ifdef __wasip2__
68-
// Gets an `output-stream` borrow from the `fd` provided.
69-
int __wasilibc_write_stream(int fd,
70-
streams_borrow_output_stream_t *out,
71-
off_t **off,
72-
poll_borrow_pollable_t *pollable);
73-
74-
// Gets an `input-stream` borrow from the `fd` provided.
75-
int __wasilibc_read_stream(int fd,
76-
streams_borrow_input_stream_t *out,
77-
off_t **off,
78-
poll_borrow_pollable_t *pollable);
68+
// Reads from `read` into `buf`/`len`
69+
//
70+
// This perform the read configured by `read`, e.g. whether it's blocking or
71+
// not, and places the result in the specified buffer. Used to implement
72+
// `read` and `recvfrom`, for example.
73+
ssize_t __wasilibc_read(wasip2_read_t *read, void *buf, size_t len);
74+
// Same as `__wasilibc_read`, but for writes.
75+
ssize_t __wasilibc_write(wasip2_write_t *write, const void *buf, size_t len);
7976
#endif
77+
8078
#ifdef __wasip3__
8179
int __wasilibc_write_stream3(int fildes, wasip3_write_t **write_end, off_t **off);
8280
int __wasilibc_read_stream3(int fildes, filesystem_tuple2_stream_u8_future_result_void_error_code_t **stream, off_t **off);

0 commit comments

Comments
 (0)