libc: cancel select when POSIX signal occurs

With this patch, Vim running via the 'bash.run' script becomes able to
adopt itself to changed window dimensions.

Issue #3544
This commit is contained in:
Norman Feske 2019-10-29 10:52:49 +01:00 committed by Christian Helmuth
parent fafa409cf9
commit 636e0f6444
3 changed files with 33 additions and 8 deletions

View File

@ -65,7 +65,7 @@ namespace Libc {
/**
* Select support
*/
void init_select(Suspend &, Resume &, Select &);
void init_select(Suspend &, Resume &, Select &, Signal &);
/**
* Support for querying available RAM quota in sysctl functions

View File

@ -347,7 +347,7 @@ Libc::Kernel::Kernel(Genode::Env &env, Genode::Allocator &heap)
init_sleep(*this);
init_vfs_plugin(*this);
init_time(*this, _rtc_path, *this);
init_select(*this, *this, *this);
init_select(*this, *this, *this, _signal);
init_socket_fs(*this);
init_passwd(_passwd_config());
init_signal(_signal);

View File

@ -36,9 +36,11 @@
/* libc-internal includes */
#include <internal/init.h>
#include <internal/signal.h>
#include <internal/suspend.h>
#include <internal/resume.h>
#include <internal/select.h>
#include <internal/errno.h>
namespace Libc {
struct Select_cb;
@ -51,13 +53,16 @@ using namespace Libc;
static Suspend *_suspend_ptr;
static Resume *_resume_ptr;
static Select *_select_ptr;
static Libc::Signal *_signal_ptr;
void Libc::init_select(Suspend &suspend, Resume &resume, Select &select)
void Libc::init_select(Suspend &suspend, Resume &resume, Select &select,
Signal &signal)
{
_suspend_ptr = &suspend;
_resume_ptr = &resume;
_select_ptr = &select;
_signal_ptr = &signal;
}
@ -312,18 +317,38 @@ int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
{
struct Missing_call_of_init_select : Exception { };
if (!_suspend_ptr)
if (!_suspend_ptr || !_signal_ptr)
throw Missing_call_of_init_select();
}
while (!timeout.expired() && select_cb->nready == 0)
unsigned const orig_signal_count = _signal_ptr->count();
auto signal_occurred_during_select = [&] ()
{
return _signal_ptr->count() != orig_signal_count;
};
for (;;) {
if (timeout.expired())
break;
if (select_cb->nready != 0)
break;
if (signal_occurred_during_select())
break;
timeout.duration = _suspend_ptr->suspend(check, timeout.duration);
}
select_cb_list().remove(&(*select_cb));
if (timeout.expired())
return 0;
if (signal_occurred_during_select())
return Errno(EINTR);
/* not timed out -> results have been stored in select_cb by select_notify() */
if (readfds) *readfds = select_cb->readfds;