base: Dispatch server signal in entry point

Currently, when a signal arrives in the main thread, the signal dispatcher is
retrieved and called from the main thread, the dispatcher uses a proxy object
that in turn sends an RPC to the entry point. This becomes a problem when the
entry point destroys the dispatcher object, before the dispatch function has
been called by the main thread. Therefore, the main thread should simply send an
RPC to the entry point upon signal arrival and the dispatching should be handled
solely by the entry point.

Issue #1738
This commit is contained in:
Sebastian Sumpf 2015-10-08 17:45:12 +02:00 committed by Christian Helmuth
parent 0879a9570c
commit 2b429ee84c
6 changed files with 143 additions and 198 deletions

View File

@ -195,6 +195,41 @@ void Signal_receiver::dissolve(Signal_context * const context)
bool Signal_receiver::pending() { return Kernel::signal_pending(_cap.dst()); }
/*
* Last signal received by 'block_for_signal'
*/
void Signal_receiver::block_for_signal()
{
/* await a signal */
if (Kernel::await_signal(_cap.dst())) {
PERR("failed to receive signal");
return;
}
/* get signal */
Signal::Data *data = (Signal::Data *)Thread_base::myself()->utcb()->base();
Signal s(*data);
/* save signal data in context list */
s.context()->_curr_signal = *data;
_contexts.insert(&s.context()->_receiver_le);
}
Signal Signal_receiver::pending_signal()
{
List_element<Signal_context> *le = _contexts.first();
if (!le)
throw Signal_not_pending();
/* remove from context list */
Signal_context *context = le->object();
_contexts.remove(le);
return Signal(context->_curr_signal);
}
Signal Signal_receiver::wait_for_signal()
{
/* await a signal */

View File

@ -207,6 +207,7 @@ class Genode::Signal_receiver : Noncopyable
*/
class Context_already_in_use { };
class Context_not_associated { };
class Signal_not_pending { };
/**
* Constructor
@ -243,12 +244,25 @@ class Genode::Signal_receiver : Noncopyable
bool pending();
/**
* Block until a signal is received
* Block until a signal is received and return the signal
*
* \return received signal
*/
Signal wait_for_signal();
/**
* Block until a signal is received
*/
void block_for_signal();
/**
* Retrieve pending signal
*
* \throw 'Signal_not_pending' no pending signal found
* \return received signal
*/
Signal pending_signal();
/**
* Locally submit signal to the receiver
*

View File

@ -332,52 +332,66 @@ bool Signal_receiver::pending()
}
Signal Signal_receiver::pending_signal()
{
Lock::Guard list_lock_guard(_contexts_lock);
/* look up the contexts for the pending signal */
for (List_element<Signal_context> *le = _contexts.first(); le; le = le->next()) {
Signal_context *context = le->object();
Lock::Guard lock_guard(context->_lock);
/* check if context has a pending signal */
if (!context->_pending)
continue;
context->_pending = false;
Signal::Data result = context->_curr_signal;
/* invalidate current signal in context */
context->_curr_signal = Signal::Data(0, 0);
if (result.num == 0)
PWRN("returning signal with num == 0");
Trace::Signal_received trace_event(*context, result.num);
/* return last received signal */
return result;
}
/*
* Normally, we should never arrive at this point because that would
* mean, the '_signal_available' semaphore was increased without
* registering the signal in any context associated to the receiver.
*
* However, if a context gets dissolved right after submitting a
* signal, we may have increased the semaphore already. In this case
* the signal-causing context is absent from the list.
*/
throw Signal_not_pending();
}
void Signal_receiver::block_for_signal()
{
_signal_available.down();
}
Signal Signal_receiver::wait_for_signal()
{
for (;;) {
/* block until the receiver has received a signal */
_signal_available.down();
block_for_signal();
Lock::Guard list_lock_guard(_contexts_lock);
/* look up the contexts for the pending signal */
for (List_element<Signal_context> *le = _contexts.first(); le; le = le->next()) {
Signal_context *context = le->object();
Lock::Guard lock_guard(context->_lock);
/* check if context has a pending signal */
if (!context->_pending)
continue;
context->_pending = false;
Signal::Data result = context->_curr_signal;
/* invalidate current signal in context */
context->_curr_signal = Signal::Data(0, 0);
if (result.num == 0)
PWRN("returning signal with num == 0");
Trace::Signal_received trace_event(*context, result.num);
/* return last received signal */
return result;
}
/*
* Normally, we should never arrive at this point because that would
* mean, the '_signal_available' semaphore was increased without
* registering the signal in any context associated to the receiver.
*
* However, if a context gets dissolved right after submitting a
* signal, we may have increased the semaphore already. In this case
* the signal-causing context is absent from the list.
*/
try {
return pending_signal();
} catch (Signal_not_pending) { }
}
return Signal::Data(0, 0); /* unreachable */
}

View File

@ -69,12 +69,12 @@ class Server::Entrypoint
/**
* Associate signal dispatcher with entry point
*/
Signal_context_capability manage(Signal_rpc_dispatcher_base &);
Signal_context_capability manage(Signal_dispatcher_base &);
/**
* Disassociate signal dispatcher from entry point
*/
void dissolve(Signal_rpc_dispatcher_base &);
void dissolve(Signal_dispatcher_base &);
/**
* Return RPC entrypoint

View File

@ -18,120 +18,13 @@
#include <base/signal.h>
namespace Genode {
class Signal_rpc_dispatcher_base;
template <typename> class Signal_rpc_functor;
template <typename, typename> class Signal_rpc_member;
template <typename FUNCTOR>
Signal_rpc_functor<FUNCTOR> signal_rpc_functor(FUNCTOR &);
}
struct Genode::Signal_rpc_dispatcher_base : Genode::Signal_dispatcher_base
{
private:
struct Proxy
{
GENODE_RPC(Rpc_handle_signal, void, handle_signal, unsigned);
GENODE_RPC_INTERFACE(Rpc_handle_signal);
};
struct Proxy_component : Genode::Rpc_object<Proxy, Proxy_component>
{
Genode::Signal_rpc_dispatcher_base &_dispatcher;
Proxy_component(Signal_rpc_dispatcher_base &dispatcher)
: _dispatcher(dispatcher) { }
void handle_signal(unsigned num)
{
_dispatcher.dispatch_at_entrypoint(num);
}
};
Proxy_component _proxy;
Capability<Proxy> _proxy_cap;
protected:
Signal_rpc_dispatcher_base() : _proxy(*this) { }
Capability<Proxy> proxy_cap() { return _proxy_cap; }
public:
/**
* Associate signal dispatcher with entrypoint
*/
Signal_context_capability manage(Signal_receiver &sig_rec, Rpc_entrypoint &ep)
{
_proxy_cap = ep.manage(&_proxy);
return sig_rec.manage(this);
}
/**
* Disassociate signal dispatcher from entrypoint
*/
void dissolve(Signal_receiver &sig_rec, Rpc_entrypoint &ep)
{
ep.dissolve(&_proxy);
_proxy_cap = Capability<Proxy>();
sig_rec.dissolve(this);
}
public:
/**
* Interface of Signal_dispatcher_base
*/
void dispatch(unsigned num) {
proxy_cap().call<Proxy::Rpc_handle_signal>(num); }
/**
* To be implemented by the derived class
*/
virtual void dispatch_at_entrypoint(unsigned num) = 0;
};
/**
* Signal dispatcher that executes the signal handling code in the context
* of an RPC entrypoint
*
* The 'Signal_rpc_dispatcher' provides an easy way for a server to serialize
* the handling of signals with incoming RPC requests. Incoming signals are
* delegated to the RPC entrypoint via a local RPC call. The signal handling
* code is then executed in the context of the RPC entrypoint.
*/
template <typename FUNCTOR>
struct Genode::Signal_rpc_functor : Genode::Signal_rpc_dispatcher_base
{
FUNCTOR &functor;
/**
* Constructor
*
* \param func functor taking containing the signal-handling code
*
* The functor 'func' has the signature 'void func(unsigned num)'
* whereas 'num' is the number of signals received at once.
*/
Signal_rpc_functor(FUNCTOR &functor) : functor(functor) { }
/**
* Interface of Signal_rpc_dispatcher_base
*/
void dispatch_at_entrypoint(unsigned num) { functor(num); }
};
namespace Server{
class Entrypoint;
}
/**
* Signal dispatcher for directing signals via RPC to object methods
*
@ -146,7 +39,7 @@ namespace Server{
* \param EP type of entrypoint handling signal RPC
*/
template <typename T, typename EP = Server::Entrypoint>
struct Genode::Signal_rpc_member : Genode::Signal_rpc_dispatcher_base,
struct Genode::Signal_rpc_member : Genode::Signal_dispatcher_base,
Genode::Signal_context_capability
{
EP &ep;
@ -167,19 +60,9 @@ struct Genode::Signal_rpc_member : Genode::Signal_rpc_dispatcher_base,
~Signal_rpc_member() { ep.dissolve(*this); }
/**
* Interface of Signal_rpc_dispatcher_base
* Interface of Signal_dispatcher_base
*/
void dispatch_at_entrypoint(unsigned num) { (obj.*member)(num); }
void dispatch(unsigned num) { (obj.*member)(num); }
};
/**
* Convenience utility for creating 'Signal_rpc_dispatcher' objects
*/
template <typename FUNCTOR>
Genode::Signal_rpc_functor<FUNCTOR> Genode::signal_rpc_functor(FUNCTOR &func)
{
return Signal_rpc_functor<FUNCTOR>(func);
}
#endif /* _INCLUDE__OS__SIGNAL_RPC_DISPATCHER_H_ */

View File

@ -47,55 +47,43 @@ static Genode::Signal_receiver &global_sig_rec()
}
static void wait_and_dispatch_one_signal(bool entrypoint)
static void dispatch(Signal &sig)
{
/*
* We call the signal dispatcher outside of the scope of 'Signal'
* object because we block the RPC interface in the input handler
* when the kill mode gets actived. While kill mode is active, we
* do not serve incoming RPC requests but we need to stay responsive
* to user input. Hence, we wait for signals in the input dispatcher
* in this case. An already existing 'Signal' object would lock the
* signal receiver and thereby prevent this nested way of signal
* handling.
*/
Signal_rpc_dispatcher_base *dispatcher = 0;
unsigned num = 0;
{
Signal sig = global_sig_rec().wait_for_signal();
dispatcher = dynamic_cast<Signal_rpc_dispatcher_base *>(sig.context());
num = sig.num();
}
Signal_dispatcher_base *dispatcher = 0;
dispatcher = dynamic_cast<Signal_dispatcher_base *>(sig.context());
if (!dispatcher)
return;
if (entrypoint)
dispatcher->dispatch_at_entrypoint(num);
else
dispatcher->dispatch(num);
}
Signal_context_capability Entrypoint::manage(Signal_rpc_dispatcher_base &dispatcher)
{
return dispatcher.manage(global_sig_rec(), global_rpc_ep());
dispatcher->dispatch(sig.num());
}
void Server::Entrypoint::dissolve(Signal_rpc_dispatcher_base &dispatcher)
/**
* Dispatch a signal at entry point
*/
void Server::wait_and_dispatch_one_signal()
{
dispatcher.dissolve(global_sig_rec(), global_rpc_ep());
Signal sig = global_sig_rec().wait_for_signal();
dispatch(sig);
}
Signal_context_capability Entrypoint::manage(Signal_dispatcher_base &dispatcher)
{
return global_sig_rec().manage(&dispatcher);
}
void Server::Entrypoint::dissolve(Signal_dispatcher_base &dispatcher)
{
global_sig_rec().dissolve(&dispatcher);
}
Server::Entrypoint::Entrypoint() : _rpc_ep(global_rpc_ep()) { }
void Server::wait_and_dispatch_one_signal() {
::wait_and_dispatch_one_signal(true); }
namespace Server {
struct Constructor;
struct Constructor_component;
@ -105,7 +93,8 @@ namespace Server {
struct Server::Constructor
{
GENODE_RPC(Rpc_construct, void, construct);
GENODE_RPC_INTERFACE(Rpc_construct);
GENODE_RPC(Rpc_signal, void, signal);
GENODE_RPC_INTERFACE(Rpc_construct, Rpc_signal);
};
@ -113,6 +102,14 @@ struct Server::Constructor_component : Rpc_object<Server::Constructor,
Server::Constructor_component>
{
void construct() { Server::construct(global_ep()); }
void signal()
{
try {
Signal sig = global_sig_rec().pending_signal();
::dispatch(sig);
} catch (Signal_receiver::Signal_not_pending) { }
}
};
@ -130,13 +127,15 @@ int main(int argc, char **argv)
/* process incoming signals */
for (;;) {
global_sig_rec().block_for_signal();
/*
* 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 {
wait_and_dispatch_one_signal(false);
constructor_cap.call<Server::Constructor::Rpc_signal>();
} catch(Genode::Ipc_error) { }
}
}