genode/repos/gems/src/app/sculpt_manager/model/runtime_state.h

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_ */