ApplicationLifecycle.h
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package ArmarXCore::core
17 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
18 * GNU General Public License
19 */
20
21#pragma once
22
23#include <condition_variable>
24#include <memory>
25#include <mutex>
26#include <shared_mutex>
27#include <string>
28
31#include "ApplicationStarter.h"
32#include "StopStrategy.h"
33
35{
37 using ApplicationLifecyclePtr = std::shared_ptr<ApplicationLifecycle>;
38
39 /**
40 * Serializes the actual spawning of applications.
41 *
42 * Every launch forks this process, and forking a GUI with Qt, Ice and the whole scenario model
43 * mapped is expensive. Starting a scenario asks every one of its applications to start at the
44 * same time, so without this the process would fork N times concurrently and stall - including
45 * the GUI thread. Held only around the spawn itself, never around a wait.
46 */
47 std::mutex& LaunchMutex();
48
49 /**
50 * Held *shared* by every application operation, and *exclusively* by global operations that
51 * act on all armarx processes at once, such as `armarx killAll`.
52 *
53 * This is what gives "kill everything" a defined outcome: a start requested while a kill-all is
54 * running waits for it and then runs, rather than racing it and leaving an application alive
55 * that was supposed to be killed.
56 */
57 std::shared_mutex& GlobalOperationMutex();
58
59 /**
60 * @class ApplicationLifecycle
61 * @ingroup exec
62 * @brief Owns every start and stop of one application - nothing else may launch or signal it.
63 *
64 * Callers do not execute operations, they *request an intent*. Requesting an intent supersedes
65 * any intent that has not been carried out yet, so pressing Start, Stop, Start, Stop in quick
66 * succession runs at most one superfluous operation and always ends in the state the last press
67 * asked for ("last intent wins"). Nothing is ever refused and nothing is ever aborted halfway:
68 * a fork/exec or a wait for a process to die always runs to completion, and a newer intent is
69 * only picked up at a phase boundary.
70 *
71 * The reason this class exists is that lifecycle state used to be owned by three classes at
72 * once - the Executor set a "write block" flag, the starter cleared it in a scope guard and the
73 * stop strategy cleared it as well. A restart's internal stop therefore released the flag that
74 * the restart itself still needed, which let a concurrent start launch a second process that
75 * the ScenarioManager then lost track of.
76 *
77 * Threading: a worker thread is spawned on demand and exits as soon as there is nothing left to
78 * do, so only applications that are currently being operated on cost a thread. The worker holds
79 * a shared_ptr to this object and to the application, so neither can be destroyed underneath it.
80 */
81 class ApplicationLifecycle : public std::enable_shared_from_this<ApplicationLifecycle>
82 {
83 public:
85 StatusManager statusManager);
86
87 /**
88 * Requests that the application be started, stopped or restarted, superseding any intent
89 * that has not been executed yet. Returns immediately; the work happens on a worker thread.
90 *
91 * @param intent what should happen to the application
92 * @param starter used to launch the application
93 * @param stopStrategy used to terminate the application
94 */
96 const ApplicationStarterPtr& starter,
97 const StopStrategyPtr& stopStrategy,
98 const std::string& commandLineParameters = "",
99 bool printOnly = false);
100
101 /**
102 * @return the intent the application is moving towards: the superseding one if a newer
103 * request has arrived, otherwise the one being executed, or None when idle.
104 */
106
107 /// @return whether an operation is requested or running.
108 bool busy() const;
109
110 /**
111 * Blocks until nothing is requested or running any more.
112 *
113 * Meant for callers that have to see the operation through before they can go away, such
114 * as the command line interface, which would otherwise exit while the worker is still
115 * launching. Returns immediately when the application is already idle.
116 */
117 void wait();
118
119 private:
120 struct Request
121 {
123 ApplicationStarterPtr starter;
124 StopStrategyPtr stopStrategy;
125 std::string commandLineParameters;
126 bool printOnly = false;
127 };
128
129 /// Worker loop: execute the pending request, then whatever superseded it, then exit.
130 void run();
131
132 void execute(const Request& request);
133 void executeStart(const Request& request);
134 void executeStop(const Request& request);
135 void executeRestart(const Request& request);
136
137 /// Loads the property definitions the launch needs (notably the config domain).
138 void prepare();
139 void launch(const Request& request);
140 void stopAndWait(const Request& request);
141
142 /// @return whether the application currently has a live process. Clears a stale pid.
143 bool isRunning();
144
145 /**
146 * @return whether a newer request has made the rest of the running operation pointless.
147 * Only a superseding Stop can do that: any other intent still wants the application up, so
148 * finishing the current operation first is both correct and cheaper.
149 */
150 bool supersededByStop() const;
151
152 /// Publishes effectiveIntent() on the application for the GUI. Call with `mutex` held.
153 void publishIntent();
154
156 StatusManager statusManager;
157
158 mutable std::mutex mutex;
159 std::condition_variable idleCondition;
160 Request pending;
161 Request current;
162 bool workerRunning = false;
163 };
164} // namespace ScenarioManager::Exec
Owns every start and stop of one application - nothing else may launch or signal it.
ApplicationLifecycle(Data_Structure::ApplicationInstancePtr application, StatusManager statusManager)
void request(Data_Structure::Intent intent, const ApplicationStarterPtr &starter, const StopStrategyPtr &stopStrategy, const std::string &commandLineParameters="", bool printOnly=false)
Requests that the application be started, stopped or restarted, superseding any intent that has not b...
void wait()
Blocks until nothing is requested or running any more.
Intent
What the ScenarioManager is currently trying to do with an application.
std::shared_ptr< ApplicationInstance > ApplicationInstancePtr
std::shared_mutex & GlobalOperationMutex()
Held shared by every application operation, and exclusively by global operations that act on all arma...
std::shared_ptr< StopStrategy > StopStrategyPtr
Definition Executor.h:48
std::mutex & LaunchMutex()
Serializes the actual spawning of applications.
std::shared_ptr< ApplicationStarter > ApplicationStarterPtr
std::shared_ptr< ApplicationLifecycle > ApplicationLifecyclePtr