-
Notifications
You must be signed in to change notification settings - Fork 188
/
poll.c
174 lines (161 loc) · 5.65 KB
/
poll.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
//
// SPDX-License-Identifier: BSD-2-Clause
#include <wasi/api.h>
#include <errno.h>
#include <poll.h>
#include <stdbool.h>
static int poll_wasip1(struct pollfd *fds, size_t nfds, int timeout) {
// Construct events for poll().
size_t maxevents = 2 * nfds + 1;
__wasi_subscription_t subscriptions[maxevents];
size_t nsubscriptions = 0;
for (size_t i = 0; i < nfds; ++i) {
struct pollfd *pollfd = &fds[i];
if (pollfd->fd < 0)
continue;
bool created_events = false;
if ((pollfd->events & POLLRDNORM) != 0) {
__wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
*subscription = (__wasi_subscription_t){
.userdata = (uintptr_t)pollfd,
.u.tag = __WASI_EVENTTYPE_FD_READ,
.u.u.fd_read.file_descriptor = pollfd->fd,
};
created_events = true;
}
if ((pollfd->events & POLLWRNORM) != 0) {
__wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
*subscription = (__wasi_subscription_t){
.userdata = (uintptr_t)pollfd,
.u.tag = __WASI_EVENTTYPE_FD_WRITE,
.u.u.fd_write.file_descriptor = pollfd->fd,
};
created_events = true;
}
// As entries are decomposed into separate read/write subscriptions,
// we cannot detect POLLERR, POLLHUP and POLLNVAL if POLLRDNORM and
// POLLWRNORM are not specified. Disallow this for now.
if (!created_events) {
errno = ENOSYS;
return -1;
}
}
// Create extra event for the timeout.
if (timeout >= 0) {
__wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
*subscription = (__wasi_subscription_t){
.u.tag = __WASI_EVENTTYPE_CLOCK,
.u.u.clock.id = __WASI_CLOCKID_REALTIME,
.u.u.clock.timeout = (__wasi_timestamp_t)timeout * 1000000,
};
}
// Execute poll().
size_t nevents;
__wasi_event_t events[nsubscriptions];
__wasi_errno_t error =
__wasi_poll_oneoff(subscriptions, events, nsubscriptions, &nevents);
if (error != 0) {
// WASI's poll requires at least one subscription, or else it returns
// `EINVAL`. Since a `poll` with nothing to wait for is valid in POSIX,
// return `ENOTSUP` to indicate that we don't support that case.
//
// Wasm has no signal handling, so if none of the user-provided `pollfd`
// elements, nor the timeout, led us to producing even one subscription
// to wait for, there would be no way for the poll to wake up. WASI
// returns `EINVAL` in this case, but for users of `poll`, `ENOTSUP` is
// more likely to be understood.
if (nsubscriptions == 0)
errno = ENOTSUP;
else
errno = error;
return -1;
}
// Clear revents fields.
for (size_t i = 0; i < nfds; ++i) {
struct pollfd *pollfd = &fds[i];
pollfd->revents = 0;
}
// Set revents fields.
for (size_t i = 0; i < nevents; ++i) {
const __wasi_event_t *event = &events[i];
if (event->type == __WASI_EVENTTYPE_FD_READ ||
event->type == __WASI_EVENTTYPE_FD_WRITE) {
struct pollfd *pollfd = (struct pollfd *)(uintptr_t)event->userdata;
if (event->error == __WASI_ERRNO_BADF) {
// Invalid file descriptor.
pollfd->revents |= POLLNVAL;
} else if (event->error == __WASI_ERRNO_PIPE) {
// Hangup on write side of pipe.
pollfd->revents |= POLLHUP;
} else if (event->error != 0) {
// Another error occurred.
pollfd->revents |= POLLERR;
} else {
// Data can be read or written.
if (event->type == __WASI_EVENTTYPE_FD_READ) {
pollfd->revents |= POLLRDNORM;
if (event->fd_readwrite.flags & __WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP) {
pollfd->revents |= POLLHUP;
}
} else if (event->type == __WASI_EVENTTYPE_FD_WRITE) {
pollfd->revents |= POLLWRNORM;
if (event->fd_readwrite.flags & __WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP) {
pollfd->revents |= POLLHUP;
}
}
}
}
}
// Return the number of events with a non-zero revents value.
int retval = 0;
for (size_t i = 0; i < nfds; ++i) {
struct pollfd *pollfd = &fds[i];
// POLLHUP contradicts with POLLWRNORM.
if ((pollfd->revents & POLLHUP) != 0)
pollfd->revents &= ~POLLWRNORM;
if (pollfd->revents != 0)
++retval;
}
return retval;
}
#ifdef __wasilibc_use_wasip2
#include <wasi/descriptor_table.h>
int poll_wasip2(struct pollfd *fds, size_t nfds, int timeout);
int poll(struct pollfd* fds, nfds_t nfds, int timeout)
{
bool found_socket = false;
bool found_non_socket = false;
for (size_t i = 0; i < nfds; ++i) {
descriptor_table_entry_t* entry;
if (descriptor_table_get_ref(fds[i].fd, &entry)) {
found_socket = true;
} else {
found_non_socket = true;
}
}
if (found_socket) {
if (found_non_socket) {
// We currently don't support polling a mix of non-sockets and
// sockets here (though you can do it by using the host APIs
// directly), and we probably won't until we've migrated entirely to
// WASI 0.2.
errno = ENOTSUP;
return -1;
}
return poll_wasip2(fds, nfds, timeout);
} else if (found_non_socket) {
return poll_wasip1(fds, nfds, timeout);
} else if (timeout >= 0) {
return poll_wasip2(fds, nfds, timeout);
} else {
errno = ENOTSUP;
return -1;
}
}
#else // not __wasilibc_use_wasip2
int poll(struct pollfd* fds, nfds_t nfds, int timeout)
{
return poll_wasip1(fds, nfds, timeout);
}
#endif // not __wasilibc_use_wasip2