Skip to content

Commit a2ed34e

Browse files
dicejbadeend
andauthored
wasip2 support for close, poll, pselect (#486)
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>
1 parent 6593687 commit a2ed34e

6 files changed

Lines changed: 419 additions & 66 deletions

File tree

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ LIBC_BOTTOM_HALF_OMIT_SOURCES := \
9191
$(LIBC_BOTTOM_HALF_SOURCES)/listen.c \
9292
$(LIBC_BOTTOM_HALF_SOURCES)/accept-wasip2.c \
9393
$(LIBC_BOTTOM_HALF_SOURCES)/shutdown.c \
94-
$(LIBC_BOTTOM_HALF_SOURCES)/sockopt.c
94+
$(LIBC_BOTTOM_HALF_SOURCES)/sockopt.c \
95+
$(LIBC_BOTTOM_HALF_SOURCES)/poll-wasip2.c
9596
LIBC_BOTTOM_HALF_ALL_SOURCES := $(filter-out $(LIBC_BOTTOM_HALF_OMIT_SOURCES),$(LIBC_BOTTOM_HALF_ALL_SOURCES))
9697
# Omit p2-specific headers from include-all.c test.
9798
INCLUDE_ALL_CLAUSES := -not -name wasip2.h -not -name descriptor_table.h

expected/wasm32-wasip2/defined-symbols.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,7 @@ poll_method_pollable_ready
10121012
poll_poll
10131013
poll_pollable_drop_borrow
10141014
poll_pollable_drop_own
1015+
poll_wasip2
10151016
posix_close
10161017
posix_fadvise
10171018
posix_fallocate

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

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include <poll.h>
88
#include <stdbool.h>
99

10-
int poll(struct pollfd *fds, size_t nfds, int timeout) {
10+
static int poll_wasip1(struct pollfd *fds, size_t nfds, int timeout) {
1111
// Construct events for poll().
1212
size_t maxevents = 2 * nfds + 1;
1313
__wasi_subscription_t subscriptions[maxevents];
@@ -127,3 +127,48 @@ int poll(struct pollfd *fds, size_t nfds, int timeout) {
127127
}
128128
return retval;
129129
}
130+
131+
#ifdef __wasilibc_use_wasip2
132+
#include <wasi/descriptor_table.h>
133+
134+
int poll_wasip2(struct pollfd *fds, size_t nfds, int timeout);
135+
136+
int poll(struct pollfd* fds, nfds_t nfds, int timeout)
137+
{
138+
bool found_socket = false;
139+
bool found_non_socket = false;
140+
for (size_t i = 0; i < nfds; ++i) {
141+
descriptor_table_entry_t* entry;
142+
if (descriptor_table_get_ref(fds[i].fd, &entry)) {
143+
found_socket = true;
144+
} else {
145+
found_non_socket = true;
146+
}
147+
}
148+
149+
if (found_socket) {
150+
if (found_non_socket) {
151+
// We currently don't support polling a mix of non-sockets and
152+
// sockets here (though you can do it by using the host APIs
153+
// directly), and we probably won't until we've migrated entirely to
154+
// WASI 0.2.
155+
errno = ENOTSUP;
156+
return -1;
157+
}
158+
159+
return poll_wasip2(fds, nfds, timeout);
160+
} else if (found_non_socket) {
161+
return poll_wasip1(fds, nfds, timeout);
162+
} else if (timeout >= 0) {
163+
return poll_wasip2(fds, nfds, timeout);
164+
} else {
165+
errno = ENOTSUP;
166+
return -1;
167+
}
168+
}
169+
#else // not __wasilibc_use_wasip2
170+
int poll(struct pollfd* fds, nfds_t nfds, int timeout)
171+
{
172+
return poll_wasip1(fds, nfds, timeout);
173+
}
174+
#endif // not __wasilibc_use_wasip2

libc-bottom-half/cloudlibc/src/libc/sys/select/pselect.c

Lines changed: 38 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <wasi/api.h>
1010
#include <errno.h>
11+
#include <poll.h>
1112

1213
int pselect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds,
1314
fd_set *restrict errorfds, const struct timespec *restrict timeout,
@@ -33,93 +34,66 @@ int pselect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds,
3334
if (writefds == NULL)
3435
writefds = &empty;
3536

36-
// Determine the maximum number of events.
37-
size_t maxevents = readfds->__nfds + writefds->__nfds + 1;
38-
__wasi_subscription_t subscriptions[maxevents];
39-
size_t nsubscriptions = 0;
40-
41-
// Convert the readfds set.
37+
struct pollfd poll_fds[readfds->__nfds + writefds->__nfds];
38+
size_t poll_nfds = 0;
39+
4240
for (size_t i = 0; i < readfds->__nfds; ++i) {
4341
int fd = readfds->__fds[i];
4442
if (fd < nfds) {
45-
__wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
46-
*subscription = (__wasi_subscription_t){
47-
.userdata = fd,
48-
.u.tag = __WASI_EVENTTYPE_FD_READ,
49-
.u.u.fd_read.file_descriptor = fd,
50-
};
43+
poll_fds[poll_nfds++] = (struct pollfd){
44+
.fd = fd,
45+
.events = POLLRDNORM,
46+
.revents = 0
47+
};
5148
}
5249
}
53-
54-
// Convert the writefds set.
50+
5551
for (size_t i = 0; i < writefds->__nfds; ++i) {
5652
int fd = writefds->__fds[i];
5753
if (fd < nfds) {
58-
__wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
59-
*subscription = (__wasi_subscription_t){
60-
.userdata = fd,
61-
.u.tag = __WASI_EVENTTYPE_FD_WRITE,
62-
.u.u.fd_write.file_descriptor = fd,
54+
poll_fds[poll_nfds++] = (struct pollfd){
55+
.fd = fd,
56+
.events = POLLWRNORM,
57+
.revents = 0
6358
};
6459
}
6560
}
6661

67-
// Create extra event for the timeout.
68-
if (timeout != NULL) {
69-
__wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
70-
*subscription = (__wasi_subscription_t){
71-
.u.tag = __WASI_EVENTTYPE_CLOCK,
72-
.u.u.clock.id = __WASI_CLOCKID_REALTIME,
73-
};
74-
if (!timespec_to_timestamp_clamp(timeout, &subscription->u.u.clock.timeout)) {
62+
int poll_timeout;
63+
if (timeout) {
64+
uint64_t timeout_u64;
65+
if (!timespec_to_timestamp_clamp(timeout, &timeout_u64) ) {
7566
errno = EINVAL;
7667
return -1;
7768
}
78-
}
7969

80-
// Execute poll().
81-
size_t nevents;
82-
__wasi_event_t events[nsubscriptions];
83-
__wasi_errno_t error =
84-
__wasi_poll_oneoff(subscriptions, events, nsubscriptions, &nevents);
85-
if (error != 0) {
86-
// WASI's poll requires at least one subscription, or else it returns
87-
// `EINVAL`. Since a `pselect` with nothing to wait for is valid in POSIX,
88-
// return `ENOTSUP` to indicate that we don't support that case.
89-
//
90-
// Wasm has no signal handling, so if none of the user-provided `pollfd`
91-
// elements, nor the timeout, led us to producing even one subscription
92-
// to wait for, there would be no way for the poll to wake up. WASI
93-
// returns `EINVAL` in this case, but for users of `poll`, `ENOTSUP` is
94-
// more likely to be understood.
95-
if (nsubscriptions == 0)
96-
errno = ENOTSUP;
97-
else
98-
errno = error;
99-
return -1;
100-
}
70+
// Convert nanoseconds to milliseconds:
71+
timeout_u64 /= 1000000;
10172

102-
// Test for EBADF.
103-
for (size_t i = 0; i < nevents; ++i) {
104-
const __wasi_event_t *event = &events[i];
105-
if ((event->type == __WASI_EVENTTYPE_FD_READ ||
106-
event->type == __WASI_EVENTTYPE_FD_WRITE) &&
107-
event->error == __WASI_ERRNO_BADF) {
108-
errno = EBADF;
109-
return -1;
73+
if (timeout_u64 > INT_MAX) {
74+
timeout_u64 = INT_MAX;
11075
}
76+
77+
poll_timeout = (int) timeout_u64;
78+
} else {
79+
poll_timeout = -1;
80+
};
81+
82+
if (poll(poll_fds, poll_nfds, poll_timeout) < 0) {
83+
return -1;
11184
}
11285

113-
// Clear and set entries in the result sets.
11486
FD_ZERO(readfds);
11587
FD_ZERO(writefds);
116-
for (size_t i = 0; i < nevents; ++i) {
117-
const __wasi_event_t *event = &events[i];
118-
if (event->type == __WASI_EVENTTYPE_FD_READ) {
119-
readfds->__fds[readfds->__nfds++] = event->userdata;
120-
} else if (event->type == __WASI_EVENTTYPE_FD_WRITE) {
121-
writefds->__fds[writefds->__nfds++] = event->userdata;
88+
for (size_t i = 0; i < poll_nfds; ++i) {
89+
struct pollfd* pollfd = poll_fds + i;
90+
if ((pollfd->revents & POLLRDNORM) != 0) {
91+
readfds->__fds[readfds->__nfds++] = pollfd->fd;
92+
}
93+
if ((pollfd->revents & POLLWRNORM) != 0) {
94+
writefds->__fds[writefds->__nfds++] = pollfd->fd;
12295
}
12396
}
97+
12498
return readfds->__nfds + writefds->__nfds;
12599
}

libc-bottom-half/sources/__wasilibc_fd_renumber.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,85 @@ int __wasilibc_fd_renumber(int fd, int newfd) {
1515
return 0;
1616
}
1717

18+
#ifdef __wasilibc_use_wasip2
19+
#include <wasi/descriptor_table.h>
20+
21+
void drop_tcp_socket(tcp_socket_t socket) {
22+
switch (socket.state.tag) {
23+
case TCP_SOCKET_STATE_UNBOUND:
24+
case TCP_SOCKET_STATE_BOUND:
25+
case TCP_SOCKET_STATE_CONNECTING:
26+
case TCP_SOCKET_STATE_LISTENING:
27+
case TCP_SOCKET_STATE_CONNECT_FAILED:
28+
// No additional resources to drop.
29+
break;
30+
case TCP_SOCKET_STATE_CONNECTED: {
31+
tcp_socket_state_connected_t connection = socket.state.connected;
32+
33+
poll_pollable_drop_own(connection.input_pollable);
34+
poll_pollable_drop_own(connection.output_pollable);
35+
streams_input_stream_drop_own(connection.input);
36+
streams_output_stream_drop_own(connection.output);
37+
break;
38+
}
39+
default: /* unreachable */ abort();
40+
}
41+
42+
poll_pollable_drop_own(socket.socket_pollable);
43+
tcp_tcp_socket_drop_own(socket.socket);
44+
}
45+
46+
void drop_udp_socket_streams(udp_socket_streams_t streams) {
47+
poll_pollable_drop_own(streams.incoming_pollable);
48+
poll_pollable_drop_own(streams.outgoing_pollable);
49+
udp_incoming_datagram_stream_drop_own(streams.incoming);
50+
udp_outgoing_datagram_stream_drop_own(streams.outgoing);
51+
}
52+
53+
void drop_udp_socket(udp_socket_t socket) {
54+
switch (socket.state.tag) {
55+
case UDP_SOCKET_STATE_UNBOUND:
56+
case UDP_SOCKET_STATE_BOUND_NOSTREAMS:
57+
// No additional resources to drop.
58+
break;
59+
case UDP_SOCKET_STATE_BOUND_STREAMING:
60+
drop_udp_socket_streams(socket.state.bound_streaming.streams);
61+
break;
62+
case UDP_SOCKET_STATE_CONNECTED: {
63+
drop_udp_socket_streams(socket.state.connected.streams);
64+
break;
65+
}
66+
default: /* unreachable */ abort();
67+
}
68+
69+
poll_pollable_drop_own(socket.socket_pollable);
70+
udp_udp_socket_drop_own(socket.socket);
71+
}
72+
#endif // __wasilibc_use_wasip2
73+
1874
int close(int fd) {
1975
// Scan the preopen fds before making any changes.
2076
__wasilibc_populate_preopens();
2177

78+
#ifdef __wasilibc_use_wasip2
79+
descriptor_table_entry_t entry;
80+
if (descriptor_table_remove(fd, &entry)) {
81+
82+
switch (entry.tag)
83+
{
84+
case DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET:
85+
drop_tcp_socket(entry.tcp_socket);
86+
break;
87+
case DESCRIPTOR_TABLE_ENTRY_UDP_SOCKET:
88+
drop_udp_socket(entry.udp_socket);
89+
break;
90+
default: /* unreachable */ abort();
91+
}
92+
93+
return 0;
94+
}
95+
#endif // __wasilibc_use_wasip2
96+
2297
__wasi_errno_t error = __wasi_fd_close(fd);
2398
if (error != 0) {
2499
errno = error;

0 commit comments

Comments
 (0)