--- /dev/null
+#include "poll_builder.h"
+#include "internal/safe_math.h"
+#include <assert.h>
+
+OSSL_SAFE_MATH_UNSIGNED(size_t, size_t)
+
+int ossl_rio_poll_builder_init(RIO_POLL_BUILDER *rpb)
+{
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
+ FD_ZERO(&rpb->rfd);
+ FD_ZERO(&rpb->wfd);
+ FD_ZERO(&rpb->efd);
+ rpb->hwm_fd = -1;
+#elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+ rpb->pfd_heap = NULL;
+ rpb->pfd_num = 0;
+ rpb->pfd_alloc = OSSL_NELEM(rpb->pfds);
+#endif
+ return 1;
+}
+
+void ossl_rio_poll_builder_cleanup(RIO_POLL_BUILDER *rpb)
+{
+ if (rpb == NULL)
+ return;
+
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+ OPENSSL_free(rpb->pfd_heap);
+#endif
+}
+
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+static int rpb_ensure_alloc(RIO_POLL_BUILDER *rpb, size_t alloc)
+{
+ struct pollfd *pfd_heap_new;
+ size_t total_size;
+ int error = 0;
+
+ if (alloc <= rpb->pfd_alloc)
+ return 1;
+
+ total_size = safe_mul_size_t(alloc, sizeof(struct pollfd), &error);
+ if (error)
+ return 0;
+
+ pfd_heap_new = OPENSSL_realloc(rpb->pfd_heap, total_size);
+ if (pfd_heap_new == NULL)
+ return 0;
+
+ rpb->pfd_heap = pfd_heap_new;
+ rpb->pfd_alloc = alloc;
+ return 1;
+}
+#endif
+
+int ossl_rio_poll_builder_add_fd(RIO_POLL_BUILDER *rpb, int fd,
+ int want_read, int want_write)
+{
+ if (fd < 0)
+ return 0;
+
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
+
+# ifndef OPENSSL_SYS_WINDOWS
+ /*
+ * On Windows there is no relevant limit to the magnitude of a fd value (see
+ * above). On *NIX the fd_set uses a bitmap and we must check the limit.
+ */
+ if (fd >= FD_SETSIZE)
+ return 0;
+# endif
+
+ if (want_read)
+ openssl_fdset(fd, &rpb->rfd);
+
+ if (want_write)
+ openssl_fdset(fd, &rpb->wfd);
+
+ openssl_fdset(fd, &rpb->efd);
+ if (fd > rpb->hwm_fd)
+ rpb->hwm_fd = fd;
+
+ return 1;
+
+#elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+ size_t num_loop;
+
+ for (num_loop = 0;; ++num_loop) {
+ size_t i;
+ struct pollfd *pfds = (rpb->pfd_heap != NULL ? rpb->pfd_heap : rpb->pfds);
+ struct pollfd *pfd;
+
+ for (i = 0; i < rpb->pfd_num; ++i) {
+ pfd = &pfds[i];
+
+ if (pfd->fd == -1 || pfd->fd == fd)
+ break;
+ }
+
+ if (i >= rpb->pfd_alloc) {
+ /* Was not able to find room - enlarge and try again. */
+ if (num_loop > 0)
+ /* Should not happen twice. */
+ return 0;
+
+ if (!rpb_ensure_alloc(rpb, rpb->pfd_alloc * 2))
+ return 0;
+
+ continue;
+ }
+
+ assert(i <= rpb->pfd_num && rpb->pfd_num <= rpb->pfd_alloc);
+ pfds[i].fd = fd;
+ pfds[i].events = 0;
+
+ if (want_read)
+ pfds[i].events |= POLLIN;
+ if (want_write)
+ pfds[i].events |= POLLOUT;
+
+ if (i == rpb->pfd_num)
+ ++rpb->pfd_num;
+
+ return 1;
+ }
+#endif
+}
+
+int ossl_rio_poll_builder_poll(RIO_POLL_BUILDER *rpb, OSSL_TIME deadline)
+{
+ int rc;
+
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
+ do {
+ struct timeval timeout, *p_timeout = &timeout;
+
+ /*
+ * select expects a timeout, not a deadline, so do the conversion.
+ * Update for each call to ensure the correct value is used if we repeat
+ * due to EINTR.
+ */
+ if (ossl_time_is_infinite(deadline))
+ p_timeout = NULL;
+ else
+ /*
+ * ossl_time_subtract saturates to zero so we don't need to check if
+ * now > deadline.
+ */
+ timeout = ossl_time_to_timeval(ossl_time_subtract(deadline,
+ ossl_time_now()));
+
+ rc = select(rpb->hwm_fd + 1, &rpb->rfd, &rpb->wfd, &rpb->efd, p_timeout);
+ } while (rc == -1 && get_last_socket_error_is_eintr());
+#elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+ do {
+ int timeout_ms;
+
+ if (ossl_time_is_infinite(deadline))
+ timeout_ms = -1;
+ else
+ timeout_ms = ossl_time2ms(ossl_time_subtract(deadline,
+ ossl_time_now()));
+
+ rc = poll(rpb->pfd_heap != NULL ? rpb->pfd_heap : rpb->pfds,
+ rpb->pfd_num, timeout_ms);
+ } while (rc == -1 && get_last_socket_error_is_eintr());
+#endif
+
+ return rc < 0 ? 0 : 1;
+}
--- /dev/null
+/*
+ * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+#ifndef OSSL_POLL_BUILDER_H
+# define OSSL_POLL_BUILDER_H
+
+# include "poll_method.h"
+# include "internal/time.h"
+
+/*
+ * RIO_POLL_BUILDER
+ * ================
+ *
+ * RIO_POLL_BUILDER provides support for immediate-mode polling architectures.
+ * It abstracts OS-specific immediate-mode polling APIs such as select(2) and
+ * poll(2) and allows an arbitrarily large number of FDs to be polled for while
+ * providing minimal overhead (over the OS APIs themselves) for small numbers of
+ * FDs.
+ */
+typedef struct rio_poll_builder_st {
+# if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
+ fd_set rfd, wfd, efd;
+ int hwm_fd;
+# elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+# define RIO_NUM_STACK_PFDS 32
+ struct pollfd *pfd_heap;
+ struct pollfd pfds[RIO_NUM_STACK_PFDS];
+ size_t pfd_num, pfd_alloc;
+# else
+# error Unknown RIO poll method
+# endif
+} RIO_POLL_BUILDER;
+
+/*
+ * Initialises a new poll builder.
+ *
+ * Returns 1 on success and 0 on failure.
+ */
+int ossl_rio_poll_builder_init(RIO_POLL_BUILDER *rpb);
+
+/*
+ * Tears down a poll builder, freeing any heap allocations (if any) which may
+ * have been made internally.
+ */
+void ossl_rio_poll_builder_cleanup(RIO_POLL_BUILDER *rpb);
+
+/*
+ * Adds a file descriptor to a poll builder. If want_read is 1, listens for
+ * readability events (POLLIN). If want_write is 1, listens for writability
+ * events (POLLOUT).
+ *
+ * If this is called with the same fd twice, the result equivalent to calling
+ * it one time with logically OR'd values of want_read and want_write.
+ *
+ * Returns 1 on success and 0 on failure.
+ */
+int ossl_rio_poll_builder_add_fd(RIO_POLL_BUILDER *rpb, int fd,
+ int want_read, int want_write);
+
+/*
+ * Polls the set of file descriptors added to a poll builder. deadline is a
+ * deadline time based on the ossl_time_now() clock or ossl_time_infinite() for
+ * no timeout. Returns 1 on success or 0 on failure.
+ */
+int ossl_rio_poll_builder_poll(RIO_POLL_BUILDER *rpb, OSSL_TIME deadline);
+
+/*
+ * TODO(RIO): No support currently for readout of what was readable/writeable as
+ * it is currently not needed.
+ */
+
+#endif
--- /dev/null
+/*
+ * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+#ifndef OSSL_POLL_METHOD_H
+# define OSSL_POLL_METHOD_H
+
+# include "internal/common.h"
+# include "internal/sockets.h"
+
+# define RIO_POLL_METHOD_SELECT 1
+# define RIO_POLL_METHOD_POLL 2
+
+# ifndef RIO_POLL_METHOD
+# if !defined(OPENSSL_SYS_WINDOWS) && defined(POLLIN)
+# define RIO_POLL_METHOD RIO_POLL_METHOD_POLL
+# else
+# define RIO_POLL_METHOD RIO_POLL_METHOD_SELECT
+# endif
+# endif
+
+#endif