From ee28a69c984846c31a00937e366c612142d06a62 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Wed, 22 May 2013 14:41:47 +0200 Subject: [PATCH] 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 --- base-hw/include/kernel/syscalls.h | 9 ++- base-hw/src/base/signal/signal.cc | 6 +- base-hw/src/base/thread_support.cc | 4 +- base-hw/src/core/kernel.cc | 121 +++++++++++++++++++++-------- base-hw/src/core/kernel/thread.h | 68 ++++++++++------ base-hw/src/core/thread.cc | 6 +- 6 files changed, 150 insertions(+), 64 deletions(-) diff --git a/base-hw/include/kernel/syscalls.h b/base-hw/include/kernel/syscalls.h index 6dacd7755..1c6fa1245 100644 --- a/base-hw/include/kernel/syscalls.h +++ b/base-hw/include/kernel/syscalls.h @@ -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 diff --git a/base-hw/src/base/signal/signal.cc b/base-hw/src/base/signal/signal.cc index 01fdd1481..8b8a611ee 100644 --- a/base-hw/src/base/signal/signal.cc +++ b/base-hw/src/base/signal/signal.cc @@ -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. diff --git a/base-hw/src/base/thread_support.cc b/base-hw/src/base/thread_support.cc index 5a1056ae5..e917027cd 100644 --- a/base-hw/src/base/thread_support.cc +++ b/base-hw/src/base/thread_support.cc @@ -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); } diff --git a/base-hw/src/core/kernel.cc b/base-hw/src/core/kernel.cc index 8b10e6c7f..fa55abcc9 100644 --- a/base-hw/src/core/kernel.cc +++ b/base-hw/src/core/kernel.cc @@ -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(); } diff --git a/base-hw/src/core/kernel/thread.h b/base-hw/src/core/kernel/thread.h index e3c7e8e70..3ca1a6d01 100644 --- a/base-hw/src/core/kernel/thread.h +++ b/base-hw/src/core/kernel/thread.h @@ -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 _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 diff --git a/base-hw/src/core/thread.cc b/base-hw/src/core/thread.cc index 7b1566080..11dd96bb4 100644 --- a/base-hw/src/core/thread.cc +++ b/base-hw/src/core/thread.cc @@ -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(); }