Skip to content

Commit 55cc34a

Browse files
authored
support non-blocking wasip2 sockets in read and write (#734)
For descriptors which have `sendto` and `recvfrom` in their vtables, use those for writing and reading respectively since they know how to handle non-blocking descriptors. In the future, we could generalize this to support non-blocking descriptors which aren't sockets, also.
1 parent d3ca061 commit 55cc34a

3 files changed

Lines changed: 108 additions & 15 deletions

File tree

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ 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`:
32+
descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes);
33+
if (!entry)
34+
return -1;
35+
if (entry->vtable->recvfrom != NULL)
36+
return entry->vtable->recvfrom(entry->data, buf, nbyte, 0, NULL, NULL);
37+
3138
bool ok = false;
3239

3340
// Translate the file descriptor to an internal handle

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,17 @@ 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`:
33+
descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes);
34+
if (!entry)
35+
return -1;
36+
if (entry->vtable->sendto != NULL)
37+
return entry->vtable->sendto(entry->data, buf, nbyte, 0, NULL, 0);
38+
3239
streams_borrow_output_stream_t output_stream;
3340
poll_borrow_pollable_t pollable;
3441
bool ok = false;
3542
filesystem_error_code_t error_code;
36-
descriptor_table_entry_t* entry = 0;
3743

3844
// Translate the file descriptor to an internal handle
3945
off_t *off;

test/src/poll-connect.c

Lines changed: 94 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
#include <errno.h>
33
#include <netinet/in.h>
44
#include <poll.h>
5+
#include <stdlib.h>
6+
#include <sys/ioctl.h>
57
#include <sys/socket.h>
8+
#include <unistd.h>
69

710
#define TEST(c) \
811
do { \
@@ -73,6 +76,9 @@ int main() {
7376
// finishes accepting.
7477
int loop_count = 0;
7578
while ((server_client == -1 || fds[0].events) && loop_count < 10) {
79+
if (t_status)
80+
exit(t_status);
81+
7682
++loop_count;
7783

7884
int fd_count = 1;
@@ -101,23 +107,97 @@ int main() {
101107
}
102108
ASSERT(loop_count < 10);
103109

104-
// Send some data from the server to the client.
105-
uint8_t data[5] = {1, 2, 3, 4, 5};
106-
TEST(send(server_client, data, sizeof(data), 0) == sizeof(data));
110+
int one = 1;
111+
TEST(ioctl(server_client, FIONBIO, &one) != -1);
112+
113+
// Test that we can `recv` what we `send`
114+
{
115+
// Send some data from the server to the client.
116+
uint8_t data[5] = {1, 2, 3, 4, 5};
117+
TEST(send(server_client, data, sizeof(data), 0) == sizeof(data));
118+
119+
fds[0].events = POLLRDNORM;
120+
errno = 0;
121+
int count = poll(fds, 1, 100);
122+
TEST2(count == 1);
123+
// Now the server has sent something, so the client socket should be
124+
// readable.
125+
ASSERT((fds[0].revents & POLLRDNORM) != 0);
126+
127+
uint8_t received[sizeof(data)];
128+
TEST(recv(client, received, sizeof(data), 0) == sizeof(data));
129+
130+
// Assert that what was received matches what was sent.
131+
for (int i = 0; i < sizeof(data); ++i) {
132+
ASSERT(received[i] == data[i]);
133+
}
134+
}
107135

108-
fds[0].events = POLLRDNORM;
109-
errno = 0;
110-
int count = poll(fds, 1, 100);
111-
TEST2(count == 1);
112-
// Now the server has sent something, so the client socket should be readable.
113-
ASSERT((fds[0].revents & POLLRDNORM) != 0);
136+
// Test that we can `read` what we `write` and that neither of them block.
137+
{
138+
size_t data_len = 16 * 1024;
139+
errno = 0;
140+
uint8_t *received = malloc(data_len);
141+
TEST2(received != NULL);
142+
143+
// Nothing should be readable, yet.
144+
TEST(read(client, received, data_len) == -1 &&
145+
(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS));
146+
147+
errno = 0;
148+
uint8_t *data = malloc(data_len);
149+
TEST2(data != NULL);
150+
151+
for (size_t i = 0; i < data_len; ++i) {
152+
data[i] = 42;
153+
}
114154

115-
uint8_t received[sizeof(data)];
116-
TEST(recv(client, received, sizeof(data), 0) == sizeof(data));
155+
// Write until we hit backpressure
156+
ssize_t write_total = 0;
157+
while (1) {
158+
if (t_status)
159+
exit(t_status);
117160

118-
// Assert that what was received matches what was sent.
119-
for (int i = 0; i < sizeof(data); ++i) {
120-
ASSERT(received[i] == data[i]);
161+
errno = 0;
162+
ssize_t count = write(server_client, data, data_len);
163+
if (count == -1) {
164+
TEST2(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS);
165+
break;
166+
} else {
167+
write_total += count;
168+
}
169+
}
170+
171+
fds[0].events = POLLRDNORM;
172+
173+
// Read what we wrote
174+
ssize_t read_total = 0;
175+
while (1) {
176+
if (t_status)
177+
exit(t_status);
178+
179+
{
180+
errno = 0;
181+
int count = poll(fds, 1, 100);
182+
TEST2(count == 1);
183+
ASSERT((fds[0].revents & POLLRDNORM) != 0);
184+
}
185+
186+
errno = 0;
187+
ssize_t count = read(client, received, data_len);
188+
TEST2(count != -1 || errno == EAGAIN || errno == EWOULDBLOCK ||
189+
errno == EINPROGRESS);
190+
191+
if (count > 0) {
192+
read_total += count;
193+
for (ssize_t i = 0; i < count; ++i) {
194+
ASSERT(received[i] == 42);
195+
}
196+
if (read_total == data_len) {
197+
break;
198+
}
199+
}
200+
}
121201
}
122202

123203
return t_status;

0 commit comments

Comments
 (0)