mirror of https://github.com/WebAssembly/wasi-libc
Browse Source
This enables `wasm32-wasip2` support for `close`, `poll`, and `pselect`. I cheated a bit for the latter by re-implementing `pselect` in terms of `poll` to avoid having to implement wasip2 versions of both. Signed-off-by: Joel Dice <joel.dice@fermyon.com> Co-authored-by: Dave Bakker <github@davebakker.io>pull/488/head
Joel Dice
7 months ago
committed by
GitHub
6 changed files with 419 additions and 66 deletions
@ -0,0 +1,257 @@ |
|||
#include <errno.h> |
|||
#include <poll.h> |
|||
|
|||
#include <wasi/descriptor_table.h> |
|||
|
|||
typedef struct { |
|||
poll_own_pollable_t pollable; |
|||
struct pollfd *pollfd; |
|||
descriptor_table_entry_t *entry; |
|||
short events; |
|||
} state_t; |
|||
|
|||
int poll_wasip2(struct pollfd *fds, size_t nfds, int timeout) |
|||
{ |
|||
int event_count = 0; |
|||
for (size_t i = 0; i < nfds; ++i) { |
|||
fds[i].revents = 0; |
|||
} |
|||
|
|||
size_t max_pollables = (2 * nfds) + 1; |
|||
state_t states[max_pollables]; |
|||
size_t state_index = 0; |
|||
for (size_t i = 0; i < nfds; ++i) { |
|||
struct pollfd *pollfd = fds + i; |
|||
descriptor_table_entry_t *entry; |
|||
if (descriptor_table_get_ref(pollfd->fd, &entry)) { |
|||
switch (entry->tag) { |
|||
case DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET: { |
|||
tcp_socket_t *socket = &(entry->tcp_socket); |
|||
switch (socket->state.tag) { |
|||
case TCP_SOCKET_STATE_CONNECTING: |
|||
case TCP_SOCKET_STATE_LISTENING: { |
|||
if ((pollfd->events & |
|||
(POLLRDNORM | POLLWRNORM)) != 0) { |
|||
states[state_index++] = (state_t){ |
|||
.pollable = |
|||
socket->socket_pollable, |
|||
.pollfd = pollfd, |
|||
.entry = entry, |
|||
.events = pollfd->events |
|||
}; |
|||
} |
|||
break; |
|||
} |
|||
|
|||
case TCP_SOCKET_STATE_CONNECTED: { |
|||
if ((pollfd->events & POLLRDNORM) != |
|||
0) { |
|||
states[state_index++] = (state_t){ |
|||
.pollable = |
|||
socket->state |
|||
.connected |
|||
.input_pollable, |
|||
.pollfd = pollfd, |
|||
.entry = entry, |
|||
.events = POLLRDNORM |
|||
}; |
|||
} |
|||
if ((pollfd->events & POLLWRNORM) != |
|||
0) { |
|||
states[state_index++] = (state_t){ |
|||
.pollable = |
|||
socket->state |
|||
.connected |
|||
.output_pollable, |
|||
.pollfd = pollfd, |
|||
.entry = entry, |
|||
.events = POLLWRNORM |
|||
}; |
|||
} |
|||
break; |
|||
} |
|||
|
|||
case TCP_SOCKET_STATE_CONNECT_FAILED: { |
|||
if (pollfd->revents == 0) { |
|||
++event_count; |
|||
} |
|||
pollfd->revents |= pollfd->events; |
|||
break; |
|||
} |
|||
|
|||
default: |
|||
errno = ENOTSUP; |
|||
return -1; |
|||
} |
|||
break; |
|||
} |
|||
|
|||
case DESCRIPTOR_TABLE_ENTRY_UDP_SOCKET: { |
|||
udp_socket_t *socket = &(entry->udp_socket); |
|||
switch (socket->state.tag) { |
|||
case UDP_SOCKET_STATE_UNBOUND: |
|||
case UDP_SOCKET_STATE_BOUND_NOSTREAMS: { |
|||
if (pollfd->revents == 0) { |
|||
++event_count; |
|||
} |
|||
pollfd->revents |= pollfd->events; |
|||
break; |
|||
} |
|||
|
|||
case UDP_SOCKET_STATE_BOUND_STREAMING: |
|||
case UDP_SOCKET_STATE_CONNECTED: { |
|||
udp_socket_streams_t *streams; |
|||
if (socket->state.tag == |
|||
UDP_SOCKET_STATE_BOUND_STREAMING) { |
|||
streams = &( |
|||
socket->state |
|||
.bound_streaming |
|||
.streams); |
|||
} else { |
|||
streams = &( |
|||
socket->state.connected |
|||
.streams); |
|||
} |
|||
if ((pollfd->events & POLLRDNORM) != |
|||
0) { |
|||
states[state_index++] = (state_t){ |
|||
.pollable = |
|||
streams->incoming_pollable, |
|||
.pollfd = pollfd, |
|||
.entry = entry, |
|||
.events = POLLRDNORM |
|||
}; |
|||
} |
|||
if ((pollfd->events & POLLWRNORM) != |
|||
0) { |
|||
states[state_index++] = (state_t){ |
|||
.pollable = |
|||
streams->outgoing_pollable, |
|||
.pollfd = pollfd, |
|||
.entry = entry, |
|||
.events = POLLWRNORM |
|||
}; |
|||
} |
|||
break; |
|||
} |
|||
|
|||
default: |
|||
errno = ENOTSUP; |
|||
return -1; |
|||
} |
|||
break; |
|||
} |
|||
|
|||
default: |
|||
errno = ENOTSUP; |
|||
return -1; |
|||
} |
|||
} else { |
|||
abort(); |
|||
} |
|||
} |
|||
|
|||
if (event_count > 0 && timeout != 0) { |
|||
return event_count; |
|||
} |
|||
|
|||
poll_borrow_pollable_t pollables[state_index + 1]; |
|||
for (size_t i = 0; i < state_index; ++i) { |
|||
pollables[i] = poll_borrow_pollable(states[i].pollable); |
|||
} |
|||
|
|||
poll_own_pollable_t timeout_pollable; |
|||
size_t pollable_count = state_index; |
|||
if (timeout >= 0) { |
|||
timeout_pollable = monotonic_clock_subscribe_duration( |
|||
((monotonic_clock_duration_t)timeout) * 1000000); |
|||
pollables[pollable_count++] = |
|||
poll_borrow_pollable(timeout_pollable); |
|||
} |
|||
|
|||
wasip2_list_u32_t ready; |
|||
poll_list_borrow_pollable_t list = { |
|||
.ptr = (poll_borrow_pollable_t *)&pollables, |
|||
.len = pollable_count |
|||
}; |
|||
poll_poll(&list, &ready); |
|||
|
|||
for (size_t i = 0; i < ready.len; ++i) { |
|||
size_t index = ready.ptr[i]; |
|||
if (index < state_index) { |
|||
state_t *state = &states[index]; |
|||
if (state->entry->tag == |
|||
DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET && |
|||
state->entry->tcp_socket.state.tag == |
|||
TCP_SOCKET_STATE_CONNECTING) { |
|||
tcp_socket_t *socket = |
|||
&(state->entry->tcp_socket); |
|||
tcp_borrow_tcp_socket_t borrow = |
|||
tcp_borrow_tcp_socket(socket->socket); |
|||
tcp_tuple2_own_input_stream_own_output_stream_t |
|||
tuple; |
|||
tcp_error_code_t error; |
|||
if (tcp_method_tcp_socket_finish_connect( |
|||
borrow, &tuple, &error)) { |
|||
streams_borrow_input_stream_t |
|||
input_stream_borrow = |
|||
streams_borrow_input_stream( |
|||
tuple.f0); |
|||
streams_own_pollable_t input_pollable = |
|||
streams_method_input_stream_subscribe( |
|||
input_stream_borrow); |
|||
streams_borrow_output_stream_t |
|||
output_stream_borrow = |
|||
streams_borrow_output_stream( |
|||
tuple.f1); |
|||
streams_own_pollable_t output_pollable = |
|||
streams_method_output_stream_subscribe( |
|||
output_stream_borrow); |
|||
socket->state = |
|||
(tcp_socket_state_t){ .tag = TCP_SOCKET_STATE_CONNECTED, |
|||
.connected = { |
|||
.input_pollable = |
|||
input_pollable, |
|||
.input = |
|||
tuple.f0, |
|||
.output_pollable = |
|||
output_pollable, |
|||
.output = |
|||
tuple.f1, |
|||
} }; |
|||
if (state->pollfd->revents == 0) { |
|||
++event_count; |
|||
} |
|||
state->pollfd->revents |= state->events; |
|||
} else if (error == |
|||
NETWORK_ERROR_CODE_WOULD_BLOCK) { |
|||
// No events yet -- application will need to poll again
|
|||
} else { |
|||
socket->state = |
|||
(tcp_socket_state_t){ .tag = TCP_SOCKET_STATE_CONNECT_FAILED, |
|||
.connect_failed = { |
|||
.error_code = |
|||
error, |
|||
} }; |
|||
if (state->pollfd->revents == 0) { |
|||
++event_count; |
|||
} |
|||
state->pollfd->revents |= state->events; |
|||
} |
|||
} else { |
|||
if (state->pollfd->revents == 0) { |
|||
++event_count; |
|||
} |
|||
state->pollfd->revents |= state->events; |
|||
} |
|||
} |
|||
} |
|||
|
|||
wasip2_list_u32_free(&ready); |
|||
|
|||
if (timeout >= 0) { |
|||
poll_pollable_drop_own(timeout_pollable); |
|||
} |
|||
|
|||
return event_count; |
|||
} |
Loading…
Reference in new issue