Skip to content

Commit 7401ae4

Browse files
authored
[WasmFS] Implement select() system call in terms of poll() (#25965)
This change also adds some testing of select to `test/unistd/pipe.c` where there was none before.
1 parent edb2d32 commit 7401ae4

File tree

2 files changed

+124
-18
lines changed

2 files changed

+124
-18
lines changed

system/lib/wasmfs/syscalls.cpp

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,15 +1769,86 @@ int __syscall_fadvise64(int fd, off_t offset, off_t length, int advice) {
17691769
}
17701770

17711771
int __syscall__newselect(int nfds,
1772-
intptr_t readfds_,
1773-
intptr_t writefds_,
1774-
intptr_t exceptfds_,
1775-
intptr_t timeout_) {
1776-
// TODO: Implement this syscall. For now, we return an error code,
1777-
// specifically ENOMEM which is valid per the docs:
1778-
// ENOMEM Unable to allocate memory for internal tables
1779-
// https://man7.org/linux/man-pages/man2/select.2.html
1780-
return -ENOMEM;
1772+
intptr_t _readfds,
1773+
intptr_t _writefds,
1774+
intptr_t _exceptfds,
1775+
intptr_t _timeout) {
1776+
// Implement select in terms of `poll()`
1777+
1778+
// Part 1: convert select arguments into poll arguments
1779+
fd_set* readfds = (fd_set*)_readfds;
1780+
fd_set* writefds = (fd_set*)_writefds;
1781+
fd_set* exceptfds = (fd_set*)_exceptfds;
1782+
timeval* timeout = (timeval*)_timeout;
1783+
1784+
int n = 0;
1785+
struct pollfd* fds = (struct pollfd*)calloc(nfds, sizeof(struct pollfd));
1786+
1787+
for (int i = 0; i < nfds; i++) {
1788+
if (readfds && FD_ISSET(i, readfds)) {
1789+
fds[n].events |= POLLIN;
1790+
}
1791+
if (writefds && FD_ISSET(i, writefds)) {
1792+
fds[n].events |= POLLOUT;
1793+
}
1794+
if (exceptfds && FD_ISSET(i, exceptfds)) {
1795+
fds[n].events |= POLLPRI;
1796+
}
1797+
if (fds[n].events) {
1798+
fds[n].fd = i;
1799+
n++;
1800+
}
1801+
}
1802+
1803+
// __syscall_poll currently ignores timeout but we calculate it here for
1804+
// completeness.
1805+
int timeout_ms = -1; // Infinite
1806+
if (timeout) {
1807+
timeout_ms = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
1808+
}
1809+
1810+
int rtn = __syscall_poll((intptr_t)fds, n, timeout_ms);
1811+
if (rtn < 0) {
1812+
free(fds);
1813+
return -1;
1814+
}
1815+
1816+
// Part 2: Translate the result of poll into the results of select();
1817+
1818+
if (readfds) FD_ZERO(readfds);
1819+
if (writefds) FD_ZERO(writefds);
1820+
if (exceptfds) FD_ZERO(exceptfds);
1821+
1822+
int count = 0;
1823+
1824+
if (rtn > 0) {
1825+
for (int i = 0; i < n; i++) {
1826+
int fd = fds[i].fd;
1827+
short revents = fds[i].revents;
1828+
if (revents) {
1829+
// Map POLLIN to readfds
1830+
// POLLHUP/POLLERR usually count as readable (EOF or Error)
1831+
if (readfds && (revents & POLLIN || revents & POLLHUP || revents & POLLERR)) {
1832+
FD_SET(fd, readfds);
1833+
count++;
1834+
}
1835+
// Map POLLOUT to writefds
1836+
// POLLERR usually counts as writable (so write fails immediately)
1837+
if (writefds && (revents & POLLOUT || revents & POLLERR)) {
1838+
FD_SET(fd, writefds);
1839+
count++;
1840+
}
1841+
// Map POLLPRI to exceptfds
1842+
if (exceptfds && (revents & POLLPRI)) {
1843+
FD_SET(fd, exceptfds);
1844+
count++;
1845+
}
1846+
}
1847+
}
1848+
}
1849+
1850+
free(fds);
1851+
return count;
17811852
}
17821853

17831854
} // extern "C"

test/unistd/pipe.c

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
#include <unistd.h>
9+
#include <stdbool.h>
910
#include <sys/types.h>
1011
#include <sys/stat.h>
1112
#include <fcntl.h>
@@ -17,8 +18,8 @@
1718

1819
unsigned char buf[1 << 16];
1920

20-
#define FALSE 0
21-
#define TRUE 1
21+
#define MAX(a, b) (((a) >= (b)) ? (a) : (b))
22+
2223

2324
// This test program relies on the simplest read/write behavior when
2425
// all the data can be read/written in one call.
@@ -40,6 +41,37 @@ void test_read(int fd0, unsigned char *ch, int size) {
4041
}
4142
}
4243

44+
// test_select and test_poll perform the exact same actions/assertions but
45+
// with two different system calls. They should always give the same
46+
// retult.
47+
48+
void test_select(int *fd, bool data_available) {
49+
fd_set rfds;
50+
FD_ZERO(&rfds);
51+
FD_SET(fd[0], &rfds);
52+
FD_SET(fd[1], &rfds);
53+
54+
fd_set wfds;
55+
FD_ZERO(&wfds);
56+
FD_SET(fd[0], &wfds);
57+
FD_SET(fd[1], &wfds);
58+
59+
// Don't block at all
60+
struct timeval tv = { 0, 0};
61+
int maxfd = MAX(fd[0], fd[1]) + 1;
62+
int ret = select(maxfd, &rfds, &wfds, NULL, &tv);
63+
64+
if (data_available) {
65+
assert(ret == 2);
66+
assert(FD_ISSET(fd[0], &rfds));
67+
} else {
68+
assert(ret == 1);
69+
assert(!FD_ISSET(fd[0], &rfds));
70+
}
71+
72+
assert(FD_ISSET(fd[1], &wfds));
73+
}
74+
4375
void test_poll(int *fd, int data_available) {
4476
struct pollfd pfds[2];
4577
memset(pfds, 0, sizeof pfds);
@@ -57,6 +89,9 @@ void test_poll(int *fd, int data_available) {
5789
assert(pfds[0].revents == 0);
5890
}
5991
assert(pfds[1].revents == POLLOUT);
92+
93+
// select should report the exact same status.
94+
test_select(fd, data_available);
6095
}
6196

6297
int test_most() {
@@ -86,13 +121,13 @@ int test_most() {
86121
// write about 40 Kb of data
87122
for (int i = 1; i < 200; ++i) {
88123
test_write(fd[1], &wchar, i + 2);
89-
test_poll(fd, TRUE);
124+
test_poll(fd, true);
90125
test_read (fd[0], &rchar, i);
91-
test_poll(fd, TRUE);
126+
test_poll(fd, true);
92127
test_write(fd[1], &wchar, i + 1);
93-
test_poll(fd, TRUE);
128+
test_poll(fd, true);
94129
test_read (fd[0], &rchar, i + 3);
95-
test_poll(fd, FALSE);
130+
test_poll(fd, false);
96131
}
97132

98133
// Test reading when there is less data available than the read buffer size
@@ -126,11 +161,11 @@ int test_most() {
126161
#endif
127162

128163
// Normal operations still work in non-blocking mode
129-
test_poll(fd, FALSE);
164+
test_poll(fd, false);
130165
test_write(fd[1], &wchar, 10);
131-
test_poll(fd, TRUE);
166+
test_poll(fd, true);
132167
test_read (fd[0], &rchar, 10);
133-
test_poll(fd, FALSE);
168+
test_poll(fd, false);
134169

135170
// Clear buffer
136171
memset(buf, 0, sizeof(buf));

0 commit comments

Comments
 (0)