/* * \brief Kernel backend for asynchronous inter-process communication * \author Martin Stein * \date 2012-11-30 */ /* * Copyright (C) 2012-2013 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ #ifndef _KERNEL__SIGNAL_RECEIVER_H_ #define _KERNEL__SIGNAL_RECEIVER_H_ /* Genode includes */ #include #include /* core include */ #include #include namespace Kernel { /** * Ability to receive signals from signal receivers */ class Signal_handler; /** * Ability to get informed about signal acks */ class Signal_ack_handler; /** * Ability to destruct signal contexts */ class Signal_context_killer; /** * Signal types that are assigned to a signal receiver each */ class Signal_context; /** * Combines signal contexts to an entity that handlers can listen to */ class Signal_receiver; class Signal_context_ids : public Id_allocator { }; class Signal_receiver_ids : public Id_allocator { }; typedef Object_pool Signal_context_pool; typedef Object_pool Signal_receiver_pool; Signal_context_ids * signal_context_ids(); Signal_context_pool * signal_context_pool(); Signal_receiver_ids * signal_receiver_ids(); Signal_receiver_pool * signal_receiver_pool(); } class Kernel::Signal_ack_handler { friend class Signal_context; private: Signal_context * _signal_context; protected: /** * Provide custom handler for acks at a signal context */ virtual void _signal_acknowledged() = 0; /** * Constructor */ Signal_ack_handler() : _signal_context(0) { } /** * Destructor */ virtual ~Signal_ack_handler(); }; class Kernel::Signal_handler { friend class Signal_receiver; private: typedef Genode::Fifo_element Fifo_element; Fifo_element _handlers_fe; Signal_receiver * _receiver; /** * Backend for for destructor and cancel_waiting */ void _cancel_waiting(); /** * Let the handler block for signal receipt * * \param receiver the signal pool that the thread blocks for */ virtual void _await_signal(Signal_receiver * const receiver) = 0; /** * Signal delivery backend * * \param base signal-data base * \param size signal-data size */ virtual void _receive_signal(void * const base, size_t const size) = 0; protected: /*************** ** Accessors ** ***************/ Signal_receiver * receiver() const { return _receiver; } public: /** * Constructor */ Signal_handler() : _handlers_fe(this), _receiver(0) { } /** * Destructor */ virtual ~Signal_handler() { _cancel_waiting(); } /** * Stop waiting for a signal receiver */ void cancel_waiting() { _cancel_waiting(); } }; class Kernel::Signal_context_killer { friend class Signal_context; private: Signal_context * _context; /** * Backend for destructor and cancel_waiting */ void _cancel_waiting(); /** * Notice that the kill operation is pending */ virtual void _signal_context_kill_pending() = 0; /** * Notice that pending kill operation is done */ virtual void _signal_context_kill_done() = 0; /** * Notice that pending kill operation failed */ virtual void _signal_context_kill_failed() = 0; protected: /*************** ** Accessors ** ***************/ Signal_context * context() const { return _context; } public: /** * Constructor */ Signal_context_killer() : _context(0) { } /** * Destructor */ virtual ~Signal_context_killer() { _cancel_waiting(); } /** * Stop waiting for a signal context */ void cancel_waiting() { _cancel_waiting(); } }; class Kernel::Signal_context : public Object { friend class Signal_receiver; friend class Signal_context_killer; private: typedef Genode::Fifo_element Fifo_element; /** * Dummy handler that is used every time no other handler is available */ class Default_ack_handler : public Signal_ack_handler { private: /************************ ** Signal_ack_handler ** ************************/ void _signal_acknowledged() { } }; Fifo_element _deliver_fe; Fifo_element _contexts_fe; Signal_receiver * const _receiver; unsigned const _imprint; unsigned _submits; bool _ack; bool _killed; Signal_context_killer * _killer; Default_ack_handler _default_ack_handler; Signal_ack_handler * _ack_handler; /** * Tell receiver about the submits of the context if any */ void _deliverable(); /** * Called by receiver when all submits have been delivered */ void _delivered() { _submits = 0; _ack = 0; } /** * Notice that the killer of the context has cancelled waiting */ void _killer_cancelled() { _killer = 0; } public: /** * Destructor */ ~Signal_context(); /** * Constructor * * \param r receiver that the context shall be assigned to * \param imprint userland identification of the context * * \throw Assign_to_receiver_failed */ Signal_context(Signal_receiver * const r, unsigned const imprint); /** * Attach or detach a handler for acknowledgments at this context * * \param h handler that shall be attached or 0 to detach handler */ void ack_handler(Signal_ack_handler * const h) { _ack_handler = h ? h : &_default_ack_handler; _ack_handler->_signal_context = this; } /** * Submit the signal * * \param n number of submits * * \retval 0 succeeded * \retval -1 failed */ int submit(unsigned const n) { if (_killed || _submits >= (unsigned)~0 - n) { return -1; } _submits += n; if (_ack) { _deliverable(); } return 0; } /** * Acknowledge delivery of signal */ void ack() { _ack_handler->_signal_acknowledged(); if (_ack) { return; } if (!_killed) { _ack = 1; _deliverable(); return; } if (_killer) { _killer->_context = 0; _killer->_signal_context_kill_done(); _killer = 0; } } /** * Destruct context or prepare to do it as soon as delivery is done * * \param killer object that shall receive progress reports * * \retval 0 succeeded * \retval -1 failed */ int kill(Signal_context_killer * const k) { /* check if in a kill operation or already killed */ if (_killed) { if (_ack) { return 0; } return -1; } /* kill directly if there is no unacknowledged delivery */ if (_ack) { _killed = 1; return 0; } /* wait for delivery acknowledgement */ _killer = k; _killed = 1; _killer->_context = this; _killer->_signal_context_kill_pending(); return 0; } }; class Kernel::Signal_receiver : public Object { friend class Signal_context; friend class Signal_handler; private: typedef Genode::Signal Signal; template class Fifo : public Genode::Fifo { }; Fifo _handlers; Fifo _deliver; Fifo _contexts; /** * Recognize that context 'c' has submits to deliver */ void _add_deliverable(Signal_context * const c) { if (!c->_deliver_fe.is_enqueued()) { _deliver.enqueue(&c->_deliver_fe); } _listen(); } /** * Deliver as much submits as possible */ void _listen() { while (1) { /* check for deliverable signals and waiting handlers */ if (_deliver.empty() || _handlers.empty()) { return; } /* create a signal data-object */ typedef Genode::Signal_context * Signal_imprint; auto const context = _deliver.dequeue()->object(); auto const imprint = reinterpret_cast(context->_imprint); Signal::Data data(imprint, context->_submits); /* communicate signal data to handler */ auto const handler = _handlers.dequeue()->object(); handler->_receiver = 0; handler->_receive_signal(&data, sizeof(data)); context->_delivered(); } } /** * Notice that a context of the receiver has been destructed * * \param c destructed context */ void _context_destructed(Signal_context * const c) { _contexts.remove(&c->_contexts_fe); if (!c->_deliver_fe.is_enqueued()) { return; } _deliver.remove(&c->_deliver_fe); } /** * Notice that handler 'h' has cancelled waiting */ void _handler_cancelled(Signal_handler * const h) { _handlers.remove(&h->_handlers_fe); } /** * Assign context 'c' to the receiver */ void _add_context(Signal_context * const c) { _contexts.enqueue(&c->_contexts_fe); } public: /** * Destructor */ ~Signal_receiver() { /* destruct all attached contexts */ while (Signal_context * c = _contexts.dequeue()->object()) { c->~Signal_context(); } } /** * Let a handler 'h' wait for signals of the receiver * * \retval 0 succeeded * \retval -1 failed */ int add_handler(Signal_handler * const h) { if (h->_receiver) { return -1; } _handlers.enqueue(&h->_handlers_fe); h->_receiver = this; h->_await_signal(this); _listen(); return 0; } /** * Return wether any of the contexts of this receiver is deliverable */ bool deliverable() { return !_deliver.empty(); } }; #endif /* _KERNEL__SIGNAL_RECEIVER_ */