hw: fully functional Thread_base::cancel_blocking

Thread_base::cancel_blocking brings a thread back to execution
from every state, except the thread is created but not started yet.

Fix #745
This commit is contained in:
Martin Stein 2013-05-22 14:41:47 +02:00 committed by Norman Feske
parent 89a8c2c211
commit ee28a69c98
6 changed files with 150 additions and 64 deletions

View File

@ -268,6 +268,9 @@ namespace Kernel
* \retval >0 if syscall was successful and thread were already active
* \retval <0 if targeted thread doesn't participate in CPU
* scheduling after
*
* If the targeted thread blocks for any event except a 'start_thread'
* call this call cancels the blocking.
*/
inline int resume_thread(unsigned const id = 0) {
return syscall(RESUME_THREAD, id); }
@ -524,12 +527,14 @@ namespace Kernel
*
* \param context_id kernel name of the targeted signal context
*
* \return wether the context could be destructed
*
* Blocks the caller until the last delivered signal of the targeted
* context is acknowledged. Then the context gets destructed, losing
* all submits that were not delivered when this syscall occured.
*/
inline void kill_signal_context(unsigned context_id) {
syscall(KILL_SIGNAL_CONTEXT, (Syscall_arg)context_id); }
inline bool kill_signal_context(unsigned context_id) {
return syscall(KILL_SIGNAL_CONTEXT, (Syscall_arg)context_id); }
/**
* Create a new virtual-machine that is stopped initially

View File

@ -115,8 +115,12 @@ void Signal_receiver::_unsynchronized_dissolve(Signal_context * c)
* that no delivered but unacked signals of this context exist
* in userland anymore.
*/
Kernel::kill_signal_context(c->_cap.dst());
if (!Kernel::kill_signal_context(c->_cap.dst())) {
PERR("failed to kill signal context");
/* we have to keep the signal context alive for other */
while (1) ;
}
/*
* Now we can tell core to regain the memory of the
* destructed kernel object.

View File

@ -99,6 +99,6 @@ void Thread_base::start()
}
void Thread_base::cancel_blocking()
{ env()->cpu_session()->cancel_blocking(_thread_cap); }
void Thread_base::cancel_blocking() {
env()->cpu_session()->cancel_blocking(_thread_cap); }

View File

@ -60,6 +60,14 @@ namespace Kernel
}
void Kernel::Ipc_node::cancel_waiting()
{
if (_state == PREPARE_AND_AWAIT_REPLY) _state = PREPARE_REPLY;
if (_state == AWAIT_REPLY || _state == AWAIT_REQUEST) _state = INACTIVE;
return;
}
void Kernel::Ipc_node::_receive_request(Message_buf * const r)
{
/* assertions */
@ -79,9 +87,11 @@ void Kernel::Ipc_node::_receive_request(Message_buf * const r)
void Kernel::Ipc_node::_receive_reply(void * const base, size_t const size)
{
/* assertions */
assert(_awaits_reply());
assert(size <= _inbuf.size);
if (!_awaits_reply()) {
PDBG("discard unexpected IPC reply");
return;
}
/* receive reply */
Genode::memcpy(_inbuf.base, base, size);
_inbuf.size = size;
@ -373,6 +383,10 @@ void Kernel::Irq_owner::await_irq()
}
void Kernel::Irq_owner::cancel_waiting() {
if (_id) pic()->mask(id_to_irq(_id)); }
void Kernel::Irq_owner::receive_irq(unsigned const irq)
{
assert(_id == irq_to_id(irq));
@ -433,16 +447,16 @@ namespace Kernel
}
void Kernel::Thread::_activate()
void Kernel::Thread::_schedule()
{
cpu_scheduler()->insert(this);
_state = ACTIVE;
_state = SCHEDULED;
}
void Kernel::Thread::pause()
{
assert(_state == AWAIT_RESUMPTION || _state == ACTIVE);
assert(_state == AWAIT_RESUMPTION || _state == SCHEDULED);
cpu_scheduler()->remove(this);
_state = AWAIT_RESUMPTION;
}
@ -451,20 +465,7 @@ void Kernel::Thread::pause()
void Kernel::Thread::stop()
{
cpu_scheduler()->remove(this);
_state = STOPPED;
}
int Kernel::Thread::resume()
{
if (_state != AWAIT_RESUMPTION && _state != ACTIVE) {
PDBG("Unexpected thread state");
return -1;
}
cpu_scheduler()->insert(this);
if (_state == ACTIVE) return 1;
_state = ACTIVE;
return 0;
_state = AWAIT_START;
}
@ -489,17 +490,25 @@ void Kernel::Thread::reply(size_t const size, bool const await_request)
}
void Kernel::Thread::await_signal()
void Kernel::Thread::await_signal(Kernel::Signal_receiver * receiver)
{
cpu_scheduler()->remove(this);
_state = AWAIT_IRQ;
_state = AWAIT_SIGNAL;
_signal_receiver = receiver;
}
void Kernel::Thread::received_signal()
{
assert(_state == AWAIT_SIGNAL);
_schedule();
}
void Kernel::Thread::_received_irq()
{
assert(_state == AWAIT_IRQ);
_activate();
_schedule();
}
@ -533,7 +542,7 @@ void Kernel::Thread::scheduled_next()
void Kernel::Thread::_has_received(size_t const s)
{
user_arg_0(s);
if (_state != ACTIVE) _activate();
if (_state != SCHEDULED) _schedule();
}
@ -598,16 +607,19 @@ namespace Kernel
/**
* Destruct or prepare to do it at next call of 'ack'
*
* \return wether destruction is done
*/
void kill(Thread * const killer)
bool kill(Thread * const killer)
{
assert(!_killer);
_killer = killer;
if (_await_ack) {
_killer->kill_signal_context_blocks();
return;
return 0;
}
this->~Signal_context();
return 1;
}
};
@ -655,11 +667,17 @@ namespace Kernel
*/
void add_listener(Thread * const t)
{
t->await_signal();
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
*/
@ -1279,7 +1297,7 @@ namespace Kernel
Signal_context * const c =
Signal_context::pool()->object(user->user_arg_1());
assert(c);
c->kill(user);
user->user_arg_0(c->kill(user));
}
/**
@ -1476,13 +1494,48 @@ extern "C" void kernel()
** Kernel::Thread **
********************/
int Kernel::Thread::resume()
{
switch (_state) {
case AWAIT_RESUMPTION:
_schedule();
return 0;
case SCHEDULED:
return 1;
case AWAIT_IPC:
PDBG("cancel IPC receipt");
Ipc_node::cancel_waiting();
_schedule();
return 0;
case AWAIT_IRQ:
PDBG("cancel IRQ receipt");
Irq_owner::cancel_waiting();
_schedule();
return 0;
case AWAIT_SIGNAL:
PDBG("cancel signal receipt");
_signal_receiver->remove_listener(this);
_schedule();
return 0;
case AWAIT_SIGNAL_CONTEXT_DESTRUCT:
PDBG("cancel signal context destruction");
_schedule();
return 0;
case AWAIT_START:
default:
PERR("unresumable state");
return -1;
}
}
int Thread::start(void *ip, void *sp, unsigned cpu_no,
unsigned const pd_id,
Native_utcb * const phys_utcb,
Native_utcb * const virt_utcb)
{
/* check state and arguments */
assert(_state == STOPPED)
assert(_state == AWAIT_START)
assert(!cpu_no);
/* apply thread configuration */
@ -1494,7 +1547,7 @@ int Thread::start(void *ip, void *sp, unsigned cpu_no,
user_arg_0((unsigned)_virt_utcb);
/* start thread */
_activate();
_schedule();
return 0;
}
@ -1531,14 +1584,18 @@ void Thread::pagefault(addr_t const va, bool const w)
void Thread::kill_signal_context_blocks()
{
cpu_scheduler()->remove(this);
_state = KILL_SIGNAL_CONTEXT_BLOCKS;
_state = AWAIT_SIGNAL_CONTEXT_DESTRUCT;
}
void Thread::kill_signal_context_done()
{
assert(_state == KILL_SIGNAL_CONTEXT_BLOCKS)
_activate();
if (_state != AWAIT_SIGNAL_CONTEXT_DESTRUCT) {
PDBG("ignore unexpected signal-context destruction");
return;
}
user_arg_0(1);
_schedule();
}

View File

@ -532,23 +532,26 @@ namespace Kernel
* IPC node states:
*
* +----------+ +---------------+ +---------------+
* --new-->| inactive |--send-request-await-reply---->| await reply | +--send-note--| prepare reply |
* --new-->| inactive |---send-request-await-reply--->| await reply | +--send-note--| prepare reply |
* | |<--receive-reply---------------| | | | |
* | |<--cancel-waiting--------------| | | | |
* | | +---------------+ +------------>| |
* | |<--request-is-a-note-------+---request-is-not-a-note------------------------>| |
* | |<--------------------------(---not-await-request-----+ | |
* | | | +---------------+ | | |
* | |--await-request------------+-->| await request |<----+--send-reply-----------| |
* | |--send-reply---------+-----+-->| |--announce-request-+-------->| |
* | |--send-note--+ | | +---------------+ | | |
* | | | | request available | | |
* | |<------------+ | | | | |
* | |<--not-await-request-+ | | | |
* | |<--request-is-a-note-------+---request-is-not-a-note---------------|-------->| |
* | |<--request-is-a-note-----------------------------------------------+ | |
* | |<--------------------------(---not-await-request---+ | |
* | | | +---------------+ | | |
* | |---await-request-----------+-->| await request |<--+--send-reply-------------| |
* | |<--cancel-waiting--------------| |------announce-request--+--->| |
* | |---send-reply---------+----+-->| | | | |
* | |---send-note--+ | | +---------------+ | | |
* | | | | | | | |
* | |<-------------+ | request available | | |
* | |<--not-await-request--+ | | | |
* | |<--request-is-a-note-------+-------------------request-is-not-a-note----(--->| |
* | |<--request-is-a-note----------------------------------------------------+ | |
* +----------+ +-------------------------+ | |
* | prepare and await reply |<--send-request-and-await-reply--| |
* | |--receive-reply----------------->| |
* | |---receive-reply---------------->| |
* | |---cancel-waiting--------------->| |
* +-------------------------+ +---------------+
*
* State model propagated to deriving classes:
@ -565,6 +568,7 @@ namespace Kernel
* | |<--request-available-or-not-await-request--+ | |
* | |<--announce-request----------------------------| |
* | |<--receive-reply-------------------------------| |
* | |<--cancel-waiting------------------------------| |
* +--------------+ +----------------+
*/
class Ipc_node
@ -593,9 +597,9 @@ namespace Kernel
Fifo<Message_buf> _request_queue; /* requests that waits to be
* received by us */
Message_buf _inbuf; /* buffers message we have received lastly */
Message_buf _inbuf; /* buffers message we have received lastly */
Message_buf _outbuf; /* buffers the message we aim to send */
State _state; /* current node state */
State _state; /* current node state */
/**
* Buffer next request from request queue in 'r' to handle it
@ -690,6 +694,11 @@ namespace Kernel
void send_note(Ipc_node * const dest,
void * const note_base,
size_t const note_size);
/**
* Stop waiting for a receipt if in a waiting state
*/
void cancel_waiting();
};
/**
@ -754,6 +763,11 @@ namespace Kernel
*/
void await_irq();
/**
* Stop waiting for an IRQ if in a waiting state
*/
void cancel_waiting();
/**
* Denote occurence of an IRQ if we own it and awaited it
*/
@ -775,8 +789,16 @@ namespace Kernel
public Ipc_node,
public Irq_owner
{
enum State { STOPPED, ACTIVE, AWAIT_IPC, AWAIT_RESUMPTION,
AWAIT_IRQ, AWAIT_SIGNAL, KILL_SIGNAL_CONTEXT_BLOCKS };
enum State
{
SCHEDULED,
AWAIT_START,
AWAIT_IPC,
AWAIT_RESUMPTION,
AWAIT_IRQ,
AWAIT_SIGNAL,
AWAIT_SIGNAL_CONTEXT_DESTRUCT,
};
Platform_thread * const _platform_thread; /* userland object wich
* addresses this thread */
@ -786,11 +808,13 @@ namespace Kernel
unsigned _pd_id; /* ID of the PD this thread runs on */
Native_utcb * _phys_utcb; /* physical UTCB base */
Native_utcb * _virt_utcb; /* virtual UTCB base */
Signal_receiver * _signal_receiver; /* receiver we are currently
* listen to */
/**
* Resume execution
*/
void _activate();
void _schedule();
/**************
@ -806,7 +830,7 @@ namespace Kernel
** Irq_owner **
***************/
void _received_irq() { _activate(); }
void _received_irq();
void _awaits_irq();
@ -819,8 +843,8 @@ namespace Kernel
*/
Thread(Platform_thread * const platform_thread) :
_platform_thread(platform_thread),
_state(STOPPED), _pager(0), _pd_id(0),
_phys_utcb(0), _virt_utcb(0)
_state(AWAIT_START), _pager(0), _pd_id(0),
_phys_utcb(0), _virt_utcb(0), _signal_receiver(0)
{ }
/**
@ -891,9 +915,9 @@ namespace Kernel
unsigned id() const { return Object::id(); }
/**
* Gets called when we await a signal at a signal receiver
* Gets called when we await a signal at 'receiver'
*/
void await_signal();
void await_signal(Kernel::Signal_receiver * receiver);
/**
* Gets called when we have received a signal at a signal receiver

View File

@ -91,9 +91,5 @@ void Thread_base::join()
}
void Thread_base::cancel_blocking()
{
kernel_log() << __PRETTY_FUNCTION__ << ": Not implemented\n";
while (1) ;
}
void Thread_base::cancel_blocking() { _tid.pt->cancel_blocking(); }