diff --git a/repos/base/include/base/entrypoint.h b/repos/base/include/base/entrypoint.h index 66a662290..158724f0f 100644 --- a/repos/base/include/base/entrypoint.h +++ b/repos/base/include/base/entrypoint.h @@ -51,16 +51,7 @@ class Genode::Entrypoint : Genode::Noncopyable Entrypoint &ep; Signal_proxy_component(Entrypoint &ep) : ep(ep) { } - void signal() - { - /* XXX introduce while-pending loop */ - try { - Signal sig = ep._sig_rec->pending_signal(); - ep._dispatch_signal(sig); - } catch (Signal_receiver::Signal_not_pending) { } - - ep._execute_post_signal_hook(); - } + void signal(); }; struct Signal_proxy_thread : Thread @@ -85,6 +76,12 @@ class Genode::Entrypoint : Genode::Noncopyable Reconstructible _sig_rec; + Lock _deferred_signals_mutex; + List> _deferred_signals; + + void _handle_deferred_signals() { } + Constructible> _deferred_signal_handler; + bool _suspended = false; void (*_suspended_callback) () = nullptr; void (*_resumed_callback) () = nullptr; @@ -116,7 +113,8 @@ class Genode::Entrypoint : Genode::Noncopyable Constructible> _suspend_dispatcher; void _dispatch_signal(Signal &sig); - + void _defer_signal(Signal &sig); + void _process_deferred_signals(); void _process_incoming_signals(); Constructible _signal_proxy_thread; @@ -168,15 +166,19 @@ class Genode::Entrypoint : Genode::Noncopyable void dissolve(Signal_dispatcher_base &); /** - * Block and dispatch a single signal, return afterwards + * Block and dispatch a single I/O-level signal, return afterwards * * \noapi * + * Only I/O signals are dispatched by this function. If an + * application-level signal occurs the dispatching of the signal is + * deferred until the entrypoint would block for the next time. + * * XXX Turn into static function that ensures that the used signal * receiver belongs to the calling entrypoint. Alternatively, * remove it. */ - void wait_and_dispatch_one_signal(); + void wait_and_dispatch_one_io_signal(); /** * Return RPC entrypoint diff --git a/repos/base/include/base/signal.h b/repos/base/include/base/signal.h index af8fdf44e..3ae7c8399 100644 --- a/repos/base/include/base/signal.h +++ b/repos/base/include/base/signal.h @@ -39,7 +39,9 @@ namespace Genode { class Signal_dispatcher_base; template class Signal_dispatcher; + template class Io_signal_dispatcher; template class Signal_handler; + template class Io_signal_handler; typedef Capability Signal_context_capability; } @@ -305,9 +307,18 @@ class Genode::Signal_receiver : Noncopyable * to multple contexts. If a signal arrives, the context is provided with the * signal. This enables the receiver to distinguish different signal sources * and dispatch incoming signals context-specific. + * + * Signal contexts are classified to represent one of two levels: application + * and I/O. The signal level determines how a signal is handled by + * 'wait_and_dispatch_one_io_signal', which defers signals corresponding to + * application-level contexts and dispatches only I/O-level signals. */ class Genode::Signal_context { + public: + + enum class Level { App, Io }; + private: /** @@ -320,6 +331,11 @@ class Genode::Signal_context */ List_element _registry_le; + /** + * List element in deferred application signal list + */ + List_element _deferred_le; + /** * Receiver to which the context is associated with * @@ -347,13 +363,17 @@ class Genode::Signal_context friend class Signal_receiver; friend class Signal_context_registry; + protected: + + Level _level = Level::App; + public: /** * Constructor */ Signal_context() - : _receiver_le(this), _registry_le(this), + : _receiver_le(this), _registry_le(this), _deferred_le(this), _receiver(0), _pending(0), _ref_cnt(0) { } /** @@ -365,6 +385,10 @@ class Genode::Signal_context */ virtual ~Signal_context(); + Level level() const { return _level; } + + List_element *deferred_le() { return &_deferred_le; } + /** * Local signal submission (DEPRECATED) * @@ -441,6 +465,19 @@ class Genode::Signal_dispatcher : public Signal_dispatcher_base, }; +/** + * Signal dispatcher for I/O-level signals + */ +template +struct Genode::Io_signal_dispatcher : Signal_dispatcher +{ + Io_signal_dispatcher(Signal_receiver &sig_rec, + T &obj, void (T::*member)(unsigned)) + : Signal_dispatcher(sig_rec, obj, member) + { Signal_context::_level = Signal_context::Level::Io; } +}; + + /** * Signal dispatcher for handling signals by an object method * @@ -479,4 +516,16 @@ struct Genode::Signal_handler : Genode::Signal_dispatcher_base, void dispatch(unsigned num) override { (obj.*member)(); } }; + +/** + * Signal handler for I/O-level signals + */ +template +struct Genode::Io_signal_handler : Signal_handler +{ + Io_signal_handler(EP &ep, T &obj, void (T::*member)()) + : Signal_handler(ep, obj, member) + { Signal_context::_level = Signal_context::Level::Io; } +}; + #endif /* _INCLUDE__BASE__SIGNAL_H_ */ diff --git a/repos/base/lib/symbols/ld b/repos/base/lib/symbols/ld index 11b65686f..eb820dd2f 100644 --- a/repos/base/lib/symbols/ld +++ b/repos/base/lib/symbols/ld @@ -49,13 +49,14 @@ _Z22__ldso_raise_exceptionv T _ZN6Genode10Entrypoint16_dispatch_signalERNS_6SignalE T _ZN6Genode10Entrypoint16schedule_suspendEPFvvES2_ T _ZN6Genode10Entrypoint25_process_incoming_signalsEv T -_ZN6Genode10Entrypoint28wait_and_dispatch_one_signalEv T +_ZN6Genode10Entrypoint31wait_and_dispatch_one_io_signalEv T _ZN6Genode10Entrypoint6manageERNS_22Signal_dispatcher_baseE T _ZN6Genode10Entrypoint8dissolveERNS_22Signal_dispatcher_baseE T _ZN6Genode10EntrypointC1ERNS_3EnvE T _ZN6Genode10EntrypointC1ERNS_3EnvEmPKc T _ZN6Genode10EntrypointC2ERNS_3EnvE T _ZN6Genode10EntrypointC2ERNS_3EnvEmPKc T +_ZN6Genode10Entrypoint22Signal_proxy_component6signalEv T _ZN6Genode10Ipc_serverC1Ev T _ZN6Genode10Ipc_serverC2Ev T _ZN6Genode10Ipc_serverD1Ev T diff --git a/repos/base/src/lib/base/entrypoint.cc b/repos/base/src/lib/base/entrypoint.cc index 78c9ba43d..dfbee8b02 100644 --- a/repos/base/src/lib/base/entrypoint.cc +++ b/repos/base/src/lib/base/entrypoint.cc @@ -46,6 +46,19 @@ namespace Genode { static char const *initial_ep_name() { return "ep"; } +void Entrypoint::Signal_proxy_component::signal() +{ + /* XXX introduce while-pending loop */ + try { + Signal sig = ep._sig_rec->pending_signal(); + ep._dispatch_signal(sig); + } catch (Signal_receiver::Signal_not_pending) { } + + ep._execute_post_signal_hook(); + ep._process_deferred_signals(); +} + + void Entrypoint::_dispatch_signal(Signal &sig) { Signal_dispatcher_base *dispatcher = 0; @@ -58,6 +71,35 @@ void Entrypoint::_dispatch_signal(Signal &sig) } +void Entrypoint::_defer_signal(Signal &sig) +{ + Signal_context *context = sig.context(); + + Lock::Guard guard(_deferred_signals_mutex); + _deferred_signals.remove(context->deferred_le()); + _deferred_signals.insert(context->deferred_le()); +} + + +void Entrypoint::_process_deferred_signals() +{ + for (;;) { + Signal_context *context = nullptr; + { + Lock::Guard guard(_deferred_signals_mutex); + if (!_deferred_signals.first()) return; + + context = _deferred_signals.first()->object(); + _deferred_signals.remove(_deferred_signals.first()); + } + + Signal_dispatcher_base *dispatcher = + dynamic_cast(context); + if (dispatcher) dispatcher->dispatch(1); + } +} + + void Entrypoint::_process_incoming_signals() { for (;;) { @@ -71,7 +113,7 @@ void Entrypoint::_process_incoming_signals() success = cmpxchg(&_signal_recipient, NONE, SIGNAL_PROXY); } - /* common case, entrypoint is not in 'wait_and_dispatch_one_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 @@ -86,7 +128,7 @@ void Entrypoint::_process_incoming_signals() cmpxchg(&_signal_recipient, SIGNAL_PROXY, NONE); } else { /* - * Entrypoint is in 'wait_and_dispatch_one_signal', wakup it up and + * Entrypoint is in 'wait_and_dispatch_one_io_signal', wakup it up and * block for next signal */ _sig_rec->unblock_signal_waiter(*_rpc_ep); @@ -98,6 +140,7 @@ void Entrypoint::_process_incoming_signals() } } while (!_suspended); + _deferred_signal_handler.destruct(); _suspend_dispatcher.destruct(); _sig_rec.destruct(); dissolve(_signal_proxy); @@ -129,7 +172,7 @@ void Entrypoint::_process_incoming_signals() } -void Entrypoint::wait_and_dispatch_one_signal() +void Entrypoint::wait_and_dispatch_one_io_signal() { for (;;) { @@ -144,6 +187,12 @@ void Entrypoint::wait_and_dispatch_one_signal() _signal_pending_ack_lock.unlock(); + /* defer application-level signals */ + if (sig.context()->level() == Signal_context::Level::App) { + _defer_signal(sig); + continue; + } + _dispatch_signal(sig); break; @@ -154,6 +203,15 @@ void Entrypoint::wait_and_dispatch_one_signal() } _execute_post_signal_hook(); + + /* initiate potential deferred-signal handling in entrypoint */ + if (_deferred_signals.first()) { + /* construct the handler on demand (otherwise we break core) */ + if (!_deferred_signal_handler.constructed()) + _deferred_signal_handler.construct(*this, *this, + &Entrypoint::_handle_deferred_signals); + Signal_transmitter(*_deferred_signal_handler).submit(); + } } @@ -186,6 +244,12 @@ void Genode::Entrypoint::dissolve(Signal_dispatcher_base &dispatcher) /* _sig_rec is invalid for a small window in _process_incoming_signals */ if (_sig_rec.constructed()) _sig_rec->dissolve(&dispatcher); + + /* also remove context from deferred signal list */ + { + Lock::Guard guard(_deferred_signals_mutex); + _deferred_signals.remove(dispatcher.deferred_le()); + } } diff --git a/repos/base/src/lib/base/signal_common.cc b/repos/base/src/lib/base/signal_common.cc index 1f5cbe7d3..18443e3bc 100644 --- a/repos/base/src/lib/base/signal_common.cc +++ b/repos/base/src/lib/base/signal_common.cc @@ -86,7 +86,7 @@ Signal::Signal(Signal::Data data) : _data(data) * Normally, the context can only have one 'Signal' in flight, which is * destroyed before 'pending_signal' is called the next time. However, * one exception is a signal handler that unexpectedly calls - * 'pending_signal' itself (i.e., via 'wait_and_dispatch_one_signal'). + * 'pending_signal' itself (i.e., via 'wait_and_dispatch_one_io_signal'). * As this is dangerous programming pattern (that should be fixed), we * print a warning. * @@ -221,7 +221,7 @@ void Signal_receiver::_unsynchronized_dissolve(Signal_context * const context) env_deprecated()->pd_session()->free_context(context->_cap); /* restore default initialization of signal context */ - context->_receiver = 0; + context->_receiver = nullptr; context->_cap = Signal_context_capability(); /* remove context from context list */ diff --git a/repos/dde_linux/src/lib/lxip/lxcc_emul.cc b/repos/dde_linux/src/lib/lxip/lxcc_emul.cc index 8291b2d80..87f26e656 100644 --- a/repos/dde_linux/src/lib/lxip/lxcc_emul.cc +++ b/repos/dde_linux/src/lib/lxip/lxcc_emul.cc @@ -305,7 +305,7 @@ void *memmove(void *d, const void *s, size_t n) ** linux/sched.h ** *******************/ -struct Timeout : Genode::Signal_handler +struct Timeout : Genode::Io_signal_handler { Genode::Entrypoint &ep; Timer::Connection timer; @@ -321,7 +321,7 @@ struct Timeout : Genode::Signal_handler Timeout(Genode::Env &env, Genode::Entrypoint &ep, void (*ticker)()) : - Signal_handler(ep, *this, &Timeout::handle), + Io_signal_handler(ep, *this, &Timeout::handle), ep(ep), timer(env), tick(ticker) { timer.sigh(*this); @@ -334,7 +334,7 @@ struct Timeout : Genode::Signal_handler void wait() { - ep.wait_and_dispatch_one_signal(); + ep.wait_and_dispatch_one_io_signal(); } }; diff --git a/repos/dde_linux/src/lib/lxip/nic_handler.cc b/repos/dde_linux/src/lib/lxip/nic_handler.cc index 6ab1a43c7..79825aa21 100644 --- a/repos/dde_linux/src/lib/lxip/nic_handler.cc +++ b/repos/dde_linux/src/lib/lxip/nic_handler.cc @@ -35,10 +35,10 @@ class Nic_client Nic::Packet_allocator _tx_block_alloc; Nic::Connection _nic; - Genode::Signal_handler _sink_ack; - Genode::Signal_handler _sink_submit; - Genode::Signal_handler _source_ack; - Genode::Signal_handler _link_state_change; + Genode::Io_signal_handler _sink_ack; + Genode::Io_signal_handler _sink_submit; + Genode::Io_signal_handler _source_ack; + Genode::Io_signal_handler _link_state_change; void (*_tick)(); diff --git a/repos/dde_linux/src/lib/lxip/timer_handler.cc b/repos/dde_linux/src/lib/lxip/timer_handler.cc index 5849aa22b..103572eb0 100644 --- a/repos/dde_linux/src/lib/lxip/timer_handler.cc +++ b/repos/dde_linux/src/lib/lxip/timer_handler.cc @@ -86,7 +86,7 @@ class Lx::Timer ::Timer::Connection _timer_conn; Lx_kit::List _list; - Genode::Signal_handler _handler; + Genode::Io_signal_handler _handler; Genode::Tslab _timer_alloc; void (*_tick)(); diff --git a/repos/dde_linux/src/lib/usb/raw/raw.cc b/repos/dde_linux/src/lib/usb/raw/raw.cc index eafec2a2a..8b906c7df 100644 --- a/repos/dde_linux/src/lib/usb/raw/raw.cc +++ b/repos/dde_linux/src/lib/usb/raw/raw.cc @@ -533,8 +533,8 @@ class Usb::Session_component : public Session_rpc_object, long _dev = 0; Device *_device = nullptr; Signal_context_capability _sigh_state_change; - Signal_handler _packet_avail; - Signal_handler _ready_ack; + Io_signal_handler _packet_avail; + Io_signal_handler _ready_ack; Worker _worker; Ram_dataspace_capability _tx_ds; diff --git a/repos/dde_rump/src/lib/vfs/rump/vfs_rump.cc b/repos/dde_rump/src/lib/vfs/rump/vfs_rump.cc index addf712cd..ca2793089 100644 --- a/repos/dde_rump/src/lib/vfs/rump/vfs_rump.cc +++ b/repos/dde_rump/src/lib/vfs/rump/vfs_rump.cc @@ -494,8 +494,8 @@ class Rump_factory : public Vfs::File_system_factory { private: - Timer::Connection _timer; - Genode::Signal_handler _sync_handler; + Timer::Connection _timer; + Genode::Io_signal_handler _sync_handler; void _sync() { _rump_sync(); } diff --git a/repos/libports/src/lib/libc/task.cc b/repos/libports/src/lib/libc/task.cc index 33ff4942d..88f1e4da5 100644 --- a/repos/libports/src/lib/libc/task.cc +++ b/repos/libports/src/lib/libc/task.cc @@ -366,7 +366,7 @@ struct Libc::Kernel Env_implementation _libc_env { _env, _heap, _io_response_handler }; Vfs_plugin _vfs { _libc_env, _heap }; - Genode::Reconstructible> _resume_main_handler { + Genode::Reconstructible> _resume_main_handler { _env.ep(), *this, &Kernel::_resume_main }; jmp_buf _kernel_context; @@ -605,7 +605,7 @@ struct Libc::Kernel /* _setjmp() returned after _longjmp() - user context suspended */ while ((!_app_returned) && (!_suspend_scheduled)) { - _env.ep().wait_and_dispatch_one_signal(); + _env.ep().wait_and_dispatch_one_io_signal(); if (_resume_main_once && !_setjmp(_kernel_context)) _switch_to_user(); @@ -623,7 +623,7 @@ struct Libc::Kernel _switch_to_user(); while ((!_app_returned) && (!_suspend_scheduled)) { - _env.ep().wait_and_dispatch_one_signal(); + _env.ep().wait_and_dispatch_one_io_signal(); if (_resume_main_once && !_setjmp(_kernel_context)) _switch_to_user(); } diff --git a/repos/libports/src/lib/lwip/platform/nic.cc b/repos/libports/src/lib/lwip/platform/nic.cc index d95594edf..1b49adec0 100644 --- a/repos/libports/src/lib/lwip/platform/nic.cc +++ b/repos/libports/src/lib/lwip/platform/nic.cc @@ -55,9 +55,9 @@ class Nic_receiver_thread : public Genode::Thread_deprecated<8192> Genode::Signal_receiver _sig_rec; - Genode::Signal_dispatcher _link_state_dispatcher; - Genode::Signal_dispatcher _rx_packet_avail_dispatcher; - Genode::Signal_dispatcher _rx_ready_to_ack_dispatcher; + Genode::Io_signal_dispatcher _link_state_dispatcher; + Genode::Io_signal_dispatcher _rx_packet_avail_dispatcher; + Genode::Io_signal_dispatcher _rx_ready_to_ack_dispatcher; void _handle_rx_packet_avail(unsigned) { diff --git a/repos/libports/src/test/libc_with_libc/main.cc b/repos/libports/src/test/libc_with_libc/main.cc index ab3831889..8e82b9b74 100644 --- a/repos/libports/src/test/libc_with_libc/main.cc +++ b/repos/libports/src/test/libc_with_libc/main.cc @@ -55,28 +55,24 @@ struct Explicitly_nested : Test /* - * Implicitly_nested test + * App_signal_deferred test * - * Call with_libc from within a signal handler while being - * suspended in a select() call. + * Application-level signals do not interrupt blocking libc calls but are + * deferred until the component returns to the entrypoint event loop. */ -struct Implicitly_nested : Test +struct App_signal_deferred : Test { Env &_env; void _handle() { - log("calling with_libc from signal handler"); - Libc::with_libc([&] () { - - printf("Hello from with_libc in signal handler\n"); - }); + error("application-level signal was dispatched during select()"); } - Signal_handler _dispatcher { - _env.ep(), *this, &Implicitly_nested::_handle }; + Signal_handler _dispatcher { + _env.ep(), *this, &App_signal_deferred::_handle }; - Implicitly_nested(Env &env, int id) + App_signal_deferred(Env &env, int id) : Test(env, id), _env(env) { log("calling with_libc"); @@ -124,7 +120,7 @@ struct Explicitly_triple_nested : Test struct Main { Constructible test_1; - Constructible test_2; + Constructible test_2; Constructible test_3; Main(Env &env) diff --git a/repos/os/include/os/timer.h b/repos/os/include/os/timer.h index 2a9e1c3f1..4f6481f2f 100644 --- a/repos/os/include/os/timer.h +++ b/repos/os/include/os/timer.h @@ -36,7 +36,7 @@ class Genode::Timer_time_source : public Genode::Time_source enum { MIN_TIMEOUT_US = 5000 }; - using Signal_handler = Genode::Signal_handler; + using Signal_handler = Genode::Io_signal_handler; ::Timer::Session &_session; Signal_handler _signal_handler; diff --git a/repos/os/include/usb/packet_handler.h b/repos/os/include/usb/packet_handler.h index dc581d24a..6dc5f840d 100644 --- a/repos/os/include/usb/packet_handler.h +++ b/repos/os/include/usb/packet_handler.h @@ -26,11 +26,11 @@ class Usb::Packet_handler Usb::Connection &_connection; Genode::Entrypoint &_ep; - Signal_handler _rpc_ack_avail = - {_ep, *this, &Packet_handler::_packet_handler }; + Io_signal_handler _rpc_ack_avail { + _ep, *this, &Packet_handler::_packet_handler }; - Signal_handler _rpc_ready_submit = - { _ep, *this, &Packet_handler::_ready_handler }; + Io_signal_handler _rpc_ready_submit { + _ep, *this, &Packet_handler::_ready_handler }; bool _ready_submit = true; @@ -76,7 +76,7 @@ class Usb::Packet_handler void wait_for_packet() { - packet_avail() ? _packet_handler() : _ep.wait_and_dispatch_one_signal(); + packet_avail() ? _packet_handler() : _ep.wait_and_dispatch_one_io_signal(); } Packet_descriptor alloc(size_t size) @@ -108,7 +108,7 @@ class Usb::Packet_handler /* wait for ready_to_submit signal */ while (!_ready_submit) - _ep.wait_and_dispatch_one_signal(); + _ep.wait_and_dispatch_one_io_signal(); } _connection.source()->submit_packet(p); diff --git a/repos/os/run/signal.run b/repos/os/run/signal.run index 7c227d2ae..51ee34723 100644 --- a/repos/os/run/signal.run +++ b/repos/os/run/signal.run @@ -33,3 +33,7 @@ build_boot_image "core ld.lib.so init timer test-signal" append qemu_args "-nographic -m 64" run_genode_until {.*--- Signalling test finished ---.*\n} 200 + +grep_output {Error: } + +compare_output_to {} diff --git a/repos/os/src/lib/server/server.cc b/repos/os/src/lib/server/server.cc index 850f82050..d1d8b2b24 100644 --- a/repos/os/src/lib/server/server.cc +++ b/repos/os/src/lib/server/server.cc @@ -34,5 +34,5 @@ void Component::construct(Genode::Env &env) void Server::wait_and_dispatch_one_signal() { if (_env) - _env->ep().wait_and_dispatch_one_signal(); + _env->ep().wait_and_dispatch_one_io_signal(); } diff --git a/repos/os/src/lib/vfs/fs_file_system.h b/repos/os/src/lib/vfs/fs_file_system.h index f1488a717..42c89ba6c 100644 --- a/repos/os/src/lib/vfs/fs_file_system.h +++ b/repos/os/src/lib/vfs/fs_file_system.h @@ -167,9 +167,8 @@ class Vfs::Fs_file_system : public File_system /* pass packet to server side */ source.submit_packet(packet_in); - while (handle.queued_read_state != Handle_state::Queued_state::ACK) - { - _env.ep().wait_and_dispatch_one_signal(); + while (handle.queued_read_state != Handle_state::Queued_state::ACK) { + _env.ep().wait_and_dispatch_one_io_signal(); } /* obtain result packet descriptor with updated status info */ @@ -219,9 +218,8 @@ class Vfs::Fs_file_system : public File_system /* pass packet to server side */ source.submit_packet(packet_in); - while (handle.queued_write_state != Handle_state::Queued_state::ACK) - { - _env.ep().wait_and_dispatch_one_signal(); + while (handle.queued_write_state != Handle_state::Queued_state::ACK) { + _env.ep().wait_and_dispatch_one_io_signal(); } /* obtain result packet descriptor with updated status info */ @@ -274,7 +272,7 @@ class Vfs::Fs_file_system : public File_system } } - Genode::Signal_handler _ack_handler { + Genode::Io_signal_handler _ack_handler { _env.ep(), *this, &Fs_file_system::_handle_ack }; public: diff --git a/repos/os/src/lib/vfs/terminal_file_system.h b/repos/os/src/lib/vfs/terminal_file_system.h index cb6ebc208..421c8fcf8 100644 --- a/repos/os/src/lib/vfs/terminal_file_system.h +++ b/repos/os/src/lib/vfs/terminal_file_system.h @@ -74,7 +74,7 @@ class Vfs::Terminal_file_system : public Single_file_system Handle_registry _handle_registry; - Genode::Signal_handler _read_avail_handler { + Genode::Io_signal_handler _read_avail_handler { _env.ep(), *this, &Terminal_file_system::_handle_read_avail }; void _handle_read_avail() diff --git a/repos/os/src/server/blk_cache/driver.h b/repos/os/src/server/blk_cache/driver.h index 80c55eb69..56876efa4 100644 --- a/repos/os/src/server/blk_cache/driver.h +++ b/repos/os/src/server/blk_cache/driver.h @@ -123,9 +123,9 @@ class Driver : public Block::Driver Genode::size_t _blk_sz; /* block size */ Block::sector_t _blk_cnt; /* block count */ Chunk_level_0 _cache; /* chunk hierarchy */ - Genode::Signal_handler _source_ack; - Genode::Signal_handler _source_submit; - Genode::Signal_handler _yield; + Genode::Io_signal_handler _source_ack; + Genode::Io_signal_handler _source_submit; + Genode::Io_signal_handler _yield; Driver(Driver const&); /* singleton pattern */ Driver& operator=(Driver const&); /* singleton pattern */ @@ -279,7 +279,7 @@ class Driver : public Block::Driver */ off = e.off; len = _blk_sz * _blk_cnt - off; - _env.ep().wait_and_dispatch_one_signal(); + _env.ep().wait_and_dispatch_one_io_signal(); } } } diff --git a/repos/os/src/test/blk/cli/main.cc b/repos/os/src/test/blk/cli/main.cc index 143e8c383..05909281a 100644 --- a/repos/os/src/test/blk/cli/main.cc +++ b/repos/os/src/test/blk/cli/main.cc @@ -68,9 +68,9 @@ class Test Genode::Entrypoint &_ep; Genode::Allocator_avl _alloc; Block::Connection _session; - Genode::Signal_handler _disp_ack; - Genode::Signal_handler _disp_submit; - Genode::Signal_handler _disp_timeout; + Genode::Io_signal_handler _disp_ack; + Genode::Io_signal_handler _disp_submit; + Genode::Io_signal_handler _disp_timeout; Timer::Connection _timer; bool _handle; @@ -110,7 +110,7 @@ class Test void _handle_signal() { _handle = true; - while (_handle) _ep.wait_and_dispatch_one_signal(); + while (_handle) _ep.wait_and_dispatch_one_io_signal(); } }; diff --git a/repos/os/src/test/signal/main.cc b/repos/os/src/test/signal/main.cc index 752cdbd3d..228550318 100644 --- a/repos/os/src/test/signal/main.cc +++ b/repos/os/src/test/signal/main.cc @@ -434,77 +434,106 @@ struct Many_contexts_test : Signal_test }; /** - * Test 'wait_and_dispatch_one_signal' implementation for entrypoints + * Test 'wait_and_dispatch_one_io_signal' implementation for entrypoints * * Normally Genode signals are delivered by a signal thread, which blocks for * incoming signals and is woken up when a signals arrives, the thread then * sends an RPC to an entrypoint that, in turn, processes the signal. - * 'wait_and_dispatch_one_signal' allows an entrypoint to receive signals - * directly, by taking advantage of the same code as the signal thread. This - * leaves the problem that at this point two entities (the signal thread and the - * entrypoint) may wait for signals to arrive. It is not decidable which entity - * is woken up on signal arrival. If the signal thread is woken up and tries to - * deliver the signal RPC, system may dead lock when no additional signal - * arrives to pull the entrypoint out of the signal waiting code. This test - * triggers this exact situation. We also test nesting with the same signal - * context of 'wait_and_dispatch_one_signal' here, which also caused dead locks - * in the past. + * 'wait_and_dispatch_one_io_signal' allows an entrypoint to receive I/O-level + * signals directly, by taking advantage of the same code as the signal thread. + * This leaves the problem that at this point two entities (the signal thread + * and the entrypoint) may wait for signals to arrive. It is not decidable + * which entity is woken up on signal arrival. If the signal thread is woken up + * and tries to deliver the signal RPC, system may dead lock when no additional + * signal arrives to pull the entrypoint out of the signal waiting code. This + * test triggers this exact situation. We also test nesting with the same + * signal context of 'wait_and_dispatch_one_io_signal' here, which also caused + * dead locks in the past. Also, the test verifies application-level signals + * are deferred during 'wait_and_dispatch_one_io_signal'. */ struct Nested_test : Signal_test { static constexpr char const *brief = "wait and dispatch signals at entrypoint"; - struct Wait_interface + struct Test_interface { - GENODE_RPC(Rpc_dispatch_test, void, dispatch_test); - GENODE_RPC_INTERFACE(Rpc_dispatch_test); + GENODE_RPC(Rpc_test_io_dispatch, void, test_io_dispatch); + GENODE_RPC(Rpc_test_app_dispatch, void, test_app_dispatch); + GENODE_RPC_INTERFACE(Rpc_test_io_dispatch, Rpc_test_app_dispatch); }; - struct Wait_component : - Rpc_object + struct Test_component : Rpc_object { - Entrypoint &ep; + Nested_test &test; - Wait_component(Entrypoint &ep) : ep(ep) { } + Test_component(Nested_test &test) : test(test) { } - void dispatch_test() + void test_io_dispatch() { - log("1/5: [ep] wait for signal during RPC from [outside]"); - ep.wait_and_dispatch_one_signal(); - log("5/5: [ep] success"); + log("1/8: [ep] wait for I/O-level signal during RPC from [outside]"); + while (!test.io_done) test.ep.wait_and_dispatch_one_io_signal(); + log("6/8: [ep] I/O completed"); + } + + void test_app_dispatch() + { + if (!test.app_done) + error("8/8: [ep] application-level signal was not dispatched"); + else + log("8/8: [ep] success"); } }; struct Sender_thread : Thread { - Signal_context_capability cap; - Timer::Connection timer; + Nested_test &test; + Timer::Connection timer; - Sender_thread(Env &env, Signal_context_capability cap) - : Thread(env, "sender_thread", 1024 * sizeof(long)), cap(cap), timer(env) + Sender_thread(Env &env, Nested_test &test) + : + Thread(env, "sender_thread", 1024 * sizeof(long)), + test(test), timer(env) { } - void entry() { - timer.msleep(500); - log("2/5: [outside] submit initial signal"); - Signal_transmitter(cap).submit(); + void entry() + { + timer.msleep(1000); + + log("2/8: [outside] submit application-level signal (should be deferred)"); + Signal_transmitter(test.nop_handler).submit(); + Signal_transmitter(test.app_handler).submit(); + Signal_transmitter(test.nop_handler).submit(); + + log("3/8: [outside] submit I/O-level signal"); + Signal_transmitter(test.io_handler).submit(); + Signal_transmitter(test.nop_handler).submit(); } }; - Env &env; - Entrypoint ep { env, 2048 * sizeof(long), - "wait_dispatch_ep" }; - Signal_handler dispatcher { ep, *this, - &Nested_test::handle }; - Wait_component wait { ep }; - Capability wait_cap = ep.manage(wait); - Sender_thread thread { env, dispatcher }; - bool nested { false }; + Env &env; + Entrypoint ep { env, 2048 * sizeof(long), "wait_dispatch_ep" }; + + Signal_handler app_handler { ep, *this, &Nested_test::handle_app }; + Signal_handler nop_handler { ep, *this, &Nested_test::handle_nop }; + Io_signal_handler io_handler { ep, *this, &Nested_test::handle_io }; + + Test_component wait { *this }; + Capability wait_cap { ep.manage(wait) }; + Sender_thread thread { env, *this }; + bool nested { false }; + bool volatile app_done { false }; + bool volatile io_done { false }; + + Timer::Connection timer { env }; Nested_test(Env &env, int id) : Signal_test(id, brief), env(env) { thread.start(); - wait_cap.call(); + wait_cap.call(); + + /* grant the ep some time for application-signal handling */ + timer.msleep(1000); + wait_cap.call(); } ~Nested_test() @@ -512,22 +541,35 @@ struct Nested_test : Signal_test ep.dissolve(wait); } - void handle() + void handle_app() + { + if (!io_done) + error("7/8: [ep] application-level signal was not deferred"); + else + log("7/8: [ep] application-level signal received"); + + app_done = true; + } + + void handle_nop() { } + + void handle_io() { if (nested) { - log("4/5: [ep] nested signal received"); + log("5/8: [ep] nested I/O-level signal received"); + io_done = true; return; } - log("3/5: [ep] signal received - sending nested signal"); + log("4/8: [ep] I/O-level signal received - sending nested signal"); nested = true; - Signal_transmitter(dispatcher).submit(); - ep.wait_and_dispatch_one_signal(); + Signal_transmitter(io_handler).submit(); + ep.wait_and_dispatch_one_io_signal(); } }; /** - * Stress-test 'wait_and_dispatch_one_signal' implementation for entrypoints + * Stress-test 'wait_and_dispatch_one_io_signal' implementation for entrypoints * * Let multiple entrypoints directly wait and dispatch signals in a * highly nested manner and with multiple stressful senders. @@ -559,11 +601,12 @@ struct Nested_stress_test : Signal_test struct Receiver { - Entrypoint ep; - char const *name; - Signal_handler handler { ep, *this, &Receiver::handle }; - unsigned count { 0 }; - bool destruct { false }; + Entrypoint ep; + char const *name; + unsigned count { 0 }; + bool destruct { false }; + + Io_signal_handler handler { ep, *this, &Receiver::handle }; Receiver(Env &env, char const *name) : ep(env, 3 * 1024 * sizeof(long), name), name(name) { } @@ -574,8 +617,7 @@ struct Nested_stress_test : Signal_test * We have to get out of the nesting if the host wants to destroy * us to avoid a deadlock at the lock in the signal handler. */ - if (destruct) { - return; } + if (destruct) { return; } /* raise call counter */ count++; @@ -585,7 +627,7 @@ struct Nested_stress_test : Signal_test * gives zero, then unwind the whole nesting and start afresh. */ if ((count & ((1 << UNWIND_COUNT_MOD_LOG2) - 1)) != 0) { - ep.wait_and_dispatch_one_signal(); } + ep.wait_and_dispatch_one_io_signal(); } } }; @@ -599,8 +641,8 @@ struct Nested_stress_test : Signal_test Sender sender_3 { env, "sender-3", receiver_3.handler }; Signal_transmitter done; - Signal_handler poll - { env.ep(), *this, &Nested_stress_test::handle_poll }; + Io_signal_handler poll { + env.ep(), *this, &Nested_stress_test::handle_poll }; Nested_stress_test(Env &env, int id, Signal_context_capability done) : Signal_test(id, brief), env(env), done(done)