390 lines
9.5 KiB
C++
390 lines
9.5 KiB
C++
/*
|
|
* \brief State of the components hosted in the runtime subsystem
|
|
* \author Norman Feske
|
|
* \date 2018-08-22
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2018 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.
|
|
*/
|
|
|
|
#ifndef _MODEL__RUNTIME_STATE_H_
|
|
#define _MODEL__RUNTIME_STATE_H_
|
|
|
|
/* Genode includes */
|
|
#include <util/xml_node.h>
|
|
#include <util/list_model.h>
|
|
|
|
/* local includes */
|
|
#include <types.h>
|
|
#include <runtime.h>
|
|
#include <model/runtime_config.h>
|
|
#include <model/component.h>
|
|
|
|
namespace Sculpt { class Runtime_state; }
|
|
|
|
class Sculpt::Runtime_state : public Runtime_info
|
|
{
|
|
public:
|
|
|
|
struct Info
|
|
{
|
|
bool selected;
|
|
|
|
/* true if component is in the TCB of the selected one */
|
|
bool tcb;
|
|
|
|
/* true if 'tcb' is updated for the immediate dependencies */
|
|
bool tcb_updated;
|
|
|
|
unsigned long assigned_ram;
|
|
unsigned long avail_ram;
|
|
|
|
unsigned long assigned_caps;
|
|
unsigned long avail_caps;
|
|
};
|
|
|
|
private:
|
|
|
|
Allocator &_alloc;
|
|
|
|
struct Child : List_model<Child>::Element
|
|
{
|
|
Start_name const name;
|
|
|
|
Info info { .selected = false,
|
|
.tcb = false,
|
|
.tcb_updated = false,
|
|
.assigned_ram = 0, .avail_ram = 0,
|
|
.assigned_caps = 0, .avail_caps = 0 };
|
|
|
|
bool abandoned_by_user = false;
|
|
|
|
Child(Start_name const &name) : name(name) { }
|
|
};
|
|
|
|
List_model<Child> _children { };
|
|
|
|
/**
|
|
* Child present in initial deploy config but interactively removed
|
|
*/
|
|
struct Abandoned_child : Interface
|
|
{
|
|
Start_name const name;
|
|
Abandoned_child(Start_name const &name) : name(name) { };
|
|
};
|
|
|
|
Registry<Registered<Abandoned_child> > _abandoned_children { };
|
|
|
|
/**
|
|
* Child that was interactively launched
|
|
*/
|
|
struct Launched_child : Interface
|
|
{
|
|
Start_name const name;
|
|
Path const launcher;
|
|
|
|
Constructible<Component> construction { };
|
|
|
|
bool launched;
|
|
|
|
/**
|
|
* Constructor used for child started via launcher
|
|
*/
|
|
Launched_child(Start_name const &name, Path const &launcher)
|
|
: name(name), launcher(launcher), launched(true) { };
|
|
|
|
/**
|
|
* Constructor used for interactively configured child
|
|
*/
|
|
Launched_child(Allocator &alloc, Start_name const &name,
|
|
Component::Path const &pkg_path,
|
|
Component::Info const &info)
|
|
:
|
|
name(name), launcher(), launched(false)
|
|
{
|
|
construction.construct(alloc, pkg_path, info);
|
|
}
|
|
|
|
void gen_deploy_start_node(Xml_generator &xml) const
|
|
{
|
|
if (!launched)
|
|
return;
|
|
|
|
gen_named_node(xml, "start", name, [&] () {
|
|
|
|
/* interactively constructed */
|
|
if (construction.constructed()) {
|
|
xml.attribute("pkg", construction->path);
|
|
|
|
xml.node("route", [&] () {
|
|
construction->routes.for_each([&] (Route const &route) {
|
|
route.gen_xml(xml); }); });
|
|
}
|
|
|
|
/* created via launcher */
|
|
else {
|
|
if (name != launcher)
|
|
xml.attribute("launcher", launcher);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
Registry<Registered<Launched_child> > _launched_children { };
|
|
|
|
Registered<Launched_child> *_currently_constructed = nullptr;
|
|
|
|
bool _construction_in_progress() const
|
|
{
|
|
return _currently_constructed
|
|
&& _currently_constructed->construction.constructed();
|
|
}
|
|
|
|
struct Update_policy : List_model<Child>::Update_policy
|
|
{
|
|
Allocator &_alloc;
|
|
|
|
Update_policy(Allocator &alloc) : _alloc(alloc) { }
|
|
|
|
void destroy_element(Child &elem)
|
|
{
|
|
destroy(_alloc, &elem);
|
|
}
|
|
|
|
Child &create_element(Xml_node node)
|
|
{
|
|
return *new (_alloc)
|
|
Child(node.attribute_value("name", Start_name()));
|
|
}
|
|
|
|
void update_element(Child &child, Xml_node node)
|
|
{
|
|
if (node.has_sub_node("ram")) {
|
|
Xml_node const ram = node.sub_node("ram");
|
|
child.info.assigned_ram = max(ram.attribute_value("assigned", Number_of_bytes()),
|
|
ram.attribute_value("quota", Number_of_bytes()));
|
|
child.info.avail_ram = ram.attribute_value("avail", Number_of_bytes());
|
|
}
|
|
|
|
if (node.has_sub_node("caps")) {
|
|
Xml_node const caps = node.sub_node("caps");
|
|
child.info.assigned_caps = max(caps.attribute_value("assigned", 0UL),
|
|
caps.attribute_value("quota", 0UL));
|
|
child.info.avail_caps = caps.attribute_value("avail", 0UL);
|
|
}
|
|
}
|
|
|
|
static bool element_matches_xml_node(Child const &elem, Xml_node node)
|
|
{
|
|
return node.attribute_value("name", Start_name()) == elem.name;
|
|
}
|
|
|
|
static bool node_is_element(Xml_node node) { return node.has_type("child"); }
|
|
};
|
|
|
|
/*
|
|
* Noncopyable
|
|
*/
|
|
Runtime_state(Runtime_state const &);
|
|
Runtime_state &operator = (Runtime_state const &);
|
|
|
|
public:
|
|
|
|
Runtime_state(Allocator &alloc) : _alloc(alloc) { }
|
|
|
|
~Runtime_state() { reset_abandoned_and_launched_children(); }
|
|
|
|
void update_from_state_report(Xml_node state)
|
|
{
|
|
Update_policy policy(_alloc);
|
|
_children.update_from_xml(policy, state);
|
|
}
|
|
|
|
/**
|
|
* Runtime_info interface
|
|
*/
|
|
bool present_in_runtime(Start_name const &name) const override
|
|
{
|
|
bool result = false;
|
|
|
|
_children.for_each([&] (Child const &child) {
|
|
if (!result && child.name == name)
|
|
result = true; });
|
|
|
|
_launched_children.for_each([&] (Launched_child const &child) {
|
|
if (!result && child.name == name)
|
|
result = true; });
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Runtime_info interface
|
|
*/
|
|
bool abandoned_by_user(Start_name const &name) const override
|
|
{
|
|
bool result = false;
|
|
_abandoned_children.for_each([&] (Abandoned_child const &child) {
|
|
if (!result && child.name == name)
|
|
result = true; });
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Runtime_info interface
|
|
*/
|
|
void gen_launched_deploy_start_nodes(Xml_generator &xml) const override
|
|
{
|
|
_launched_children.for_each([&] (Launched_child const &child) {
|
|
child.gen_deploy_start_node(xml); });
|
|
}
|
|
|
|
Info info(Start_name const &name) const
|
|
{
|
|
Info result { };
|
|
_children.for_each([&] (Child const &child) {
|
|
if (child.name == name)
|
|
result = child.info; });
|
|
return result;
|
|
}
|
|
|
|
Start_name selected() const
|
|
{
|
|
Start_name result;
|
|
_children.for_each([&] (Child const &child) {
|
|
if (child.info.selected)
|
|
result = child.name; });
|
|
return result;
|
|
}
|
|
|
|
void toggle_selection(Start_name const &name, Runtime_config const &config)
|
|
{
|
|
_children.for_each([&] (Child &child) {
|
|
child.info.selected = (child.name == name) && !child.info.selected;
|
|
child.info.tcb = child.info.selected;
|
|
child.info.tcb_updated = false;
|
|
});
|
|
|
|
/*
|
|
* Update the TCB flag of the selected child's transitive
|
|
* dependencies.
|
|
*/
|
|
for (;;) {
|
|
|
|
Start_name name_of_updated { };
|
|
|
|
/*
|
|
* Search child that belongs to TCB but its dependencies
|
|
* have not been added to the TCB yet.
|
|
*/
|
|
_children.for_each([&] (Child &child) {
|
|
if (!name_of_updated.valid() && child.info.tcb && !child.info.tcb_updated) {
|
|
name_of_updated = child.name;
|
|
child.info.tcb_updated = true; /* skip in next iteration */
|
|
}
|
|
});
|
|
|
|
if (!name_of_updated.valid())
|
|
break;
|
|
|
|
/* tag all dependencies as part of the TCB */
|
|
config.for_each_dependency(name_of_updated, [&] (Start_name const &dep) {
|
|
_children.for_each([&] (Child &child) {
|
|
if (child.name == dep)
|
|
child.info.tcb = true; }); });
|
|
}
|
|
}
|
|
|
|
void abandon(Start_name const &name)
|
|
{
|
|
/*
|
|
* If child was launched interactively, remove corresponding
|
|
* entry from '_launched_children'.
|
|
*/
|
|
bool was_interactively_launched = false;
|
|
_launched_children.for_each([&] (Registered<Launched_child> &child) {
|
|
if (child.name == name) {
|
|
was_interactively_launched = true;
|
|
destroy(_alloc, &child);
|
|
}
|
|
});
|
|
|
|
if (was_interactively_launched)
|
|
return;
|
|
|
|
/*
|
|
* Child was present at initial deploy config, mark as abandoned
|
|
*/
|
|
new (_alloc) Registered<Abandoned_child>(_abandoned_children, name);
|
|
}
|
|
|
|
void launch(Start_name const &name, Path const &launcher)
|
|
{
|
|
new (_alloc) Registered<Launched_child>(_launched_children, name, launcher);
|
|
}
|
|
|
|
Start_name new_construction(Component::Path const pkg,
|
|
Component::Info const &info)
|
|
{
|
|
/* allow only one construction at a time */
|
|
discard_construction();
|
|
|
|
/* determine unique name for new child */
|
|
Depot::Archive::Name const archive_name = Depot::Archive::name(pkg);
|
|
Start_name unique_name = archive_name;
|
|
unsigned cnt = 1;
|
|
while (present_in_runtime(unique_name))
|
|
unique_name = Start_name(archive_name, ".", ++cnt);
|
|
|
|
_currently_constructed = new (_alloc)
|
|
Registered<Launched_child>(_launched_children, _alloc,
|
|
unique_name, pkg, info);
|
|
return unique_name;
|
|
}
|
|
|
|
void discard_construction()
|
|
{
|
|
if (_currently_constructed) {
|
|
destroy(_alloc, _currently_constructed);
|
|
_currently_constructed = nullptr;
|
|
}
|
|
}
|
|
|
|
template <typename FN>
|
|
void apply_to_construction(FN const &fn)
|
|
{
|
|
if (_construction_in_progress())
|
|
fn(*_currently_constructed->construction);
|
|
}
|
|
|
|
template <typename FN>
|
|
void with_construction(FN const &fn) const
|
|
{
|
|
if (_construction_in_progress())
|
|
fn(*_currently_constructed->construction);
|
|
}
|
|
|
|
void launch_construction()
|
|
{
|
|
if (_currently_constructed)
|
|
_currently_constructed->launched = true;
|
|
|
|
_currently_constructed = nullptr;
|
|
}
|
|
|
|
void reset_abandoned_and_launched_children()
|
|
{
|
|
_abandoned_children.for_each([&] (Abandoned_child &child) {
|
|
destroy(_alloc, &child); });
|
|
|
|
_launched_children.for_each([&] (Launched_child &child) {
|
|
destroy(_alloc, &child); });
|
|
}
|
|
};
|
|
|
|
#endif /* _MODEL__RUNTIME_STATE_H_ */
|