mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 17: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;
|
||||
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;
|
||||
dst_len -= in_fds.num_rd;
|
||||
if (readfds != NULL) {
|
||||
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;
|
||||
dst_len -= in_fds.num_wr;
|
||||
if (writefds != NULL) {
|
||||
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()) {
|
||||
errno = ENOMEM;
|
||||
@ -294,14 +308,6 @@ extern "C" int select(int nfds, fd_set *readfds, fd_set *writefds,
|
||||
if (timeout) {
|
||||
sysio()->select_in.timeout.sec = timeout->tv_sec;
|
||||
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 {
|
||||
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;
|
||||
|
||||
int *src = out_fds.array;
|
||||
int total_fds = 0;
|
||||
|
||||
unmarshal_fds(src, out_fds.num_rd, readfds);
|
||||
src += out_fds.num_rd;
|
||||
if (readfds != NULL) {
|
||||
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);
|
||||
src += out_fds.num_wr;
|
||||
if (writefds != NULL) {
|
||||
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 <base/platform_env.h>
|
||||
|
||||
|
@ -60,6 +60,12 @@ namespace Noux {
|
||||
*/
|
||||
Pid_allocator *pid_allocator();
|
||||
|
||||
/**
|
||||
* Return singleton instance of timeout scheduler
|
||||
*/
|
||||
class Timeout_scheduler;
|
||||
Timeout_scheduler *timeout_scheduler();
|
||||
|
||||
|
||||
class Child;
|
||||
|
||||
|
@ -14,6 +14,8 @@
|
||||
/* Genode includes */
|
||||
#include <cap_session/connection.h>
|
||||
#include <os/config.h>
|
||||
#include <os/alarm.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
/* Noux includes */
|
||||
#include <child.h>
|
||||
@ -41,6 +43,75 @@ extern void (*close_socket)(int);
|
||||
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 **
|
||||
*****************************/
|
||||
@ -231,6 +302,14 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
||||
case SYSCALL_SELECT:
|
||||
{
|
||||
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
|
||||
@ -242,7 +321,12 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
|
||||
* unblock condition. Return if one I/O channel satisfies
|
||||
* 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];
|
||||
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);
|
||||
|
||||
if (io->check_unblock(in_fds.watch_for_rd(i),
|
||||
in_fds.watch_for_wr(i),
|
||||
in_fds.watch_for_ex(i))) {
|
||||
in_fds.watch_for_wr(i),
|
||||
in_fds.watch_for_ex(i))) {
|
||||
|
||||
/*
|
||||
* Return single file descriptor that triggered the
|
||||
* unblocking. For now, only a single file
|
||||
* descriptor is returned on each call of select.
|
||||
*/
|
||||
Sysio::Select_fds &out_fds = _sysio->select_out.fds;
|
||||
if (io->check_unblock(true, false, false)) {
|
||||
_rd_array[unblock_rd++] = fd;
|
||||
// io->clear_unblock(true, false, false);
|
||||
}
|
||||
if (io->check_unblock(false, true, false)) {
|
||||
_wr_array[unblock_wr++] = fd;
|
||||
// io->clear_unblock(false, true, false);
|
||||
}
|
||||
|
||||
out_fds.array[0] = fd;
|
||||
out_fds.num_rd = io->check_unblock(true, false, false);
|
||||
out_fds.num_wr = io->check_unblock(false, true, false);
|
||||
out_fds.num_ex = io->check_unblock(false, false, true);
|
||||
if (io->check_unblock(false, false, true)) {
|
||||
unblock_ex++;
|
||||
// io->clear_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
|
||||
*/
|
||||
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_wr = 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.
|
||||
* ...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];
|
||||
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
|
||||
*/
|
||||
_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
|
||||
*/
|
||||
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];
|
||||
if (!fd_in_use(fd)) continue;
|
||||
|
||||
Shared_pointer<Io_channel> io = io_channel_by_fd(fd);
|
||||
io->unregister_wake_up_notifier(¬ifiers[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -567,6 +697,11 @@ Noux::Pid_allocator *Noux::pid_allocator()
|
||||
return &inst;
|
||||
}
|
||||
|
||||
Noux::Timeout_scheduler *Noux::timeout_scheduler()
|
||||
{
|
||||
static Noux::Timeout_scheduler inst;
|
||||
return &inst;
|
||||
}
|
||||
|
||||
void *operator new (Genode::size_t size) {
|
||||
return Genode::env()->heap()->alloc(size); }
|
||||
|
@ -1,5 +1,5 @@
|
||||
TARGET = noux
|
||||
LIBS = cxx env server process signal
|
||||
LIBS = cxx env server process signal thread alarm
|
||||
SRC_CC = main.cc dummy_net.cc
|
||||
INC_DIR += $(PRG_DIR)
|
||||
INC_DIR += $(PRG_DIR)/../
|
||||
|
@ -1,5 +1,5 @@
|
||||
TARGET = noux_net
|
||||
LIBS = cxx env server process signal lwip
|
||||
LIBS = cxx env server process signal lwip thread alarm
|
||||
|
||||
LIBS += libc libc_lwip
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user