genode/repos/base/src/lib/base/signal_common.cc

242 lines
5.4 KiB
C++

/*
* \brief Platform-independent part of signal framework
* \author Norman Feske
* \author Christian Prochaska
* \author Martin Stein
* \date 2013-02-21
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/env.h>
#include <base/signal.h>
#include <base/trace/events.h>
#include <deprecated/env.h>
using namespace Genode;
/************
** Signal **
************/
Signal::Signal(Signal const &other)
:
_data(other._data.context, other._data.num)
{
_inc_ref();
}
Signal & Signal::operator=(Signal const &other)
{
bool const same_context = (_data.context == other._data.context);
/* don't change ref cnt if it's the same context */
if (!same_context)
_dec_ref_and_unlock();
_data.context = other._data.context;
_data.num = other._data.num;
if (!same_context)
_inc_ref();
return *this;
}
Signal::~Signal() { _dec_ref_and_unlock(); }
void Signal::_dec_ref_and_unlock()
{
if (_data.context) {
Lock::Guard lock_guard(_data.context->_lock);
_data.context->_ref_cnt--;
if (_data.context->_ref_cnt == 0)
_data.context->_destroy_lock.unlock();
}
}
void Signal::_inc_ref()
{
if (_data.context) {
Lock::Guard lock_guard(_data.context->_lock);
_data.context->_ref_cnt++;
}
}
Signal::Signal(Signal::Data data) : _data(data)
{
if (_data.context) {
_data.context->_ref_cnt++;
/*
* Defer the destruction of the context until the handling of the
* 'Signal' has completed.
*
* Normally, the context can only have one 'Signal' in flight, which is
* destroyed before 'pending_signal' is called the next time. However,
* one exception is a signal handler that unexpectedly calls
* 'pending_signal' itself (i.e., via 'wait_and_dispatch_one_io_signal').
* As this is dangerous programming pattern (that should be fixed), we
* print a warning.
*
* In this situation, the context-destroy lock is already taken by the
* outer scope. To avoid a deadlock during the attempt to create
* a second 'Signal' object for the same context, we take the lock
* only in the outer scope (where the context's reference counter
* is in its clear state).
*/
if (_data.context->_ref_cnt == 1) {
_data.context->_destroy_lock.lock();
} else {
/* print warning only once to avoid flooding the log */
static bool printed;
if (!printed) {
warning("attempt to handle the same signal context twice (nested)");
printed = true;
}
}
}
}
/********************
** Signal_context **
********************/
Signal_context::~Signal_context()
{
/*
* Detect bug in an application where a signal context is destroyed prior
* dissolving it from the signal receiver.
*/
if (_receiver)
error("Destructing undissolved signal context");
}
/************************
** Signal_transmitter **
************************/
Signal_transmitter::Signal_transmitter(Signal_context_capability context)
: _context(context) { }
void Signal_transmitter::context(Signal_context_capability context) {
_context = context; }
Signal_context_capability Signal_transmitter::context() { return _context; }
/*********************
** Signal_receiver **
*********************/
Signal Signal_receiver::wait_for_signal()
{
for (;;) {
try {
return pending_signal();
} catch (Signal_not_pending) { }
/* block until the receiver has received a signal */
block_for_signal();
}
}
Signal_receiver::~Signal_receiver()
{
Lock::Guard contexts_lock_guard(_contexts_lock);
/* disassociate contexts from the receiver */
while (Signal_context *context = _contexts.head()) {
_platform_begin_dissolve(context);
_unsynchronized_dissolve(context);
_platform_finish_dissolve(context);
}
_platform_destructor();
}
void Signal_receiver::_unsynchronized_dissolve(Signal_context * const context)
{
/* tell core to stop sending signals referring to the context */
env_deprecated()->pd_session()->free_context(context->_cap);
/* restore default initialization of signal context */
context->_receiver = nullptr;
context->_cap = Signal_context_capability();
/* remove context from context list */
_contexts.remove(context);
}
void Signal_receiver::dissolve(Signal_context *context)
{
if (context->_receiver != this)
throw Context_not_associated();
{
/*
* We must adhere to the following lock-taking order:
*
* 1. Taking the lock for the list of contexts ('_contexts_lock')
* 2. Taking the context-registry lock (this happens inside
* '_platform_begin_dissolve' on platforms that use such a
* registry)
* 3. Taking the lock for an individual signal context
*/
Lock::Guard contexts_lock_guard(_contexts_lock);
_platform_begin_dissolve(context);
Lock::Guard context_lock_guard(context->_lock);
_unsynchronized_dissolve(context);
}
_platform_finish_dissolve(context);
Lock::Guard context_destroy_lock_guard(context->_destroy_lock);
}
void Signal_receiver::Context_ring::insert_as_tail(Signal_context *re)
{
if (_head) {
re->_prev = _head->_prev;
re->_next = _head;
_head->_prev = _head->_prev->_next = re;
} else {
_head = re->_prev = re->_next = re;
}
}
void Signal_receiver::Context_ring::remove(Signal_context const *re)
{
if (re->_next != re) {
if (re == _head) { _head = re->_next; }
re->_prev->_next = re->_next;
re->_next->_prev = re->_prev;
}
else { _head = nullptr; }
}