summaryrefslogtreecommitdiffstats
path: root/libc-bottom-half/cloudlibc/src/libc/poll/poll.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 13:54:38 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 13:54:38 +0000
commit8c1ab65c0f548d20b7f177bdb736daaf603340e1 (patch)
treedf55b7e75bf43f2bf500845b105afe3ac3a5157e /libc-bottom-half/cloudlibc/src/libc/poll/poll.c
parentInitial commit. (diff)
downloadwasi-libc-b0f726bfa464c79fdf040fa7daed6094ddbffb4c.tar.xz
wasi-libc-b0f726bfa464c79fdf040fa7daed6094ddbffb4c.zip
Adding upstream version 0.0~git20221206.8b7148f.upstream/0.0_git20221206.8b7148f
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libc-bottom-half/cloudlibc/src/libc/poll/poll.c')
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/poll/poll.c129
1 files changed, 129 insertions, 0 deletions
diff --git a/libc-bottom-half/cloudlibc/src/libc/poll/poll.c b/libc-bottom-half/cloudlibc/src/libc/poll/poll.c
new file mode 100644
index 0000000..cde4e81
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/poll/poll.c
@@ -0,0 +1,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;
+}