include/simdb/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
429 / 532
80.6%
Branches:
421 / 1006
41.8%
Functions:
89 / 90
98.9%

apps/AppManager.hpp
Line Branch Exec Source
1 // <AppManager.hpp> -*- C++ -*-
2
3 #pragma once
4
5 #include "simdb/Assert.hpp"
6 #include "simdb/apps/App.hpp"
7 #include "simdb/pipeline/PipelineManager.hpp"
8 #include "simdb/sqlite/DatabaseManager.hpp"
9 #include "simdb/utils/StreamFormatters.hpp"
10 #include "simdb/utils/ThreadSafeLogger.hpp"
11
12 #include <filesystem>
13 #include <iostream>
14 #include <map>
15 #include <set>
16 #include <string>
17 #include <string_view>
18 #include <vector>
19
20 #define PROFILE_APP_PHASE [[maybe_unused]] ScopedTimer timer(getDatabaseManager(), __FUNCTION__);
21
22 namespace simdb {
23
24 namespace utils {
25 template <typename, typename = void> struct has_nested_factory : std::false_type
26 {
27 };
28
29 template <typename T> struct has_nested_factory<T, std::void_t<typename T::AppFactory>> : std::true_type
30 {
31 };
32 } // namespace utils
33
34 class AppManager;
35 class AppManagers;
36
37 /*!
38 * \class AppRegistrationBase
39 *
40 * \brief Abstract registration that adds an app type to an AppManager when
41 * registerApp() is called. Used by AppManagers to register app types
42 * before createAppManager().
43 */
44 class AppRegistrationBase
45 {
46 public:
47 11 virtual ~AppRegistrationBase() = default;
48 /// \brief Register this app type with the given AppManager (e.g. enable it for later creation).
49 virtual void registerApp(AppManager* app_manager) const = 0;
50 };
51
52 /// \brief Type-specific registration for AppT; created by AppManagers::registerApp<AppT>().
53 template <typename AppT> class AppRegistration : public AppRegistrationBase
54 {
55 private:
56 11 AppRegistration() = default;
57 void registerApp(AppManager* app_manager) const override;
58 friend class AppManagers;
59 };
60
61 /*!
62 * \class AppManager
63 *
64 * \brief Registers, enables, instantiates, and manages the lifecycle of SimDB
65 * apps for one database. Obtainfrom AppManagers::createAppManager() or
66 * AppManagers::getAppManager().
67 */
68 class AppManager
69 {
70 public:
71 /// \brief Return the associated DatabaseManager.
72 46 DatabaseManager* getDatabaseManager() const { return db_mgr_; }
73
74 /// \brief Parameterize an app factory. Must be called before createEnabledApps().
75 /// Your app subclass must have a public nested class called "AppFactory",
76 /// inheriting publicly from simdb::AppFactoryBase. Your nested AppFactory
77 /// can have any signature it needs to accept all required app constructor
78 /// arguments.
79 ///
80 /// void parameterize(int x, float y)
81 /// {
82 /// ...
83 /// }
84 ///
85 /// For example:
86 ///
87 /// class MyApp : public simdb::App
88 /// {
89 /// public:
90 /// MyApp(simdb::DatabaseManager* db_mgr, int x, float y);
91 ///
92 /// class AppFactory : public simdb::AppFactoryBase
93 /// {
94 /// public:
95 /// void parameterize(int x, float y)
96 /// {
97 /// ctor_args_ = std::make_pair(x, y);
98 /// }
99 ///
100 /// simdb::App* createApp(simdb::DatabaseManager* db_mgr) override
101 /// {
102 /// const auto & [x, y] = ctor_args_;
103 /// return new MyApp(db_mgr, x, y);
104 /// }
105 ///
106 /// void defineSchema(simdb::Schema& schema) const override
107 /// {
108 /// ...
109 /// }
110 ///
111 /// private:
112 /// std::pair<int,float> ctor_args_;
113 /// };
114 /// };
115 ///
116 /// Then do this:
117 ///
118 /// // Parameterize before createEnabledApps()
119 ///
120 /// // How to parameterize all instances of your app:
121 /// app_mgr.parameterizeAppFactory<MyApp>(1 /*x*/, 2.2 /*y*/);
122 ///
123 /// // How to parameterize one instance of your app:
124 /// app_mgr.parameterizeAppFactoryInstance<MyApp>(1 /*inst num*/, 1
125 /// /*x*/, 2.2 /*y*/); app_mgr.parameterizeAppFactoryInstance<MyApp>(2
126 /// /*inst num*/, 3 /*x*/, 4.4 /*y*/);
127 ///
128 /// // Assume have the AppManager by now:
129 /// app_mgr.enableApp(MyApp::NAME);
130 /// app_mgr.enableApp(MyApp::NAME, 2 /*inst count*/);
131 ///
132 /// // Then continue and create the apps:
133 /// app_mgr.createEnabledApps();
134 ///
135 /// IMPORTANT: If you already configured specific app instance factories
136 /// with parameterizeAppFactoryInstance(), this method will throw away
137 /// all your factory configurations, and all instances will end up using
138 /// the same factory that is being "globally" configured right now (unless
139 /// you call parameterizeAppFactoryInstance() again). Only a warning will
140 /// be issued.
141 3 template <typename AppT, typename... Args> void parameterizeAppFactory([[maybe_unused]] Args&&... args)
142 {
143 if constexpr (utils::has_nested_factory<AppT>::value)
144 {
145
26/28
void simdb::AppManager::parameterizeAppFactory<UnregisteredAppWithFactory>():
✓ Branch 4 → 5 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 27 not taken.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 14 → 15 taken 1 time.
✓ Branch 18 → 19 taken 1 time.
✓ Branch 19 → 20 taken 1 time.
✓ Branch 20 → 21 taken 1 time.
✓ Branch 21 → 22 taken 1 time.
✓ Branch 22 → 23 taken 1 time.
✓ Branch 23 → 24 taken 1 time.
✓ Branch 25 → 26 taken 1 time.
void simdb::AppManager::parameterizeAppFactory<MyApp, int, double>(int&&, double&&):
✓ Branch 4 → 5 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 27 not taken.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 14 → 15 taken 1 time.
✓ Branch 18 → 19 taken 1 time.
✓ Branch 19 → 20 taken 1 time.
✓ Branch 20 → 21 taken 1 time.
✓ Branch 21 → 22 taken 1 time.
✓ Branch 22 → 23 taken 1 time.
✓ Branch 23 → 24 taken 1 time.
✓ Branch 25 → 26 taken 1 time.
8 simdb_assert(enabled(AppT::NAME), "You need to call enableApp() before parameterizing factories.");
146
147 ✗ auto& app_factories = getDefaultAppFactories_();
148
149
0/4
void simdb::AppManager::parameterizeAppFactory<UnregisteredAppWithFactory>():
✗ Branch 30 → 31 not taken.
✗ Branch 31 → 32 not taken.
void simdb::AppManager::parameterizeAppFactory<MyApp, int, double>(int&&, double&&):
✗ Branch 30 → 31 not taken.
✗ Branch 31 → 32 not taken.
✗ auto num_overwritten = app_factories[AppT::NAME].size();
150
0/4
void simdb::AppManager::parameterizeAppFactory<UnregisteredAppWithFactory>():
✗ Branch 35 → 36 not taken.
✗ Branch 35 → 46 not taken.
void simdb::AppManager::parameterizeAppFactory<MyApp, int, double>(int&&, double&&):
✗ Branch 35 → 36 not taken.
✗ Branch 35 → 46 not taken.
✗ if (num_overwritten > 0)
151 {
152 ✗ std::cout << "WARNING: Throwing away " << num_overwritten << " app factor"
153 << (num_overwritten > 1 ? "ies " : "y") << " and creating a new one for all apps of type "
154
0/4
void simdb::AppManager::parameterizeAppFactory<UnregisteredAppWithFactory>():
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
void simdb::AppManager::parameterizeAppFactory<MyApp, int, double>(int&&, double&&):
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
✗ << AppT::NAME << ". Was this intentional?\n";
155 }
156
157
0/4
void simdb::AppManager::parameterizeAppFactory<UnregisteredAppWithFactory>():
✗ Branch 48 → 49 not taken.
✗ Branch 49 → 50 not taken.
void simdb::AppManager::parameterizeAppFactory<MyApp, int, double>(int&&, double&&):
✗ Branch 48 → 49 not taken.
✗ Branch 49 → 50 not taken.
✗ app_factories[AppT::NAME].clear();
158
159 ✗ constexpr size_t global_instance_num = 0;
160 ✗ auto factory = getNestedAppFactory_<AppT>(global_instance_num);
161 ✗ factory->parameterize(std::forward<Args>(args)...);
162 } else
163 {
164
4/4
✓ Branch 4 → 5 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 6 → 7 taken 1 time.
✓ Branch 7 → 8 taken 1 time.
1 throw DBException("No nested class 'AppFactory' exists for app '") << AppT::NAME << "'.";
165 }
166 ✗ }
167
168 /// Parameterize an app factory. Call this after enableApp() but before
169 /// createEnabledApps(). Your app subclass must have a public nested class
170 /// called "AppFactory", inheriting publicly from simdb::AppFactoryBase.
171 template <typename AppT, typename... Args>
172 8 void parameterizeAppFactoryInstance(size_t instance_num, [[maybe_unused]] Args&&... args)
173 {
174 if constexpr (utils::has_nested_factory<AppT>::value)
175 {
176
29/42
void simdb::AppManager::parameterizeAppFactoryInstance<UnregisteredAppWithFactory>(unsigned long):
✓ Branch 4 → 5 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 27 not taken.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 14 → 15 taken 1 time.
✓ Branch 18 → 19 taken 1 time.
✓ Branch 19 → 20 taken 1 time.
✓ Branch 20 → 21 taken 1 time.
✓ Branch 21 → 22 taken 1 time.
✓ Branch 22 → 23 taken 1 time.
✓ Branch 23 → 24 taken 1 time.
✓ Branch 25 → 26 taken 1 time.
void simdb::AppManager::parameterizeAppFactoryInstance<MyApp, int const&, float const&>(unsigned long, int const&, float const&):
✓ Branch 4 → 5 taken 4 times.
✓ Branch 5 → 6 taken 4 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 27 taken 4 times.
✗ Branch 11 → 12 not taken.
✗ Branch 12 → 13 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 19 → 20 not taken.
✗ Branch 20 → 21 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 25 → 26 not taken.
void simdb::AppManager::parameterizeAppFactoryInstance<MyApp, int, double>(unsigned long, int&&, double&&):
✓ Branch 4 → 5 taken 2 times.
✓ Branch 5 → 6 taken 2 times.
✓ Branch 8 → 9 taken 2 times.
✗ Branch 8 → 27 not taken.
✓ Branch 11 → 12 taken 2 times.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 14 → 15 taken 2 times.
✓ Branch 18 → 19 taken 2 times.
✓ Branch 19 → 20 taken 2 times.
✓ Branch 20 → 21 taken 2 times.
✓ Branch 21 → 22 taken 2 times.
✓ Branch 22 → 23 taken 2 times.
✓ Branch 23 → 24 taken 2 times.
✓ Branch 25 → 26 taken 2 times.
20 simdb_assert(enabled(AppT::NAME), "You need to call enableApp() before parameterizing factories.");
177
178 4 std::cout << "Parameterizing '" << AppT::NAME << "' app, instance " << instance_num << "\n";
179 4 auto factory = getNestedAppFactory_<AppT>(instance_num);
180 4 factory->parameterize(std::forward<Args>(args)...);
181 } else
182 {
183
4/4
✓ Branch 4 → 5 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
✓ Branch 6 → 7 taken 1 time.
✓ Branch 7 → 8 taken 1 time.
1 throw DBException("No nested class 'AppFactory' exists for app '") << AppT::NAME << "'.";
184 }
185 4 }
186
187 /// Enable verbose mode for all apps in this AppManager.
188 12 void setVerbose(bool verbose = true)
189 {
190 12 verbose_ = verbose;
191
2/3
✓ Branch 2 → 3 taken 12 times.
✗ Branch 8 → 5 not taken.
✓ Branch 8 → 9 taken 12 times.
12 for (auto app : getApps_())
192 {
193 ✗ app->verbose_ = verbose_;
194 12 }
195 12 }
196
197 /// After parsing command line arguments or configuration files,
198 /// enable an app by its name. This will allow the app to be instantiated
199 /// and run during the simulation lifecycle.
200 ///
201 /// class MyApp : public simdb::App
202 /// {
203 /// public:
204 /// static constexpr const char* NAME = "MyApp";
205 /// // ...
206 /// };
207 ///
208 /// // Somewhere in your main function or config parsing code (meaning,
209 /// // after command line args are parsed and very early in the
210 /// simulation): simdb::AppManager::getInstance().enableApp(MyApp::NAME);
211 13 void enableApp(const std::string& app_name, size_t num_instances = 1)
212 {
213 13 getEnabledApps_()[app_name] = num_instances;
214 13 }
215
216 template <typename AppT> void enableApp(size_t num_instances = 1)
217 {
218 static_assert(std::is_base_of<App, AppT>::value, "AppT must derive from App");
219 enableApp(AppT::NAME, num_instances);
220 }
221
222 /// Check if your app is enabled (might not be instantiated yet).
223 28 bool enabled(const std::string& app_name) const
224 {
225 28 const auto& enabled_apps = getEnabledApps_();
226
1/1
✓ Branch 4 → 5 taken 28 times.
28 return enabled_apps.find(app_name) != enabled_apps.end();
227 }
228
229 /// Check if your app is enabled (might not be instantiated yet).
230 18 template <typename AppT> bool enabled() const
231 {
232 static_assert(std::is_base_of<App, AppT>::value, "AppT must derive from App");
233
12/12
bool simdb::AppManager::enabled<SimplePipeline>() const:
✓ Branch 4 → 5 taken 6 times.
✓ Branch 5 → 6 taken 6 times.
bool simdb::AppManager::enabled<PipelineSnooper>() const:
✓ Branch 4 → 5 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
bool simdb::AppManager::enabled<WatchedPipeline>() const:
✓ Branch 4 → 5 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
bool simdb::AppManager::enabled<DatabaseWatchdog>() const:
✓ Branch 4 → 5 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
bool simdb::AppManager::enabled<MyApp>() const:
✓ Branch 4 → 5 taken 4 times.
✓ Branch 5 → 6 taken 4 times.
bool simdb::AppManager::enabled<simdb::argos::ArgosCollector>() const:
✓ Branch 4 → 5 taken 5 times.
✓ Branch 5 → 6 taken 5 times.
36 return enabled(AppT::NAME);
234 }
235
236 /// See how many instances of a particular app are enabled.
237 1 size_t getEnabledAppInstances(const std::string& app_name) const
238 {
239
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 7 not taken.
1 return enabled(app_name) ? getEnabledApps_().at(app_name) : 0;
240 }
241
242 /// See how many instances of a particular app are enabled.
243
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
3 template <typename AppT> size_t getEnabledAppInstances() const { return getEnabledAppInstances(AppT::NAME); }
244
245 /// Get an instantiated app. You may only call this method if AppT had
246 /// exactly one instance configured:
247 /// AppManager::enableApp(AppT::NAME);
248 /// AppManager::enableApp(AppT::NAME, 1);
249 ///
250 /// For multi-instance apps, call getAppInstance<AppT>(instance_num)
251 /// (zero-based). Note that for single-instance apps, you can still call
252 /// getAppInstance<AppT>(0).
253 10 template <typename AppT> AppT* getApp()
254 {
255 static_assert(std::is_base_of<App, AppT>::value, "AppT must derive from App");
256
257
10/15
SimplePipeline* simdb::AppManager::getApp<SimplePipeline>():
✓ Branch 2 → 3 taken 2 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2 times.
PipelineSnooper* simdb::AppManager::getApp<PipelineSnooper>():
✓ Branch 2 → 3 taken 1 time.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
WatchedPipeline* simdb::AppManager::getApp<WatchedPipeline>():
✓ Branch 2 → 3 taken 1 time.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
DatabaseWatchdog* simdb::AppManager::getApp<DatabaseWatchdog>():
✓ Branch 2 → 3 taken 1 time.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
simdb::argos::ArgosCollector* simdb::AppManager::getApp<simdb::argos::ArgosCollector>():
✓ Branch 2 → 3 taken 5 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 5 times.
10 if (!enabled<AppT>())
258 {
259 ✗ return nullptr;
260 }
261
262 10 auto& enabled_apps = getEnabledApps_();
263
33/105
SimplePipeline* simdb::AppManager::getApp<SimplePipeline>():
✓ Branch 8 → 9 taken 2 times.
✓ Branch 9 → 10 taken 2 times.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 42 taken 1 time.
✓ Branch 15 → 16 taken 1 time.
✓ Branch 16 → 17 taken 1 time.
✓ Branch 18 → 19 taken 1 time.
✓ Branch 22 → 23 taken 1 time.
✓ Branch 23 → 24 taken 1 time.
✓ Branch 24 → 25 taken 1 time.
✓ Branch 25 → 26 taken 1 time.
✓ Branch 28 → 29 taken 1 time.
✓ Branch 29 → 30 taken 1 time.
✓ Branch 30 → 31 taken 1 time.
✓ Branch 31 → 32 taken 1 time.
✓ Branch 34 → 35 taken 1 time.
✓ Branch 35 → 36 taken 1 time.
✓ Branch 36 → 37 taken 1 time.
✓ Branch 37 → 38 taken 1 time.
✓ Branch 38 → 39 taken 1 time.
✓ Branch 40 → 41 taken 1 time.
PipelineSnooper* simdb::AppManager::getApp<PipelineSnooper>():
✓ Branch 8 → 9 taken 1 time.
✓ Branch 9 → 10 taken 1 time.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 42 taken 1 time.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 28 → 29 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 30 → 31 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 34 → 35 not taken.
✗ Branch 35 → 36 not taken.
✗ Branch 36 → 37 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 38 → 39 not taken.
✗ Branch 40 → 41 not taken.
WatchedPipeline* simdb::AppManager::getApp<WatchedPipeline>():
✓ Branch 8 → 9 taken 1 time.
✓ Branch 9 → 10 taken 1 time.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 42 taken 1 time.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 28 → 29 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 30 → 31 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 34 → 35 not taken.
✗ Branch 35 → 36 not taken.
✗ Branch 36 → 37 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 38 → 39 not taken.
✗ Branch 40 → 41 not taken.
DatabaseWatchdog* simdb::AppManager::getApp<DatabaseWatchdog>():
✓ Branch 8 → 9 taken 1 time.
✓ Branch 9 → 10 taken 1 time.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 42 taken 1 time.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 28 → 29 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 30 → 31 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 34 → 35 not taken.
✗ Branch 35 → 36 not taken.
✗ Branch 36 → 37 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 38 → 39 not taken.
✗ Branch 40 → 41 not taken.
simdb::argos::ArgosCollector* simdb::AppManager::getApp<simdb::argos::ArgosCollector>():
✓ Branch 8 → 9 taken 5 times.
✓ Branch 9 → 10 taken 5 times.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 42 taken 5 times.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 28 → 29 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 30 → 31 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 34 → 35 not taken.
✗ Branch 35 → 36 not taken.
✗ Branch 36 → 37 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 38 → 39 not taken.
✗ Branch 40 → 41 not taken.
24 simdb_assert(enabled_apps.at(AppT::NAME) <= 1, "Need to call getAppInstance<AppT>(instance_num) since "
264 << "the '" << AppT::NAME << "' app was configured to have "
265 << enabled_apps.at(AppT::NAME) << " instances.");
266
267 // Look for instance name
268
10/10
SimplePipeline* simdb::AppManager::getApp<SimplePipeline>():
✓ Branch 44 → 45 taken 1 time.
✓ Branch 45 → 46 taken 1 time.
PipelineSnooper* simdb::AppManager::getApp<PipelineSnooper>():
✓ Branch 44 → 45 taken 1 time.
✓ Branch 45 → 46 taken 1 time.
WatchedPipeline* simdb::AppManager::getApp<WatchedPipeline>():
✓ Branch 44 → 45 taken 1 time.
✓ Branch 45 → 46 taken 1 time.
DatabaseWatchdog* simdb::AppManager::getApp<DatabaseWatchdog>():
✓ Branch 44 → 45 taken 1 time.
✓ Branch 45 → 46 taken 1 time.
simdb::argos::ArgosCollector* simdb::AppManager::getApp<simdb::argos::ArgosCollector>():
✓ Branch 44 → 45 taken 5 times.
✓ Branch 45 → 46 taken 5 times.
9 std::string instance_name = AppT::NAME + std::string("-0");
269
5/5
SimplePipeline* simdb::AppManager::getApp<SimplePipeline>():
✓ Branch 48 → 49 taken 1 time.
PipelineSnooper* simdb::AppManager::getApp<PipelineSnooper>():
✓ Branch 48 → 49 taken 1 time.
WatchedPipeline* simdb::AppManager::getApp<WatchedPipeline>():
✓ Branch 48 → 49 taken 1 time.
DatabaseWatchdog* simdb::AppManager::getApp<DatabaseWatchdog>():
✓ Branch 48 → 49 taken 1 time.
simdb::argos::ArgosCollector* simdb::AppManager::getApp<simdb::argos::ArgosCollector>():
✓ Branch 48 → 49 taken 5 times.
9 auto it = apps_.find(instance_name);
270
5/10
SimplePipeline* simdb::AppManager::getApp<SimplePipeline>():
✓ Branch 51 → 52 taken 1 time.
✗ Branch 51 → 81 not taken.
PipelineSnooper* simdb::AppManager::getApp<PipelineSnooper>():
✓ Branch 51 → 52 taken 1 time.
✗ Branch 51 → 81 not taken.
WatchedPipeline* simdb::AppManager::getApp<WatchedPipeline>():
✓ Branch 51 → 52 taken 1 time.
✗ Branch 51 → 81 not taken.
DatabaseWatchdog* simdb::AppManager::getApp<DatabaseWatchdog>():
✓ Branch 51 → 52 taken 1 time.
✗ Branch 51 → 81 not taken.
simdb::argos::ArgosCollector* simdb::AppManager::getApp<simdb::argos::ArgosCollector>():
✓ Branch 51 → 52 taken 5 times.
✗ Branch 51 → 81 not taken.
9 if (it != apps_.end())
271 {
272
5/10
SimplePipeline* simdb::AppManager::getApp<SimplePipeline>():
✓ Branch 54 → 55 taken 1 time.
✗ Branch 54 → 56 not taken.
PipelineSnooper* simdb::AppManager::getApp<PipelineSnooper>():
✓ Branch 54 → 55 taken 1 time.
✗ Branch 54 → 56 not taken.
WatchedPipeline* simdb::AppManager::getApp<WatchedPipeline>():
✓ Branch 54 → 55 taken 1 time.
✗ Branch 54 → 56 not taken.
DatabaseWatchdog* simdb::AppManager::getApp<DatabaseWatchdog>():
✓ Branch 54 → 55 taken 1 time.
✗ Branch 54 → 56 not taken.
simdb::argos::ArgosCollector* simdb::AppManager::getApp<simdb::argos::ArgosCollector>():
✓ Branch 54 → 55 taken 5 times.
✗ Branch 54 → 56 not taken.
9 auto app = dynamic_cast<AppT*>(it->second.get());
273
5/75
SimplePipeline* simdb::AppManager::getApp<SimplePipeline>():
✗ Branch 57 → 58 not taken.
✓ Branch 57 → 80 taken 1 time.
✗ Branch 60 → 61 not taken.
✗ Branch 61 → 62 not taken.
✗ Branch 63 → 64 not taken.
✗ Branch 67 → 68 not taken.
✗ Branch 68 → 69 not taken.
✗ Branch 69 → 70 not taken.
✗ Branch 71 → 72 not taken.
✗ Branch 72 → 73 not taken.
✗ Branch 73 → 74 not taken.
✗ Branch 74 → 75 not taken.
✗ Branch 75 → 76 not taken.
✗ Branch 76 → 77 not taken.
✗ Branch 78 → 79 not taken.
PipelineSnooper* simdb::AppManager::getApp<PipelineSnooper>():
✗ Branch 57 → 58 not taken.
✓ Branch 57 → 80 taken 1 time.
✗ Branch 60 → 61 not taken.
✗ Branch 61 → 62 not taken.
✗ Branch 63 → 64 not taken.
✗ Branch 67 → 68 not taken.
✗ Branch 68 → 69 not taken.
✗ Branch 69 → 70 not taken.
✗ Branch 71 → 72 not taken.
✗ Branch 72 → 73 not taken.
✗ Branch 73 → 74 not taken.
✗ Branch 74 → 75 not taken.
✗ Branch 75 → 76 not taken.
✗ Branch 76 → 77 not taken.
✗ Branch 78 → 79 not taken.
WatchedPipeline* simdb::AppManager::getApp<WatchedPipeline>():
✗ Branch 57 → 58 not taken.
✓ Branch 57 → 80 taken 1 time.
✗ Branch 60 → 61 not taken.
✗ Branch 61 → 62 not taken.
✗ Branch 63 → 64 not taken.
✗ Branch 67 → 68 not taken.
✗ Branch 68 → 69 not taken.
✗ Branch 69 → 70 not taken.
✗ Branch 71 → 72 not taken.
✗ Branch 72 → 73 not taken.
✗ Branch 73 → 74 not taken.
✗ Branch 74 → 75 not taken.
✗ Branch 75 → 76 not taken.
✗ Branch 76 → 77 not taken.
✗ Branch 78 → 79 not taken.
DatabaseWatchdog* simdb::AppManager::getApp<DatabaseWatchdog>():
✗ Branch 57 → 58 not taken.
✓ Branch 57 → 80 taken 1 time.
✗ Branch 60 → 61 not taken.
✗ Branch 61 → 62 not taken.
✗ Branch 63 → 64 not taken.
✗ Branch 67 → 68 not taken.
✗ Branch 68 → 69 not taken.
✗ Branch 69 → 70 not taken.
✗ Branch 71 → 72 not taken.
✗ Branch 72 → 73 not taken.
✗ Branch 73 → 74 not taken.
✗ Branch 74 → 75 not taken.
✗ Branch 75 → 76 not taken.
✗ Branch 76 → 77 not taken.
✗ Branch 78 → 79 not taken.
simdb::argos::ArgosCollector* simdb::AppManager::getApp<simdb::argos::ArgosCollector>():
✗ Branch 57 → 58 not taken.
✓ Branch 57 → 80 taken 5 times.
✗ Branch 60 → 61 not taken.
✗ Branch 61 → 62 not taken.
✗ Branch 63 → 64 not taken.
✗ Branch 67 → 68 not taken.
✗ Branch 68 → 69 not taken.
✗ Branch 69 → 70 not taken.
✗ Branch 71 → 72 not taken.
✗ Branch 72 → 73 not taken.
✗ Branch 73 → 74 not taken.
✗ Branch 74 → 75 not taken.
✗ Branch 75 → 76 not taken.
✗ Branch 76 → 77 not taken.
✗ Branch 78 → 79 not taken.
9 simdb_assert(app, "App of type " << AppT::NAME << " is not of type " << typeid(AppT).name());
274 9 return app;
275 }
276
277 ✗ return nullptr;
278 9 }
279
280 /// Get an instantiated app. Call this method for multi-instance apps.
281 8 template <typename AppT> AppT* getAppInstance(size_t instance_num)
282 {
283 static_assert(std::is_base_of<App, AppT>::value, "AppT must derive from App");
284
285
4/6
SimplePipeline* simdb::AppManager::getAppInstance<SimplePipeline>(unsigned long):
✓ Branch 2 → 3 taken 4 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 4 times.
MyApp* simdb::AppManager::getAppInstance<MyApp>(unsigned long):
✓ Branch 2 → 3 taken 4 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 4 times.
8 if (!enabled<AppT>())
286 {
287 ✗ return nullptr;
288 }
289
290 // Look for instance name
291
8/8
SimplePipeline* simdb::AppManager::getAppInstance<SimplePipeline>(unsigned long):
✓ Branch 5 → 6 taken 4 times.
✓ Branch 8 → 9 taken 4 times.
✓ Branch 9 → 10 taken 4 times.
✓ Branch 10 → 11 taken 4 times.
MyApp* simdb::AppManager::getAppInstance<MyApp>(unsigned long):
✓ Branch 5 → 6 taken 4 times.
✓ Branch 8 → 9 taken 4 times.
✓ Branch 9 → 10 taken 4 times.
✓ Branch 10 → 11 taken 4 times.
24 std::string instance_name = AppT::NAME + std::string("-") + std::to_string(instance_num);
292
2/2
SimplePipeline* simdb::AppManager::getAppInstance<SimplePipeline>(unsigned long):
✓ Branch 15 → 16 taken 4 times.
MyApp* simdb::AppManager::getAppInstance<MyApp>(unsigned long):
✓ Branch 15 → 16 taken 4 times.
8 auto it = apps_.find(instance_name);
293
2/4
SimplePipeline* simdb::AppManager::getAppInstance<SimplePipeline>(unsigned long):
✓ Branch 18 → 19 taken 4 times.
✗ Branch 18 → 48 not taken.
MyApp* simdb::AppManager::getAppInstance<MyApp>(unsigned long):
✓ Branch 18 → 19 taken 4 times.
✗ Branch 18 → 48 not taken.
8 if (it != apps_.end())
294 {
295
2/4
SimplePipeline* simdb::AppManager::getAppInstance<SimplePipeline>(unsigned long):
✓ Branch 21 → 22 taken 4 times.
✗ Branch 21 → 23 not taken.
MyApp* simdb::AppManager::getAppInstance<MyApp>(unsigned long):
✓ Branch 21 → 22 taken 4 times.
✗ Branch 21 → 23 not taken.
8 auto app = dynamic_cast<AppT*>(it->second.get());
296
2/30
SimplePipeline* simdb::AppManager::getAppInstance<SimplePipeline>(unsigned long):
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 47 taken 4 times.
✗ Branch 27 → 28 not taken.
✗ Branch 28 → 29 not taken.
✗ Branch 30 → 31 not taken.
✗ Branch 34 → 35 not taken.
✗ Branch 35 → 36 not taken.
✗ Branch 36 → 37 not taken.
✗ Branch 38 → 39 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 40 → 41 not taken.
✗ Branch 41 → 42 not taken.
✗ Branch 42 → 43 not taken.
✗ Branch 43 → 44 not taken.
✗ Branch 45 → 46 not taken.
MyApp* simdb::AppManager::getAppInstance<MyApp>(unsigned long):
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 47 taken 4 times.
✗ Branch 27 → 28 not taken.
✗ Branch 28 → 29 not taken.
✗ Branch 30 → 31 not taken.
✗ Branch 34 → 35 not taken.
✗ Branch 35 → 36 not taken.
✗ Branch 36 → 37 not taken.
✗ Branch 38 → 39 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 40 → 41 not taken.
✗ Branch 41 → 42 not taken.
✗ Branch 42 → 43 not taken.
✗ Branch 43 → 44 not taken.
✗ Branch 45 → 46 not taken.
8 simdb_assert(app, "App of type " << AppT::NAME << " is not of type " << typeid(AppT).name());
297 8 return app;
298 }
299
300 ✗ return nullptr;
301 8 }
302
303 /// Optionally call this method after initializePipelines(), but before
304 /// openPipelines(). This will reduce the number of non-database threads
305 /// to the minimum across all app pipelines.
306 ///
307 /// Note that you can either call minimizeThreads() OR minimizeThreads(app1,
308 /// app2, ...) but you cannot call both.
309 void minimizeThreads()
310 {
311 simdb_assert(pipeline_mgr_, "Pipeline manager not set - did you call initializePipelines()?");
312 pipeline_mgr_->minimizeThreads();
313 }
314
315 /// Optionally call this method after initializePipelines(), but before
316 /// openPipelines(). This will share the minimum number of non-database
317 /// threads across the given apps' pipelines.
318 template <typename... Apps> void minimizeThreads(const App* app, Apps&&... rest)
319 {
320 simdb_assert(pipeline_mgr_, "Pipeline manager not set - did you call initializePipelines()?");
321 pipeline_mgr_->minimizeThreads(app, std::forward<Apps>(rest)...);
322 }
323
324 /// \brief Set the order in which app lifecycle hooks are invoked.
325 ///
326 /// postInit(), preTeardown(), and postTeardown() are called in this order
327 /// across apps. \p ordered_app_names lists base app names (e.g. \c "App2",
328 /// \c "App1"); hooks run first for all instances of the first name, then
329 /// the second, etc. Apps not listed are invoked after the listed apps, in
330 /// map order. Call before createEnabledApps() (or at least before the
331 /// hooks run). Pass an empty vector to clear ordering and use default
332 /// (map) order.
333 void setAppOrderingForHooks(const std::vector<std::string>& ordered_app_names)
334 {
335 hook_ordering_ = ordered_app_names;
336 }
337
338 /// \brief Set the order in which app lifecycle hooks are invoked (variadic).
339 ///
340 /// Same as setAppOrderingForHooks(vector) but takes a list of app names,
341 /// e.g. setAppOrderingForHooks("App2", "App1") so App2's hooks run before
342 /// App1's.
343 template <typename... AppNames>
344 void setAppOrderingForHooks(const std::string_view app_name, AppNames&&... app_names)
345 {
346 hook_ordering_.clear();
347 hook_ordering_.reserve(1 + sizeof...(AppNames));
348 hook_ordering_.emplace_back(app_name);
349 if constexpr (sizeof...(AppNames) > 0)
350 {
351 (hook_ordering_.emplace_back(std::forward<AppNames>(app_names)), ...);
352 }
353 }
354
355 private:
356 /// AppManagers are associated 1-to-1 with a DatabaseManager.
357 12 AppManager(DatabaseManager* db_mgr, ThreadSafeLogger* app_logger = nullptr) :
358 12 db_mgr_(db_mgr),
359 12 app_logger_(app_logger)
360 {
361 12 }
362
363 /// AppManager only to be instantiated by simdb::AppManagers
364 friend class AppManagers;
365
366 /// App registration only to be done by simdb::AppRegistration<AppT>
367 template <typename AppT> friend class AppRegistration;
368
369 /// Register an app as early as possible (doesn't create it yet).
370 13 template <typename AppT> void registerApp_()
371 {
372 if constexpr (utils::has_nested_factory<AppT>::value)
373 {
374 // We don't need to create the nested factory object now.
375 // When apps provide a nested factory, they are supposed
376 // to call parameterizeApp<T>() / parameterizeAppInstance<T>()
377 // before calling createEnabledApps().
378 //
379 // If they forget to parameterize their own custom-factory app,
380 // we will know it since we won't have the factory. These factories
381 // are created in the parameterize*() calls.
382 3 return;
383 } else
384 {
385 10 auto& app_factories = getDefaultAppFactories_();
386
15/75
void simdb::AppManager::registerApp_<SimplePipeline>():
✓ Branch 6 → 7 taken 2 times.
✓ Branch 7 → 8 taken 2 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 31 taken 2 times.
✗ Branch 14 → 15 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 26 → 27 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 29 → 30 not taken.
void simdb::AppManager::registerApp_<PipelineSnooper>():
✓ Branch 6 → 7 taken 1 time.
✓ Branch 7 → 8 taken 1 time.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 31 taken 1 time.
✗ Branch 14 → 15 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 26 → 27 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 29 → 30 not taken.
void simdb::AppManager::registerApp_<WatchedPipeline>():
✓ Branch 6 → 7 taken 1 time.
✓ Branch 7 → 8 taken 1 time.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 31 taken 1 time.
✗ Branch 14 → 15 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 26 → 27 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 29 → 30 not taken.
void simdb::AppManager::registerApp_<DatabaseWatchdog>():
✓ Branch 6 → 7 taken 1 time.
✓ Branch 7 → 8 taken 1 time.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 31 taken 1 time.
✗ Branch 14 → 15 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 26 → 27 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 29 → 30 not taken.
void simdb::AppManager::registerApp_<simdb::argos::ArgosCollector>():
✓ Branch 6 → 7 taken 5 times.
✓ Branch 7 → 8 taken 5 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 31 taken 5 times.
✗ Branch 14 → 15 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 26 → 27 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 29 → 30 not taken.
30 simdb_assert(app_factories.find(AppT::NAME) == app_factories.end(),
387 "App already registered: " << AppT::NAME);
388
389 10 constexpr size_t global_instance_num = 0;
390
15/15
void simdb::AppManager::registerApp_<SimplePipeline>():
✓ Branch 33 → 34 taken 2 times.
✓ Branch 34 → 35 taken 2 times.
✓ Branch 35 → 36 taken 2 times.
void simdb::AppManager::registerApp_<PipelineSnooper>():
✓ Branch 33 → 34 taken 1 time.
✓ Branch 34 → 35 taken 1 time.
✓ Branch 35 → 36 taken 1 time.
void simdb::AppManager::registerApp_<WatchedPipeline>():
✓ Branch 33 → 34 taken 1 time.
✓ Branch 34 → 35 taken 1 time.
✓ Branch 35 → 36 taken 1 time.
void simdb::AppManager::registerApp_<DatabaseWatchdog>():
✓ Branch 33 → 34 taken 1 time.
✓ Branch 34 → 35 taken 1 time.
✓ Branch 35 → 36 taken 1 time.
void simdb::AppManager::registerApp_<simdb::argos::ArgosCollector>():
✓ Branch 33 → 34 taken 5 times.
✓ Branch 34 → 35 taken 5 times.
✓ Branch 35 → 36 taken 5 times.
10 auto& factory = app_factories[AppT::NAME][global_instance_num];
391
5/5
void simdb::AppManager::registerApp_<SimplePipeline>():
✓ Branch 38 → 39 taken 2 times.
void simdb::AppManager::registerApp_<PipelineSnooper>():
✓ Branch 38 → 39 taken 1 time.
void simdb::AppManager::registerApp_<WatchedPipeline>():
✓ Branch 38 → 39 taken 1 time.
void simdb::AppManager::registerApp_<DatabaseWatchdog>():
✓ Branch 38 → 39 taken 1 time.
void simdb::AppManager::registerApp_<simdb::argos::ArgosCollector>():
✓ Branch 38 → 39 taken 5 times.
10 factory = std::make_shared<AppFactory<AppT>>();
392 }
393 10 }
394
395 /// Get all Apps that belong to the given database.
396 36 std::vector<App*> getApps_()
397 {
398 36 std::vector<App*> apps;
399
2/2
✓ Branch 12 → 5 taken 32 times.
✓ Branch 12 → 13 taken 36 times.
68 for (const auto& [key, app] : apps_)
400 {
401
1/1
✓ Branch 9 → 10 taken 32 times.
32 apps.push_back(app.get());
402 }
403 36 return apps;
404 ✗ }
405
406 /// Get apps in the order to use for lifecycle hooks (postInit, preTeardown, postTeardown).
407 24 std::vector<App*> getAppsInHookOrder_()
408 {
409
1/2
✓ Branch 3 → 4 taken 24 times.
✗ Branch 3 → 6 not taken.
24 if (hook_ordering_.empty())
410 {
411
1/1
✓ Branch 4 → 5 taken 24 times.
24 return getApps_();
412 }
413 // Group by base app name (instance name is "BaseName-0", "BaseName-1", ...).
414 ✗ std::map<std::string, std::vector<App*>> by_base;
415
0/2
✗ Branch 22 → 9 not taken.
✗ Branch 22 → 23 not taken.
✗ for (const auto& [instance_name, app] : apps_)
416 {
417 ✗ auto dash = instance_name.find_last_of('-');
418
0/4
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 15 not taken.
✗ Branch 14 → 16 not taken.
✗ Branch 15 → 16 not taken.
✗ std::string base = (dash != std::string::npos) ? instance_name.substr(0, dash) : instance_name;
419
0/2
✗ Branch 16 → 17 not taken.
✗ Branch 18 → 19 not taken.
✗ by_base[base].push_back(app.get());
420 ✗ }
421 ✗ std::vector<App*> out;
422
0/1
✗ Branch 25 → 26 not taken.
✗ out.reserve(apps_.size());
423
0/2
✗ Branch 44 → 28 not taken.
✗ Branch 44 → 45 not taken.
✗ for (const std::string& base : hook_ordering_)
424 {
425
0/1
✗ Branch 29 → 30 not taken.
✗ auto it = by_base.find(base);
426
0/2
✗ Branch 32 → 33 not taken.
✗ Branch 32 → 42 not taken.
✗ if (it != by_base.end())
427 {
428
0/2
✗ Branch 40 → 36 not taken.
✗ Branch 40 → 41 not taken.
✗ for (App* app : it->second)
429 {
430
0/1
✗ Branch 37 → 38 not taken.
✗ out.push_back(app);
431 }
432 }
433 }
434
435 // Now append any apps that were not given explicit hook ordering
436
0/2
✗ Branch 62 → 47 not taken.
✗ Branch 62 → 63 not taken.
✗ for (auto& [_, app] : apps_)
437 {
438
0/3
✗ Branch 54 → 55 not taken.
✗ Branch 56 → 57 not taken.
✗ Branch 56 → 60 not taken.
✗ if (std::find(out.begin(), out.end(), app.get()) == out.end())
439 {
440
0/1
✗ Branch 58 → 59 not taken.
✗ out.push_back(app.get());
441 }
442 }
443
444
0/1
✗ Branch 63 → 64 not taken.
✗ auto apps = getApps_();
445
0/12
✗ Branch 66 → 67 not taken.
✗ Branch 69 → 70 not taken.
✗ Branch 70 → 71 not taken.
✗ Branch 73 → 74 not taken.
✗ Branch 73 → 84 not taken.
✗ Branch 75 → 76 not taken.
✗ Branch 76 → 77 not taken.
✗ Branch 77 → 78 not taken.
✗ Branch 78 → 79 not taken.
✗ Branch 79 → 80 not taken.
✗ Branch 80 → 81 not taken.
✗ Branch 82 → 83 not taken.
✗ simdb_assert(std::set<App*>(out.begin(), out.end()) == std::set<App*>(apps.begin(), apps.end()));
446 ✗ return out;
447 ✗ }
448
449 /// Call after command line args and config files are parsed.
450 12 void createEnabledApps_()
451 {
452
2/2
✓ Branch 33 → 5 taken 13 times.
✓ Branch 33 → 34 taken 12 times.
25 for (const auto& [app_name, num_instances] : getEnabledApps_())
453 {
454
2/2
✓ Branch 30 → 9 taken 17 times.
✓ Branch 30 → 31 taken 13 times.
30 for (size_t instance_num = 0; instance_num < num_instances; ++instance_num)
455 {
456
1/1
✓ Branch 9 → 10 taken 17 times.
17 auto factory = getAppFactory_(app_name, instance_num, false /*do not create*/);
457
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 17 times.
17 if (!factory)
458 {
459 ✗ continue;
460 }
461
462
1/1
✓ Branch 12 → 13 taken 17 times.
17 App* app = factory->createApp(db_mgr_);
463 17 app->app_logger_ = app_logger_;
464 17 app->instance_ = instance_num;
465 17 app->verbose_ = verbose_;
466
4/4
✓ Branch 13 → 14 taken 17 times.
✓ Branch 16 → 17 taken 17 times.
✓ Branch 17 → 18 taken 17 times.
✓ Branch 18 → 19 taken 17 times.
51 std::string instance_name = app_name + std::string("-") + std::to_string(instance_num);
467
1/1
✓ Branch 24 → 25 taken 17 times.
17 apps_[instance_name] = std::unique_ptr<App>(app);
468 17 }
469 }
470 12 }
471
472 /// Create app-specific schemas for the instantiated apps.
473 9 void createSchemas_()
474 {
475
2/2
✓ Branch 4 → 5 taken 9 times.
✓ Branch 6 → 7 taken 9 times.
9 PROFILE_APP_PHASE
476
477 // TODO cnyce: We are currently creating the apps first, then creating the schemas.
478 // There is no reason to do this in that order, and we should create the schemas
479 // first. The doc (book) was written assuming schemas come first; leave the doc
480 // that way and change the C++ code to match. Call out that you can freely write
481 // to your tables in defineSchema right in your App's constructor. Then ensure
482 // that the createEnabledApps call is inside a safeTransaction.
483
1/1
✓ Branch 11 → 12 taken 9 times.
9 db_mgr_->safeTransaction([&]() {
484
2/2
✓ Branch 34 → 4 taken 13 times.
✓ Branch 34 → 35 taken 9 times.
22 for (const auto& [app_name, app] : apps_)
485 {
486
1/1
✓ Branch 7 → 8 taken 13 times.
13 auto user_app_name = app_name;
487 13 auto idx = user_app_name.find_last_of("-");
488
1/9
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 20 taken 13 times.
✗ Branch 11 → 12 not taken.
✗ Branch 12 → 13 not taken.
✗ Branch 13 → 14 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 18 → 19 not taken.
13 simdb_assert(idx != std::string::npos);
489
1/1
✓ Branch 20 → 21 taken 13 times.
13 user_app_name = user_app_name.substr(0, idx);
490
491
1/1
✓ Branch 23 → 24 taken 13 times.
13 auto tmp_substr = app_name.substr(idx);
492
1/1
✓ Branch 24 → 25 taken 13 times.
13 auto instance_num = static_cast<size_t>(std::stoull(tmp_substr));
493
1/1
✓ Branch 25 → 26 taken 13 times.
13 AppFactoryBase* factory = getAppFactory_(user_app_name, instance_num);
494
495 // TODO cnyce: We should consider automatically prepending "<app name>$"
496 // before every table in the app schema. With a growing number of apps,
497 // and SimDB living in open source, this will eventually trip someone
498 // up from e.g. using a generic table like "SimMetadata" which collides.
499 // You will have to document that the app name might have restrictions
500 // that will match whatever restrictions SQLite puts on table names.
501
1/1
✓ Branch 26 → 27 taken 13 times.
13 Schema app_schema;
502
1/1
✓ Branch 27 → 28 taken 13 times.
13 factory->defineSchema(app_schema);
503
1/1
✓ Branch 28 → 29 taken 13 times.
13 db_mgr_->appendSchema(app_schema);
504 13 }
505 9 });
506 9 }
507
508 /// Call this after command line args and config files are parsed.
509 6 void postInit_(int argc, char** argv)
510 {
511
2/2
✓ Branch 4 → 5 taken 6 times.
✓ Branch 6 → 7 taken 6 times.
6 PROFILE_APP_PHASE
512
513
2/2
✓ Branch 10 → 11 taken 6 times.
✓ Branch 11 → 12 taken 6 times.
6 db_mgr_->safeTransaction([&]() {
514
3/3
✓ Branch 2 → 3 taken 6 times.
✓ Branch 9 → 5 taken 6 times.
✓ Branch 9 → 10 taken 6 times.
12 for (auto app : getAppsInHookOrder_())
515 {
516
1/1
✓ Branch 6 → 7 taken 6 times.
6 app->postInit(argc, argv);
517 6 }
518 6 });
519 6 }
520
521 /// Call this once after postInit(). This will create all pipelines
522 /// for all enabled apps, but it will not open the threads yet.
523 9 void initializePipelines_()
524 {
525
2/2
✓ Branch 4 → 5 taken 9 times.
✓ Branch 6 → 7 taken 9 times.
9 PROFILE_APP_PHASE
526
527
1/12
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 29 taken 9 times.
✗ Branch 13 → 14 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 20 → 21 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 27 → 28 not taken.
9 simdb_assert(!pipeline_mgr_, "Pipelines already open");
528
529
1/1
✓ Branch 29 → 30 taken 9 times.
9 pipeline_mgr_ = std::make_unique<pipeline::PipelineManager>(db_mgr_);
530
2/2
✓ Branch 42 → 34 taken 13 times.
✓ Branch 42 → 43 taken 9 times.
22 for (const auto& [app_name, app] : apps_)
531 {
532
1/1
✓ Branch 39 → 40 taken 13 times.
13 app->createPipeline(pipeline_mgr_.get());
533 }
534
535 // Print final pipeline configurations.
536
3/3
✓ Branch 43 → 44 taken 9 times.
✓ Branch 45 → 46 taken 9 times.
✓ Branch 46 → 47 taken 9 times.
9 std::cout << "\nSimDB app pipeline configuration for database '" << db_mgr_->getDatabaseFilePath() << "':\n";
537
3/3
✓ Branch 48 → 49 taken 9 times.
✓ Branch 72 → 51 taken 13 times.
✓ Branch 72 → 73 taken 9 times.
22 for (auto pipeline : pipeline_mgr_->getPipelines())
538 {
539
4/4
✓ Branch 52 → 53 taken 13 times.
✓ Branch 53 → 54 taken 13 times.
✓ Branch 54 → 55 taken 13 times.
✓ Branch 55 → 56 taken 13 times.
13 std::cout << "---- Pipeline: " << pipeline->getName() << "\n";
540
3/3
✓ Branch 57 → 58 taken 13 times.
✓ Branch 68 → 60 taken 32 times.
✓ Branch 68 → 69 taken 13 times.
45 for (auto& [stage_name, stage] : pipeline->getOrderedStages())
541 {
542
3/3
✓ Branch 63 → 64 taken 32 times.
✓ Branch 64 → 65 taken 32 times.
✓ Branch 65 → 66 taken 32 times.
32 std::cout << "------ Stage: " << stage_name << "\n";
543 13 }
544 9 }
545
546
1/1
✓ Branch 74 → 75 taken 9 times.
9 std::cout << std::endl;
547 9 }
548
549 /// Call this once after initializePipelines() (and after minimizeThreads()
550 /// if you called that too).
551 9 void openPipelines_()
552 {
553
2/2
✓ Branch 4 → 5 taken 9 times.
✓ Branch 6 → 7 taken 9 times.
9 PROFILE_APP_PHASE
554
555
1/12
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 29 taken 9 times.
✗ Branch 13 → 14 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 20 → 21 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 27 → 28 not taken.
9 simdb_assert(pipeline_mgr_, "Pipeline manager not set - did you call initializePipelines()?");
556
1/1
✓ Branch 30 → 31 taken 9 times.
9 pipeline_mgr_->openPipelines();
557 9 }
558
559 /// This method is to be called after the main simulation loop ends.
560 /// All running apps' pipelines will be flushed, and all threads
561 /// will be torn down.
562 9 void postSimLoopTeardown_()
563 {
564
2/2
✓ Branch 4 → 5 taken 9 times.
✓ Branch 6 → 7 taken 9 times.
9 PROFILE_APP_PHASE
565
566
3/3
✓ Branch 9 → 10 taken 9 times.
✓ Branch 16 → 12 taken 13 times.
✓ Branch 16 → 17 taken 9 times.
22 for (auto app : getAppsInHookOrder_())
567 {
568
1/1
✓ Branch 13 → 14 taken 13 times.
13 app->preTeardown();
569 9 }
570
571
1/2
✓ Branch 19 → 20 taken 9 times.
✗ Branch 19 → 22 not taken.
9 if (pipeline_mgr_)
572 {
573
1/1
✓ Branch 21 → 22 taken 9 times.
9 pipeline_mgr_->postSimLoopTeardown();
574 }
575
576
1/1
✓ Branch 24 → 25 taken 9 times.
9 db_mgr_->safeTransaction([&]() {
577
3/3
✓ Branch 2 → 3 taken 9 times.
✓ Branch 9 → 5 taken 13 times.
✓ Branch 9 → 10 taken 9 times.
22 for (auto app : getAppsInHookOrder_())
578 {
579
1/1
✓ Branch 6 → 7 taken 13 times.
13 app->postTeardown();
580 9 }
581
582 // TODO cnyce: We should associate the DatabaseManager and
583 // the TinyStrings so that it gets flushed automatically.
584 // This will let us document that postSimLoopTeardown()
585 // is the only thing you have to call in the event of
586 // an exception/abort/terminate during simulation.
587 9 });
588 9 }
589
590 /// Delete all instantiated apps. This may be needed since AppManager
591 /// is a singleton and your simulator might want to call app destructors
592 /// before the AppManager itself is destroyed on program exit.
593 10 void destroyAllApps_()
594 {
595 10 pipeline_mgr_.reset();
596 10 apps_.clear();
597 10 }
598
599 using app_factories_t = std::map<std::string, // App name
600 std::map<size_t, // App instance
601 std::shared_ptr<AppFactoryBase>>>; // Factory
602
603 /// Get a map for all registered app factories.
604 10 app_factories_t& getDefaultAppFactories_() { return default_app_factories_; }
605
606 /// Get a map for all registered app factories.
607 36 const app_factories_t& getDefaultAppFactories_() const { return default_app_factories_; }
608
609 /// Factories for apps with default constructors.
610 app_factories_t default_app_factories_;
611
612 /// Factories for apps that have a nested AppFactory class.
613 app_factories_t nested_app_factories_;
614
615 /// Access an app factory.
616 template <typename AppT>
617 4 typename AppT::AppFactory* getNestedAppFactory_(size_t instance_num, bool create_if_needed = true)
618 {
619
1/4
UnregisteredAppWithFactory::AppFactory* simdb::AppManager::getNestedAppFactory_<UnregisteredAppWithFactory>(unsigned long, bool):
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 48 not taken.
MyApp::AppFactory* simdb::AppManager::getNestedAppFactory_<MyApp>(unsigned long, bool):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 48 taken 4 times.
4 if (!create_if_needed)
620 {
621
0/4
UnregisteredAppWithFactory::AppFactory* simdb::AppManager::getNestedAppFactory_<UnregisteredAppWithFactory>(unsigned long, bool):
✗ Branch 5 → 6 not taken.
✗ Branch 6 → 7 not taken.
MyApp::AppFactory* simdb::AppManager::getNestedAppFactory_<MyApp>(unsigned long, bool):
✗ Branch 5 → 6 not taken.
✗ Branch 6 → 7 not taken.
✗ auto it = nested_app_factories_.find(AppT::NAME);
622
0/4
UnregisteredAppWithFactory::AppFactory* simdb::AppManager::getNestedAppFactory_<UnregisteredAppWithFactory>(unsigned long, bool):
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 13 not taken.
MyApp::AppFactory* simdb::AppManager::getNestedAppFactory_<MyApp>(unsigned long, bool):
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 13 not taken.
✗ if (it == nested_app_factories_.end())
623 {
624 ✗ return nullptr;
625 }
626
627
0/2
UnregisteredAppWithFactory::AppFactory* simdb::AppManager::getNestedAppFactory_<UnregisteredAppWithFactory>(unsigned long, bool):
✗ Branch 14 → 15 not taken.
MyApp::AppFactory* simdb::AppManager::getNestedAppFactory_<MyApp>(unsigned long, bool):
✗ Branch 14 → 15 not taken.
✗ auto it2 = it->second.find(instance_num);
628
0/4
UnregisteredAppWithFactory::AppFactory* simdb::AppManager::getNestedAppFactory_<UnregisteredAppWithFactory>(unsigned long, bool):
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 20 not taken.
MyApp::AppFactory* simdb::AppManager::getNestedAppFactory_<MyApp>(unsigned long, bool):
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 20 not taken.
✗ if (it2 == it->second.end())
629 {
630 ✗ return nullptr;
631 }
632
633
0/4
UnregisteredAppWithFactory::AppFactory* simdb::AppManager::getNestedAppFactory_<UnregisteredAppWithFactory>(unsigned long, bool):
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
MyApp::AppFactory* simdb::AppManager::getNestedAppFactory_<MyApp>(unsigned long, bool):
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
✗ auto factory = dynamic_cast<typename AppT::AppFactory*>(it2->second.get());
634
0/28
UnregisteredAppWithFactory::AppFactory* simdb::AppManager::getNestedAppFactory_<UnregisteredAppWithFactory>(unsigned long, bool):
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 46 not taken.
✗ Branch 28 → 29 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 35 → 36 not taken.
✗ Branch 36 → 37 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 38 → 39 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 40 → 41 not taken.
✗ Branch 41 → 42 not taken.
✗ Branch 42 → 43 not taken.
✗ Branch 44 → 45 not taken.
MyApp::AppFactory* simdb::AppManager::getNestedAppFactory_<MyApp>(unsigned long, bool):
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 46 not taken.
✗ Branch 28 → 29 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 35 → 36 not taken.
✗ Branch 36 → 37 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 38 → 39 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 40 → 41 not taken.
✗ Branch 41 → 42 not taken.
✗ Branch 42 → 43 not taken.
✗ Branch 44 → 45 not taken.
✗ simdb_assert(factory, "Failed to downcast app factory for '" << AppT::NAME << "'.");
635
636 ✗ return factory;
637 }
638
639
3/6
UnregisteredAppWithFactory::AppFactory* simdb::AppManager::getNestedAppFactory_<UnregisteredAppWithFactory>(unsigned long, bool):
✗ Branch 50 → 51 not taken.
✗ Branch 51 → 52 not taken.
✗ Branch 52 → 53 not taken.
MyApp::AppFactory* simdb::AppManager::getNestedAppFactory_<MyApp>(unsigned long, bool):
✓ Branch 50 → 51 taken 4 times.
✓ Branch 51 → 52 taken 4 times.
✓ Branch 52 → 53 taken 4 times.
8 auto& factory = nested_app_factories_[AppT::NAME][instance_num];
640
1/4
UnregisteredAppWithFactory::AppFactory* simdb::AppManager::getNestedAppFactory_<UnregisteredAppWithFactory>(unsigned long, bool):
✗ Branch 56 → 57 not taken.
✗ Branch 56 → 61 not taken.
MyApp::AppFactory* simdb::AppManager::getNestedAppFactory_<MyApp>(unsigned long, bool):
✓ Branch 56 → 57 taken 4 times.
✗ Branch 56 → 61 not taken.
4 if (!factory)
641 {
642
1/2
UnregisteredAppWithFactory::AppFactory* simdb::AppManager::getNestedAppFactory_<UnregisteredAppWithFactory>(unsigned long, bool):
✗ Branch 57 → 58 not taken.
MyApp::AppFactory* simdb::AppManager::getNestedAppFactory_<MyApp>(unsigned long, bool):
✓ Branch 57 → 58 taken 4 times.
4 factory = std::make_shared<typename AppT::AppFactory>();
643 }
644
645
1/4
UnregisteredAppWithFactory::AppFactory* simdb::AppManager::getNestedAppFactory_<UnregisteredAppWithFactory>(unsigned long, bool):
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
MyApp::AppFactory* simdb::AppManager::getNestedAppFactory_<MyApp>(unsigned long, bool):
✓ Branch 62 → 63 taken 4 times.
✗ Branch 62 → 64 not taken.
4 return dynamic_cast<typename AppT::AppFactory*>(factory.get());
646 }
647
648 36 AppFactoryBase* getAppFactory_(const std::string& app_name, size_t instance_num, bool must_exist = true) const
649 {
650
1/1
✓ Branch 2 → 3 taken 36 times.
36 auto app_factories = getAllAppFactories_();
651
652
1/1
✓ Branch 3 → 4 taken 36 times.
36 auto it = app_factories.find(app_name);
653
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 28 taken 36 times.
36 if (it == app_factories.end())
654 {
655
0/13
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 27 not taken.
✗ Branch 10 → 11 not taken.
✗ Branch 11 → 12 not taken.
✗ Branch 13 → 14 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 19 → 20 not taken.
✗ Branch 20 → 21 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ Branch 25 → 26 not taken.
✗ simdb_assert(!must_exist, "No factory exists for app: " << app_name);
656 ✗ return nullptr;
657 }
658
659
1/1
✓ Branch 29 → 30 taken 36 times.
36 auto it2 = it->second.find(instance_num);
660
2/2
✓ Branch 33 → 34 taken 6 times.
✓ Branch 33 → 60 taken 30 times.
36 if (it2 == it->second.end())
661 {
662 // If we looked for a specific app instance factory (non-zero)
663 // and could not find it, look for a "global" factory with
664 // instance_num 0 for this app.
665
1/2
✓ Branch 34 → 35 taken 6 times.
✗ Branch 34 → 37 not taken.
6 if (instance_num > 0)
666 {
667 6 constexpr size_t global_instance_num = 0;
668
1/1
✓ Branch 35 → 36 taken 6 times.
6 return getAppFactory_(app_name, global_instance_num, must_exist);
669 }
670
671
0/15
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 59 not taken.
✗ Branch 40 → 41 not taken.
✗ Branch 41 → 42 not taken.
✗ Branch 43 → 44 not taken.
✗ Branch 47 → 48 not taken.
✗ Branch 48 → 49 not taken.
✗ Branch 49 → 50 not taken.
✗ Branch 50 → 51 not taken.
✗ Branch 51 → 52 not taken.
✗ Branch 52 → 53 not taken.
✗ Branch 53 → 54 not taken.
✗ Branch 54 → 55 not taken.
✗ Branch 55 → 56 not taken.
✗ Branch 57 → 58 not taken.
✗ simdb_assert(!must_exist, "No factory exists for instance " << instance_num << " for app: " << app_name);
672 ✗ return nullptr;
673 }
674
675 30 return it2->second.get();
676 36 }
677
678 /// Return a union of the global and local app factories.
679 36 app_factories_t getAllAppFactories_() const
680 {
681
1/1
✓ Branch 3 → 4 taken 36 times.
36 auto default_app_factories = getDefaultAppFactories_();
682
1/1
✓ Branch 4 → 5 taken 36 times.
72 return combineFactories_(default_app_factories, nested_app_factories_);
683 36 }
684
685 /// Merge two app_factories_t together, verifying that they
686 /// have no overlap.
687 36 app_factories_t combineFactories_(const app_factories_t& global, const app_factories_t& local) const
688 {
689 36 app_factories_t result = global;
690
691
2/2
✓ Branch 34 → 5 taken 4 times.
✓ Branch 34 → 35 taken 36 times.
40 for (const auto& [app_name, local_instances] : local)
692 {
693
1/1
✓ Branch 8 → 9 taken 4 times.
4 auto& result_instances = result[app_name];
694
695
2/2
✓ Branch 31 → 11 taken 6 times.
✓ Branch 31 → 32 taken 4 times.
10 for (const auto& [instance_id, factory] : local_instances)
696 {
697
1/1
✓ Branch 14 → 15 taken 6 times.
6 const auto [it, inserted] = result_instances.emplace(instance_id, factory);
698
699
1/2
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 29 taken 6 times.
6 if (!inserted)
700 {
701
0/2
✗ Branch 20 → 21 not taken.
✗ Branch 21 → 22 not taken.
✗ throw std::logic_error("Duplicate AppFactory entry for app '" + app_name + "', instance " +
702
0/3
✗ Branch 19 → 20 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 23 → 24 not taken.
✗ std::to_string(instance_id));
703 }
704 }
705 }
706
707 36 return result;
708 ✗ }
709
710 /// Instantiated apps (implicitly enabled).
711 /// Key is the App's NAME static member, or NAME-<instance>
712 /// for apps with multiple instances (one-based).
713 std::map<std::string, std::unique_ptr<App>> apps_;
714
715 /// Optional order for lifecycle hooks (postInit, preTeardown, postTeardown).
716 /// Empty means use default (map) order.
717 std::vector<std::string> hook_ordering_;
718
719 /// Enabled apps (may or may not be instantiated).
720 /// Key is the App's NAME static member.
721 /// Value is the number of instances to create.
722 35 std::map<std::string, size_t>& getEnabledApps_() { return enabled_apps_; }
723
724 /// Enabled apps (may or may not be instantiated).
725 /// Key is the App's NAME static member.
726 /// Value is the number of instances to create.
727 29 const std::map<std::string, size_t>& getEnabledApps_() const { return enabled_apps_; }
728
729 /// Enabled apps and their app instance count.
730 std::map<std::string, size_t> enabled_apps_;
731
732 /// Associated database.
733 DatabaseManager* db_mgr_ = nullptr;
734
735 /// All pipelines and threads are managed by PipelineManager.
736 std::unique_ptr<pipeline::PipelineManager> pipeline_mgr_;
737
738 /// App logger (thread-safe). Owned by AppManagers.
739 ThreadSafeLogger* app_logger_ = nullptr;
740
741 /// Verbose flag.
742 bool verbose_ = false;
743
744 /// RAII timer to measure the performance of various app setup/teardown
745 /// phases.
746 class ScopedTimer
747 {
748 public:
749 42 ScopedTimer(const DatabaseManager* db_mgr, const std::string& block_name) :
750 42 start_(std::chrono::high_resolution_clock::now()),
751 42 block_name_(block_name)
752 {
753
1/1
✓ Branch 5 → 6 taken 42 times.
42 auto db_filepath = db_mgr->getDatabaseFilePath();
754
5/5
✓ Branch 6 → 7 taken 42 times.
✓ Branch 7 → 8 taken 42 times.
✓ Branch 8 → 9 taken 42 times.
✓ Branch 9 → 10 taken 42 times.
✓ Branch 10 → 11 taken 42 times.
42 std::cout << "SimDB: Entering " << block_name << " for database: " << db_filepath << "\n";
755 42 }
756
757 42 ~ScopedTimer()
758 {
759 42 [[maybe_unused]] ios_format_saver fmt_saver(std::cout);
760
761 42 auto end = std::chrono::high_resolution_clock::now();
762 42 std::chrono::duration<double> dur = end - start_;
763 42 auto us = std::chrono::duration_cast<std::chrono::microseconds>(dur).count();
764
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 17 taken 42 times.
42 if (us >= 1000000)
765 {
766 ✗ auto sec = (double)us / 1000000;
767 ✗ std::cout << "SimDB: Completed " << block_name_ << " in ";
768 ✗ std::cout << std::fixed << std::setprecision(2) << sec << " seconds.\n";
769
2/2
✓ Branch 17 → 18 taken 21 times.
✓ Branch 17 → 26 taken 21 times.
42 } else if (us >= 1000)
770 {
771 21 auto milli = (double)us / 1000;
772 21 std::cout << "SimDB: Completed " << block_name_ << " in ";
773 21 std::cout << std::fixed << std::setprecision(0) << milli << " milliseconds.\n";
774 } else
775 {
776 21 auto micro = (double)us;
777 21 std::cout << "SimDB: Completed " << block_name_ << " in ";
778 21 std::cout << std::fixed << std::setprecision(0) << micro << " microseconds.\n";
779 }
780 42 }
781
782 private:
783 std::chrono::high_resolution_clock::time_point start_;
784 std::string block_name_;
785 };
786 };
787
788 13 template <typename AppT> void AppRegistration<AppT>::registerApp(AppManager* app_manager) const
789 {
790 13 app_manager->registerApp_<AppT>();
791 13 }
792
793 /*!
794 * \class AppManagers
795 *
796 * \brief Singleton-like holder for all DatabaseManagers and AppManagers. Call
797 * registerApp<AppT>() for each app type before createAppManager(); then
798 * createAppManager(db_file), createEnabledApps(), createSchemas(),
799 * postInit(), initializePipelines(), openPipelines(). Use getAppManager()
800 * or getDatabaseManager() to access managers. Obtain from
801 * AppManagers::getInstance() or AppRegistrations::getInstance().
802 */
803 class AppManagers
804 {
805 public:
806 /// \brief Register an app type; must be called before createAppManager()
807 /// \note You need to call this method for all apps you might end up
808 /// creating **before** calling createAppManager(). As soon as
809 /// createAppManager() is called the first time, you can no
810 /// longer call registerApp().
811 11 template <typename AppT> void registerApp()
812 {
813
6/78
void simdb::AppManagers::registerApp<SimplePipeline>():
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 22 taken 2 times.
✗ Branch 5 → 6 not taken.
✗ Branch 6 → 7 not taken.
✗ Branch 8 → 9 not taken.
✗ Branch 12 → 13 not taken.
✗ Branch 13 → 14 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 20 → 21 not taken.
void simdb::AppManagers::registerApp<PipelineSnooper>():
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 22 taken 1 time.
✗ Branch 5 → 6 not taken.
✗ Branch 6 → 7 not taken.
✗ Branch 8 → 9 not taken.
✗ Branch 12 → 13 not taken.
✗ Branch 13 → 14 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 20 → 21 not taken.
void simdb::AppManagers::registerApp<WatchedPipeline>():
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 22 taken 1 time.
✗ Branch 5 → 6 not taken.
✗ Branch 6 → 7 not taken.
✗ Branch 8 → 9 not taken.
✗ Branch 12 → 13 not taken.
✗ Branch 13 → 14 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 20 → 21 not taken.
void simdb::AppManagers::registerApp<DatabaseWatchdog>():
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 22 taken 1 time.
✗ Branch 5 → 6 not taken.
✗ Branch 6 → 7 not taken.
✗ Branch 8 → 9 not taken.
✗ Branch 12 → 13 not taken.
✗ Branch 13 → 14 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 20 → 21 not taken.
void simdb::AppManagers::registerApp<MyApp>():
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 22 taken 1 time.
✗ Branch 5 → 6 not taken.
✗ Branch 6 → 7 not taken.
✗ Branch 8 → 9 not taken.
✗ Branch 12 → 13 not taken.
✗ Branch 13 → 14 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 20 → 21 not taken.
void simdb::AppManagers::registerApp<simdb::argos::ArgosCollector>():
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 22 taken 5 times.
✗ Branch 5 → 6 not taken.
✗ Branch 6 → 7 not taken.
✗ Branch 8 → 9 not taken.
✗ Branch 12 → 13 not taken.
✗ Branch 13 → 14 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 20 → 21 not taken.
11 simdb_assert(!app_registration_locked_,
814 "No more apps can be registered since createAppManager() " << "has already been called.");
815
12/24
void simdb::AppManagers::registerApp<SimplePipeline>():
✓ Branch 22 → 23 taken 2 times.
✓ Branch 24 → 25 taken 2 times.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
void simdb::AppManagers::registerApp<PipelineSnooper>():
✓ Branch 22 → 23 taken 1 time.
✓ Branch 24 → 25 taken 1 time.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
void simdb::AppManagers::registerApp<WatchedPipeline>():
✓ Branch 22 → 23 taken 1 time.
✓ Branch 24 → 25 taken 1 time.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
void simdb::AppManagers::registerApp<DatabaseWatchdog>():
✓ Branch 22 → 23 taken 1 time.
✓ Branch 24 → 25 taken 1 time.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
void simdb::AppManagers::registerApp<MyApp>():
✓ Branch 22 → 23 taken 1 time.
✓ Branch 24 → 25 taken 1 time.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
void simdb::AppManagers::registerApp<simdb::argos::ArgosCollector>():
✓ Branch 22 → 23 taken 5 times.
✓ Branch 24 → 25 taken 5 times.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
11 app_registrations_.emplace_back(new AppRegistration<AppT>());
816 11 }
817
818 /// If you want all apps to share a global thread-safe logger, call this
819 /// method before createAppManager().
820 ///
821 /// Every line written to the logger will start with the given prefix.
822 void useThreadSafeLogger(const std::string& prefix = "[simdb-log]")
823 {
824 simdb_assert(accepting_logger_requests_, "No longer accepting thread-safe logger requests");
825 app_logger_ = std::make_unique<ThreadSafeLogger>(prefix);
826 }
827
828 /// If you want all apps to share a global thread-safe file logger,
829 /// call this method before createAppManager().
830 ///
831 /// Every line written to the logger will start with the given prefix.
832 void useThreadSafeFileLogger(const std::string& filename)
833 {
834 simdb_assert(accepting_logger_requests_, "No longer accepting thread-safe logger requests");
835 app_logger_ = std::make_unique<ThreadSafeFileLogger>(filename);
836 }
837
838 /// Enable verbose mode for all apps in all AppManager's.
839 1 void setVerbose(bool verbose = true)
840 {
841 1 verbose_ = verbose;
842
1/2
✗ Branch 10 → 4 not taken.
✓ Branch 10 → 11 taken 1 time.
1 for (auto& [app_mgr, _] : db_mgrs_by_app_mgr_)
843 {
844
0/1
✗ Branch 7 → 8 not taken.
✗ app_mgr->setVerbose(verbose_);
845 }
846 1 }
847
848 /// Create a new AppManager with a new database.
849 ///
850 /// Pass in new_db=true to overwrite existing database, or new_db=false to use
851 /// the existing database on disk. If new_db=false and the db_file does not
852 /// exist, throws an exception.
853 ///
854 /// Throws if an AppManager for this database already exists.
855 12 AppManager& createAppManager(const std::string& db_file, bool new_db = true)
856 {
857 12 accepting_logger_requests_ = false;
858
859
2/14
✓ Branch 3 → 4 taken 12 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 25 taken 12 times.
✗ Branch 8 → 9 not taken.
✗ Branch 9 → 10 not taken.
✗ Branch 11 → 12 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 19 → 20 not taken.
✗ Branch 20 → 21 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 23 → 24 not taken.
12 simdb_assert(app_mgrs_by_db_file_.find(db_file) == app_mgrs_by_db_file_.end(),
860 "AppManager already exists for database: " << db_file);
861
862
3/23
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 30 taken 12 times.
✗ Branch 26 → 27 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 28 → 29 not taken.
✗ Branch 28 → 30 not taken.
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 12 times.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 53 taken 12 times.
✗ Branch 36 → 37 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 43 → 44 not taken.
✗ Branch 44 → 45 not taken.
✗ Branch 45 → 46 not taken.
✗ Branch 46 → 47 not taken.
✗ Branch 47 → 48 not taken.
✗ Branch 48 → 49 not taken.
✗ Branch 49 → 50 not taken.
✗ Branch 51 → 52 not taken.
✗ Branch 107 → 108 not taken.
✗ Branch 107 → 109 not taken.
12 simdb_assert(new_db || std::filesystem::exists(db_file),
863 "Cannot reuse database since it doesn't exist: " << db_file);
864
865
3/5
✓ Branch 53 → 54 taken 12 times.
✓ Branch 55 → 56 taken 12 times.
✓ Branch 56 → 57 taken 12 times.
✗ Branch 129 → 130 not taken.
✗ Branch 129 → 131 not taken.
12 std::shared_ptr<DatabaseManager> db_mgr(new DatabaseManager(db_file, new_db));
866
2/4
✓ Branch 58 → 59 taken 12 times.
✓ Branch 62 → 63 taken 12 times.
✗ Branch 132 → 133 not taken.
✗ Branch 132 → 134 not taken.
12 std::shared_ptr<AppManager> app_mgr(new AppManager(db_mgr.get(), app_logger_.get()));
867
868
1/1
✓ Branch 64 → 65 taken 12 times.
12 app_mgr->setVerbose(verbose_);
869
870
1/1
✓ Branch 65 → 66 taken 12 times.
12 db_mgrs_by_db_file_[db_file] = db_mgr;
871
1/1
✓ Branch 67 → 68 taken 12 times.
12 app_mgrs_by_db_file_[db_file] = app_mgr;
872
873
1/1
✓ Branch 70 → 71 taken 12 times.
12 app_mgrs_by_db_mgr_[db_mgr.get()] = app_mgr;
874
1/1
✓ Branch 73 → 74 taken 12 times.
12 db_mgrs_by_app_mgr_[app_mgr.get()] = db_mgr;
875
876
2/2
✓ Branch 83 → 77 taken 13 times.
✓ Branch 83 → 84 taken 12 times.
25 for (const auto& app_reg : app_registrations_)
877 {
878
1/1
✓ Branch 80 → 81 taken 13 times.
13 app_reg->registerApp(app_mgr.get());
879 }
880 12 app_registration_locked_ = true;
881
1/1
✓ Branch 84 → 85 taken 12 times.
24 return *app_mgrs_by_db_file_[db_file];
882 12 }
883
884 /// Get an existing AppManager with a database filename / filepath.
885 /// Throws if createAppManager() was never called with this database.
886 1 AppManager& getAppManager(const std::string& db_file)
887 {
888
13/14
✓ Branch 3 → 4 taken 1 time.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 25 not taken.
✓ Branch 8 → 9 taken 1 time.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 11 → 12 taken 1 time.
✓ Branch 15 → 16 taken 1 time.
✓ Branch 16 → 17 taken 1 time.
✓ Branch 17 → 18 taken 1 time.
✓ Branch 18 → 19 taken 1 time.
✓ Branch 19 → 20 taken 1 time.
✓ Branch 20 → 21 taken 1 time.
✓ Branch 21 → 22 taken 1 time.
✓ Branch 23 → 24 taken 1 time.
3 simdb_assert(app_mgrs_by_db_file_.find(db_file) != app_mgrs_by_db_file_.end(),
889 "AppManager does not exist for this database: " << db_file);
890 ✗ return *app_mgrs_by_db_file_[db_file];
891 }
892
893 /// If there is only one AppManager, return it. Otherwise throw.
894 2 AppManager& getAppManager()
895 {
896
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 8 taken 1 time.
2 if (app_mgrs_by_db_file_.size() == 1)
897 {
898 1 return *app_mgrs_by_db_file_.begin()->second;
899 }
900
901
1/1
✓ Branch 10 → 11 taken 1 time.
1 throw DBException("Cannot call getAppManager() since there are ")
902
3/3
✓ Branch 12 → 13 taken 1 time.
✓ Branch 13 → 14 taken 1 time.
✓ Branch 14 → 15 taken 1 time.
2 << app_mgrs_by_db_file_.size() << " AppManager's. Must be only one.";
903 }
904
905 /// If there is only one AppManager, return its DatabaseManager. Otherwise
906 /// throw.
907 5 DatabaseManager& getDatabaseManager()
908 {
909
2/2
✓ Branch 3 → 4 taken 4 times.
✓ Branch 3 → 8 taken 1 time.
5 if (db_mgrs_by_db_file_.size() == 1)
910 {
911 4 return *db_mgrs_by_db_file_.begin()->second;
912 }
913
914
1/1
✓ Branch 10 → 11 taken 1 time.
1 throw DBException("Cannot call getDatabaseManager() since there are ")
915
3/3
✓ Branch 12 → 13 taken 1 time.
✓ Branch 13 → 14 taken 1 time.
✓ Branch 14 → 15 taken 1 time.
2 << db_mgrs_by_db_file_.size() << " AppManager's. Must be only one.";
916 }
917
918 /// Get a DatabaseManager for the given DB file. Throws if not found.
919 1 DatabaseManager& getDatabaseManager(const std::string& db_file)
920 {
921
1/1
✓ Branch 2 → 3 taken 1 time.
1 auto it = db_mgrs_by_db_file_.find(db_file);
922
1/14
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 26 taken 1 time.
✗ Branch 8 → 9 not taken.
✗ Branch 9 → 10 not taken.
✗ Branch 11 → 12 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 19 → 20 not taken.
✗ Branch 20 → 21 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 24 → 25 not taken.
1 simdb_assert(it != db_mgrs_by_db_file_.end(), "DatabaseManager does not exist for file '" << db_file << "'.");
923 2 return *it->second;
924 }
925
926 /// Get a mapping from all active AppManager's and their associated
927 /// DatabaseManager's.
928 46 std::vector<std::pair<AppManager*, DatabaseManager*>> getAllManagers()
929 {
930 46 std::vector<std::pair<AppManager*, DatabaseManager*>> mgrs;
931
2/2
✓ Branch 21 → 5 taken 47 times.
✓ Branch 21 → 22 taken 46 times.
93 for (auto& [db_file, db_mgr] : db_mgrs_by_db_file_)
932 {
933
1/1
✓ Branch 8 → 9 taken 47 times.
47 auto it = app_mgrs_by_db_file_.find(db_file);
934
2/2
✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 46 times.
47 if (it == app_mgrs_by_db_file_.end())
935 {
936 1 continue;
937 }
938
939 46 auto& app_mgr = it->second;
940
2/2
✓ Branch 16 → 17 taken 46 times.
✓ Branch 17 → 18 taken 46 times.
46 mgrs.push_back(std::make_pair(app_mgr.get(), db_mgr.get()));
941 }
942 46 return mgrs;
943 ✗ }
944
945 /// Call after command line args and config files are parsed.
946 11 void createEnabledApps()
947 {
948
3/3
✓ Branch 2 → 3 taken 11 times.
✓ Branch 11 → 5 taken 12 times.
✓ Branch 11 → 12 taken 11 times.
23 for (auto& [app_mgr, _] : getAllManagers())
949 {
950
1/1
✓ Branch 8 → 9 taken 12 times.
12 app_mgr->createEnabledApps_();
951 11 }
952 11 }
953
954 /// Create app-specific schemas for the instantiated apps.
955 9 void createSchemas()
956 {
957
3/3
✓ Branch 2 → 3 taken 9 times.
✓ Branch 11 → 5 taken 9 times.
✓ Branch 11 → 12 taken 9 times.
18 for (auto& [app_mgr, _] : getAllManagers())
958 {
959
1/1
✓ Branch 8 → 9 taken 9 times.
9 app_mgr->createSchemas_();
960 9 }
961 9 }
962
963 /// Call this after command line args and config files are parsed.
964 6 void postInit(int argc, char** argv)
965 {
966
3/3
✓ Branch 2 → 3 taken 6 times.
✓ Branch 11 → 5 taken 6 times.
✓ Branch 11 → 12 taken 6 times.
12 for (auto& [app_mgr, _] : getAllManagers())
967 {
968
1/1
✓ Branch 8 → 9 taken 6 times.
6 app_mgr->postInit_(argc, argv);
969 6 }
970 6 }
971
972 /// Call this once after postInit(). This will create all pipelines
973 /// for all enabled apps, but it will not open the threads yet.
974 9 void initializePipelines()
975 {
976
3/3
✓ Branch 2 → 3 taken 9 times.
✓ Branch 11 → 5 taken 9 times.
✓ Branch 11 → 12 taken 9 times.
18 for (auto& [app_mgr, _] : getAllManagers())
977 {
978
1/1
✓ Branch 8 → 9 taken 9 times.
9 app_mgr->initializePipelines_();
979 9 }
980 9 }
981
982 /// Call this once after initializePipelines() (and after minimizeThreads()
983 /// if you called that too).
984 9 void openPipelines()
985 {
986
3/3
✓ Branch 2 → 3 taken 9 times.
✓ Branch 11 → 5 taken 9 times.
✓ Branch 11 → 12 taken 9 times.
18 for (auto& [app_mgr, _] : getAllManagers())
987 {
988
1/1
✓ Branch 8 → 9 taken 9 times.
9 app_mgr->openPipelines_();
989 9 }
990 9 }
991
992 /// Call postSimLoopTeardown() on all AppManager's. This destroys all Apps
993 /// and AppManager's. It does NOT destroy the DatabaseManager's. Once this
994 /// method is called, you have to access the DatabaseManager's using the
995 /// 'getDatabaseManager(db_file)' API.
996 9 void postSimLoopTeardown()
997 {
998
2/2
✓ Branch 11 → 4 taken 9 times.
✓ Branch 11 → 12 taken 9 times.
18 for (auto& [_, app_mgr] : app_mgrs_by_db_file_)
999 {
1000
1/1
✓ Branch 8 → 9 taken 9 times.
9 app_mgr->postSimLoopTeardown_();
1001 }
1002
1003 9 destroy_(false);
1004 9 }
1005
1006 /// Destroy every AppManager and DatabaseManager we have.
1007 1 void destroyAll() { destroy_(true); }
1008
1009 private:
1010 10 void destroy_(bool destroy_all)
1011 {
1012 /// Destroy all AppManagers, DatabaseManagers, and Apps
1013
2/2
✓ Branch 11 → 4 taken 10 times.
✓ Branch 11 → 12 taken 10 times.
20 for (auto& [_, app_mgr] : app_mgrs_by_db_file_)
1014 {
1015 10 app_mgr->destroyAllApps_();
1016 }
1017
1018 // Destroy AppManager's first in case they need to touch
1019 // the database in their destructors.
1020 10 app_mgrs_by_db_mgr_.clear();
1021 10 app_mgrs_by_db_file_.clear();
1022
1023 // Clear DatabaseManager maps. Hang onto db_mgrs_by_db_file_
1024 // so 'getDatabaseManager(db_file)' is still available.
1025 10 db_mgrs_by_app_mgr_.clear();
1026
1027 // Destroy DatabaseManagers
1028
2/2
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 17 taken 9 times.
10 if (destroy_all)
1029 {
1030 1 db_mgrs_by_db_file_.clear();
1031 }
1032 10 }
1033
1034 std::vector<std::unique_ptr<AppRegistrationBase>> app_registrations_;
1035 bool app_registration_locked_ = false;
1036
1037 std::map<std::string, std::shared_ptr<DatabaseManager>> db_mgrs_by_db_file_;
1038 std::map<std::string, std::shared_ptr<AppManager>> app_mgrs_by_db_file_;
1039 std::map<DatabaseManager*, std::shared_ptr<AppManager>> app_mgrs_by_db_mgr_;
1040 std::map<AppManager*, std::shared_ptr<DatabaseManager>> db_mgrs_by_app_mgr_;
1041
1042 std::unique_ptr<ThreadSafeLogger> app_logger_;
1043 bool accepting_logger_requests_ = true;
1044 bool verbose_ = false;
1045 };
1046
1047 /*!
1048 * \class AppRegistrations
1049 *
1050 * \brief Thin wrapper that exposes only registerApp<AppT>() on an AppManagers
1051 * instance. Use when you want to restrict API surface to registration only.
1052 */
1053 class AppRegistrations
1054 {
1055 public:
1056 /// \brief Construct with the AppManagers instance to wrap.
1057 explicit AppRegistrations(AppManagers* app_managers) :
1058 app_managers_(app_managers)
1059 {
1060 }
1061
1062 /// \brief Register an app type (forwards to AppManagers::registerApp<AppT>()).
1063 template <typename AppT> void registerApp() { app_managers_->registerApp<AppT>(); }
1064
1065 private:
1066 AppManagers* app_managers_ = nullptr;
1067 };
1068
1069 } // namespace simdb
1070