Noux: delayed 'Child' object destruction

With this patch the destruction of Noux 'Child' objects gets delayed
further until the exit signal has been dispatched. This prevents the
self-destruction of the signal dispatcher, which is a member of the
'Child' object.

Fixes #603.
This commit is contained in:
Christian Prochaska 2013-01-03 15:52:27 +01:00 committed by Norman Feske
parent 13d4108fea
commit 6fa57141ae
5 changed files with 201 additions and 88 deletions

View File

@ -30,6 +30,8 @@
#include <cpu_session_component.h>
#include <child_policy.h>
#include <io_receptor_registry.h>
#include <destruct_queue.h>
#include <destruct_dispatcher.h>
namespace Noux {
@ -83,63 +85,10 @@ namespace Noux {
bool is_init_process(Child *child);
void init_process_exited();
/**
* Signal context used for child exit
*/
class Child_exit_dispatcher : public Signal_dispatcher_base
{
private:
Child *_child;
public:
Child_exit_dispatcher(Child *child) : _child(child) { }
void dispatch(unsigned)
{
if (is_init_process(_child)) {
PINF("init process exited");
/* trigger exit of main event loop */
init_process_exited();
} else {
/* destroy 'Noux::Child' */
destroy(env()->heap(), _child);
PINF("destroy %p", _child);
PINF("quota: avail=%zd, used=%zd",
env()->ram_session()->avail(),
env()->ram_session()->used());
}
}
};
/**
* Signal context used for removing the child after having executed 'execve'
*/
class Child_execve_cleanup_dispatcher : public Signal_dispatcher_base
{
private:
Child *_child;
public:
Child_execve_cleanup_dispatcher(Child *child) : _child(child) { }
void dispatch(unsigned)
{
destroy(env()->heap(), _child);
}
};
class Child : public Rpc_object<Session>,
public File_descriptor_registry,
public Family_member
public Family_member,
public Destruct_queue::Element<Child>
{
private:
@ -150,11 +99,10 @@ namespace Noux {
*/
Semaphore _blocker;
Child_exit_dispatcher _exit_dispatcher;
Signal_context_capability _exit_context_cap;
Child_execve_cleanup_dispatcher _execve_cleanup_dispatcher;
Signal_context_capability _execve_cleanup_context_cap;
Allocator *_alloc;
Destruct_queue &_destruct_queue;
Destruct_dispatcher _destruct_dispatcher;
Signal_context_capability _destruct_context_cap;
Cap_session * const _cap_session;
@ -292,24 +240,26 @@ namespace Noux {
* looked up at the virtual file
* system
*/
Child(char const *name,
Family_member *parent,
int pid,
Signal_receiver *sig_rec,
Dir_file_system *root_dir,
Args const &args,
Sysio::Env const &env,
Cap_session *cap_session,
Service_registry &parent_services,
Rpc_entrypoint &resources_ep,
bool forked)
Child(char const *name,
Family_member *parent,
int pid,
Signal_receiver *sig_rec,
Dir_file_system *root_dir,
Args const &args,
Sysio::Env const &env,
Cap_session *cap_session,
Service_registry &parent_services,
Rpc_entrypoint &resources_ep,
bool forked,
Allocator *destruct_alloc,
Destruct_queue &destruct_queue)
:
Family_member(pid, parent),
Destruct_queue::Element<Child>(destruct_alloc),
_sig_rec(sig_rec),
_exit_dispatcher(this),
_exit_context_cap(sig_rec->manage(&_exit_dispatcher)),
_execve_cleanup_dispatcher(this),
_execve_cleanup_context_cap(sig_rec->manage(&_execve_cleanup_dispatcher)),
_destruct_queue(destruct_queue),
_destruct_dispatcher(_destruct_queue, this),
_destruct_context_cap(sig_rec->manage(&_destruct_dispatcher)),
_cap_session(cap_session),
_entrypoint(cap_session, STACK_SIZE, "noux_process", false),
_resources(name, resources_ep, false),
@ -327,7 +277,7 @@ namespace Noux {
_child_policy(name, _binary_ds, _args.cap(), _env.cap(),
_entrypoint, _local_noux_service,
_local_rm_service, _parent_services,
*this, *this, _exit_context_cap, _resources.ram),
*this, *this, _destruct_context_cap, _resources.ram),
_child(_binary_ds, _resources.ram.cap(), _resources.cpu.cap(),
_resources.rm.cap(), &_entrypoint, &_child_policy)
{
@ -341,8 +291,7 @@ namespace Noux {
~Child()
{
_sig_rec->dissolve(&_execve_cleanup_dispatcher);
_sig_rec->dissolve(&_exit_dispatcher);
_sig_rec->dissolve(&_destruct_dispatcher);
_entrypoint.dissolve(this);
@ -364,7 +313,14 @@ namespace Noux {
void submit_exit_signal()
{
Signal_transmitter(_exit_context_cap).submit();
if (is_init_process(this)) {
PINF("init process exited");
/* trigger exit of main event loop */
init_process_exited();
} else {
Signal_transmitter(_destruct_context_cap).submit();
}
}
Ram_session_capability ram() const { return _resources.ram.cap(); }

View File

@ -41,7 +41,7 @@ namespace Noux {
Service_registry &_parent_services;
Family_member &_family_member;
File_descriptor_registry &_file_descriptor_registry;
Signal_context_capability _exit_context_cap;
Signal_context_capability _destruct_context_cap;
Ram_session &_ref_ram_session;
public:
@ -56,7 +56,7 @@ namespace Noux {
Service_registry &parent_services,
Family_member &family_member,
File_descriptor_registry &file_descriptor_registry,
Signal_context_capability exit_context_cap,
Signal_context_capability destruct_context_cap,
Ram_session &ref_ram_session)
:
_name(strncpy(_name_buf, name, sizeof(_name_buf))),
@ -69,7 +69,7 @@ namespace Noux {
_parent_services(parent_services),
_family_member(family_member),
_file_descriptor_registry(file_descriptor_registry),
_exit_context_cap(exit_context_cap),
_destruct_context_cap(destruct_context_cap),
_ref_ram_session(ref_ram_session)
{ }
@ -122,7 +122,7 @@ namespace Noux {
/* handle exit of the init process */
if (_family_member.parent() == 0)
Signal_transmitter(_exit_context_cap).submit();
Signal_transmitter(_destruct_context_cap).submit();
}
Ram_session *ref_ram_session()

View File

@ -0,0 +1,48 @@
/*
* \brief Signal_dispatcher which adds a destruct queue element into a
* destruct queue
* \author Christian Prochaska
* \date 2013-01-03
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _NOUX__DESTRUCT_DISPATCHER_H_
#define _NOUX__DESTRUCT_DISPATCHER_H_
/* Genode includes */
#include <base/signal.h>
/* Noux includes */
#include <destruct_queue.h>
namespace Noux {
using namespace Genode;
class Destruct_dispatcher : public Signal_dispatcher_base
{
private:
Destruct_queue &_destruct_queue;
Destruct_queue::Element_base *_element;
public:
Destruct_dispatcher(Destruct_queue &destruct_queue,
Destruct_queue::Element_base *element)
: _destruct_queue(destruct_queue), _element(element) { }
void dispatch(unsigned)
{
_destruct_queue.insert(_element);
}
};
}
#endif /* _NOUX__DESTRUCT_DISPATCHER_H_ */

View File

@ -0,0 +1,94 @@
/*
* \brief Queue for delayed object destruction
* \author Christian Prochaska
* \date 2013-01-03
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _NOUX__DESTRUCT_QUEUE_H_
#define _NOUX__DESTRUCT_QUEUE_H_
/* Genode includes */
#include <base/allocator.h>
#include <util/list.h>
namespace Noux {
class Destruct_queue
{
public:
struct Element_base : Genode::List<Element_base>::Element
{
virtual void destroy() = 0;
};
/*
* When a pointer to an object which inherits 'Element' among other
* base classes gets static-casted to a pointer to the 'Element'
* base object, the resulting address can differ from the start
* address of the inherited object. To be able to pass the start
* address of the inherited object to the allocator, a static-cast
* back to the inherited class needs to be performed. Therefore the
* type of the class inheriting from 'Element' needs to be given as
* template parameter.
*/
template <typename T>
class Element : public Element_base
{
private:
Genode::Allocator *_alloc;
public:
/**
* Constructor
*
* \param alloc the allocator which was used to allocate
* the element
*/
Element(Genode::Allocator *alloc) : _alloc(alloc) { }
virtual ~Element() { };
void destroy()
{
Genode::destroy(_alloc, static_cast<T*>(this));
}
};
private:
Genode::List<Element_base> _destruct_list;
Genode::Lock _destruct_list_lock;
public:
void insert(Element_base *element)
{
Genode::Lock::Guard guard(_destruct_list_lock);
_destruct_list.insert(element);
}
void flush()
{
Genode::Lock::Guard guard(_destruct_list_lock);
Element_base *element;
while ((element = _destruct_list.first())) {
_destruct_list.remove(element);
element->destroy();
}
}
};
}
#endif /* _NOUX__DESTRUCT_QUEUE_H_ */

View File

@ -28,10 +28,11 @@
#include <dir_file_system.h>
#include <user_info.h>
#include <io_receptor_registry.h>
#include <destruct_queue.h>
static bool trace_syscalls = false;
static bool verbose_quota = false;
namespace Noux {
@ -272,7 +273,9 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
_cap_session,
_parent_services,
_resources.ep,
false);
false,
env()->heap(),
_destruct_queue);
/* replace ourself by the new child at the parent */
parent()->remove(this);
@ -281,7 +284,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
_assign_io_channels_to(child);
/* signal main thread to remove ourself */
Genode::Signal_transmitter(_execve_cleanup_context_cap).submit();
Genode::Signal_transmitter(_destruct_context_cap).submit();
/* start executing the new process */
child->start();
@ -486,7 +489,9 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
_cap_session,
_parent_services,
_resources.ep,
true);
true,
env()->heap(),
_destruct_queue);
Family_member::insert(child);
@ -863,6 +868,7 @@ int main(int argc, char **argv)
/* create init process */
static Genode::Signal_receiver sig_rec;
static Destruct_queue destruct_queue;
init_child = new Noux::Child(name_of_init_process(),
0,
@ -874,7 +880,9 @@ int main(int argc, char **argv)
&cap,
parent_services,
resources_ep,
false);
false,
env()->heap(),
destruct_queue);
/*
* I/O channels must be dynamically allocated to handle cases where the
@ -902,6 +910,13 @@ int main(int argc, char **argv)
for (unsigned i = 0; i < signal.num(); i++)
dispatcher->dispatch(1);
destruct_queue.flush();
if (verbose_quota)
PINF("quota: avail=%zd, used=%zd",
env()->ram_session()->avail(),
env()->ram_session()->used());
}
PINF("-- exiting noux ---");