471 lines
9.6 KiB
C++
471 lines
9.6 KiB
C++
/*
|
|
* \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 <util/fifo.h>
|
|
#include <base/signal.h>
|
|
|
|
/* core include */
|
|
#include <kernel/configuration.h>
|
|
#include <kernel/object.h>
|
|
|
|
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<MAX_SIGNAL_CONTEXTS> { };
|
|
class Signal_receiver_ids : public Id_allocator<MAX_SIGNAL_RECEIVERS> { };
|
|
typedef Object_pool<Signal_context> Signal_context_pool;
|
|
typedef Object_pool<Signal_receiver> 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<Signal_handler> 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<Signal_context, MAX_SIGNAL_CONTEXTS,
|
|
Signal_context_ids, signal_context_ids, signal_context_pool>
|
|
{
|
|
friend class Signal_receiver;
|
|
friend class Signal_context_killer;
|
|
|
|
private:
|
|
|
|
typedef Genode::Fifo_element<Signal_context> 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<Signal_receiver, MAX_SIGNAL_RECEIVERS,
|
|
Signal_receiver_ids, signal_receiver_ids,
|
|
signal_receiver_pool>
|
|
{
|
|
friend class Signal_context;
|
|
friend class Signal_handler;
|
|
|
|
private:
|
|
|
|
typedef Genode::Signal Signal;
|
|
|
|
template <typename T> class Fifo : public Genode::Fifo<T> { };
|
|
|
|
Fifo<Signal_handler::Fifo_element> _handlers;
|
|
Fifo<Signal_context::Fifo_element> _deliver;
|
|
Fifo<Signal_context::Fifo_element> _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<Signal_imprint>(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_ */
|