mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-02 16:52:52 +00:00
Noux: add timeout handling to select()
Previously there was not actual timeout handling. If a select() call set an timeout it would be set to zero instead and was always handled as blocking i/o. While this works fine for file descriptors which will be triggerd externally (for example vim through terminal i/o) it does not work at all for socket descriptors and network operations in general. So this commit introduces proper timeout handling and changes the behaviour of SYSCALL_SELECT so that it now returns more than just one descriptor at a time. noux/minimal and noux/net now depend on thread and alarm libraries.
This commit is contained in:
parent
d4d229bdc7
commit
ae524e4beb
@ -271,17 +271,31 @@ extern "C" int select(int nfds, fd_set *readfds, fd_set *writefds,
|
|||||||
int *dst = in_fds.array;
|
int *dst = in_fds.array;
|
||||||
size_t dst_len = Noux::Sysio::Select_fds::MAX_FDS;
|
size_t dst_len = Noux::Sysio::Select_fds::MAX_FDS;
|
||||||
|
|
||||||
in_fds.num_rd = marshal_fds(readfds, nfds, dst, dst_len);
|
/**
|
||||||
|
* These variables are used in max_fds_exceeded() calculation, so
|
||||||
|
* they need to be proper initialized.
|
||||||
|
*/
|
||||||
|
in_fds.num_rd = 0;
|
||||||
|
in_fds.num_wr = 0;
|
||||||
|
in_fds.num_ex = 0;
|
||||||
|
|
||||||
dst += in_fds.num_rd;
|
if (readfds != NULL) {
|
||||||
dst_len -= in_fds.num_rd;
|
in_fds.num_rd = marshal_fds(readfds, nfds, dst, dst_len);
|
||||||
|
|
||||||
in_fds.num_wr = marshal_fds(writefds, nfds, dst, dst_len);
|
dst += in_fds.num_rd;
|
||||||
|
dst_len -= in_fds.num_rd;
|
||||||
|
}
|
||||||
|
|
||||||
dst += in_fds.num_wr;
|
if (writefds != NULL) {
|
||||||
dst_len -= in_fds.num_wr;
|
in_fds.num_wr = marshal_fds(writefds, nfds, dst, dst_len);
|
||||||
|
|
||||||
in_fds.num_ex = marshal_fds(exceptfds, nfds, dst, dst_len);
|
dst += in_fds.num_wr;
|
||||||
|
dst_len -= in_fds.num_wr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exceptfds != NULL) {
|
||||||
|
in_fds.num_ex = marshal_fds(exceptfds, nfds, dst, dst_len);
|
||||||
|
}
|
||||||
|
|
||||||
if (in_fds.max_fds_exceeded()) {
|
if (in_fds.max_fds_exceeded()) {
|
||||||
errno = ENOMEM;
|
errno = ENOMEM;
|
||||||
@ -294,14 +308,6 @@ extern "C" int select(int nfds, fd_set *readfds, fd_set *writefds,
|
|||||||
if (timeout) {
|
if (timeout) {
|
||||||
sysio()->select_in.timeout.sec = timeout->tv_sec;
|
sysio()->select_in.timeout.sec = timeout->tv_sec;
|
||||||
sysio()->select_in.timeout.usec = timeout->tv_usec;
|
sysio()->select_in.timeout.usec = timeout->tv_usec;
|
||||||
|
|
||||||
if (!sysio()->select_in.timeout.zero()) {
|
|
||||||
// PDBG("timeout=%d,%d -> replaced by zero timeout",
|
|
||||||
// (int)timeout->tv_sec, (int)timeout->tv_usec);
|
|
||||||
|
|
||||||
sysio()->select_in.timeout.sec = 0;
|
|
||||||
sysio()->select_in.timeout.usec = 0;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
sysio()->select_in.timeout.set_infinite();
|
sysio()->select_in.timeout.set_infinite();
|
||||||
}
|
}
|
||||||
@ -323,18 +329,29 @@ extern "C" int select(int nfds, fd_set *readfds, fd_set *writefds,
|
|||||||
Noux::Sysio::Select_fds &out_fds = sysio()->select_out.fds;
|
Noux::Sysio::Select_fds &out_fds = sysio()->select_out.fds;
|
||||||
|
|
||||||
int *src = out_fds.array;
|
int *src = out_fds.array;
|
||||||
|
int total_fds = 0;
|
||||||
|
|
||||||
unmarshal_fds(src, out_fds.num_rd, readfds);
|
if (readfds != NULL) {
|
||||||
src += out_fds.num_rd;
|
unmarshal_fds(src, out_fds.num_rd, readfds);
|
||||||
|
src += out_fds.num_rd;
|
||||||
|
total_fds += out_fds.num_rd;
|
||||||
|
}
|
||||||
|
|
||||||
unmarshal_fds(src, out_fds.num_wr, writefds);
|
if (writefds != NULL) {
|
||||||
src += out_fds.num_wr;
|
unmarshal_fds(src, out_fds.num_wr, writefds);
|
||||||
|
src += out_fds.num_wr;
|
||||||
|
total_fds += out_fds.num_wr;
|
||||||
|
}
|
||||||
|
|
||||||
unmarshal_fds(src, out_fds.num_ex, exceptfds);
|
if (exceptfds != NULL) {
|
||||||
|
unmarshal_fds(src, out_fds.num_ex, exceptfds);
|
||||||
|
/* exceptfds are currently ignored */
|
||||||
|
}
|
||||||
|
|
||||||
return out_fds.total_fds();
|
return total_fds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <base/platform_env.h>
|
#include <base/platform_env.h>
|
||||||
|
|
||||||
|
@ -60,6 +60,12 @@ namespace Noux {
|
|||||||
*/
|
*/
|
||||||
Pid_allocator *pid_allocator();
|
Pid_allocator *pid_allocator();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return singleton instance of timeout scheduler
|
||||||
|
*/
|
||||||
|
class Timeout_scheduler;
|
||||||
|
Timeout_scheduler *timeout_scheduler();
|
||||||
|
|
||||||
|
|
||||||
class Child;
|
class Child;
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
/* Genode includes */
|
/* Genode includes */
|
||||||
#include <cap_session/connection.h>
|
#include <cap_session/connection.h>
|
||||||
#include <os/config.h>
|
#include <os/config.h>
|
||||||
|
#include <os/alarm.h>
|
||||||
|
#include <timer_session/connection.h>
|
||||||
|
|
||||||
/* Noux includes */
|
/* Noux includes */
|
||||||
#include <child.h>
|
#include <child.h>
|
||||||
@ -41,6 +43,75 @@ extern void (*close_socket)(int);
|
|||||||
extern void init_network();
|
extern void init_network();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timeout thread for SYSCALL_SELECT
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Noux {
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
class Timeout_scheduler : Thread<4096>, public Alarm_scheduler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Timer::Connection _timer;
|
||||||
|
Alarm::Time _curr_time;
|
||||||
|
|
||||||
|
enum { TIMER_GRANULARITY_MSEC = 10 };
|
||||||
|
|
||||||
|
void entry()
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
_timer.msleep(TIMER_GRANULARITY_MSEC);
|
||||||
|
Alarm_scheduler::handle(_curr_time);
|
||||||
|
_curr_time += TIMER_GRANULARITY_MSEC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Timeout_scheduler() : _curr_time(0) { start(); }
|
||||||
|
|
||||||
|
Alarm::Time curr_time() const { return _curr_time; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Timeout_state
|
||||||
|
{
|
||||||
|
bool timed_out;
|
||||||
|
|
||||||
|
Timeout_state() : timed_out(false) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Timeout_alarm : public Alarm
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Timeout_state *_state;
|
||||||
|
Semaphore *_blocker;
|
||||||
|
Timeout_scheduler *_scheduler;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Timeout_alarm(Timeout_state *st, Semaphore *blocker, Timeout_scheduler *scheduler, Time timeout)
|
||||||
|
:
|
||||||
|
_state(st),
|
||||||
|
_blocker(blocker),
|
||||||
|
_scheduler(scheduler)
|
||||||
|
{
|
||||||
|
_scheduler->schedule_absolute(this, _scheduler->curr_time() + timeout);
|
||||||
|
_state->timed_out = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void discard() { _scheduler->discard(this); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool on_alarm()
|
||||||
|
{
|
||||||
|
_state->timed_out = true;
|
||||||
|
_blocker->up();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*****************************
|
/*****************************
|
||||||
** Noux syscall dispatcher **
|
** Noux syscall dispatcher **
|
||||||
*****************************/
|
*****************************/
|
||||||
@ -231,6 +302,14 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
|||||||
case SYSCALL_SELECT:
|
case SYSCALL_SELECT:
|
||||||
{
|
{
|
||||||
Sysio::Select_fds &in_fds = _sysio->select_in.fds;
|
Sysio::Select_fds &in_fds = _sysio->select_in.fds;
|
||||||
|
size_t in_fds_total = in_fds.total_fds();
|
||||||
|
|
||||||
|
int _rd_array[in_fds_total];
|
||||||
|
int _wr_array[in_fds_total];
|
||||||
|
|
||||||
|
long timeout_sec = _sysio->select_in.timeout.sec;
|
||||||
|
long timeout_usec = _sysio->select_in.timeout.usec;
|
||||||
|
bool timeout_reached = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Block for one action of the watched file descriptors
|
* Block for one action of the watched file descriptors
|
||||||
@ -242,7 +321,12 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
|||||||
* unblock condition. Return if one I/O channel satisfies
|
* unblock condition. Return if one I/O channel satisfies
|
||||||
* the condition.
|
* the condition.
|
||||||
*/
|
*/
|
||||||
for (Genode::size_t i = 0; i < in_fds.total_fds(); i++) {
|
size_t unblock_rd = 0;
|
||||||
|
size_t unblock_wr = 0;
|
||||||
|
size_t unblock_ex = 0;
|
||||||
|
|
||||||
|
/* process read fds */
|
||||||
|
for (Genode::size_t i = 0; i < in_fds_total; i++) {
|
||||||
|
|
||||||
int fd = in_fds.array[i];
|
int fd = in_fds.array[i];
|
||||||
if (!fd_in_use(fd)) continue;
|
if (!fd_in_use(fd)) continue;
|
||||||
@ -250,29 +334,50 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
|||||||
Shared_pointer<Io_channel> io = io_channel_by_fd(fd);
|
Shared_pointer<Io_channel> io = io_channel_by_fd(fd);
|
||||||
|
|
||||||
if (io->check_unblock(in_fds.watch_for_rd(i),
|
if (io->check_unblock(in_fds.watch_for_rd(i),
|
||||||
in_fds.watch_for_wr(i),
|
in_fds.watch_for_wr(i),
|
||||||
in_fds.watch_for_ex(i))) {
|
in_fds.watch_for_ex(i))) {
|
||||||
|
|
||||||
/*
|
if (io->check_unblock(true, false, false)) {
|
||||||
* Return single file descriptor that triggered the
|
_rd_array[unblock_rd++] = fd;
|
||||||
* unblocking. For now, only a single file
|
// io->clear_unblock(true, false, false);
|
||||||
* descriptor is returned on each call of select.
|
}
|
||||||
*/
|
if (io->check_unblock(false, true, false)) {
|
||||||
Sysio::Select_fds &out_fds = _sysio->select_out.fds;
|
_wr_array[unblock_wr++] = fd;
|
||||||
|
// io->clear_unblock(false, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
out_fds.array[0] = fd;
|
if (io->check_unblock(false, false, true)) {
|
||||||
out_fds.num_rd = io->check_unblock(true, false, false);
|
unblock_ex++;
|
||||||
out_fds.num_wr = io->check_unblock(false, true, false);
|
// io->clear_unblock(false, false, true);
|
||||||
out_fds.num_ex = io->check_unblock(false, false, true);
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unblock_rd || unblock_wr || unblock_ex) {
|
||||||
|
for (size_t i = 0; i < unblock_rd; i++) {
|
||||||
|
_sysio->select_out.fds.array[i] = _rd_array[i];
|
||||||
|
_sysio->select_out.fds.num_rd = unblock_rd;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < unblock_wr; i++) {
|
||||||
|
_sysio->select_out.fds.array[i] = _wr_array[i];
|
||||||
|
_sysio->select_out.fds.num_wr = unblock_wr;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sysio->select_out.fds.num_ex = unblock_ex;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return if I/O channel triggered, but timeout exceeded
|
* Return if I/O channel triggered, but timeout exceeded
|
||||||
*/
|
*/
|
||||||
if (_sysio->select_in.timeout.zero()) {
|
|
||||||
|
if (_sysio->select_in.timeout.zero() || timeout_reached) {
|
||||||
|
/*
|
||||||
|
if (timeout_reached) PINF("timeout_reached");
|
||||||
|
else PINF("timeout.zero()");
|
||||||
|
*/
|
||||||
_sysio->select_out.fds.num_rd = 0;
|
_sysio->select_out.fds.num_rd = 0;
|
||||||
_sysio->select_out.fds.num_wr = 0;
|
_sysio->select_out.fds.num_wr = 0;
|
||||||
_sysio->select_out.fds.num_ex = 0;
|
_sysio->select_out.fds.num_ex = 0;
|
||||||
@ -292,9 +397,10 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
|||||||
* conditions such as the destruction of the child.
|
* conditions such as the destruction of the child.
|
||||||
* ...to be done.
|
* ...to be done.
|
||||||
*/
|
*/
|
||||||
Wake_up_notifier notifiers[in_fds.total_fds()];
|
|
||||||
|
|
||||||
for (Genode::size_t i = 0; i < in_fds.total_fds(); i++) {
|
Wake_up_notifier notifiers[in_fds_total];
|
||||||
|
|
||||||
|
for (Genode::size_t i = 0; i < in_fds_total; i++) {
|
||||||
int fd = in_fds.array[i];
|
int fd = in_fds.array[i];
|
||||||
if (!fd_in_use(fd)) continue;
|
if (!fd_in_use(fd)) continue;
|
||||||
|
|
||||||
@ -306,18 +412,42 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
|||||||
/*
|
/*
|
||||||
* Block at barrier except when reaching the timeout
|
* Block at barrier except when reaching the timeout
|
||||||
*/
|
*/
|
||||||
_blocker.down();
|
|
||||||
|
if (!_sysio->select_in.timeout.infinite()) {
|
||||||
|
unsigned long to_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
|
||||||
|
Timeout_state ts;
|
||||||
|
Timeout_alarm ta(&ts, &_blocker, Noux::timeout_scheduler(), to_msec);
|
||||||
|
|
||||||
|
/* block until timeout is reached or we were unblocked */
|
||||||
|
_blocker.down();
|
||||||
|
|
||||||
|
if (ts.timed_out) {
|
||||||
|
timeout_reached = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/*
|
||||||
|
* We woke up before reaching the timeout,
|
||||||
|
* so we discard the alarm
|
||||||
|
*/
|
||||||
|
ta.discard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* let's block infinitely */
|
||||||
|
_blocker.down();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unregister barrier at watched I/O channels
|
* Unregister barrier at watched I/O channels
|
||||||
*/
|
*/
|
||||||
for (Genode::size_t i = 0; i < in_fds.total_fds(); i++) {
|
for (Genode::size_t i = 0; i < in_fds_total; i++) {
|
||||||
int fd = in_fds.array[i];
|
int fd = in_fds.array[i];
|
||||||
if (!fd_in_use(fd)) continue;
|
if (!fd_in_use(fd)) continue;
|
||||||
|
|
||||||
Shared_pointer<Io_channel> io = io_channel_by_fd(fd);
|
Shared_pointer<Io_channel> io = io_channel_by_fd(fd);
|
||||||
io->unregister_wake_up_notifier(¬ifiers[i]);
|
io->unregister_wake_up_notifier(¬ifiers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -567,6 +697,11 @@ Noux::Pid_allocator *Noux::pid_allocator()
|
|||||||
return &inst;
|
return &inst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Noux::Timeout_scheduler *Noux::timeout_scheduler()
|
||||||
|
{
|
||||||
|
static Noux::Timeout_scheduler inst;
|
||||||
|
return &inst;
|
||||||
|
}
|
||||||
|
|
||||||
void *operator new (Genode::size_t size) {
|
void *operator new (Genode::size_t size) {
|
||||||
return Genode::env()->heap()->alloc(size); }
|
return Genode::env()->heap()->alloc(size); }
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
TARGET = noux
|
TARGET = noux
|
||||||
LIBS = cxx env server process signal
|
LIBS = cxx env server process signal thread alarm
|
||||||
SRC_CC = main.cc dummy_net.cc
|
SRC_CC = main.cc dummy_net.cc
|
||||||
INC_DIR += $(PRG_DIR)
|
INC_DIR += $(PRG_DIR)
|
||||||
INC_DIR += $(PRG_DIR)/../
|
INC_DIR += $(PRG_DIR)/../
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
TARGET = noux_net
|
TARGET = noux_net
|
||||||
LIBS = cxx env server process signal lwip
|
LIBS = cxx env server process signal lwip thread alarm
|
||||||
|
|
||||||
LIBS += libc libc_lwip
|
LIBS += libc libc_lwip
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user