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)
5  2008, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 */
13 
14 
38 #ifndef LUMIERA_SUBSYSTEMRUNNER_H
39 #define LUMIERA_SUBSYSTEMRUNNER_H
40 
41 #include "lib/error.hpp"
42 #include "lib/util.hpp"
43 #include "lib/util-foreach.hpp"
44 #include "lib/format-string.hpp"
45 #include "common/subsys.hpp"
46 #include "lib/sync.hpp"
47 
48 #include <vector>
49 #include <string>
50 
51 
52 namespace lumiera {
53 
54  using lib::Sync;
55  using lib::RecursiveLock_Waitable;
56  using std::chrono_literals::operator ""s;
57  using std::vector;
58  using std::string;
59  using util::_Fmt;
60  using util::isnil;
61  using util::and_all;
62  using util::for_each;
63  using util::removeall;
64 
65  namespace {
68  const auto EMERGENCY_STOP = 5s;
69  }
70 
71 
72 
73  /*************************************************************************/
110  : public Sync<RecursiveLock_Waitable>
111  {
112  Option& opts_;
113  volatile bool emergency_;
114  vector<Subsys*> running_;
115 
116  bool isEmergency() { return emergency_; }
117  bool allDead(){ return isnil (running_); }
118 
119 
120  public:
121  SubsystemRunner (Option& opts)
122  : opts_{opts}
123  , emergency_{false}
124  { }
125 
126  void
127  maybeRun (Subsys& susy)
128  {
129  Lock guard{this};
130 
131  if (!susy.isRunning() && susy.shouldStart (opts_))
132  triggerStartup (&susy);
133  }
134 
135  void
136  shutdownAll()
137  {
138  Lock guard{this};
139  for_each (running_, [](Subsys* susy){ susy->triggerShutdown(); });
140  }
141 
142  void
143  triggerEmergency (bool cond)
144  {
145  Lock guard{this};
146  if (cond) emergency_= true;
147  }
148 
149  bool
150  wait()
151  {
152  Lock blocking{this, [&]{ return allDead() or isEmergency(); }};
153  if (isEmergency())
154  blocking.wait_for (EMERGENCY_STOP, [&]{ return allDead(); });
155  // ...prevent deadlock on emergency by limiting shutdown wait
156  return isEmergency();
157  }
158 
159 
160 
161  private:
162  void
163  triggerStartup (Subsys* susy)
164  {
165  auto isRunning = [](Subsys* susy){ return susy->isRunning(); };
166  auto triggerStart = [this](Subsys* susy){ triggerStartup(susy); };
167  auto termCallback = [this,susy]
168  (string* problem)
169  {
170  this->sigTerm (susy, problem);
171  };
172  REQUIRE (susy);
173  if (isRunning(susy)) return;
174 
175  INFO (subsystem, "Triggering startup of subsystem \"%s\"", cStr(*susy));
176 
177  for_each (susy->getPrerequisites(), triggerStart );
178  bool started = susy->start (opts_, termCallback);
179 
180  if (started)
181  {
182  if (isRunning(susy))
183  running_.push_back (susy); // now responsible for managing the started subsystem
184  else
185  throw error::Logic(_Fmt{"Subsystem %s failed to start"} % *susy);
186  }
187 
188  if (not and_all (susy->getPrerequisites(), isRunning ))
189  {
190  susy->triggerShutdown();
191  throw error::State(_Fmt{"Unable to start all prerequisites of Subsystem %s"} % *susy);
192  } }
193 
194  void
195  sigTerm (Subsys* susy, string* problem)
196  {
197  REQUIRE (susy);
198  Lock sync{this};
199  triggerEmergency(not isnil (problem));
200  INFO (subsystem, "Subsystem '%s' terminated.", cStr(*susy));
201  WARN_IF (not isnil(problem), subsystem, "Irregular shutdown caused by: %s", cStr(*problem));
202  ERROR_IF (susy->isRunning(), subsystem, "Subsystem '%s' signals termination, "
203  "without resetting running state", cStr(*susy));
204  removeall (running_, susy);
205  shutdownAll();
206  sync.notify_one();
207  }
208  };
209 
210 
211 
212 } // namespace lumiera
213 #endif
Facility for monitor object based locking.
Definition: sync.hpp:209
Dependencies and lifecycle of a partially independent Subsystem of the Application.
Definition: subsys.hpp:61
void sigTerm(Subsys *susy, string *problem)
CStr cStr(std::string const &rendered)
convenience shortcut: forced conversion to c-String via string.
Definition: symbol.hpp:59
Front-end for printf-style string template interpolation.
scoped guard to control the actual locking.
Definition: sync.hpp:226
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:68
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:190
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:50
Lumiera error handling (C++ interface).
Lumiera public interface.
Definition: advice.cpp:104
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.