From 5dc8e330b64e1385765432ff1605a894aa8b06b5 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Thu, 10 Sep 2020 15:21:56 +0200 Subject: [PATCH] sculpt: add button for restarting a component Fixes #3885 --- repos/gems/src/app/sculpt_manager/deploy.h | 39 ++++++- repos/gems/src/app/sculpt_manager/graph.cc | 41 ++++--- repos/gems/src/app/sculpt_manager/graph.h | 5 +- repos/gems/src/app/sculpt_manager/main.cc | 11 ++ .../app/sculpt_manager/model/child_state.h | 6 +- .../app/sculpt_manager/model/runtime_state.h | 100 +++++++++++++++--- repos/gems/src/app/sculpt_manager/runtime.h | 5 + .../sculpt_manager/view/activatable_item.h | 2 +- 8 files changed, 176 insertions(+), 33 deletions(-) diff --git a/repos/gems/src/app/sculpt_manager/deploy.h b/repos/gems/src/app/sculpt_manager/deploy.h index 2c636abfa1..161a665e3e 100644 --- a/repos/gems/src/app/sculpt_manager/deploy.h +++ b/repos/gems/src/app/sculpt_manager/deploy.h @@ -108,8 +108,43 @@ struct Sculpt::Deploy */ deploy.for_each_sub_node("start", [&] (Xml_node node) { Start_name const name = node.attribute_value("name", Start_name()); - if (!_runtime_info.abandoned_by_user(name)) - append_xml_node(node); + if (_runtime_info.abandoned_by_user(name)) + return; + + xml.node("start", [&] () { + + /* + * Copy attributes + */ + + xml.attribute("name", name); + + /* override version with restarted version, after restart */ + using Version = Child_state::Version; + Version version = _runtime_info.restarted_version(name); + if (version.value == 0) + version = Version { node.attribute_value("version", 0U) }; + + if (version.value > 0) + xml.attribute("version", version.value); + + auto copy_attribute = [&] (auto attr) + { + if (node.has_attribute(attr)) { + using Value = String<128>; + xml.attribute(attr, node.attribute_value(attr, Value())); + } + }; + + copy_attribute("caps"); + copy_attribute("ram"); + copy_attribute("cpu"); + copy_attribute("pkg"); + + /* copy start-node content */ + node.with_raw_content([&] (char const *start, size_t length) { + xml.append(start, length); }); + }); }); /* diff --git a/repos/gems/src/app/sculpt_manager/graph.cc b/repos/gems/src/app/sculpt_manager/graph.cc index aa1b8ecf3a..1c4034edaa 100644 --- a/repos/gems/src/app/sculpt_manager/graph.cc +++ b/repos/gems/src/app/sculpt_manager/graph.cc @@ -24,13 +24,16 @@ void Graph::_gen_selected_node_content(Xml_generator &xml, Start_name const &nam if (removable) { gen_named_node(xml, "frame", "operations", [&] () { - xml.node("vbox", [&] () { + xml.node("hbox", [&] () { gen_named_node(xml, "button", "remove", [&] () { - _remove_item.gen_button_attr(xml, "remove"); + _action_item.gen_button_attr(xml, "remove"); xml.node("label", [&] () { - xml.attribute("text", "Remove"); - }); - }); + xml.attribute("text", "Remove"); }); }); + + gen_named_node(xml, "button", "restart", [&] () { + _action_item.gen_button_attr(xml, "restart"); + xml.node("label", [&] () { + xml.attribute("text", "Restart"); }); }); }); }); } @@ -276,8 +279,8 @@ Dialog::Hover_result Graph::hover(Xml_node hover) _ram_fs_dialog.match_sub_dialog(hover, "depgraph", "frame", "vbox", "frame", "vbox"), _node_button_item.match(hover, "depgraph", "frame", "vbox", "button", "name"), _add_button_item .match(hover, "depgraph", "button", "name"), - _remove_item .match(hover, "depgraph", "frame", "vbox", - "frame", "vbox", "button", "name")); + _action_item .match(hover, "depgraph", "frame", "vbox", + "frame", "hbox", "button", "name")); if (_add_button_item.hovered("global+")) { @@ -335,11 +338,11 @@ void Graph::click(Action &action) _runtime_state.toggle_selection(_node_button_item._hovered, _runtime_config); - _remove_item.reset(); + _action_item.reset(); } - if (_remove_item.hovered("remove")) - _remove_item.propose_activation_on_click(); + if (_action_item.hovered("remove") || _action_item.hovered("restart")) + _action_item.propose_activation_on_click(); } @@ -352,11 +355,11 @@ void Graph::clack(Action &action, Ram_fs_dialog::Action &ram_fs_action) if (_storage_dialog->clack(action) == Clack_result::CONSUMED) return; - if (_remove_item.hovered("remove")) { + if (_action_item.hovered("remove")) { - _remove_item.confirm_activation_on_clack(); + _action_item.confirm_activation_on_clack(); - if (_remove_item.activated("remove")) { + if (_action_item.activated("remove")) { action.remove_deployed_component(_runtime_state.selected()); /* @@ -366,8 +369,16 @@ void Graph::clack(Action &action, Ram_fs_dialog::Action &ram_fs_action) _runtime_state.toggle_selection(_runtime_state.selected(), _runtime_config); } - } else { - _remove_item.reset(); } + + if (_action_item.hovered("restart")) { + + _action_item.confirm_activation_on_clack(); + + if (_action_item.activated("restart")) + action.restart_deployed_component(_runtime_state.selected()); + } + + _action_item.reset(); } diff --git a/repos/gems/src/app/sculpt_manager/graph.h b/repos/gems/src/app/sculpt_manager/graph.h index 04989b1cab..8dca9f4575 100644 --- a/repos/gems/src/app/sculpt_manager/graph.h +++ b/repos/gems/src/app/sculpt_manager/graph.h @@ -51,8 +51,8 @@ struct Sculpt::Graph : Dialog Depot_deploy::Children const &_deploy_children; Hoverable_item _node_button_item { }; - Hoverable_item _add_button_item { }; - Activatable_item _remove_item { }; + Hoverable_item _add_button_item { }; + Activatable_item _action_item { }; /* * Defined when '+' button is hovered @@ -106,6 +106,7 @@ struct Sculpt::Graph : Dialog struct Action : Storage_dialog::Action { virtual void remove_deployed_component(Start_name const &) = 0; + virtual void restart_deployed_component(Start_name const &) = 0; virtual void toggle_launcher_selector(Rect) = 0; }; diff --git a/repos/gems/src/app/sculpt_manager/main.cc b/repos/gems/src/app/sculpt_manager/main.cc index 57837809f9..13ca777ed4 100644 --- a/repos/gems/src/app/sculpt_manager/main.cc +++ b/repos/gems/src/app/sculpt_manager/main.cc @@ -642,6 +642,17 @@ struct Sculpt::Main : Input_event_handler, _deploy.update_managed_deploy_config(_manual_deploy_rom.xml()); } + /* + * Graph::Action interface + */ + void restart_deployed_component(Start_name const &name) override + { + _runtime_state.restart(name); + + /* update config/managed/deploy with the component 'name' removed */ + _deploy.update_managed_deploy_config(_manual_deploy_rom.xml()); + } + /* * Graph::Action interface */ diff --git a/repos/gems/src/app/sculpt_manager/model/child_state.h b/repos/gems/src/app/sculpt_manager/model/child_state.h index 50dd819801..751733e67b 100644 --- a/repos/gems/src/app/sculpt_manager/model/child_state.h +++ b/repos/gems/src/app/sculpt_manager/model/child_state.h @@ -25,6 +25,10 @@ namespace Sculpt { struct Child_state; } struct Sculpt::Child_state : Noncopyable { + public: + + struct Version { unsigned value; }; + private: Registry::Element _element; @@ -37,7 +41,7 @@ struct Sculpt::Child_state : Noncopyable Ram_quota _ram_quota = _initial_ram_quota; Cap_quota _cap_quota = _initial_cap_quota; - struct Version { unsigned value; } _version { 0 }; + Version _version { 0 }; public: diff --git a/repos/gems/src/app/sculpt_manager/model/runtime_state.h b/repos/gems/src/app/sculpt_manager/model/runtime_state.h index bcf0da8950..a0be27e46b 100644 --- a/repos/gems/src/app/sculpt_manager/model/runtime_state.h +++ b/repos/gems/src/app/sculpt_manager/model/runtime_state.h @@ -30,6 +30,8 @@ class Sculpt::Runtime_state : public Runtime_info { public: + using Version = Child_state::Version; + struct Info { bool selected; @@ -45,6 +47,8 @@ class Sculpt::Runtime_state : public Runtime_info unsigned long assigned_caps; unsigned long avail_caps; + + Version version; }; private: @@ -57,11 +61,14 @@ class Sculpt::Runtime_state : public Runtime_info { 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 }; + Info info { .selected = false, + .tcb = false, + .tcb_updated = false, + .assigned_ram = 0, + .avail_ram = 0, + .assigned_caps = 0, + .avail_caps = 0, + .version = { 0 }}; bool abandoned_by_user = false; @@ -79,11 +86,27 @@ class Sculpt::Runtime_state : public Runtime_info struct Abandoned_child : Interface { Start_name const name; + Abandoned_child(Start_name const &name) : name(name) { }; }; Registry > _abandoned_children { }; + /** + * Child that was interactively restarted + */ + struct Restarted_child : Interface + { + Start_name const name; + + Version version; + + Restarted_child(Start_name const &name, Version const &version) + : name(name), version(version) { }; + }; + + Registry > _restarted_children { }; + /** * Child that was interactively launched */ @@ -115,20 +138,25 @@ class Sculpt::Runtime_state : public Runtime_info construction.construct(alloc, pkg_path, info, space); } - void gen_deploy_start_node(Xml_generator &xml) const + void gen_deploy_start_node(Xml_generator &xml, Runtime_state const &state) const { if (!launched) return; gen_named_node(xml, "start", name, [&] () { + Version const version = state.restarted_version(name); + + if (version.value > 0) + xml.attribute("version", version.value); + /* interactively constructed */ if (construction.constructed()) { xml.attribute("pkg", construction->path); - xml.attribute("xpos", construction->affinity_location.xpos()); - xml.attribute("ypos", construction->affinity_location.ypos()); - xml.attribute("width", construction->affinity_location.width()); + xml.attribute("xpos", construction->affinity_location.xpos()); + xml.attribute("ypos", construction->affinity_location.ypos()); + xml.attribute("width", construction->affinity_location.width()); xml.attribute("height", construction->affinity_location.height()); xml.node("route", [&] () { @@ -178,15 +206,17 @@ class Sculpt::Runtime_state : public Runtime_info 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()); + 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); + child.info.avail_caps = caps.attribute_value("avail", 0UL); } + + child.info.version.value = node.attribute_value("version", 0U); } static bool element_matches_xml_node(Child const &elem, Xml_node node) @@ -240,9 +270,28 @@ class Sculpt::Runtime_state : public Runtime_info 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; + } + + /** + * Return version of restarted child + * + * If the returned value is version 0, the child has not been + * restarted. + */ + Version restarted_version(Start_name const &name) const override + { + Version result { 0 }; + + _restarted_children.for_each([&] (Restarted_child const &child) { + if (!result.value && child.name == name) + result = child.version; }); + return result; } @@ -252,7 +301,7 @@ class Sculpt::Runtime_state : public Runtime_info 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); }); + child.gen_deploy_start_node(xml, *this); }); } Info info(Start_name const &name) const @@ -363,6 +412,30 @@ class Sculpt::Runtime_state : public Runtime_info new (_alloc) Registered(_abandoned_children, name); } + void restart(Start_name const &name) + { + /* determin current version from most recent state report */ + Version current_version { 0 }; + _children.for_each([&] (Child const &child) { + if (child.name == name) + current_version = child.info.version; }); + + Version const next_version { current_version.value + 1 }; + + bool already_restarted = false; + + _restarted_children.for_each([&] (Restarted_child &child) { + if (child.name == name) { + already_restarted = true; + child.version = next_version; + } + }); + + if (!already_restarted) + new (_alloc) + Registered(_restarted_children, name, next_version); + } + void launch(Start_name const &name, Path const &launcher) { new (_alloc) Registered(_launched_children, name, launcher); @@ -431,6 +504,9 @@ class Sculpt::Runtime_state : public Runtime_info _launched_children.for_each([&] (Launched_child &child) { destroy(_alloc, &child); }); + + _restarted_children.for_each([&] (Restarted_child &child) { + destroy(_alloc, &child); }); } }; diff --git a/repos/gems/src/app/sculpt_manager/runtime.h b/repos/gems/src/app/sculpt_manager/runtime.h index 5d2f58bb31..25dcc8a7e7 100644 --- a/repos/gems/src/app/sculpt_manager/runtime.h +++ b/repos/gems/src/app/sculpt_manager/runtime.h @@ -19,6 +19,7 @@ #include #include #include +#include namespace Sculpt { @@ -29,6 +30,8 @@ namespace Sculpt { struct Runtime_info : Interface { + using Version = Child_state::Version; + /** * Return true if specified child is present in the runtime subsystem */ @@ -36,6 +39,8 @@ namespace Sculpt { virtual bool abandoned_by_user(Start_name const &) const = 0; + virtual Version restarted_version(Start_name const &) const = 0; + virtual void gen_launched_deploy_start_nodes(Xml_generator &) const = 0; }; diff --git a/repos/gems/src/app/sculpt_manager/view/activatable_item.h b/repos/gems/src/app/sculpt_manager/view/activatable_item.h index df46f8e6e2..2061f62275 100644 --- a/repos/gems/src/app/sculpt_manager/view/activatable_item.h +++ b/repos/gems/src/app/sculpt_manager/view/activatable_item.h @@ -57,7 +57,7 @@ struct Sculpt::Activatable_item : Hoverable_item if (!_selected.valid() || !_activated.valid()) Hoverable_item::gen_button_attr(xml, id); - if (_selected.valid() && _selected == _hovered) + if (_selected.valid() && _selected == _hovered && _selected == id) xml.attribute("selected", "yes"); } };