/* * \brief Delivery and reception of asynchronous notifications * \author Norman Feske * \date 2008-09-05 * * Each transmitter sends signals to one fixed destination. * A receiver can receive signals from multiple sources. */ /* * Copyright (C) 2008-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 _INCLUDE__BASE__SIGNAL_H__ #define _INCLUDE__BASE__SIGNAL_H__ #include #include /* only needed for base-hw */ namespace Kernel { struct Signal_receiver; } namespace Genode { class Signal_source; class Signal_receiver; class Signal_context; class Signal_context_registry; /** * Signal * * A signal represents a number of asynchronous notifications produced by * one transmitter. If notifications are generated at a higher rate than as * they can be processed at the receiver, the transmitter counts the * notifications and delivers the total amount with the next signal * transmission. This way, the total number of notifications gets properly * communicated to the receiver even if the receiver is not highly * responsive. * * Asynchronous notifications do not carry any payload because this payload * would need to be queued at the transmitter. However, each transmitter * imprints a signal-context reference into each signal. This context * can be used by the receiver to distinguish signals coming from different * transmitters. */ class Signal { private: struct Data { Signal_context *context; unsigned num; /** * Constructor * * \param context signal context specific for the * signal-receiver capability used for signal * transmission * \param num number of signals received from the same * transmitter */ Data(Signal_context *context, unsigned num) : context(context), num(num) { } /** * Default constructor, representing an invalid signal */ Data() : context(0), num(0) { } } _data; /** * Constructor * * Signal objects are constructed by signal receivers only. */ Signal(Data data); friend class Kernel::Signal_receiver; friend class Signal_receiver; friend class Signal_context; void _dec_ref_and_unlock(); void _inc_ref(); public: Signal(Signal const &other); Signal &operator=(Signal const &other); ~Signal(); Signal_context *context() { return _data.context; } unsigned num() const { return _data.num; } }; /** * Signal context * * A signal context is a destination for signals. One receiver can listen * to multple contexts. If a signal arrives, the context is provided with the * signel. This enables the receiver to distinguish different signal sources * and dispatch incoming signals context-specific. */ class Signal_context { private: /** * List element in 'Signal_receiver' */ List_element _receiver_le; /** * List element in process-global registry */ List_element _registry_le; /** * Receiver to which the context is associated with * * This member is initialized by the receiver when associating * the context with the receiver via the 'cap' function. */ Signal_receiver *_receiver; Lock _lock; /* protect '_curr_signal' */ Signal::Data _curr_signal; /* most-currently received signal */ bool _pending; /* current signal is valid */ unsigned int _ref_cnt; /* number of references to this context */ Lock _destroy_lock; /* prevent destruction while the context is in use */ /** * Capability assigned to this context after being assocated with * a 'Signal_receiver' via the 'manage' function. We store this * capability in the 'Signal_context' for the mere reason to * properly destruct the context (see '_unsynchronized_dissolve'). */ Signal_context_capability _cap; friend class Signal; friend class Signal_receiver; friend class Signal_context_registry; public: /** * Constructor */ Signal_context() : _receiver_le(this), _registry_le(this), _receiver(0), _pending(0), _ref_cnt(0) { } /** * Destructor * * The virtual destructor is just there to generate a vtable for * signal-context objects such that signal contexts can be dynamically * casted. */ virtual ~Signal_context() { } /* * Signal contexts are never invoked but only used as arguments for * 'Signal_session' functions. Hence, there exists a capability * type for it but no real RPC interface. */ GENODE_RPC_INTERFACE(); }; /** * Signal transmitter * * Each signal-transmitter instance acts on behalf the context specified * as constructor argument. Therefore, the resources needed for the * transmitter such as the consumed memory 'sizeof(Signal_transmitter)' * should be accounted to the owner of the context. */ class Signal_transmitter { private: Signal_context_capability _context; /* destination */ public: /** * Constructor * * \param context capability to signal context that is going to * receive signals produced by the transmitter */ Signal_transmitter(Signal_context_capability context = Signal_context_capability()); /** * Set signal context */ void context(Signal_context_capability context); /** * Trigger signal submission to context * * \param cnt number of signals to submit at once */ void submit(unsigned cnt = 1); }; /** * Signal receiver */ class Signal_receiver { private: /** * Semaphore used to indicate that signal(s) are ready to be picked * up. This is needed for platforms other than 'base-hw' only. */ Semaphore _signal_available; /** * Provides the kernel-object name via the 'dst' method. This is * needed for 'base-hw' only. */ Signal_receiver_capability _cap; /** * List of associated contexts */ Lock _contexts_lock; List > _contexts; /** * Helper to dissolve given context * * This function prevents duplicated code in '~Signal_receiver' * and 'dissolve'. Note that '_contexts_lock' must be held when * calling this function. */ void _unsynchronized_dissolve(Signal_context *context); public: /** * Exception class */ class Context_already_in_use { }; class Context_not_associated { }; /** * Constructor */ Signal_receiver(); /** * Destructor */ ~Signal_receiver(); /** * Manage signal context and return new signal-context capability * * \param context context associated with signals delivered to the * receiver * \throw 'Context_already_in_use' * \return new signal-context capability that can be * passed to a signal transmitter */ Signal_context_capability manage(Signal_context *context); /** * Dissolve signal context from receiver * * \param context context to remove from receiver * \throw 'Context_not_associated' */ void dissolve(Signal_context *context); /** * Return true if signal was received */ bool pending(); /** * Block until a signal is received * * \return received signal */ Signal wait_for_signal(); /** * Locally submit signal to the receiver */ void local_submit(Signal::Data signal); /** * Framework-internal signal-dispatcher * * This function is called from the thread that monitors the signal * source associated with the process. It must not be used for other * purposes. */ static void dispatch_signals(Signal_source *signal_source); }; /** * Abstract interface to be implemented by signal dispatchers */ struct Signal_dispatcher_base : Signal_context { virtual void dispatch(unsigned num) = 0; }; /** * Adapter for directing signals to member functions * * This utility associates member functions with signals. It is intended to * be used as a member variable of the class that handles incoming signals * of a certain type. The constructor takes a pointer-to-member to the * signal handling function as argument. If a signal is received at the * common signal reception code, this function will be invoked by calling * 'Signal_dispatcher_base::dispatch'. * * \param T type of signal-handling class */ template class Signal_dispatcher : private Signal_dispatcher_base, public Signal_context_capability { private: T &obj; void (T::*member) (unsigned); Signal_receiver &sig_rec; public: /** * Constructor * * \param sig_rec signal receiver to associate the signal * handler with * \param obj,member object and member function to call when * the signal occurs */ Signal_dispatcher(Signal_receiver &sig_rec, T &obj, void (T::*member)(unsigned)) : Signal_context_capability(sig_rec.manage(this)), obj(obj), member(member), sig_rec(sig_rec) { } ~Signal_dispatcher() { sig_rec.dissolve(this); } void dispatch(unsigned num) { (obj.*member)(num); } }; } #endif /* _INCLUDE__BASE__SIGNAL_H__ */