Skip to content

polling on mix of libc-posix socket/file descriptors with non-libc WASI pollable handles #542

Open
@pavelsavara

Description

In context of dotnet sockets I would like to be able to wasi:poll_poll on mix of handles

  • which were created by consuming other WASI APIs, like wasi:http
  • and file handles obtained by using libc sockets APIs

The motivation to do it on single "system" call is to

  • be able to make progress on events from HTTP while socket is not ready and vice versa
    • if we called each select/poll or poll_poll separately, we would get blocked only on part of pollables
  • to be able to block (stop spinning) if there are no I/O events (and no CPU bound jobs)

At the moment, we can hack it via using libc internal descriptor_table_get_ref and it's data structures to obtain underlying pollables.
@dicej made similar hack for mio
But it's fragile and bad practice.

I can see few possible solutions

  • expose libc function accepting both lists of resources
    • void wasi_poll_plus_fd(poll_list_borrow_pollable_t *in, list_pollfd_t *in, wasip2_list_u32_t *ret)
    • this is with wit-bindgen generated C structures on APIs and with list of pollfd struct
  • we can try simpler C types, something like
    • void wasi_poll_plus_fd(int *pollables, int pollables_count, struct pollfd *fds, int fds_count, int **ret, int *ret_count)
  • or we can expose just the mapping from fds to list of pollables
    • void socket_fds_to_wasi_pollables(struct pollfd *fds, int fds_count, int **pollables, int pollables_count)
    • this would have to be called before each call to wasi:poll_poll
  • create libc file descriptor by registering external pollable
    • this would have to be un-registered later
    • calling socket oriented poll() API on any pollable (clock) feels confusing to me

Relevant code, possibly to be refactored and reused for above.

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();
}

cc @badeend

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions