diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 13:54:38 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 13:54:38 +0000 |
commit | 8c1ab65c0f548d20b7f177bdb736daaf603340e1 (patch) | |
tree | df55b7e75bf43f2bf500845b105afe3ac3a5157e /libc-bottom-half/cloudlibc/src/libc/poll/poll.c | |
parent | Initial commit. (diff) | |
download | wasi-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.c | 129 |
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; +} |