diff --git a/repos/base-hw/include/kernel/interface.h b/repos/base-hw/include/kernel/interface.h index 8fc169e44..1e2e881c6 100644 --- a/repos/base-hw/include/kernel/interface.h +++ b/repos/base-hw/include/kernel/interface.h @@ -32,14 +32,15 @@ namespace Kernel constexpr Call_arg call_id_kill_signal_context() { return 6; } constexpr Call_arg call_id_submit_signal() { return 7; } constexpr Call_arg call_id_await_signal() { return 8; } - constexpr Call_arg call_id_cancel_next_await_signal() { return 9; } - constexpr Call_arg call_id_ack_signal() { return 10; } - constexpr Call_arg call_id_print_char() { return 11; } - constexpr Call_arg call_id_update_data_region() { return 12; } - constexpr Call_arg call_id_update_instr_region() { return 13; } - constexpr Call_arg call_id_ack_cap() { return 14; } - constexpr Call_arg call_id_delete_cap() { return 15; } - constexpr Call_arg call_id_timeout() { return 16; } + constexpr Call_arg call_id_pending_signal() { return 9; } + constexpr Call_arg call_id_cancel_next_await_signal() { return 10; } + constexpr Call_arg call_id_ack_signal() { return 11; } + constexpr Call_arg call_id_print_char() { return 12; } + constexpr Call_arg call_id_update_data_region() { return 13; } + constexpr Call_arg call_id_update_instr_region() { return 14; } + constexpr Call_arg call_id_ack_cap() { return 15; } + constexpr Call_arg call_id_delete_cap() { return 16; } + constexpr Call_arg call_id_timeout() { return 17; } constexpr Call_arg call_id_timeout_max_us() { return 18; } constexpr Call_arg call_id_time() { return 19; } @@ -285,6 +286,25 @@ namespace Kernel return call(call_id_await_signal(), receiver_id); } + + /** + * Check for any pending signal of a context of a receiver the calling + * thread relates to + * + * \param receiver_id capability id of the targeted signal receiver + * + * \retval 0 suceeded + * \retval -1 failed + * + * If this call returns 0, an instance of 'Signal::Data' is located at the + * base of the callers UTCB. + */ + inline int pending_signal(capid_t const receiver_id) + { + return call(call_id_pending_signal(), receiver_id); + } + + /** * Request to cancel the next signal blocking of a local thread * diff --git a/repos/base-hw/src/core/kernel/thread.cc b/repos/base-hw/src/core/kernel/thread.cc index deb17e870..26c115741 100644 --- a/repos/base-hw/src/core/kernel/thread.cc +++ b/repos/base-hw/src/core/kernel/thread.cc @@ -464,6 +464,33 @@ void Thread::_call_await_signal() } +void Thread::_call_pending_signal() +{ + /* lookup receiver */ + Signal_receiver * const r = pd().cap_tree().find(user_arg_1()); + if (!r) { + Genode::warning(*this, ": cannot await, unknown signal receiver ", + (unsigned)user_arg_1()); + user_arg_0(-1); + return; + } + + /* register handler at the receiver */ + if (r->add_handler(this)) { + user_arg_0(-1); + return; + } + + if (_state == AWAITS_SIGNAL) { + _cancel_blocking(); + user_arg_0(-1); + return; + } + + user_arg_0(0); +} + + void Thread::_call_cancel_next_await_signal() { /* kill the caller if the capability of the target thread is invalid */ @@ -631,6 +658,7 @@ void Thread::_call() case call_id_kill_signal_context(): _call_kill_signal_context(); return; case call_id_submit_signal(): _call_submit_signal(); return; case call_id_await_signal(): _call_await_signal(); return; + case call_id_pending_signal(): _call_pending_signal(); return; case call_id_cancel_next_await_signal(): _call_cancel_next_await_signal(); return; case call_id_ack_signal(): _call_ack_signal(); return; case call_id_print_char(): _call_print_char(); return; diff --git a/repos/base-hw/src/core/kernel/thread.h b/repos/base-hw/src/core/kernel/thread.h index e1ba29060..ce5ab6782 100644 --- a/repos/base-hw/src/core/kernel/thread.h +++ b/repos/base-hw/src/core/kernel/thread.h @@ -216,6 +216,7 @@ class Kernel::Thread void _call_update_instr_region(); void _call_print_char(); void _call_await_signal(); + void _call_pending_signal(); void _call_cancel_next_await_signal(); void _call_submit_signal(); void _call_ack_signal(); diff --git a/repos/base-hw/src/lib/base/signal_receiver.cc b/repos/base-hw/src/lib/base/signal_receiver.cc index 179318231..6ad769adc 100644 --- a/repos/base-hw/src/lib/base/signal_receiver.cc +++ b/repos/base-hw/src/lib/base/signal_receiver.cc @@ -129,9 +129,10 @@ void Signal_receiver::block_for_signal() /* canceled */ return; } + /* read signal data */ - const void * const utcb = Thread::myself()->utcb()->data(); - Signal::Data * const data = (Signal::Data *)utcb; + Signal::Data * const data = + (Signal::Data *)Thread::myself()->utcb()->data(); Signal_context * const context = data->context; /** @@ -145,11 +146,60 @@ void Signal_receiver::block_for_signal() context->_pending = true; context->_curr_signal = Signal::Data(context, num); } + /* end kernel-aided life-time management */ Kernel::ack_signal(Capability_space::capid(data->context->_cap)); } +Signal Signal_receiver::pending_signal() +{ + Lock::Guard contexts_lock_guard(_contexts_lock); + Signal::Data result; + _contexts.for_each_locked([&] (Signal_context &context) { + + if (!context._pending) return; + + _contexts.head(context._next); + context._pending = false; + result = context._curr_signal; + context._curr_signal = Signal::Data(0, 0); + + Trace::Signal_received trace_event(context, result.num); + throw Context_ring::Break_for_each(); + }); + if (result.context) { + Lock::Guard lock_guard(result.context->_lock); + if (result.num == 0) + warning("returning signal with num == 0"); + + return result; + } + + /* look for pending signals */ + if (Kernel::pending_signal(Capability_space::capid(_cap)) != 0) { + throw Signal_not_pending(); + } + + /* read signal data */ + Signal::Data * const data = + (Signal::Data *)Thread::myself()->utcb()->data(); + Signal_context * const context = data->context; + + { + /* update signal context */ + Lock::Guard lock_guard(context->_lock); + context->_pending = false; + context->_curr_signal = Signal::Data(context, data->num); + result = context->_curr_signal; + } + + /* end kernel-aided life-time management */ + Kernel::ack_signal(Capability_space::capid(data->context->_cap)); + return result; +} + + void Signal_receiver::unblock_signal_waiter(Rpc_entrypoint &rpc_ep) { Kernel::cancel_next_await_signal(native_thread_id(&rpc_ep)); diff --git a/repos/base/src/core/signal_receiver.cc b/repos/base/src/core/signal_receiver.cc index f84b733a8..d26f9611f 100644 --- a/repos/base/src/core/signal_receiver.cc +++ b/repos/base/src/core/signal_receiver.cc @@ -49,4 +49,7 @@ void Signal_receiver::block_for_signal() sleep_forever(); } +Signal Signal_receiver::pending_signal() { + throw Signal_not_pending(); } + void Signal_receiver::local_submit(Signal::Data) { ASSERT_NEVER_CALLED; } diff --git a/repos/base/src/lib/base/signal.cc b/repos/base/src/lib/base/signal.cc index 24974ffbd..57a694f56 100644 --- a/repos/base/src/lib/base/signal.cc +++ b/repos/base/src/lib/base/signal.cc @@ -235,6 +235,42 @@ void Signal_receiver::block_for_signal() _signal_available.down(); } +Signal Signal_receiver::pending_signal() +{ + Lock::Guard contexts_lock_guard(_contexts_lock); + Signal::Data result; + _contexts.for_each_locked([&] (Signal_context &context) { + + if (!context._pending) return; + + _contexts.head(context._next); + context._pending = false; + result = context._curr_signal; + context._curr_signal = Signal::Data(0, 0); + + Trace::Signal_received trace_event(context, result.num); + throw Context_ring::Break_for_each(); + }); + if (result.context) { + Lock::Guard lock_guard(result.context->_lock); + if (result.num == 0) + warning("returning signal with num == 0"); + + return result; + } + + /* + * Normally, we should never arrive at this point because that would + * mean, the '_signal_available' semaphore was increased without + * registering the signal in any context associated to the receiver. + * + * However, if a context gets dissolved right after submitting a + * signal, we may have increased the semaphore already. In this case + * the signal-causing context is absent from the list. + */ + throw Signal_not_pending(); +} + void Signal_receiver::unblock_signal_waiter(Rpc_entrypoint &) { diff --git a/repos/base/src/lib/base/signal_common.cc b/repos/base/src/lib/base/signal_common.cc index 2eeaefbc4..a92dbe5cb 100644 --- a/repos/base/src/lib/base/signal_common.cc +++ b/repos/base/src/lib/base/signal_common.cc @@ -159,43 +159,6 @@ Signal Signal_receiver::wait_for_signal() } -Signal Signal_receiver::pending_signal() -{ - Lock::Guard contexts_lock_guard(_contexts_lock); - Signal::Data result; - _contexts.for_each_locked([&] (Signal_context &context) { - - if (!context._pending) return; - - _contexts.head(context._next); - context._pending = false; - result = context._curr_signal; - context._curr_signal = Signal::Data(0, 0); - - Trace::Signal_received trace_event(context, result.num); - throw Context_ring::Break_for_each(); - }); - if (result.context) { - Lock::Guard lock_guard(result.context->_lock); - if (result.num == 0) - warning("returning signal with num == 0"); - - return result; - } - - /* - * Normally, we should never arrive at this point because that would - * mean, the '_signal_available' semaphore was increased without - * registering the signal in any context associated to the receiver. - * - * However, if a context gets dissolved right after submitting a - * signal, we may have increased the semaphore already. In this case - * the signal-causing context is absent from the list. - */ - throw Signal_not_pending(); -} - - Signal_receiver::~Signal_receiver() { Lock::Guard contexts_lock_guard(_contexts_lock);