diff --git a/repos/base/include/base/entrypoint.h b/repos/base/include/base/entrypoint.h index 046bb50ae..fdd9955b8 100644 --- a/repos/base/include/base/entrypoint.h +++ b/repos/base/include/base/entrypoint.h @@ -19,6 +19,7 @@ #include #include #include +#include namespace Genode { class Startup; @@ -101,13 +102,8 @@ class Genode::Entrypoint : Noncopyable void (*_suspended_callback) () = nullptr; void (*_resumed_callback) () = nullptr; - enum Signal_recipient { - NONE = 0, ENTRYPOINT = 1, SIGNAL_PROXY = 2 - }; - - int _signal_recipient { NONE }; - Genode::Lock _signal_pending_lock { }; - Genode::Lock _signal_pending_ack_lock { }; + bool _signal_proxy_delivers_signal { false }; + Genode::Mutex _block_for_signal_mutex { }; Io_progress_handler *_io_progress_handler { nullptr }; diff --git a/repos/base/src/lib/base/entrypoint.cc b/repos/base/src/lib/base/entrypoint.cc index 2c4b56e32..116b9e966 100644 --- a/repos/base/src/lib/base/entrypoint.cc +++ b/repos/base/src/lib/base/entrypoint.cc @@ -43,6 +43,9 @@ static char const *initial_ep_name() { return "ep"; } void Entrypoint::Signal_proxy_component::signal() { + /* signal delivered successfully */ + ep._signal_proxy_delivers_signal = false; + ep._process_deferred_signals(); bool io_progress = false; @@ -113,41 +116,27 @@ void Entrypoint::_process_incoming_signals() for (;;) { do { - _sig_rec->block_for_signal(); - - int success; { - Lock::Guard guard(_signal_pending_lock); - success = cmpxchg(&_signal_recipient, NONE, SIGNAL_PROXY); + /* see documentation in 'wait_and_dispatch_one_io_signal()' */ + + Mutex::Guard guard { _block_for_signal_mutex }; + + _signal_proxy_delivers_signal = true; + + _sig_rec->block_for_signal(); } - /* common case, entrypoint is not in 'wait_and_dispatch_one_io_signal' */ - if (success) { - /* - * It might happen that we try to forward a signal to the - * entrypoint, while the context of that signal is already - * destroyed. In that case we will get an ipc error exception - * as result, which has to be caught. - */ - try { - retry( - [&] () { _signal_proxy_cap.call(); }, - [] () { warning("blocking canceled during signal processing"); }); - } catch (Genode::Ipc_error) { /* ignore - context got destroyed in meantime */ } - - cmpxchg(&_signal_recipient, SIGNAL_PROXY, NONE); - } else { - /* - * Entrypoint is in 'wait_and_dispatch_one_io_signal', wakup it up and - * block for next signal - */ - _sig_rec->unblock_signal_waiter(*_rpc_ep); - - /* - * wait for the acknowledgment by the entrypoint - */ - _signal_pending_ack_lock.lock(); - } + /* + * It might happen that we try to forward a signal to the + * entrypoint, while the context of that signal is already + * destroyed. In that case we will get an ipc error exception + * as result, which has to be caught. + */ + try { + retry( + [&] () { _signal_proxy_cap.call(); }, + [] () { warning("blocking canceled during signal processing"); }); + } catch (Genode::Ipc_error) { /* ignore - context got destroyed in meantime */ } /* entrypoint destructor requested to stop signal handling */ if (_stop_signal_proxy) { @@ -198,15 +187,7 @@ bool Entrypoint::_wait_and_dispatch_one_io_signal(bool const dont_block) for (;;) { try { - _signal_pending_lock.lock(); - - cmpxchg(&_signal_recipient, NONE, ENTRYPOINT); Signal sig =_sig_rec->pending_signal(); - cmpxchg(&_signal_recipient, ENTRYPOINT, NONE); - - _signal_pending_lock.unlock(); - - _signal_pending_ack_lock.unlock(); /* defer application-level signals */ if (sig.context()->level() == Signal_context::Level::App) { @@ -218,13 +199,38 @@ bool Entrypoint::_wait_and_dispatch_one_io_signal(bool const dont_block) break; } catch (Signal_receiver::Signal_not_pending) { - _signal_pending_lock.unlock(); - if (dont_block) { - /* indicate that we leave wait_and_dispatch_one_io_signal */ - cmpxchg(&_signal_recipient, ENTRYPOINT, NONE); + if (dont_block) return false; + + { + /* + * The signal-proxy thread as well as the entrypoint via + * 'wait_and_dispatch_one_io_signal' never call + * 'block_for_signal()' without the '_block_for_signal_mutex' + * aquired. The signal-proxy thread also flags when it was + * unblocked by an incoming signal and delivers the signal via + * RPC in '_signal_proxy_delivers_signal'. + */ + Mutex::Guard guard { _block_for_signal_mutex }; + + /* + * If the signal proxy is blocked in the signal-delivery + * RPC but the call did not yet arrive in the entrypoint + * (_signal_proxy_delivers_signal == true), we acknowledge the + * delivery here (like in 'Signal_proxy_component::signal()') and + * retry to fetch one pending signal at the beginning of the + * loop above. Otherwise, we block for the next incoming + * signal. + * + * There exist cases were we already processed the signal + * flagged in '_signal_proxy_delivers_signal' and will end + * up here again. In these cases we also 'block_for_signal()'. + */ + if (_signal_proxy_delivers_signal) + _signal_proxy_delivers_signal = false; + else + _sig_rec->block_for_signal(); } - _sig_rec->block_for_signal(); } }