algorithms
2.2.0
Framework-independent algorithms for the EIC
Toggle main menu visibility
Loading...
Searching...
No Matches
service.h
Go to the documentation of this file.
1
// SPDX-License-Identifier: LGPL-3.0-or-later
2
// Copyright (C) 2022 Wouter Deconinck, Sylvester Joosten
3
//
4
// Basic Service infrastructure, implemented as lazy-evaluated singletons (thread-safe).
5
//
6
// Provides the special ServiceSvc (that provides access to all instantiated services as
7
// Configurable*), and the ServiceMixin (and related ALGORITHMS_DEFINE_SERVICE macro).
8
//
9
#pragma once
10
11
#include <fmt/format.h>
12
#include <fmt/ranges.h>
13
#include <functional>
14
#include <map>
15
#include <mutex>
16
#include <string>
17
18
#include <
algorithms/error.h
>
19
#include <
algorithms/name.h
>
20
#include <
algorithms/property.h
>
21
22
// Add boilerplate to service class definitions
23
// - singleton --> no public constructor, no copy constructor, no assigmnent operator
24
// - constructor should be protected so we can actually inherit from this class if needed
25
// (mostly needed for the service base class)
26
#define ALGORITHMS_DEFINE_SERVICE(className) \
27
protected: \
28
className() : Service<className>(#className) {} \
29
\
30
public: \
31
friend class Service<className>; \
32
className(const className&) = delete; \
33
void operator=(const className&) = delete; \
34
constexpr static const char* kName = #className;
35
36
namespace
algorithms
{
37
38
class
ServiceError
:
public
Error
{
39
public
:
40
ServiceError
(std::string_view msg) :
Error
{msg,
"algorithms::ServiceError"
} {}
41
};
42
43
class
ServiceBase
:
public
PropertyMixin
{
44
public
:
45
// Is this service ready? (active and initialized)
46
bool
ready
()
const
{
return
m_ready; }
47
void
ready
(
bool
state) { m_ready = state; }
48
49
private
:
50
bool
m_ready{
false
};
51
};
52
53
// Service service --> keeps track of all services :)
54
// --> exposes the underlying ServiceBase* object of the service so
55
// we can configure the services by name in the framework
56
// boundary plugin
57
// --> Special service (does not use the Service base class to avoid
58
// circularity)
59
class
ServiceSvc :
public
NameMixin
{
60
public
:
61
using
ServiceMapType
= std::map<std::string_view, ServiceBase*>;
62
static
ServiceSvc&
instance
() {
63
// This is guaranteed to be thread-safe from C++11 onwards.
64
static
ServiceSvc svc;
65
return
svc;
66
}
67
// add a service to the ServiceSvc. Two options:
68
// - prior to init --> just add the service to the service stack and set the initializer
69
// - after init --> add service to call stack, set the initializer,
70
// manually call init for the service, revalidate
71
// The service must be fully constructed, as it is initialized right away after init.
72
template
<
class
Svc>
73
void
add
(
74
ServiceBase
* svc,
const
std::function<
void
(Svc&)>&
init
= [](Svc& s) { s.init(); }) {
75
registerService<Svc>
(svc,
init
);
76
initStraggler<Svc>
();
77
}
78
// add a service to the service stack and set the initializer, without initializing it
79
// (used by the Service constructor, as the service is not yet fully constructed there)
80
template
<
class
Svc>
81
void
registerService
(
82
ServiceBase
* svc,
const
std::function<
void
(Svc&)>&
init
= [](Svc& s) { s.init(); }) {
83
std::lock_guard<std::recursive_mutex> lock{m_mutex};
84
m_keys.push_back(Svc::kName);
85
m_services[Svc::kName] = svc;
86
// only add initializer if not already present (e.g. if for some reason the framework did
87
// the registration early)
88
if
(m_initializers.count(Svc::kName) == 0) {
89
setInit
(
init
);
90
}
91
}
92
// call init if we are already in an initialized state (this is a straggler)
93
template
<
class
Svc>
void
initStraggler
() {
94
std::lock_guard<std::recursive_mutex> lock{m_mutex};
95
if
(m_init && !m_services.at(Svc::kName)->ready()) {
96
initSingle<Svc>
();
97
validate
();
98
}
99
}
100
101
template
<
class
Svc>
bool
has
()
const
{
102
std::lock_guard<std::recursive_mutex> lock{m_mutex};
103
return
m_services.count(Svc::kName);
104
}
105
// The initializer gets the registered service, so it does not need Svc::instance(), which
106
// may still be under construction
107
template
<
class
Svc>
void
setInit
(
const
std::function<
void
(Svc&)>&
init
) {
108
std::lock_guard<std::recursive_mutex> lock{m_mutex};
109
m_initializers[Svc::kName] = [
init
](
ServiceBase
* svc) {
init
(*
static_cast<
Svc*
>
(svc)); };
110
}
111
112
// Loop over all services in order and initialize one-by-one
113
// Finalize by validating that all is well
114
void
init
() {
115
std::lock_guard<std::recursive_mutex> lock{m_mutex};
116
// ensure we only call init once
117
if
(m_init) {
118
throw
ServiceError
(
"Cannot initialize services twice"
);
119
}
120
// Call init for all the services and mark as ready
121
for
(
const
auto
&
name
: m_keys) {
122
auto
svc = m_services.at(
name
);
123
try
{
124
m_initializers.at(
name
)(svc);
125
}
catch
(
const
std::exception& e) {
126
// we encountered an issue, stop here so validation fails
127
break
;
128
}
129
// Ensure our init made sense -- cannot have missing properties at this stage
130
if
(svc->
missingProperties
().size() > 0) {
131
break
;
132
}
133
svc->
ready
(
true
);
134
}
135
136
// Validate all services in case we encountered issues so we get useful error
137
// reporting
138
validate
();
139
140
// Label initialization as complete
141
m_init =
true
;
142
}
143
// Single service init (meant to be called for stragglers after general init)
144
template
<
class
Svc>
void
initSingle
() {
145
std::lock_guard<std::recursive_mutex> lock{m_mutex};
146
std::string_view
name
= Svc::kName;
147
if
(!m_init) {
148
throw
ServiceError
(
149
fmt::format(
"initSingle<{}>() should not be called before/instead of init()"
,
name
));
150
}
151
auto
svc = m_services.at(
name
);
152
// ensure we only call init once
153
if
(svc->
ready
()) {
154
throw
ServiceError
(fmt::format(
"Cannot initialize service {} - already initialized"
,
name
));
155
}
156
// call init
157
m_initializers.at(
name
)(svc);
158
// mark as ready
159
svc->
ready
(
true
);
160
}
161
162
template
<
class
Svc = ServiceBase> Svc*
service
(std::string_view
name
)
const
{
163
std::lock_guard<std::recursive_mutex> lock{m_mutex};
164
return
static_cast<
Svc*
>
(m_services.at(
name
));
165
}
166
// Check if all service properties are set
167
// TODO FIXME move to implementation file
168
void
validate
()
const
{
169
std::lock_guard<std::recursive_mutex> lock{m_mutex};
170
std::map<std::string_view, std::vector<std::string_view>> missing_props;
171
std::vector<std::string_view> not_initialized;
172
for
(std::string_view
name
: m_keys) {
173
const
auto
svc = m_services.at(
name
);
174
auto
missing = svc->
missingProperties
();
175
if
(!missing.empty()) {
176
missing_props[
name
] = missing;
177
}
178
if
(!svc->
ready
()) {
179
not_initialized.push_back(
name
);
180
}
181
std::string err =
""
;
182
if
(missing_props.size() > 0) {
183
err += fmt::format(
"Encountered missing service properties: {}\n"
, missing_props);
184
}
185
if
(not_initialized.size() > 0) {
186
err += fmt::format(
"Encountered uninitialized services: {}\n"
, not_initialized);
187
}
188
if
(err.size() > 0) {
189
throw
ServiceError
(fmt::format(
"Error initializing all services:\n{}"
, err));
190
}
191
}
192
}
193
// Not protected against concurrent registration of new services
194
const
auto
&
services
()
const
{
return
m_services; }
195
bool
ready
()
const
{
196
std::lock_guard<std::recursive_mutex> lock{m_mutex};
197
for
(
const
auto
& key : m_keys) {
198
if
(!m_services.at(key)->ready()) {
199
return
false
;
200
}
201
}
202
return
true
;
203
}
204
205
private
:
206
ServiceSvc
() :
NameMixin
{
"ServiceSvc"
,
"Special service that keeps track of all services"
} {}
207
208
public
:
209
ServiceSvc
(
const
ServiceSvc&) =
delete
;
210
void
operator=
(
const
ServiceSvc&) =
delete
;
211
212
private
:
213
std::vector<std::string_view> m_keys;
// Ordered list of service keys
214
std::map<std::string_view, ServiceBase*> m_services;
// Map of services for easier lookup
215
std::map<std::string_view, std::function<void(
ServiceBase
*)>> m_initializers;
// Init calls
216
bool
m_init =
false
;
// did we initialize the services already?
217
// Services may be accessed for the first time from several threads concurrently. Recursive,
218
// as initializers can access (and thereby register) other services.
219
mutable
std::recursive_mutex m_mutex;
220
};
221
222
// Thread-safe lazy-evaluated minimal service system
223
// CRTP base class to add the instance method
224
// This could have been part of DEFINE_SERVICE macro, but I think it is better
225
// to keep the macro magic to a minimum to maximize transparency
226
template
<
class
SvcType>
class
Service
:
public
ServiceBase
,
public
NameMixin
{
227
public
:
228
static
SvcType&
instance
() {
229
// This is guaranteed to be thread-safe from C++11 onwards.
230
static
SvcType& svc = []() -> SvcType& {
231
static
SvcType s;
232
// services created after ServiceSvc::init() are initialized here, once they are fully
233
// constructed (initializing them from the constructor would be too early)
234
ServiceSvc::instance
().
initStraggler
<SvcType>();
235
return
s;
236
}();
237
return
svc;
238
}
239
// constructor for the service base class registers the service, except
240
// for the ServiceSvc which is its own thing (avoid circularity)
241
// (services don't currently have an attached description)
242
Service
(std::string_view
name
) :
NameMixin
{
name
,
name
} {
243
ServiceSvc::instance
().
registerService
<SvcType>(
this
);
244
}
245
};
246
247
}
// namespace algorithms
248
algorithms::Configurable::missingProperties
auto missingProperties() const
Definition
property.h:56
algorithms::Error::Error
Error(std::string_view msg, std::string_view type="algorithms::Error")
Definition
error.h:15
algorithms::NameMixin
Definition
name.h:13
algorithms::NameMixin::NameMixin
NameMixin(std::string_view name, std::string_view description)
Definition
name.h:15
algorithms::NameMixin::name
std::string_view name() const
Definition
name.h:17
algorithms::ServiceBase
Definition
service.h:43
algorithms::ServiceBase::ready
bool ready() const
Definition
service.h:46
algorithms::ServiceBase::ready
void ready(bool state)
Definition
service.h:47
algorithms::ServiceError
Definition
service.h:38
algorithms::ServiceError::ServiceError
ServiceError(std::string_view msg)
Definition
service.h:40
algorithms::ServiceSvc
Definition
service.h:59
algorithms::ServiceSvc::instance
static ServiceSvc & instance()
Definition
service.h:62
algorithms::ServiceSvc::ServiceSvc
ServiceSvc(const ServiceSvc &)=delete
algorithms::ServiceSvc::initStraggler
void initStraggler()
Definition
service.h:93
algorithms::ServiceSvc::has
bool has() const
Definition
service.h:101
algorithms::ServiceSvc::operator=
void operator=(const ServiceSvc &)=delete
algorithms::ServiceSvc::ServiceMapType
std::map< std::string_view, ServiceBase * > ServiceMapType
Definition
service.h:61
algorithms::ServiceSvc::ready
bool ready() const
Definition
service.h:195
algorithms::ServiceSvc::add
void add(ServiceBase *svc, const std::function< void(Svc &)> &init=[](Svc &s) { s.init();})
Definition
service.h:73
algorithms::ServiceSvc::registerService
void registerService(ServiceBase *svc, const std::function< void(Svc &)> &init=[](Svc &s) { s.init();})
Definition
service.h:81
algorithms::ServiceSvc::setInit
void setInit(const std::function< void(Svc &)> &init)
Definition
service.h:107
algorithms::ServiceSvc::service
Svc * service(std::string_view name) const
Definition
service.h:162
algorithms::ServiceSvc::init
void init()
Definition
service.h:114
algorithms::ServiceSvc::validate
void validate() const
Definition
service.h:168
algorithms::ServiceSvc::initSingle
void initSingle()
Definition
service.h:144
algorithms::ServiceSvc::services
const auto & services() const
Definition
service.h:194
algorithms::Service::Service
Service(std::string_view name)
Definition
service.h:242
algorithms::Service::instance
static SvcType & instance()
Definition
service.h:228
error.h
name.h
algorithms
Definition
algorithm.h:52
algorithms::PropertyMixin
Configurable PropertyMixin
Definition
property.h:206
property.h
core
include
algorithms
service.h
Generated by
1.17.0