libc: select() based on poll()

Fixes #5131
This commit is contained in:
Christian Prochaska 2024-03-01 11:04:42 +01:00 committed by Christian Helmuth
parent 40fd3d8c62
commit 62b3f24d1c
11 changed files with 290 additions and 522 deletions

View File

@ -23,15 +23,12 @@
namespace Libc {
struct Select_cb;
typedef Genode::Constructible<Libc::Select_cb> Select_handler_cb;
struct Select_handler_base
{
Select_handler_cb *_select_cb;
Select_handler_base();
~Select_handler_base();
int _nfds { };
fd_set _readfds { };
fd_set _writefds { };
fd_set _exceptfds { };
/**
* Traditional select()

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright (C) 2016-2020 Genode Labs GmbH
* Copyright (C) 2016-2024 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -69,10 +69,15 @@ namespace Libc {
void init_vfs_plugin(Monitor &, Genode::Region_map &);
void init_file_operations(Cwd &, Config_accessor const &);
/**
* Poll support
*/
void init_poll(Signal &, Monitor &);
/**
* Select support
*/
void init_select(Select &, Signal &, Monitor &);
void init_select(Select &);
/**
* Support for querying available RAM quota in sysctl functions

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright (C) 2010-2017 Genode Labs GmbH
* Copyright (C) 2010-2024 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -52,6 +52,17 @@ namespace Libc {
public:
struct Pollfd
{
File_descriptor *fdo;
short events;
/*
* This pointer points to 'revents' of the original
* 'struct pollfd' array.
*/
short *revents;
};
Plugin(int priority = 0);
virtual ~Plugin();
@ -65,11 +76,6 @@ namespace Libc {
virtual bool supports_readlink(const char *path, char *buf, ::size_t bufsiz);
virtual bool supports_rename(const char *oldpath, const char *newpath);
virtual bool supports_rmdir(const char *path);
virtual bool supports_select(int nfds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout);
virtual bool supports_socket(int domain, int type, int protocol);
virtual bool supports_stat(const char *path);
virtual bool supports_symlink(const char *oldpath, const char *newpath);
@ -120,7 +126,7 @@ namespace Libc {
virtual int msync(void *addr, ::size_t len, int flags);
virtual File_descriptor *open(const char *pathname, int flags);
virtual int pipe(File_descriptor *pipefd[2]);
virtual bool poll(File_descriptor&, struct pollfd &pfd);
virtual int poll(Pollfd fds[], int nfds);
virtual ssize_t read(File_descriptor *, void *buf, ::size_t count);
virtual ssize_t readlink(const char *path, char *buf, ::size_t bufsiz);
virtual ssize_t recv(File_descriptor *, void *buf, ::size_t len, int flags);
@ -129,8 +135,6 @@ namespace Libc {
virtual ssize_t recvmsg(File_descriptor *, struct msghdr *msg, int flags);
virtual int rename(const char *oldpath, const char *newpath);
virtual int rmdir(const char *pathname);
virtual int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
virtual ssize_t send(File_descriptor *, const void *buf, ::size_t len, int flags);
virtual ssize_t sendto(File_descriptor *, const void *buf,
::size_t len, int flags,

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
* Copyright (C) 2019-2024 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -31,7 +31,6 @@ namespace Libc {
virtual void deschedule_select() = 0;
};
void select_notify_from_kernel();
}
#endif /* _LIBC__INTERNAL__SELECT_H_ */

View File

@ -7,7 +7,7 @@
*/
/*
* Copyright (C) 2014-2019 Genode Labs GmbH
* Copyright (C) 2014-2024 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -223,10 +223,6 @@ class Libc::Vfs_plugin final : public Plugin
bool supports_unlink(const char *) override { return true; }
bool supports_mmap() override { return true; }
bool supports_select(int nfds,
fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout) override;
/* kernel-specific API without monitor */
File_descriptor *open_from_kernel(const char *, int, int libc_fd);
int close_from_kernel(File_descriptor *);
@ -248,7 +244,7 @@ class Libc::Vfs_plugin final : public Plugin
int mkdir(const char *, mode_t) override;
File_descriptor *open(const char *path, int flags) override;
int pipe(File_descriptor *pipefdo[2]) override;
bool poll(File_descriptor &fdo, struct pollfd &pfd) override;
int poll(Pollfd fds[], int nfds) override;
ssize_t read(File_descriptor *, void *, ::size_t) override;
ssize_t readlink(const char *, char *, ::size_t) override;
int rename(const char *, const char *) override;
@ -259,7 +255,6 @@ class Libc::Vfs_plugin final : public Plugin
ssize_t write(File_descriptor *, const void *, ::size_t ) override;
void *mmap(void *, ::size_t, int, int, File_descriptor *, ::off_t) override;
int munmap(void *, ::size_t) override;
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) override;
};
#endif /* _LIBC__INTERNAL__VFS_PLUGIN_H_ */

View File

@ -7,7 +7,7 @@
*/
/*
* Copyright (C) 2016-2020 Genode Labs GmbH
* Copyright (C) 2016-2024 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -408,9 +408,6 @@ void Libc::Kernel::_clone_state_from_parent()
}
extern void (*libc_select_notify_from_kernel)();
void Libc::Kernel::handle_io_progress()
{
if (_io_progressed) {
@ -502,7 +499,8 @@ Libc::Kernel::Kernel(Genode::Env &env, Genode::Allocator &heap)
init_vfs_plugin(*this, _env.rm());
init_file_operations(*this, _libc_env);
init_time(*this, *this);
init_select(*this, _signal, *this);
init_poll(_signal, *this);
init_select(*this);
init_socket_fs(*this, *this);
init_passwd(_passwd_config());
init_signal(_signal);

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright (C) 2010-2017 Genode Labs GmbH
* Copyright (C) 2010-2024 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -110,13 +110,6 @@ bool Plugin::supports_rmdir(const char*)
}
bool Plugin::supports_select(int, fd_set *, fd_set *,
fd_set *, struct timeval *)
{
return false;
}
bool Plugin::supports_socket(int, int, int)
{
return false;
@ -206,11 +199,10 @@ DUMMY(void *, (void *)(-1), mmap, (void *addr, ::size_t length, int prot, int fl
DUMMY(int, -1, munmap, (void *, ::size_t));
DUMMY(int, -1, msync, (void *addr, ::size_t len, int flags));
DUMMY(int, -1, pipe, (File_descriptor*[2]));
DUMMY(bool, 0, poll, (File_descriptor &, struct pollfd &));
DUMMY(int, -1, poll, (Pollfd[], int));
DUMMY(ssize_t, -1, readlink, (const char *, char *, ::size_t));
DUMMY(int, -1, rename, (const char *, const char *));
DUMMY(int, -1, rmdir, (const char*));
DUMMY(int, -1, select, (int, fd_set *, fd_set *, fd_set *, struct timeval *));
DUMMY(int, -1, stat, (const char*, struct stat*));
DUMMY(int, -1, symlink, (const char*, const char*));
DUMMY(int, -1, unlink, (const char*));

View File

@ -7,7 +7,7 @@
*/
/*
* Copyright (C) 2010-2019 Genode Labs GmbH
* Copyright (C) 2010-2024 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -22,91 +22,141 @@
#include <internal/errno.h>
#include <internal/file.h>
#include <internal/init.h>
#include <internal/monitor.h>
#include <internal/signal.h>
using namespace Libc;
/**
* The poll function was taken from OpenSSH portable (bsd-poll.c) and adepted
* to better fit within Genode's libc.
*
* Copyright (c) 2004, 2005, 2007 Darren Tucker (dtucker at zip com au).
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
static Monitor *_monitor_ptr;
static Libc::Signal *_signal_ptr;
void Libc::init_poll(Signal &signal, Monitor &monitor)
{
_signal_ptr = &signal;
_monitor_ptr = &monitor;
}
static int poll_plugins(Plugin::Pollfd pollfds[], int nfds)
{
int nready = 0;
for (Plugin *plugin = plugin_registry()->first();
plugin;
plugin = plugin->next()) {
if (!plugin->supports_poll())
continue;
/* count the number of pollfds for this plugin */
int plugin_nfds = 0;
for (int pollfd_index = 0; pollfd_index < nfds; pollfd_index++)
if (pollfds[pollfd_index].fdo->plugin == plugin)
plugin_nfds++;
if (plugin_nfds == 0)
continue;
/*
* Copy the pollfds belonging to this plugin to a plugin-specific
* array. 'revents' still points into the original structure.
*/
Plugin::Pollfd plugin_pollfds[plugin_nfds];
for (int pollfd_index = 0, plugin_pollfd_index = 0;
pollfd_index < nfds;
pollfd_index++) {
if (pollfds[pollfd_index].fdo->plugin == plugin) {
plugin_pollfds[plugin_pollfd_index] = pollfds[pollfd_index];
plugin_pollfd_index++;
}
}
int plugin_nready = plugin->poll(plugin_pollfds, plugin_nfds);
if (plugin_nready < 0)
return plugin_nready;
nready += plugin_nready;
}
return nready;
}
extern "C" int
__attribute__((weak))
poll(struct pollfd fds[], nfds_t nfds, int timeout)
poll(struct pollfd pollfds[], nfds_t nfds, int timeout_ms)
{
nfds_t i;
int ret, fd, maxfd = 0;
fd_set readfds, writefds, exceptfds;
struct timeval tv, *tvp = NULL;
/*
* Look up the file descriptor objects early-on to reduce repeated
* overhead.
*/
for (i = 0; i < nfds; i++) {
fd = fds[i].fd;
if (fd >= (int)FD_SETSIZE) {
return Libc::Errno(EINVAL);
}
maxfd = MAX(maxfd, fd);
Plugin::Pollfd plugins_pollfds[nfds];
for (nfds_t pollfd_index = 0; pollfd_index < nfds; pollfd_index++) {
pollfds[pollfd_index].revents = 0;
plugins_pollfds[pollfd_index].fdo =
file_descriptor_allocator()->find_by_libc_fd(pollfds[pollfd_index].fd);
plugins_pollfds[pollfd_index].events = pollfds[pollfd_index].events;
plugins_pollfds[pollfd_index].revents = &pollfds[pollfd_index].revents;
}
/* populate event bit vectors for the events we're interested in */
int nready = poll_plugins(plugins_pollfds, nfds);
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
/* return if any descriptor is ready or an error occurred */
if (nready != 0)
return nready;
for (i = 0; i < nfds; i++) {
fd = fds[i].fd;
if (fd == -1)
continue;
if (fds[i].events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) {
FD_SET(fd, &readfds);
FD_SET(fd, &exceptfds);
}
if (fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) {
FD_SET(fd, &writefds);
FD_SET(fd, &exceptfds);
}
/* return on zero-timeout */
if (timeout_ms == 0)
return 0;
/* convert infinite timeout to monitor interface */
if (timeout_ms < 0)
timeout_ms = 0;
if (!_monitor_ptr || !_signal_ptr) {
struct Missing_call_of_init_poll : Exception { };
throw Missing_call_of_init_poll();
}
/* poll timeout is msec, select is timeval (sec + usec) */
if (timeout >= 0) {
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
tvp = &tv;
}
ret = select(maxfd + 1, &readfds, &writefds, &exceptfds, tvp);
unsigned const orig_signal_count = _signal_ptr->count();
/* scan through select results and set poll() flags */
for (i = 0; i < nfds; i++) {
fd = fds[i].fd;
fds[i].revents = 0;
if (fd == -1)
continue;
if ((fds[i].events & POLLIN) && FD_ISSET(fd, &readfds)) {
fds[i].revents |= POLLIN;
}
if ((fds[i].events & POLLOUT) && FD_ISSET(fd, &writefds)) {
fds[i].revents |= POLLOUT;
}
if (FD_ISSET(fd, &exceptfds)) {
fds[i].revents |= POLLERR;
}
}
auto signal_occurred_during_poll = [&] ()
{
return (_signal_ptr->count() != orig_signal_count);
};
return ret;
auto monitor_fn = [&] ()
{
nready = poll_plugins(plugins_pollfds, nfds);
if (nready != 0)
return Monitor::Function_result::COMPLETE;
if (signal_occurred_during_poll())
return Monitor::Function_result::COMPLETE;
return Monitor::Function_result::INCOMPLETE;
};
Monitor::Result const monitor_result =
_monitor_ptr->monitor(monitor_fn, timeout_ms);
if (monitor_result == Monitor::Result::TIMEOUT)
return 0;
if (signal_occurred_during_poll())
return Errno(EINTR);
return nready;
}

View File

@ -6,16 +6,13 @@
* \author Norman Feske
* \date 2010-01-21
*
* the 'select()' implementation is partially based on the lwip version as
* implemented in 'src/api/sockets.c'
*
* Note what POSIX states about select(): File descriptors associated with
* regular files always select true for ready to read, ready to write, and
* error conditions.
*/
/*
* Copyright (C) 2010-2017 Genode Labs GmbH
* Copyright (C) 2010-2024 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -42,182 +39,67 @@
#include <internal/errno.h>
#include <internal/monitor.h>
namespace Libc {
struct Select_cb;
struct Select_cb_list;
}
using namespace Libc;
static Select *_select_ptr;
static Libc::Signal *_signal_ptr;
static Monitor *_monitor_ptr;
void Libc::init_select(Select &select, Signal &signal, Monitor &monitor)
void Libc::init_select(Select &select)
{
_select_ptr = &select;
_signal_ptr = &signal;
_monitor_ptr = &monitor;
}
/** Description for a task waiting in select */
struct Libc::Select_cb
static int poll_nfds_from_select_nfds(int select_nfds,
fd_set &readfds,
fd_set &writefds,
fd_set &exceptfds)
{
Select_cb *next = nullptr;
int poll_nfds = 0;
int const nfds;
int nready = 0;
fd_set readfds;
fd_set writefds;
fd_set exceptfds;
for (int fd = 0; fd < select_nfds; fd++) {
Select_cb(int nfds, fd_set const &readfds, fd_set const &writefds, fd_set const &exceptfds)
:
nfds(nfds), readfds(readfds), writefds(writefds), exceptfds(exceptfds)
{ }
};
bool fd_in_readfds = FD_ISSET(fd, &readfds);
bool fd_in_writefds = FD_ISSET(fd, &writefds);
bool fd_in_exceptfds = FD_ISSET(fd, &exceptfds);
struct Libc::Select_cb_list
{
Mutex _mutex;
Select_cb *_first = nullptr;
struct Guard : Mutex::Guard
{
Select_cb_list *l;
Guard(Select_cb_list &list) : Mutex::Guard(list._mutex), l(&list) { }
};
void unsynchronized_insert(Select_cb *scb)
{
scb->next = _first;
_first = scb;
if (fd_in_readfds || fd_in_writefds || fd_in_exceptfds)
poll_nfds++;
}
void insert(Select_cb *scb)
{
Guard guard(*this);
unsynchronized_insert(scb);
}
void remove(Select_cb *scb)
{
Guard guard(*this);
/* address of pointer to next allows to change the head */
for (Select_cb **next = &_first; *next; next = &(*next)->next) {
if (*next == scb) {
*next = scb->next;
break;
}
}
}
template <typename FUNC>
void for_each(FUNC const &func)
{
Guard guard(*this);
for (Select_cb *scb = _first; scb; scb = scb->next)
func(*scb);
}
};
/** The global list of tasks waiting for select */
static Libc::Select_cb_list &select_cb_list()
{
static Select_cb_list inst;
return inst;
return poll_nfds;
}
/**
* Poll plugin select() functions
*
* We iterate over all file descriptors in each list and count the number of
* ready descriptors. Output file-descriptor sets are cleared by this function
* (according to POSIX).
*/
static int selscan(int nfds,
fd_set *in_readfds, fd_set *in_writefds, fd_set *in_exceptfds,
fd_set *out_readfds, fd_set *out_writefds, fd_set *out_exceptfds)
void populate_pollfds(struct pollfd pollfds[], int poll_nfds,
int select_nfds,
fd_set &readfds, fd_set &writefds, fd_set &exceptfds)
{
int nready = 0;
for (int fd = 0, pollfd_index = 0; fd < select_nfds; fd++) {
/* zero timeout for polling of the plugins' select() functions */
struct timeval tv_0 = { 0, 0 };
bool fd_in_readfds = FD_ISSET(fd, &readfds);
bool fd_in_writefds = FD_ISSET(fd, &writefds);
bool fd_in_exceptfds = FD_ISSET(fd, &exceptfds);
/* temporary fd sets that are passed to the plugins */
int plugin_nready;
fd_set plugin_readfds;
fd_set plugin_writefds;
fd_set plugin_exceptfds;
if (!fd_in_readfds && !fd_in_writefds && !fd_in_exceptfds)
continue;
/* clear fd sets */
if (out_readfds) FD_ZERO(out_readfds);
if (out_writefds) FD_ZERO(out_writefds);
if (out_exceptfds) FD_ZERO(out_exceptfds);
pollfds[pollfd_index].fd = fd;
if (nfds > FD_SETSIZE)
return Libc::Errno(EINVAL);
pollfds[pollfd_index].events = 0;
for (Plugin *plugin = plugin_registry()->first();
plugin;
plugin = plugin->next()) {
if (plugin->supports_select(nfds, in_readfds, in_writefds, in_exceptfds, &tv_0)) {
if (fd_in_readfds)
pollfds[pollfd_index].events |= POLLIN;
plugin_readfds = *in_readfds;
plugin_writefds = *in_writefds;
plugin_exceptfds = *in_exceptfds;
if (fd_in_writefds)
pollfds[pollfd_index].events |= POLLOUT;
plugin_nready = plugin->select(nfds, &plugin_readfds, &plugin_writefds, &plugin_exceptfds, &tv_0);
if (fd_in_exceptfds)
pollfds[pollfd_index].events |= POLLERR;
if (plugin_nready > 0) {
for (int libc_fd = 0; libc_fd < nfds; libc_fd++) {
if (out_readfds && FD_ISSET(libc_fd, &plugin_readfds)) {
FD_SET(libc_fd, out_readfds);
}
if (out_writefds && FD_ISSET(libc_fd, &plugin_writefds)) {
FD_SET(libc_fd, out_writefds);
}
if (out_exceptfds && FD_ISSET(libc_fd, &plugin_exceptfds)) {
FD_SET(libc_fd, out_exceptfds);
}
}
nready += plugin_nready;
} else if (plugin_nready < 0) {
error("plugin->select() returned error value ", plugin_nready);
}
}
pollfd_index++;
}
return nready;
}
/* this function gets called by plugin backends when file descripors become ready */
void Libc::select_notify_from_kernel()
{
fd_set tmp_readfds, tmp_writefds, tmp_exceptfds;
/* check for each waiting select() function if one of its fds is ready now
* and if so, wake all up */
select_cb_list().for_each([&] (Select_cb &scb) {
scb.nready = selscan(scb.nfds,
&scb.readfds, &scb.writefds, &scb.exceptfds,
&tmp_readfds, &tmp_writefds, &tmp_exceptfds);
if (scb.nready > 0) {
scb.readfds = tmp_readfds;
scb.writefds = tmp_writefds;
scb.exceptfds = tmp_exceptfds;
}
});
}
@ -225,86 +107,63 @@ extern "C" __attribute__((weak))
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *tv)
{
fd_set in_readfds, in_writefds, in_exceptfds;
/*
* Create copies of the input sets or new empty input sets to avoid
* repeated nullpointer checks later on.
*/
Constructible<Select_cb> select_cb;
fd_set in_readfds, in_writefds, in_exceptfds;
if (readfds) in_readfds = *readfds; else FD_ZERO(&in_readfds);
if (writefds) in_writefds = *writefds; else FD_ZERO(&in_writefds);
if (exceptfds) in_exceptfds = *exceptfds; else FD_ZERO(&in_exceptfds);
/*
* Insert callback first to avoid race after 'selscan()'
*/
int poll_nfds = poll_nfds_from_select_nfds(nfds, in_readfds, in_writefds, in_exceptfds);
select_cb.construct(nfds, in_readfds, in_writefds, in_exceptfds);
select_cb_list().insert(&(*select_cb));
struct pollfd pollfds[poll_nfds];
int const nready = selscan(nfds,
&in_readfds, &in_writefds, &in_exceptfds,
readfds, writefds, exceptfds);
populate_pollfds(pollfds, poll_nfds,
nfds, in_readfds, in_writefds, in_exceptfds);
/* return if any descripor is ready */
if (nready) {
select_cb_list().remove(&(*select_cb));
return nready;
int const timeout_ms = (tv != nullptr)
? tv->tv_sec*1000 + tv->tv_usec/1000
: -1;
int poll_nready = poll(pollfds, poll_nfds, timeout_ms);
if (poll_nready < 0)
return poll_nready;
if (readfds) FD_ZERO(readfds);
if (writefds) FD_ZERO(writefds);
if (exceptfds) FD_ZERO(exceptfds);
int nready = 0;
if (poll_nready > 0) {
for (int pollfd_index = 0;
pollfd_index < poll_nfds;
pollfd_index++) {
if (readfds && (pollfds[pollfd_index].revents & POLLIN)) {
FD_SET(pollfds[pollfd_index].fd, readfds);
nready++;
}
if (writefds && (pollfds[pollfd_index].revents & POLLOUT)) {
FD_SET(pollfds[pollfd_index].fd, writefds);
nready++;
}
if (exceptfds && (pollfds[pollfd_index].revents & POLLERR)) {
FD_SET(pollfds[pollfd_index].fd, exceptfds);
nready++;
}
}
}
/* return on zero-timeout */
if (tv && (tv->tv_sec) == 0 && (tv->tv_usec == 0)) {
select_cb_list().remove(&(*select_cb));
return 0;
}
using Genode::uint64_t;
uint64_t const timeout_ms = (tv != nullptr)
? (uint64_t)tv->tv_sec*1000 + tv->tv_usec/1000
: 0UL;
{
struct Missing_call_of_init_select : Exception { };
if (!_monitor_ptr || !_signal_ptr)
throw Missing_call_of_init_select();
}
unsigned const orig_signal_count = _signal_ptr->count();
auto signal_occurred_during_select = [&] ()
{
return (_signal_ptr->count() != orig_signal_count);
};
auto monitor_fn = [&] ()
{
select_notify_from_kernel();
if (select_cb->nready != 0)
return Monitor::Function_result::COMPLETE;
if (signal_occurred_during_select())
return Monitor::Function_result::COMPLETE;
return Monitor::Function_result::INCOMPLETE;
};
Monitor::Result const monitor_result =
_monitor_ptr->monitor(monitor_fn, timeout_ms);
select_cb_list().remove(&(*select_cb));
if (monitor_result == Monitor::Result::TIMEOUT)
return 0;
if (signal_occurred_during_select())
return Errno(EINTR);
/* not timed out -> results have been stored in select_cb by select_notify_from_kernel() */
if (readfds) *readfds = select_cb->readfds;
if (writefds) *writefds = select_cb->writefds;
if (exceptfds) *exceptfds = select_cb->exceptfds;
return select_cb->nready;
return nready;
}
@ -355,37 +214,22 @@ int __sys_pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds
int Libc::Select_handler_base::select(int nfds, fd_set &readfds,
fd_set &writefds, fd_set &exceptfds)
{
fd_set in_readfds, in_writefds, in_exceptfds;
/*
* Save the input data before calling '::select()' which zeroes out
* the fd sets if nothing is ready.
*/
_nfds = nfds;
_readfds = readfds;
_writefds = writefds;
_exceptfds = exceptfds;
in_readfds = readfds;
in_writefds = writefds;
in_exceptfds = exceptfds;
struct timeval tv { 0, 0 };
/* remove potentially enqueued callback from list */
if (_select_cb->constructed())
select_cb_list().remove(&(**_select_cb));
int const nready = ::select(nfds, &readfds, &writefds, &exceptfds, &tv);
{
/*
* We use the guard directly to atomically check if any descripor is
* ready, and insert into select-callback list otherwise.
*/
Select_cb_list::Guard guard(select_cb_list());
int const nready = selscan(nfds,
&in_readfds, &in_writefds, &in_exceptfds,
&readfds, &writefds, &exceptfds);
/* return if any descripor is ready */
if (nready)
return nready;
/* suspend as we don't have any immediate events */
_select_cb->construct(nfds, in_readfds, in_writefds, in_exceptfds);
select_cb_list().unsynchronized_insert(&(**_select_cb));
}
/* return if any descripor is ready or an error occurred */
if (nready != 0)
return nready;
struct Missing_call_of_init_select : Exception { };
if (!_select_ptr)
@ -399,27 +243,23 @@ int Libc::Select_handler_base::select(int nfds, fd_set &readfds,
void Libc::Select_handler_base::dispatch_select()
{
Libc::select_notify_from_kernel();
/* '::select()' zeroes out the fd sets if nothing is ready */
fd_set tmp_readfds = _readfds;
fd_set tmp_writefds = _writefds;
fd_set tmp_exceptfds = _exceptfds;
Select_handler_cb &select_cb = *_select_cb;
struct timeval tv { 0, 0 };
if (select_cb->nready == 0) return;
int const nready = ::select(_nfds,
&tmp_readfds,
&tmp_writefds,
&tmp_exceptfds,
&tv);
select_cb_list().remove(&(*select_cb));
if (nready == 0) return;
if (_select_ptr)
_select_ptr->deschedule_select();
select_ready(select_cb->nready, select_cb->readfds,
select_cb->writefds, select_cb->exceptfds);
select_ready(nready, tmp_readfds, tmp_writefds, tmp_exceptfds);
}
Libc::Select_handler_base::Select_handler_base()
:
_select_cb((Select_handler_cb*)malloc(sizeof(*_select_cb)))
{ }
Libc::Select_handler_base::~Select_handler_base()
{ }

View File

@ -8,7 +8,7 @@
*/
/*
* Copyright (C) 2015-2019 Genode Labs GmbH
* Copyright (C) 2015-2024 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@ -355,14 +355,12 @@ struct Libc::Socket_fs::Local_functor : Sockaddr_functor
struct Libc::Socket_fs::Plugin : Libc::Plugin
{
bool supports_poll() override { return true; }
bool supports_select(int, fd_set *, fd_set *, fd_set *, timeval *) override;
ssize_t read(File_descriptor *, void *, ::size_t) override;
ssize_t write(File_descriptor *, const void *, ::size_t) override;
int fcntl(File_descriptor *, int, long) override;
int close(File_descriptor *) override;
bool poll(File_descriptor &fd, struct pollfd &pfd) override;
int select(int, fd_set *, fd_set *, fd_set *, timeval *) override;
int poll(Pollfd fds[], int nfds) override;
int ioctl(File_descriptor *, unsigned long, char *) override;
};
@ -1316,111 +1314,46 @@ ssize_t Socket_fs::Plugin::write(File_descriptor *fd, const void *buf, ::size_t
}
bool Socket_fs::Plugin::poll(File_descriptor &fdo, struct pollfd &pfd)
{
if (fdo.plugin != this) return false;
Socket_fs::Context *context { nullptr };
int Socket_fs::Plugin::poll(Pollfd fds[], int nfds)
try {
context = dynamic_cast<Socket_fs::Context *>(fdo.context);
} catch (Socket_fs::Context::Inaccessible) {
pfd.revents |= POLLNVAL;
return true;
}
enum {
POLLIN_MASK = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI,
POLLOUT_MASK = POLLOUT | POLLWRNORM | POLLWRBAND,
};
bool res { false };
if ((pfd.events & POLLIN_MASK) && context->read_ready()) {
pfd.revents |= pfd.events & POLLIN_MASK;
res = true;
}
if ((pfd.events & POLLOUT_MASK) && context->write_ready()) {
pfd.revents |= pfd.events & POLLOUT_MASK;
res = true;
}
return res;
}
bool Socket_fs::Plugin::supports_select(int nfds,
fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout)
{
/* return true if any file descriptor (which is set) belongs to the VFS */
for (int fd = 0; fd < nfds; ++fd) {
if (FD_ISSET(fd, readfds) || FD_ISSET(fd, writefds) || FD_ISSET(fd, exceptfds)) {
File_descriptor *fdo = file_descriptor_allocator()->find_by_libc_fd(fd);
if (fdo && (fdo->plugin == this))
return true;
}
}
return false;
}
int Socket_fs::Plugin::select(int nfds,
fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
timeval *timeout)
{
int nready = 0;
fd_set const in_readfds = *readfds;
fd_set const in_writefds = *writefds;
/* XXX exceptfds not supported */
/* clear fd sets */
FD_ZERO(readfds);
FD_ZERO(writefds);
FD_ZERO(exceptfds);
auto fn = [&] {
for (int fd = 0; fd < nfds; ++fd) {
for (int pollfd_index = 0; pollfd_index < nfds; pollfd_index++) {
bool fd_in_readfds = FD_ISSET(fd, &in_readfds);
bool fd_in_writefds = FD_ISSET(fd, &in_writefds);
bool fd_ready = false;
if (!fd_in_readfds && !fd_in_writefds)
continue;
if (fds[pollfd_index].events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) {
File_descriptor *fdo = file_descriptor_allocator()->find_by_libc_fd(fd);
/* handle only fds that belong to this plugin */
if (!fdo || (fdo->plugin != this))
continue;
if (fd_in_readfds) {
try {
Socket_fs::Context *context = dynamic_cast<Socket_fs::Context *>(fdo->context);
Socket_fs::Context *context =
static_cast<Socket_fs::Context *>(fds[pollfd_index].fdo->context);
if (context->read_ready()) {
FD_SET(fd, readfds);
++nready;
*fds[pollfd_index].revents |= POLLIN;
fd_ready = true;
}
} catch (Socket_fs::Context::Inaccessible) { }
}
if (fd_in_writefds) {
if (fds[pollfd_index].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) {
try {
Socket_fs::Context *context = dynamic_cast<Socket_fs::Context *>(fdo->context);
Socket_fs::Context *context =
static_cast<Socket_fs::Context *>(fds[pollfd_index].fdo->context);
if (context->write_ready()) {
FD_SET(fd, writefds);
++nready;
*fds[pollfd_index].revents |= POLLOUT;
fd_ready = true;
}
} catch (Socket_fs::Context::Inaccessible) { }
}
/* XXX exceptfds not supported */
/* XXX POLLERR not supported */
if (fd_ready)
nready++;
}
return Fn::COMPLETE;
};

View File

@ -2513,84 +2513,39 @@ int Libc::Vfs_plugin::pipe(Libc::File_descriptor *pipefdo[2])
}
bool Libc::Vfs_plugin::poll(File_descriptor &fd, struct pollfd &pfd)
{
error("Plugin::poll() is deprecated");
return false;
}
bool Libc::Vfs_plugin::supports_select(int nfds,
fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout)
{
/* return true if any file descriptor (which is set) belongs to the VFS */
for (int fd = 0; fd < nfds; ++fd) {
if (FD_ISSET(fd, readfds) || FD_ISSET(fd, writefds) || FD_ISSET(fd, exceptfds)) {
File_descriptor *fdo =
file_descriptor_allocator()->find_by_libc_fd(fd);
if (fdo && (fdo->plugin == this))
return true;
}
}
return false;
}
int Libc::Vfs_plugin::select(int nfds,
fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout)
int Libc::Vfs_plugin::poll(Pollfd fds[], int nfds)
{
int nready = 0;
fd_set const in_readfds = *readfds;
fd_set const in_writefds = *writefds;
/* XXX exceptfds not supported */
/* clear fd sets */
FD_ZERO(readfds);
FD_ZERO(writefds);
FD_ZERO(exceptfds);
auto fn = [&] {
for (int fd = 0; fd < nfds; ++fd) {
bool fd_in_readfds = FD_ISSET(fd, &in_readfds);
bool fd_in_writefds = FD_ISSET(fd, &in_writefds);
for (int pollfd_index = 0; pollfd_index < nfds; pollfd_index++) {
if (!fd_in_readfds && !fd_in_writefds)
continue;
File_descriptor *fdo =
file_descriptor_allocator()->find_by_libc_fd(fd);
/* handle only fds that belong to this plugin */
if (!fdo || (fdo->plugin != this))
continue;
Vfs::Vfs_handle *handle = vfs_handle(fdo);
Vfs::Vfs_handle *handle = vfs_handle(fds[pollfd_index].fdo);
if (!handle) continue;
if (fd_in_readfds) {
bool fd_ready = false;
if (fds[pollfd_index].events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) {
if (handle->fs().read_ready(*handle)) {
FD_SET(fd, readfds);
++nready;
*fds[pollfd_index].revents |= POLLIN;
fd_ready = true;
} else {
handle->fs().notify_read_ready(handle);
}
}
if (fd_in_writefds) {
if (fds[pollfd_index].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) {
if (handle->fs().write_ready(*handle)) {
FD_SET(fd, writefds);
++nready;
*fds[pollfd_index].revents |= POLLOUT;
fd_ready = true;
}
}
/* XXX exceptfds not supported */
/* XXX POLLERR not supported */
if (fd_ready)
nready++;
}
return Fn::COMPLETE;
};