base: classify signals as I/O and application level

Fixes #2363
This commit is contained in:
Christian Helmuth 2017-04-03 10:45:51 +02:00
parent e33d65aea0
commit 1d99e7ede9
22 changed files with 287 additions and 131 deletions

View File

@ -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

View File

@ -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_ */

View File

@ -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

View File

@ -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());
}
}

View File

@ -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 */

View File

@ -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();
}
};

View File

@ -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)();

View File

@ -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)();

View File

@ -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;

View File

@ -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(); }

View File

@ -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();
}

View File

@ -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)
{

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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 {}

View File

@ -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();
}

View File

@ -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:

View File

@ -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()

View File

@ -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();
}
}
}

View File

@ -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();
}
};

View File

@ -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)