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
|
// 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>
int poll(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;
}
|