diff --git a/ports/src/lib/libc_noux/plugin.cc b/ports/src/lib/libc_noux/plugin.cc index 5e6d333ca4..73beb12fb8 100644 --- a/ports/src/lib/libc_noux/plugin.cc +++ b/ports/src/lib/libc_noux/plugin.cc @@ -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 #include diff --git a/ports/src/noux/child.h b/ports/src/noux/child.h index 8af2dbb99d..4e2f248c6a 100644 --- a/ports/src/noux/child.h +++ b/ports/src/noux/child.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; diff --git a/ports/src/noux/main.cc b/ports/src/noux/main.cc index 96b08a6c9c..74ab791308 100644 --- a/ports/src/noux/main.cc +++ b/ports/src/noux/main.cc @@ -14,6 +14,8 @@ /* Genode includes */ #include #include +#include +#include /* Noux includes */ #include @@ -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 = 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 = 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); } diff --git a/ports/src/noux/minimal/target.mk b/ports/src/noux/minimal/target.mk index c6840370e3..6411fc9b63 100644 --- a/ports/src/noux/minimal/target.mk +++ b/ports/src/noux/minimal/target.mk @@ -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)/../ diff --git a/ports/src/noux/net/target.mk b/ports/src/noux/net/target.mk index 2cc35c51e5..0d680f9916 100644 --- a/ports/src/noux/net/target.mk +++ b/ports/src/noux/net/target.mk @@ -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