mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-29 15:44:02 +00:00
parent
e33d65aea0
commit
1d99e7ede9
@ -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<Signal_receiver> _sig_rec;
|
||||
|
||||
Lock _deferred_signals_mutex;
|
||||
List<List_element<Signal_context>> _deferred_signals;
|
||||
|
||||
void _handle_deferred_signals() { }
|
||||
Constructible<Signal_handler<Entrypoint>> _deferred_signal_handler;
|
||||
|
||||
bool _suspended = false;
|
||||
void (*_suspended_callback) () = nullptr;
|
||||
void (*_resumed_callback) () = nullptr;
|
||||
@ -116,7 +113,8 @@ class Genode::Entrypoint : Genode::Noncopyable
|
||||
Constructible<Genode::Signal_handler<Entrypoint>> _suspend_dispatcher;
|
||||
|
||||
void _dispatch_signal(Signal &sig);
|
||||
|
||||
void _defer_signal(Signal &sig);
|
||||
void _process_deferred_signals();
|
||||
void _process_incoming_signals();
|
||||
|
||||
Constructible<Signal_proxy_thread> _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
|
||||
|
@ -39,7 +39,9 @@ namespace Genode {
|
||||
class Signal_dispatcher_base;
|
||||
|
||||
template <typename> class Signal_dispatcher;
|
||||
template <typename> class Io_signal_dispatcher;
|
||||
template <typename, typename> class Signal_handler;
|
||||
template <typename, typename> class Io_signal_handler;
|
||||
|
||||
typedef Capability<Signal_context> 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<Signal_context> _registry_le;
|
||||
|
||||
/**
|
||||
* List element in deferred application signal list
|
||||
*/
|
||||
List_element<Signal_context> _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<Signal_context> *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 <typename T>
|
||||
struct Genode::Io_signal_dispatcher : Signal_dispatcher<T>
|
||||
{
|
||||
Io_signal_dispatcher(Signal_receiver &sig_rec,
|
||||
T &obj, void (T::*member)(unsigned))
|
||||
: Signal_dispatcher<T>(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 <typename T, typename EP = Genode::Entrypoint>
|
||||
struct Genode::Io_signal_handler : Signal_handler<T, EP>
|
||||
{
|
||||
Io_signal_handler(EP &ep, T &obj, void (T::*member)())
|
||||
: Signal_handler<T, EP>(ep, obj, member)
|
||||
{ Signal_context::_level = Signal_context::Level::Io; }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__BASE__SIGNAL_H_ */
|
||||
|
@ -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
|
||||
|
@ -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<Signal_dispatcher_base *>(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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -305,7 +305,7 @@ void *memmove(void *d, const void *s, size_t n)
|
||||
** linux/sched.h **
|
||||
*******************/
|
||||
|
||||
struct Timeout : Genode::Signal_handler<Timeout>
|
||||
struct Timeout : Genode::Io_signal_handler<Timeout>
|
||||
{
|
||||
Genode::Entrypoint &ep;
|
||||
Timer::Connection timer;
|
||||
@ -321,7 +321,7 @@ struct Timeout : Genode::Signal_handler<Timeout>
|
||||
|
||||
Timeout(Genode::Env &env, Genode::Entrypoint &ep, void (*ticker)())
|
||||
:
|
||||
Signal_handler<Timeout>(ep, *this, &Timeout::handle),
|
||||
Io_signal_handler<Timeout>(ep, *this, &Timeout::handle),
|
||||
ep(ep), timer(env), tick(ticker)
|
||||
{
|
||||
timer.sigh(*this);
|
||||
@ -334,7 +334,7 @@ struct Timeout : Genode::Signal_handler<Timeout>
|
||||
|
||||
void wait()
|
||||
{
|
||||
ep.wait_and_dispatch_one_signal();
|
||||
ep.wait_and_dispatch_one_io_signal();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -35,10 +35,10 @@ class Nic_client
|
||||
Nic::Packet_allocator _tx_block_alloc;
|
||||
Nic::Connection _nic;
|
||||
|
||||
Genode::Signal_handler<Nic_client> _sink_ack;
|
||||
Genode::Signal_handler<Nic_client> _sink_submit;
|
||||
Genode::Signal_handler<Nic_client> _source_ack;
|
||||
Genode::Signal_handler<Nic_client> _link_state_change;
|
||||
Genode::Io_signal_handler<Nic_client> _sink_ack;
|
||||
Genode::Io_signal_handler<Nic_client> _sink_submit;
|
||||
Genode::Io_signal_handler<Nic_client> _source_ack;
|
||||
Genode::Io_signal_handler<Nic_client> _link_state_change;
|
||||
|
||||
void (*_tick)();
|
||||
|
||||
|
@ -86,7 +86,7 @@ class Lx::Timer
|
||||
|
||||
::Timer::Connection _timer_conn;
|
||||
Lx_kit::List<Context> _list;
|
||||
Genode::Signal_handler<Lx::Timer> _handler;
|
||||
Genode::Io_signal_handler<Lx::Timer> _handler;
|
||||
Genode::Tslab<Context, 32 * sizeof(Context)> _timer_alloc;
|
||||
|
||||
void (*_tick)();
|
||||
|
@ -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<Session_component> _packet_avail;
|
||||
Signal_handler<Session_component> _ready_ack;
|
||||
Io_signal_handler<Session_component> _packet_avail;
|
||||
Io_signal_handler<Session_component> _ready_ack;
|
||||
Worker _worker;
|
||||
Ram_dataspace_capability _tx_ds;
|
||||
|
||||
|
@ -494,8 +494,8 @@ class Rump_factory : public Vfs::File_system_factory
|
||||
{
|
||||
private:
|
||||
|
||||
Timer::Connection _timer;
|
||||
Genode::Signal_handler<Rump_factory> _sync_handler;
|
||||
Timer::Connection _timer;
|
||||
Genode::Io_signal_handler<Rump_factory> _sync_handler;
|
||||
|
||||
void _sync() { _rump_sync(); }
|
||||
|
||||
|
@ -366,7 +366,7 @@ struct Libc::Kernel
|
||||
Env_implementation _libc_env { _env, _heap, _io_response_handler };
|
||||
Vfs_plugin _vfs { _libc_env, _heap };
|
||||
|
||||
Genode::Reconstructible<Genode::Signal_handler<Kernel>> _resume_main_handler {
|
||||
Genode::Reconstructible<Genode::Io_signal_handler<Kernel>> _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();
|
||||
}
|
||||
|
@ -55,9 +55,9 @@ class Nic_receiver_thread : public Genode::Thread_deprecated<8192>
|
||||
|
||||
Genode::Signal_receiver _sig_rec;
|
||||
|
||||
Genode::Signal_dispatcher<Nic_receiver_thread> _link_state_dispatcher;
|
||||
Genode::Signal_dispatcher<Nic_receiver_thread> _rx_packet_avail_dispatcher;
|
||||
Genode::Signal_dispatcher<Nic_receiver_thread> _rx_ready_to_ack_dispatcher;
|
||||
Genode::Io_signal_dispatcher<Nic_receiver_thread> _link_state_dispatcher;
|
||||
Genode::Io_signal_dispatcher<Nic_receiver_thread> _rx_packet_avail_dispatcher;
|
||||
Genode::Io_signal_dispatcher<Nic_receiver_thread> _rx_ready_to_ack_dispatcher;
|
||||
|
||||
void _handle_rx_packet_avail(unsigned)
|
||||
{
|
||||
|
@ -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<Implicitly_nested> _dispatcher {
|
||||
_env.ep(), *this, &Implicitly_nested::_handle };
|
||||
Signal_handler<App_signal_deferred> _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<Explicitly_nested> test_1;
|
||||
Constructible<Implicitly_nested> test_2;
|
||||
Constructible<App_signal_deferred> test_2;
|
||||
Constructible<Explicitly_triple_nested> test_3;
|
||||
|
||||
Main(Env &env)
|
||||
|
@ -36,7 +36,7 @@ class Genode::Timer_time_source : public Genode::Time_source
|
||||
|
||||
enum { MIN_TIMEOUT_US = 5000 };
|
||||
|
||||
using Signal_handler = Genode::Signal_handler<Timer_time_source>;
|
||||
using Signal_handler = Genode::Io_signal_handler<Timer_time_source>;
|
||||
|
||||
::Timer::Session &_session;
|
||||
Signal_handler _signal_handler;
|
||||
|
@ -26,11 +26,11 @@ class Usb::Packet_handler
|
||||
Usb::Connection &_connection;
|
||||
Genode::Entrypoint &_ep;
|
||||
|
||||
Signal_handler<Packet_handler> _rpc_ack_avail =
|
||||
{_ep, *this, &Packet_handler::_packet_handler };
|
||||
Io_signal_handler<Packet_handler> _rpc_ack_avail {
|
||||
_ep, *this, &Packet_handler::_packet_handler };
|
||||
|
||||
Signal_handler<Packet_handler> _rpc_ready_submit =
|
||||
{ _ep, *this, &Packet_handler::_ready_handler };
|
||||
Io_signal_handler<Packet_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);
|
||||
|
@ -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 {}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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<Fs_file_system> _ack_handler {
|
||||
Genode::Io_signal_handler<Fs_file_system> _ack_handler {
|
||||
_env.ep(), *this, &Fs_file_system::_handle_ack };
|
||||
|
||||
public:
|
||||
|
@ -74,7 +74,7 @@ class Vfs::Terminal_file_system : public Single_file_system
|
||||
|
||||
Handle_registry _handle_registry;
|
||||
|
||||
Genode::Signal_handler<Terminal_file_system> _read_avail_handler {
|
||||
Genode::Io_signal_handler<Terminal_file_system> _read_avail_handler {
|
||||
_env.ep(), *this, &Terminal_file_system::_handle_read_avail };
|
||||
|
||||
void _handle_read_avail()
|
||||
|
@ -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<Driver> _source_ack;
|
||||
Genode::Signal_handler<Driver> _source_submit;
|
||||
Genode::Signal_handler<Driver> _yield;
|
||||
Genode::Io_signal_handler<Driver> _source_ack;
|
||||
Genode::Io_signal_handler<Driver> _source_submit;
|
||||
Genode::Io_signal_handler<Driver> _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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,9 +68,9 @@ class Test
|
||||
Genode::Entrypoint &_ep;
|
||||
Genode::Allocator_avl _alloc;
|
||||
Block::Connection _session;
|
||||
Genode::Signal_handler<Test> _disp_ack;
|
||||
Genode::Signal_handler<Test> _disp_submit;
|
||||
Genode::Signal_handler<Test> _disp_timeout;
|
||||
Genode::Io_signal_handler<Test> _disp_ack;
|
||||
Genode::Io_signal_handler<Test> _disp_submit;
|
||||
Genode::Io_signal_handler<Test> _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();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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<Wait_interface, Wait_component>
|
||||
struct Test_component : Rpc_object<Test_interface, Test_component>
|
||||
{
|
||||
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<Nested_test> dispatcher { ep, *this,
|
||||
&Nested_test::handle };
|
||||
Wait_component wait { ep };
|
||||
Capability<Wait_interface> 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<Nested_test> app_handler { ep, *this, &Nested_test::handle_app };
|
||||
Signal_handler<Nested_test> nop_handler { ep, *this, &Nested_test::handle_nop };
|
||||
Io_signal_handler<Nested_test> io_handler { ep, *this, &Nested_test::handle_io };
|
||||
|
||||
Test_component wait { *this };
|
||||
Capability<Test_interface> 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_interface::Rpc_dispatch_test>();
|
||||
wait_cap.call<Test_interface::Rpc_test_io_dispatch>();
|
||||
|
||||
/* grant the ep some time for application-signal handling */
|
||||
timer.msleep(1000);
|
||||
wait_cap.call<Test_interface::Rpc_test_app_dispatch>();
|
||||
}
|
||||
|
||||
~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<Receiver> 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<Receiver> 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<Nested_stress_test> poll
|
||||
{ env.ep(), *this, &Nested_stress_test::handle_poll };
|
||||
Io_signal_handler<Nested_stress_test> 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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user