hw: signal receiver in extra header without assert

ref #528
This commit is contained in:
Martin Stein 2013-09-06 01:36:03 +02:00 committed by Norman Feske
parent bf37159eb9
commit 48f831af3c
3 changed files with 236 additions and 347 deletions

View File

@ -23,7 +23,6 @@
*/ */
/* Genode includes */ /* Genode includes */
#include <base/signal.h>
#include <cpu/cpu_state.h> #include <cpu/cpu_state.h>
#include <base/thread_state.h> #include <base/thread_state.h>
@ -56,7 +55,7 @@ namespace Kernel
{ {
/* import Genode types */ /* import Genode types */
typedef Genode::Thread_state Thread_state; typedef Genode::Thread_state Thread_state;
typedef Genode::umword_t umword_t; typedef Genode::umword_t umword_t;
class Schedule_context; class Schedule_context;
@ -297,13 +296,6 @@ void Kernel::Thread::await_signal(Kernel::Signal_receiver * receiver)
} }
void Kernel::Thread::received_signal()
{
assert(_state == AWAIT_SIGNAL);
_schedule();
}
void Kernel::Thread::_received_irq() void Kernel::Thread::_received_irq()
{ {
assert(_state == AWAIT_IRQ); assert(_state == AWAIT_IRQ);
@ -361,145 +353,13 @@ void Kernel::Thread::_awaits_irq()
namespace Kernel namespace Kernel
{ {
class Signal_receiver;
/** void deliver_signal(Signal_handler * const dst,
* Specific signal type, owned by a receiver, can be triggered asynchr. void * const base,
*/ size_t const size)
class Signal_context : public Object<Signal_context, MAX_SIGNAL_CONTEXTS>,
public Fifo<Signal_context>::Element
{ {
friend class Signal_receiver; ((Thread *)dst->id())->receive_signal(base, size);
}
Signal_receiver * const _receiver; /* the receiver that owns us */
unsigned const _imprint; /* every outgoing signals gets
* signed with this */
unsigned _submits; /* accumul. undelivered submits */
bool _await_ack; /* delivery ack pending */
Thread * _killer; /* awaits our destruction if >0 */
/**
* Utility to deliver all remaining submits
*/
void _deliver();
/**
* Called by receiver when all submits have been delivered
*/
void _delivered()
{
_submits = 0;
_await_ack = 1;
}
public:
/**
* Constructor
*/
Signal_context(Signal_receiver * const r, unsigned const imprint) :
_receiver(r), _imprint(imprint),
_submits(0), _await_ack(0), _killer(0) { }
/**
* Submit the signal
*
* \param n number of submits
*/
void submit(unsigned const n);
/**
* Acknowledge delivery of signal
*/
void ack();
/**
* Destruct or prepare to do it at next call of 'ack'
*
* \return wether destruction is done
*/
bool kill(Thread * const killer)
{
assert(!_killer);
_killer = killer;
if (_await_ack) {
_killer->kill_signal_context_blocks();
return 0;
}
this->~Signal_context();
return 1;
}
};
/**
* Manage signal contexts and enable threads to trigger and await them
*/
class Signal_receiver :
public Object<Signal_receiver, MAX_SIGNAL_RECEIVERS>
{
Fifo<Thread> _listeners;
Fifo<Signal_context> _pending_contexts;
/**
* Deliver as much submitted signals to listening threads as possible
*/
void _listen()
{
while (1)
{
/* any pending context? */
if (_pending_contexts.empty()) return;
Signal_context * const c = _pending_contexts.dequeue();
/* if there is no listener, enqueue context again and return */
if (_listeners.empty()) {
_pending_contexts.enqueue(c);
return;
}
/* awake a listener and transmit signal info to it */
Thread * const t = _listeners.dequeue();
Signal::Data data((Genode::Signal_context *)c->_imprint,
c->_submits);
*(Signal::Data *)t->phys_utcb()->base() = data;
t->received_signal();
c->_delivered();
}
}
public:
/**
* Let a thread listen to our contexts
*/
void add_listener(Thread * const t)
{
t->await_signal(this);
_listeners.enqueue(t);
_listen();
}
/**
* Stop a thread from listening to our contexts
*/
void remove_listener(Thread * const t) {
_listeners.remove(t); }
/**
* If any of our contexts is pending
*/
bool pending() { return !_pending_contexts.empty(); }
/**
* Recognize that 'c' wants to deliver
*/
void deliver(Signal_context * const c)
{
assert(c->_receiver == this);
if (!c->is_enqueued()) _pending_contexts.enqueue(c);
_listen();
}
};
class Vm : public Object<Vm, MAX_VMS>, class Vm : public Object<Vm, MAX_VMS>,
public Schedule_context public Schedule_context
@ -1053,7 +913,8 @@ namespace Kernel
assert(r); assert(r);
/* let user listen to receiver */ /* let user listen to receiver */
r->add_listener(user); user->await_signal(r);
r->add_handler(user->signal_handler());
} }
@ -1068,7 +929,7 @@ namespace Kernel
assert(r); assert(r);
/* set return value */ /* set return value */
user->user_arg_0(r->pending()); user->user_arg_0(r->deliverable());
} }
@ -1094,10 +955,11 @@ namespace Kernel
*/ */
void do_ack_signal(Thread * const user) void do_ack_signal(Thread * const user)
{ {
Signal_context * const c = unsigned id = user->user_arg_1();
Signal_context::pool()->object(user->user_arg_1()); Signal_context * const c = Signal_context::pool()->object(id);
assert(c); if (!c) return;
c->ack(); Thread * const t = (Thread *)c->ack();
if (t) { t->kill_signal_context_done(); }
} }
@ -1106,10 +968,11 @@ namespace Kernel
*/ */
void do_kill_signal_context(Thread * const user) void do_kill_signal_context(Thread * const user)
{ {
Signal_context * const c = unsigned id = user->user_arg_1();
Signal_context::pool()->object(user->user_arg_1()); Signal_context * const c = Signal_context::pool()->object(id);
assert(c); if (!c) { return; }
user->user_arg_0(c->kill(user)); if (c->kill((unsigned)user)) { return; }
user->kill_signal_context_blocks();
} }
/** /**
@ -1319,7 +1182,7 @@ int Kernel::Thread::resume()
return 0; return 0;
case AWAIT_SIGNAL: case AWAIT_SIGNAL:
PDBG("cancel signal receipt"); PDBG("cancel signal receipt");
_signal_receiver->remove_listener(this); _signal_receiver->remove_handler(signal_handler());
_schedule(); _schedule();
return 0; return 0;
case AWAIT_SIGNAL_CONTEXT_DESTRUCT: case AWAIT_SIGNAL_CONTEXT_DESTRUCT:
@ -1409,38 +1272,3 @@ void Thread::kill_signal_context_done()
user_arg_0(1); user_arg_0(1);
_schedule(); _schedule();
} }
/****************************
** Kernel::Signal_context **
****************************/
void Signal_context::_deliver()
{
if (!_submits) return;
_receiver->deliver(this);
}
void Signal_context::ack()
{
assert(_await_ack);
_await_ack = 0;
if (!_killer) {
_deliver();
return;
}
_killer->kill_signal_context_done();
this->~Signal_context();
}
void Signal_context::submit(unsigned const n)
{
assert(_submits < (unsigned)~0 - n);
if (_killer) return;
_submits += n;
if (_await_ack) return;
_deliver();
}

View File

@ -21,48 +21,83 @@
/* core include */ /* core include */
#include <kernel/configuration.h> #include <kernel/configuration.h>
#include <kernel/object.h> #include <kernel/object.h>
#include <assert.h>
namespace Kernel namespace Kernel
{ {
typedef Genode::Signal Signal; /**
* Enables external components to act as a signal handler
class Signal_receiver; */
class Signal_handler;
template <typename T> class Fifo : public Genode::Fifo<T> { };
class Signal_listener : public Fifo<Signal_listener>::Element
{
public:
virtual void receive_signal(void * const signal_base,
size_t const signal_size) = 0;
};
/** /**
* Specific signal type, owned by a receiver, can be triggered asynchr. * Signal types that are assigned to a signal receiver each
*/ */
class Signal_context : public Object<Signal_context, MAX_SIGNAL_CONTEXTS>, class Signal_context;
public Fifo<Signal_context>::Element
{
friend class Signal_receiver;
Signal_receiver * const _receiver; /* the receiver that owns us */ /**
unsigned const _imprint; /* every outgoing signals gets * Combines signal contexts to an entity that handlers can listen to
* signed with this */ */
unsigned _submits; /* accumul. undelivered submits */ class Signal_receiver;
bool _await_ack; /* delivery ack pending */
/* /**
* if not zero, _killer_id holds the ID of the actor * Signal delivery backend
* that awaits the destruction of the signal context *
*/ * \param dst destination
unsigned _killer_id; * \param base signal-data base
* \param size signal-data size
*/
void deliver_signal(Signal_handler * const dst,
void * const base,
size_t const size);
}
class Kernel::Signal_handler
{
friend class Signal_receiver;
private:
typedef Genode::Fifo_element<Signal_handler> Fifo_element;
Fifo_element _fe;
unsigned const _id;
public:
/** /**
* Utility to deliver all remaining submits * Constructor
*/ */
inline void _deliver(); Signal_handler(unsigned id) : _fe(this), _id(id) { }
/***************
** Accessors **
***************/
unsigned id() { return _id; }
};
class Kernel::Signal_context
:
public Object<Signal_context, MAX_SIGNAL_CONTEXTS>
{
friend class Signal_receiver;
private:
typedef Genode::Fifo_element<Signal_context> Fifo_element;
Fifo_element _fe;
Signal_receiver * const _receiver;
unsigned const _imprint;
unsigned _submits;
bool _ack;
unsigned _killer;
/**
* Tell receiver about the submits of the context if any
*/
inline void _deliverable();
/** /**
* Called by receiver when all submits have been delivered * Called by receiver when all submits have been delivered
@ -70,139 +105,162 @@ namespace Kernel
void _delivered() void _delivered()
{ {
_submits = 0; _submits = 0;
_await_ack = 1; _ack = 0;
} }
public: public:
/**
* Constructor
*/
Signal_context(Signal_receiver * const r, unsigned const imprint) :
_receiver(r), _imprint(imprint),
_submits(0), _await_ack(0), _killer_id(0) { }
/**
* Submit the signal
*
* \param n number of submits
*/
void submit(unsigned const n)
{
assert(_submits < (unsigned)~0 - n);
if (_killer_id) { return; }
_submits += n;
if (_await_ack) { return; }
_deliver();
}
/**
* Acknowledge delivery of signal
*
* \retval 0 no kill request finished
* \retval > 0 name of finished kill request
*/
unsigned ack()
{
assert(_await_ack);
_await_ack = 0;
if (!_killer_id) {
_deliver();
return 0;
}
this->~Signal_context();
return _killer_id;
}
/**
* Destruct or prepare to do it at next call of 'ack'
*
* \param killer_id name of the kill request
*
* \return wether destruction is done
*/
bool kill(unsigned const killer_id)
{
assert(!_killer_id);
_killer_id = killer_id;
if (_await_ack) { return 0; }
this->~Signal_context();
return 1;
}
};
/**
* Manage signal contexts & enable external actors to trigger & await them
*/
class Signal_receiver :
public Object<Signal_receiver, MAX_SIGNAL_RECEIVERS>
{
Fifo<Signal_listener> _listeners;
Fifo<Signal_context> _pending_contexts;
/** /**
* Deliver as much submitted signals to listeners as possible * Constructor
*/
Signal_context(Signal_receiver * const r, unsigned const imprint)
:
_fe(this), _receiver(r), _imprint(imprint), _submits(0), _ack(1),
_killer(0)
{ }
/**
* Submit the signal
*
* \param n number of submits
*/
void submit(unsigned const n)
{
if (_submits >= (unsigned)~0 - n) {
PERR("overflow at signal-submit count");
return;
}
if (_killer) {
PERR("signal context already in destruction");
return;
}
_submits += n;
if (!_ack) { return; }
_deliverable();
}
/**
* Acknowledge delivery of signal
*
* \retval 0 no kill request finished
* \retval > 0 name of finished kill request
*/
unsigned ack()
{
if (_ack) {
PERR("unexpected signal acknowledgment");
return 0;
}
if (!_killer) {
_ack = 1;
_deliverable();
return 0;
}
this->~Signal_context();
return _killer;
}
/**
* Destruct context or prepare to do it as soon as delivery is done
*
* \param killer name of the kill request
*
* \retval 1 destruction is done
* \retval 0 destruction is initiated, will be done with the next ack
*/
bool kill(unsigned const killer)
{
/* FIXME: aggregate or avoid multiple kill requests */
if (_killer) {
PERR("multiple kill requests");
while (1) { }
}
_killer = killer;
if (!_ack) { return 0; }
this->~Signal_context();
return 1;
}
};
class Kernel::Signal_receiver
:
public Object<Signal_receiver, MAX_SIGNAL_RECEIVERS>
{
friend class Signal_context;
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> _deliverable;
/**
* Recognize that context 'c' has submits to deliver
*/
void _add_deliverable(Signal_context * const c)
{
if (!c->_fe.is_enqueued()) _deliverable.enqueue(&c->_fe);
_listen();
}
/**
* Deliver as much submits as possible
*/ */
void _listen() void _listen()
{ {
while (1) while (1)
{ {
/* any pending context? */ /* check if there are deliverable signal */
if (_pending_contexts.empty()) return; if (_deliverable.empty()) return;
Signal_context * const c = _pending_contexts.dequeue(); Signal_context * const c = _deliverable.dequeue()->object();
/* if there is no listener, enqueue context again and return */ /* if there is no handler re-enqueue context and exit */
if (_listeners.empty()) { if (_handlers.empty()) {
_pending_contexts.enqueue(c); _deliverable.enqueue(&c->_fe);
return; return;
} }
/* awake a listener and transmit signal info to it */ /* delivery from context to handler */
Signal_listener * const l = _listeners.dequeue(); Signal_handler * const h = _handlers.dequeue()->object();
Signal::Data data((Genode::Signal_context *)c->_imprint, Signal::Data data((Genode::Signal_context *)c->_imprint,
c->_submits); c->_submits);
l->receive_signal(&data, sizeof(data)); deliver_signal(h, &data, sizeof(data));
c->_delivered(); c->_delivered();
} }
} }
public: public:
/** /**
* Let a listener listen to the contexts of the receiver * Let a handler wait for signals of the receiver
*/ */
void add_listener(Signal_listener * const l) void add_handler(Signal_handler * const h)
{ {
_listeners.enqueue(l); _handlers.enqueue(&h->_fe);
_listen(); _listen();
} }
/** /**
* Stop a listener from listen to the contexts of the receiver * Stop a handler from waiting for signals of the receiver
*/ */
void remove_listener(Signal_listener * const l) { _listeners.remove(l); } void remove_handler(Signal_handler * const h)
{
_handlers.remove(&h->_fe);
}
/** /**
* Return wether any of the contexts of this receiver is pending * Return wether any of the contexts of this receiver is deliverable
*/ */
bool pending() { return !_pending_contexts.empty(); } bool deliverable() { return !_deliverable.empty(); }
};
/**
* Recognize that context 'c' wants to be delivered
*/
void deliver(Signal_context * const c)
{
assert(c->_receiver == this);
if (!c->is_enqueued()) _pending_contexts.enqueue(c);
_listen();
}
};
}
void Kernel::Signal_context::_deliver() void Kernel::Signal_context::_deliverable()
{ {
if (!_submits) return; if (!_submits) return;
_receiver->deliver(this); _receiver->_add_deliverable(this);
} }
#endif /* _KERNEL__SIGNAL_RECEIVER_ */ #endif /* _KERNEL__SIGNAL_RECEIVER_ */

View File

@ -15,6 +15,7 @@
#define _CORE__KERNEL__THREAD_H_ #define _CORE__KERNEL__THREAD_H_
/* core includes */ /* core includes */
#include <kernel/signal_receiver.h>
#include <kernel/ipc_node.h> #include <kernel/ipc_node.h>
#include <kernel/configuration.h> #include <kernel/configuration.h>
#include <kernel/scheduler.h> #include <kernel/scheduler.h>
@ -34,7 +35,6 @@ namespace Kernel
typedef Genode::Cpu Cpu; typedef Genode::Cpu Cpu;
typedef Genode::Page_flags Page_flags; typedef Genode::Page_flags Page_flags;
typedef Genode::Core_tlb Core_tlb; typedef Genode::Core_tlb Core_tlb;
typedef Genode::Signal Signal;
typedef Genode::Pagefault Pagefault; typedef Genode::Pagefault Pagefault;
typedef Genode::Native_utcb Native_utcb; typedef Genode::Native_utcb Native_utcb;
@ -52,15 +52,12 @@ namespace Kernel
virtual void proceed() = 0; virtual void proceed() = 0;
}; };
template <typename T> class Fifo : public Genode::Fifo<T> { };
/** /**
* Kernel representation of a user thread * Kernel representation of a user thread
*/ */
class Thread : public Cpu::User_context, class Thread : public Cpu::User_context,
public Object<Thread, MAX_THREADS>, public Object<Thread, MAX_THREADS>,
public Schedule_context, public Schedule_context,
public Fifo<Thread>::Element,
public Ipc_node, public Ipc_node,
public Irq_receiver public Irq_receiver
{ {
@ -84,8 +81,8 @@ namespace Kernel
unsigned _pd_id; /* ID of the PD this thread runs on */ unsigned _pd_id; /* ID of the PD this thread runs on */
Native_utcb * _phys_utcb; /* physical UTCB base */ Native_utcb * _phys_utcb; /* physical UTCB base */
Native_utcb * _virt_utcb; /* virtual UTCB base */ Native_utcb * _virt_utcb; /* virtual UTCB base */
Signal_receiver * _signal_receiver; /* receiver we are currently Signal_receiver * _signal_receiver;
* listen to */ Signal_handler _signal_handler;
/** /**
* Resume execution * Resume execution
@ -112,15 +109,24 @@ namespace Kernel
public: public:
void receive_signal(void * const base, size_t const size)
{
assert(_state == AWAIT_SIGNAL);
assert(size <= phys_utcb()->size())
Genode::memcpy(phys_utcb()->base(), base, size);
_schedule();
}
void * operator new (size_t, void * p) { return p; } void * operator new (size_t, void * p) { return p; }
/** /**
* Constructor * Constructor
*/ */
Thread(Platform_thread * const platform_thread) : Thread(Platform_thread * const platform_thread)
_platform_thread(platform_thread), :
_state(AWAIT_START), _pager(0), _pd_id(0), _platform_thread(platform_thread), _state(AWAIT_START),
_phys_utcb(0), _virt_utcb(0), _signal_receiver(0) _pager(0), _pd_id(0), _phys_utcb(0), _virt_utcb(0),
_signal_receiver(0), _signal_handler((unsigned)this)
{ } { }
/** /**
@ -210,11 +216,6 @@ namespace Kernel
*/ */
void await_signal(Kernel::Signal_receiver * receiver); void await_signal(Kernel::Signal_receiver * receiver);
/**
* Gets called when we have received a signal at a signal receiver
*/
void received_signal();
/** /**
* Handle the exception that currently blocks this thread * Handle the exception that currently blocks this thread
*/ */
@ -241,6 +242,8 @@ namespace Kernel
unsigned pd_id() const { return _pd_id; } unsigned pd_id() const { return _pd_id; }
Native_utcb * phys_utcb() const { return _phys_utcb; } Native_utcb * phys_utcb() const { return _phys_utcb; }
Signal_handler * signal_handler() { return &_signal_handler; }
}; };
} }