Lumiera  0.pre.03
»edit your freedom«
subsystem-runner.hpp
Go to the documentation of this file.
1 /*
2  SUBSYSTEMRUNNER.hpp - helper for controlling execution of several dependent subsystems
3 
4  Copyright (C) Lumiera.org
5  2008, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 
23 
47 #ifndef LUMIERA_SUBSYSTEMRUNNER_H
48 #define LUMIERA_SUBSYSTEMRUNNER_H
49 
50 #include "lib/error.hpp"
51 #include "lib/util.hpp"
52 #include "lib/util-foreach.hpp"
53 #include "lib/format-string.hpp"
54 #include "common/subsys.hpp"
55 #include "lib/sync.hpp"
56 
57 #include <vector>
58 #include <string>
59 
60 
61 namespace lumiera {
62 
63  using lib::Sync;
64  using lib::RecursiveLock_Waitable;
65  using std::chrono_literals::operator ""s;
66  using std::vector;
67  using std::string;
68  using util::_Fmt;
69  using util::isnil;
70  using util::and_all;
71  using util::for_each;
72  using util::removeall;
73 
74  namespace {
77  const auto EMERGENCY_STOP = 5s;
78  }
79 
80 
81 
82  /*************************************************************************/
119  : public Sync<RecursiveLock_Waitable>
120  {
121  Option& opts_;
122  volatile bool emergency_;
123  vector<Subsys*> running_;
124 
125  bool isEmergency() { return emergency_; }
126  bool allDead(){ return isnil (running_); }
127 
128 
129  public:
130  SubsystemRunner (Option& opts)
131  : opts_{opts}
132  , emergency_{false}
133  { }
134 
135  void
136  maybeRun (Subsys& susy)
137  {
138  Lock guard{this};
139 
140  if (!susy.isRunning() && susy.shouldStart (opts_))
141  triggerStartup (&susy);
142  }
143 
144  void
145  shutdownAll()
146  {
147  Lock guard{this};
148  for_each (running_, [](Subsys* susy){ susy->triggerShutdown(); });
149  }
150 
151  void
152  triggerEmergency (bool cond)
153  {
154  Lock guard{this};
155  if (cond) emergency_= true;
156  }
157 
158  bool
159  wait()
160  {
161  Lock blocking{this, [&]{ return allDead() or isEmergency(); }};
162  if (isEmergency())
163  blocking.wait_for (EMERGENCY_STOP, [&]{ return allDead(); });
164  // ...prevent deadlock on emergency by limiting shutdown wait
165  return isEmergency();
166  }
167 
168 
169 
170  private:
171  void
172  triggerStartup (Subsys* susy)
173  {
174  auto isRunning = [](Subsys* susy){ return susy->isRunning(); };
175  auto triggerStart = [this](Subsys* susy){ triggerStartup(susy); };
176  auto termCallback = [this,susy]
177  (string* problem)
178  {
179  this->sigTerm (susy, problem);
180  };
181  REQUIRE (susy);
182  if (isRunning(susy)) return;
183 
184  INFO (subsystem, "Triggering startup of subsystem \"%s\"", cStr(*susy));
185 
186  for_each (susy->getPrerequisites(), triggerStart );
187  bool started = susy->start (opts_, termCallback);
188 
189  if (started)
190  {
191  if (isRunning(susy))
192  running_.push_back (susy); // now responsible for managing the started subsystem
193  else
194  throw error::Logic(_Fmt{"Subsystem %s failed to start"} % *susy);
195  }
196 
197  if (not and_all (susy->getPrerequisites(), isRunning ))
198  {
199  susy->triggerShutdown();
200  throw error::State(_Fmt{"Unable to start all prerequisites of Subsystem %s"} % *susy);
201  } }
202 
203  void
204  sigTerm (Subsys* susy, string* problem)
205  {
206  REQUIRE (susy);
207  Lock sync{this};
208  triggerEmergency(not isnil (problem));
209  INFO (subsystem, "Subsystem '%s' terminated.", cStr(*susy));
210  WARN_IF (not isnil(problem), subsystem, "Irregular shutdown caused by: %s", cStr(*problem));
211  ERROR_IF (susy->isRunning(), subsystem, "Subsystem '%s' signals termination, "
212  "without resetting running state", cStr(*susy));
213  removeall (running_, susy);
214  shutdownAll();
215  sync.notify_one();
216  }
217  };
218 
219 
220 
221 } // namespace lumiera
222 #endif
Facility for monitor object based locking.
Definition: sync.hpp:217
Dependencies and lifecycle of a partially independent Subsystem of the Application.
Definition: subsys.hpp:70
void sigTerm(Subsys *susy, string *problem)
CStr cStr(std::string const &rendered)
convenience shortcut: forced conversion to c-String via string.
Definition: symbol.hpp:68
Front-end for printf-style string template interpolation.
scoped guard to control the actual locking.
Definition: sync.hpp:234
bool and_all(CON const &elements, FUN function, P1 &&bind1, ARGS &&...args)
Accept binding for arbitrary function arguments.
Frontend for handling the Lumiera application commandline arguments.
Definition: option.hpp:77
virtual bool start(lumiera::Option &options, SigTerm)=0
attempt to bring up this subsystem up.
A front-end for using printf-style formatting.
Object Monitor based synchronisation.
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:199
virtual void triggerShutdown() noexcept=0
initiate termination of this subsystem.
Implementation helper for managing execution of a collection of subsystems, which may depend on one a...
Describing dependencies and lifecycle of the application&#39;s primary parts.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
void for_each(CON const &elements, FUN function, P1 &&bind1, ARGS &&...args)
Accept binding for arbitrary function arguments.
bool isRunning() noexcept
Definition: subsys.cpp:59
Lumiera error handling (C++ interface).
Lumiera public interface.
Definition: advice.cpp:113
virtual bool shouldStart(lumiera::Option &)=0
query application option state to determine if this subsystem should be activated.
const auto EMERGENCY_STOP
limited wait period for unwinding of remaining subsystems in case of an emergency shutdown...
SEQ::iterator removeall(SEQ &coll, typename SEQ::value_type const &val)
shortcut for removing all copies of an Element in any sequential collection
Definition: util.hpp:306
Perform operations "for each element" of a collection.