diff --git a/base-foc/src/core/signal_source_component.cc b/base-foc/src/core/signal_source_component.cc index a1d37ea2f..29fb7cd88 100644 --- a/base-foc/src/core/signal_source_component.cc +++ b/base-foc/src/core/signal_source_component.cc @@ -70,8 +70,10 @@ Signal_source::Signal Signal_source_component::wait_for_signal() Signal_source_component::Signal_source_component(Rpc_entrypoint *ep) -: Signal_source_rpc_object(cap_map()->insert(platform_specific()->cap_id_alloc()->alloc())), - _entrypoint(ep) +: + Signal_source_rpc_object(cap_map()->insert(platform_specific()->cap_id_alloc()->alloc())), + _entrypoint(ep), _finalizer(*this), + _finalizer_cap(_entrypoint->manage(&_finalizer)) { using namespace Fiasco; @@ -80,3 +82,20 @@ Signal_source_component::Signal_source_component(Rpc_entrypoint *ep) if (l4_error(res)) PERR("Allocation of irq object failed!"); } + + +Signal_source_component::~Signal_source_component() +{ + _finalizer_cap.call(); + _entrypoint->dissolve(&_finalizer); +} + + +void Signal_source_component::Finalizer_component::exit() +{ + /* + * On Fiasco.OC, the signal-source client does not use a blocking call + * to wait for signals. Hence, we do not need to take care of + * releasing the reply capability of such a call. + */ +} diff --git a/base-nova/src/core/signal_source_component.cc b/base-nova/src/core/signal_source_component.cc index e7111f058..e4b677c24 100644 --- a/base-nova/src/core/signal_source_component.cc +++ b/base-nova/src/core/signal_source_component.cc @@ -69,7 +69,9 @@ Signal_source::Signal Signal_source_component::wait_for_signal() Signal_source_component::Signal_source_component(Rpc_entrypoint *ep) -: _entrypoint(ep) +: + _entrypoint(ep), _finalizer(*this), + _finalizer_cap(_entrypoint->manage(&_finalizer)) { /* initialized blocking semaphore */ addr_t sem_sel = cap_selector_allocator()->alloc(); @@ -79,3 +81,20 @@ Signal_source_component::Signal_source_component(Rpc_entrypoint *ep) _blocking_semaphore = Native_capability(sem_sel); } + + +Signal_source_component::~Signal_source_component() +{ + _finalizer_cap.call(); + _entrypoint->dissolve(&_finalizer); +} + + +void Signal_source_component::Finalizer_component::exit() +{ + /* + * On NOVA, the signal-source client does not use a blocking call + * to wait for signals. Hence, we do not need to take care of + * releasing the reply capability of such a call. + */ +} diff --git a/base/src/core/include/signal_session_component.h b/base/src/core/include/signal_session_component.h index 73a06718e..a30db5143 100644 --- a/base/src/core/include/signal_session_component.h +++ b/base/src/core/include/signal_session_component.h @@ -71,11 +71,44 @@ namespace Genode { class Signal_source_component : public Signal_source_rpc_object { + /** + * Helper for clean destruction of signal-source component + * + * Normally, reply capabilities are implicitly destroyed when answering + * an RPC call. But when destructing a signal session while a signal- + * source client is blocking on a 'wait_for_signal' call, this + * blocking call will never return via the normal control flow + * (signal submission). In this case, the reply capability would + * outlive the signal session. To avoid the leakage of such reply + * capabilities, we let the signal-session destructor perform a + * core-local RPC call to the so-called 'Finalizer' object, which has + * the sole purpose of replying to the last outstanding + * 'wait_for_signal' call and thereby releasing the corresponding + * reply capability. + */ + struct Finalizer + { + GENODE_RPC(Rpc_exit, void, exit); + GENODE_RPC_INTERFACE(Rpc_exit); + }; + + struct Finalizer_component : Rpc_object + { + Signal_source_component &source; + + Finalizer_component(Signal_source_component &source) + : source(source) { } + + void exit(); + }; + private: - Signal_queue _signal_queue; - Rpc_entrypoint *_entrypoint; - Native_capability _reply_cap; + Signal_queue _signal_queue; + Rpc_entrypoint *_entrypoint; + Native_capability _reply_cap; + Finalizer_component _finalizer; + Capability _finalizer_cap; public: @@ -84,6 +117,8 @@ namespace Genode { */ Signal_source_component(Rpc_entrypoint *rpc_entrypoint); + ~Signal_source_component(); + void release(Signal_context_component *context); void submit(Signal_context_component *context, diff --git a/base/src/core/signal_session_component.cc b/base/src/core/signal_session_component.cc index 6778b545f..f7b06f2ae 100644 --- a/base/src/core/signal_session_component.cc +++ b/base/src/core/signal_session_component.cc @@ -46,7 +46,6 @@ Signal_session_component::~Signal_session_component() /* free all signal contexts */ while (Signal_context_component *r = _contexts_slab.first_object()) free_context(r->cap()); - } diff --git a/base/src/core/signal_source_component.cc b/base/src/core/signal_source_component.cc index 57ad6d6ad..110ad5ce5 100644 --- a/base/src/core/signal_source_component.cc +++ b/base/src/core/signal_source_component.cc @@ -90,5 +90,24 @@ Signal_source::Signal Signal_source_component::wait_for_signal() Signal_source_component::Signal_source_component(Rpc_entrypoint *ep) -: _entrypoint(ep) { } +: + _entrypoint(ep), _finalizer(*this), + _finalizer_cap(_entrypoint->manage(&_finalizer)) +{ } + +Signal_source_component::~Signal_source_component() +{ + _finalizer_cap.call(); + _entrypoint->dissolve(&_finalizer); +} + + +void Signal_source_component::Finalizer_component::exit() +{ + if (!source._reply_cap.valid()) + return; + + source._entrypoint->explicit_reply(source._reply_cap, 0); + source._reply_cap = Untyped_capability(); +}