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

386 lines
9.8 KiB
C++
Raw Normal View History

/*
* \brief Entrypoint for serving RPC requests and dispatching signals
* \author Norman Feske
* \author Christian Helmuth
* \date 2015-12-17
*/
/*
* Copyright (C) 2015-2019 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/entrypoint.h>
#include <base/component.h>
#include <cpu/atomic.h>
#include <util/retry.h>
#include <base/rpc_client.h>
/* base-internal includes */
#include <base/internal/globals.h>
using namespace Genode;
/*
* XXX move declarations to base-internal headers
*/
namespace Genode {
extern bool inhibit_tracing;
void call_global_static_constructors();
void destroy_signal_thread();
}
/**
* Return thread name used for the component's initial entrypoint
*/
static char const *initial_ep_name() { return "ep"; }
void Entrypoint::Signal_proxy_component::signal()
{
/* signal delivered successfully */
ep._signal_proxy_delivers_signal = false;
ep._process_deferred_signals();
bool io_progress = false;
/*
* Try to dispatch one pending signal picked-up by the signal-proxy thread.
* Note, we handle only one signal here to ensure fairness between RPCs and
* signals.
*/
try {
Signal sig = ep._sig_rec->pending_signal();
ep._dispatch_signal(sig);
if (sig.context()->level() == Signal_context::Level::Io) {
/* trigger the progress handler */
io_progress = true;
}
} catch (Signal_receiver::Signal_not_pending) { }
if (io_progress)
ep._handle_io_progress();
}
void Entrypoint::_dispatch_signal(Signal &sig)
{
Signal_dispatcher_base *dispatcher = 0;
dispatcher = dynamic_cast<Signal_dispatcher_base *>(sig.context());
if (!dispatcher)
return;
dispatcher->dispatch(sig.num());
}
void Entrypoint::_defer_signal(Signal &sig)
{
Signal_context *context = sig.context();
2020-02-19 16:26:40 +01:00
Mutex::Guard guard(_deferred_signals_mutex);
_deferred_signals.remove(context->deferred_le());
_deferred_signals.insert(context->deferred_le());
}
void Entrypoint::_process_deferred_signals()
{
for (;;) {
Signal_context *context = nullptr;
{
2020-02-19 16:26:40 +01:00
Mutex::Guard guard(_deferred_signals_mutex);
if (!_deferred_signals.first()) return;
context = _deferred_signals.first()->object();
_deferred_signals.remove(_deferred_signals.first());
}
Signal_dispatcher_base *dispatcher =
dynamic_cast<Signal_dispatcher_base *>(context);
if (dispatcher) dispatcher->dispatch(1);
}
}
void Entrypoint::_process_incoming_signals()
{
for (;;) {
do {
{
/* see documentation in 'wait_and_dispatch_one_io_signal()' */
Mutex::Guard guard { _block_for_signal_mutex };
_signal_proxy_delivers_signal = true;
_sig_rec->block_for_signal();
}
/*
* It might happen that we try to forward a signal to the
* entrypoint, while the context of that signal is already
* destroyed. In that case we will get an ipc error exception
* as result, which has to be caught.
*/
try {
retry<Blocking_canceled>(
[&] () { _signal_proxy_cap.call<Signal_proxy::Rpc_signal>(); },
[] () { warning("blocking canceled during signal processing"); });
} catch (Genode::Ipc_error) { /* ignore - context got destroyed in meantime */ }
/* entrypoint destructor requested to stop signal handling */
if (_stop_signal_proxy) {
return;
}
} while (!_suspended);
_deferred_signal_handler.destruct();
_suspend_dispatcher.destruct();
_sig_rec.destruct();
dissolve(_signal_proxy);
deinit_heartbeat_monitoring();
_signal_proxy_cap = Capability<Signal_proxy>();
_rpc_ep.destruct();
destroy_signal_thread();
/* execute fork magic in noux plugin */
_suspended_callback();
init_signal_thread(_env);
_rpc_ep.construct(&_env.pd(), Component::stack_size(), initial_ep_name());
init_heartbeat_monitoring(_env);
_signal_proxy_cap = manage(_signal_proxy);
_sig_rec.construct();
/*
* Before calling the resumed callback, we reset the callback pointer
* as these may be set again in the resumed code to initiate the next
* suspend-resume cycle (e.g., exit()).
*/
void (*resumed_callback)() = _resumed_callback;
_suspended_callback = nullptr;
_resumed_callback = nullptr;
_suspended = false;
resumed_callback();
}
}
bool Entrypoint::_wait_and_dispatch_one_io_signal(bool const dont_block)
{
if (!_rpc_ep->is_myself())
warning(__func__, " called from non-entrypoint thread \"",
Thread::myself()->name(), "\"");
for (;;) {
try {
Signal sig =_sig_rec->pending_signal();
/* defer application-level signals */
if (sig.context()->level() == Signal_context::Level::App) {
_defer_signal(sig);
continue;
}
_dispatch_signal(sig);
break;
} catch (Signal_receiver::Signal_not_pending) {
if (dont_block)
return false;
{
/*
* The signal-proxy thread as well as the entrypoint via
* 'wait_and_dispatch_one_io_signal' never call
* 'block_for_signal()' without the '_block_for_signal_mutex'
* aquired. The signal-proxy thread also flags when it was
* unblocked by an incoming signal and delivers the signal via
* RPC in '_signal_proxy_delivers_signal'.
*/
Mutex::Guard guard { _block_for_signal_mutex };
/*
* If the signal proxy is blocked in the signal-delivery
* RPC but the call did not yet arrive in the entrypoint
* (_signal_proxy_delivers_signal == true), we acknowledge the
* delivery here (like in 'Signal_proxy_component::signal()') and
* retry to fetch one pending signal at the beginning of the
* loop above. Otherwise, we block for the next incoming
* signal.
*
* There exist cases were we already processed the signal
* flagged in '_signal_proxy_delivers_signal' and will end
* up here again. In these cases we also 'block_for_signal()'.
*/
if (_signal_proxy_delivers_signal)
_signal_proxy_delivers_signal = false;
else
_sig_rec->block_for_signal();
}
}
}
_handle_io_progress();
/* initiate potential deferred-signal handling in entrypoint */
if (_deferred_signals.first()) {
/* construct the handler on demand (otherwise we break core) */
if (!_deferred_signal_handler.constructed())
_deferred_signal_handler.construct(*this, *this,
&Entrypoint::_handle_deferred_signals);
Signal_transmitter(*_deferred_signal_handler).submit();
}
return true;
}
void Entrypoint::schedule_suspend(void (*suspended)(), void (*resumed)())
{
_suspended_callback = suspended;
_resumed_callback = resumed;
/*
* We always construct the dispatcher when the suspend is scheduled and
* destruct it when the suspend is executed.
*/
_suspend_dispatcher.construct(*this, *this, &Entrypoint::_handle_suspend);
/* trigger wakeup of the signal-dispatch loop for suspend */
Signal_transmitter(*_suspend_dispatcher).submit();
}
Signal_context_capability Entrypoint::manage(Signal_dispatcher_base &dispatcher)
{
/* _sig_rec is invalid for a small window in _process_incoming_signals */
return _sig_rec.constructed() ? _sig_rec->manage(&dispatcher)
: Signal_context_capability();
}
void Genode::Entrypoint::dissolve(Signal_dispatcher_base &dispatcher)
{
/* _sig_rec is invalid for a small window in _process_incoming_signals */
if (_sig_rec.constructed())
_sig_rec->dissolve(&dispatcher);
/* also remove context from deferred signal list */
{
2020-02-19 16:26:40 +01:00
Mutex::Guard guard(_deferred_signals_mutex);
_deferred_signals.remove(dispatcher.deferred_le());
}
}
namespace {
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
struct Constructor : Interface
{
GENODE_RPC(Rpc_construct, void, construct);
GENODE_RPC_INTERFACE(Rpc_construct);
};
struct Constructor_component : Rpc_object<Constructor, Constructor_component>
{
Env &env;
Constructor_component(Env &env) : env(env) { }
void construct()
{
/* enable tracing support */
Genode::inhibit_tracing = false;
Genode::call_global_static_constructors();
Genode::init_signal_transmitter(env);
Genode::init_tracing(env);
/*
* Now, as signaling is available, initialize the asynchronous
* parent resource mechanism
*/
init_parent_resource_requests(env);
init_heartbeat_monitoring(env);
Component::construct(env);
}
};
void invoke_constructor_at_entrypoint(Capability<Constructor> cap)
{
cap.call<Constructor::Rpc_construct>();
}
}
Entrypoint::Entrypoint(Env &env)
:
_env(env),
_rpc_ep(&env.pd(), Component::stack_size(), initial_ep_name()),
/* initialize signalling before creating the first signal receiver */
_signalling_initialized((init_signal_thread(env), true))
{
/* initialize emulation of the original synchronous root interface */
init_root_proxy(_env);
/*
* Invoke Component::construct function in the context of the entrypoint.
*/
Constructor_component constructor(env);
_env.ep().manage(constructor);
try {
invoke_constructor_at_entrypoint(constructor.cap());
} catch (Blocking_canceled) {
warning("blocking canceled in entrypoint constructor");
}
_env.ep().dissolve(constructor);
/*
* The calling initial thread becomes the signal proxy thread for this
* entrypoint
*/
_process_incoming_signals();
}
Entrypoint::Entrypoint(Env &env, size_t stack_size, char const *name,
Affinity::Location location)
:
_env(env),
_rpc_ep(&env.pd(), stack_size, name, true, location),
_signalling_initialized(true)
{
_signal_proxy_thread.construct(env, *this, location,
Thread::Weight(), env.cpu());
}
Entrypoint::~Entrypoint()
{
/* stop the signal proxy before destruction */
_stop_signal_proxy_handler.construct(
*this, *this, &Entrypoint::_handle_stop_signal_proxy);
Signal_transmitter(*_stop_signal_proxy_handler).submit();
_signal_proxy_thread->join();
_stop_signal_proxy_handler.destruct();
_rpc_ep->dissolve(&_signal_proxy);
}