Project
Loading...
Searching...
No Matches
test_Services.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
17#include <catch_amalgamated.hpp>
18#include <fairmq/ProgOptions.h>
19#include <memory>
20#include <vector>
21
22TEST_CASE("TestServiceRegistry")
23{
25 using namespace o2::framework;
26 struct InterfaceA {
27 virtual bool method() = 0;
28 };
29
30 struct ConcreteA : InterfaceA {
31 bool method() final { return true; }
32 };
33
34 struct InterfaceB {
35 virtual bool method() = 0;
36 };
37
38 struct ConcreteB : InterfaceB {
39 bool method() final { return false; }
40 };
41
42 struct InterfaceC {
43 [[nodiscard]] virtual bool method() const = 0;
44 };
45
46 struct ConcreteC : InterfaceC {
47 [[nodiscard]] bool method() const final { return false; }
48 };
49
50 ServiceRegistry registry;
51 ServiceRegistryRef ref{registry};
52 ConcreteA serviceA;
53 ConcreteB serviceB;
54 ConcreteC const serviceC;
55 ref.registerService(ServiceRegistryHelpers::handleForService<InterfaceA>(&serviceA));
56 ref.registerService(ServiceRegistryHelpers::handleForService<InterfaceB>(&serviceB));
57 ref.registerService(ServiceRegistryHelpers::handleForService<InterfaceC const>(&serviceC));
58 REQUIRE(registry.get<InterfaceA>(ServiceRegistry::globalDeviceSalt()).method() == true);
59 REQUIRE(registry.get<InterfaceB>(ServiceRegistry::globalDeviceSalt()).method() == false);
60 REQUIRE(registry.get<InterfaceC const>(ServiceRegistry::globalDeviceSalt()).method() == false);
61 REQUIRE(registry.active<InterfaceA>(ServiceRegistry::globalDeviceSalt()) == true);
62 REQUIRE(registry.active<InterfaceB>(ServiceRegistry::globalDeviceSalt()) == true);
63 REQUIRE(registry.active<InterfaceC>(ServiceRegistry::globalDeviceSalt()) == false);
64 REQUIRE_THROWS_AS(registry.get<InterfaceA const>(ServiceRegistry::globalDeviceSalt()), RuntimeErrorRef);
65 REQUIRE_THROWS_AS(registry.get<InterfaceC>(ServiceRegistry::globalDeviceSalt()), RuntimeErrorRef);
66}
67
68TEST_CASE("TestCallbackService")
69{
70 using namespace o2::framework;
71 ServiceRegistry registry;
72 ServiceRegistryRef ref{registry};
73 auto service = std::make_unique<CallbackService>();
74 ref.registerService(ServiceRegistryHelpers::handleForService<CallbackService>(service.get()));
75
76 // the callback simply sets the captured variable to indicated that it was called
77 bool cbCalled = false;
78 auto cb = [&]() { cbCalled = true; };
79 registry.get<CallbackService>(ServiceRegistry::globalDeviceSalt()).set<CallbackService::Id::Stop>(cb);
80
81 // execute and check
82 registry.get<CallbackService>(ServiceRegistry::globalDeviceSalt()).call<CallbackService::Id::Stop>();
83 REQUIRE(cbCalled);
84}
85
88};
89
90namespace o2::framework
91{
96static ServiceRegistry::Salt salt_1_1 = ServiceRegistry::Salt{1, 1};
97} // namespace o2::framework
98
99TEST_CASE("TestSerialServices")
100{
101 using namespace o2::framework;
102 ServiceRegistry registry;
103
104 DummyService t0{0};
106 registry.registerService({TypeIdHelpers::uniqueId<DummyService>()}, &t0, ServiceKind::Serial, salt_0);
107
108 auto tt0 = reinterpret_cast<DummyService*>(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_0, ServiceKind::Serial));
109 auto tt1 = reinterpret_cast<DummyService*>(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_1, ServiceKind::Serial));
110 auto tt2 = reinterpret_cast<DummyService*>(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_2, ServiceKind::Serial));
111 REQUIRE(tt0->threadId == 0);
112 REQUIRE(tt1->threadId == 0);
113 REQUIRE(tt2->threadId == 0);
114}
115
116TEST_CASE("TestGlobalServices")
117{
118 using namespace o2::framework;
119 ServiceRegistry registry;
120
121 DummyService t0{0};
123 registry.registerService({TypeIdHelpers::uniqueId<DummyService>()}, &t0, ServiceKind::Global, salt_0);
124
125 auto tt0 = reinterpret_cast<DummyService*>(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_0, ServiceKind::Serial));
126 auto tt1 = reinterpret_cast<DummyService*>(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_1, ServiceKind::Serial));
127 auto tt2 = reinterpret_cast<DummyService*>(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_2, ServiceKind::Serial));
128 REQUIRE(tt0->threadId == 0);
129 REQUIRE(tt1->threadId == 0);
130 REQUIRE(tt2->threadId == 0);
131}
132
133TEST_CASE("TestGlobalServices02")
134{
135 using namespace o2::framework;
136 ServiceRegistry registry;
137
139 fair::mq::ProgOptions options;
140 ServiceSpec spec{.name = "dummy-service",
141 .uniqueId = CommonServices::simpleServiceId<DummyService>(),
142 .init = [](ServiceRegistryRef, DeviceState&, fair::mq::ProgOptions&) -> ServiceHandle {
143 // this is needed to check we register it only once
144 static int i = 1;
145 return ServiceHandle{TypeIdHelpers::uniqueId<DummyService>(), new DummyService{i++}};
146 },
147 .configure = CommonServices::noConfiguration(),
148 .kind = ServiceKind::Global};
149
150 // If the service was not declared, we should not be able to register it from a stream context.
151 REQUIRE_THROWS_AS(registry.registerService({TypeIdHelpers::uniqueId<DummyService>()}, nullptr, ServiceKind::Global, salt_1), RuntimeErrorRef);
152 // Declare the service
153 registry.declareService(spec, state, options, ServiceRegistry::globalDeviceSalt());
154
156 try {
157 registry.registerService({TypeIdHelpers::uniqueId<DummyService>()}, nullptr, ServiceKind::Global, salt_1);
158 } catch (RuntimeErrorRef e) {
159 INFO(error_from_ref(e).what);
160 REQUIRE(false);
161 }
162
163 auto tt0 = reinterpret_cast<DummyService*>(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_0, ServiceKind::Global));
164 auto tt1 = reinterpret_cast<DummyService*>(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_1, ServiceKind::Global));
165 auto tt2 = reinterpret_cast<DummyService*>(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_2, ServiceKind::Global));
166 REQUIRE(tt0->threadId == 1);
167 REQUIRE(tt1->threadId == 1);
168 REQUIRE(tt2->threadId == 1);
169}
170
171TEST_CASE("TestStreamServices")
172{
173 using namespace o2::framework;
174 ServiceRegistry registry;
175
176 DummyService t1{1};
177 DummyService t2{2};
178 DummyService t3{3};
179 DummyService t2_d1{2};
180
181 ServiceSpec spec{.name = "dummy-service",
182 .uniqueId = CommonServices::simpleServiceId<DummyService>(),
183 .init = CommonServices::simpleServiceInit<DummyService, DummyService>(),
184 .configure = CommonServices::noConfiguration(),
185 .kind = ServiceKind::Stream};
186
188 fair::mq::ProgOptions options;
189 // This will raise an exception because we have not declared the service yet.
190 REQUIRE_THROWS_AS(registry.registerService({TypeIdHelpers::uniqueId<DummyService>()}, nullptr, ServiceKind::Stream, ServiceRegistry::Salt{1, 0}), RuntimeErrorRef);
191 registry.declareService(spec, state, options, ServiceRegistry::globalDeviceSalt());
192
194 registry.registerService({TypeIdHelpers::uniqueId<DummyService>()}, &t1, ServiceKind::Stream, ServiceRegistry::Salt{1, 0}, "dummy-service1", ServiceRegistry::SpecIndex{0});
195 registry.registerService({TypeIdHelpers::uniqueId<DummyService>()}, &t2, ServiceKind::Stream, ServiceRegistry::Salt{2, 0}, "dummy-service2", ServiceRegistry::SpecIndex{0});
196 registry.registerService({TypeIdHelpers::uniqueId<DummyService>()}, &t3, ServiceKind::Stream, ServiceRegistry::Salt{3, 0}, "dummy-service3", ServiceRegistry::SpecIndex{0});
197
198 auto tt1 = reinterpret_cast<DummyService*>(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_1, ServiceKind::Stream));
199 auto tt2 = reinterpret_cast<DummyService*>(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_2, ServiceKind::Stream));
200 auto tt3 = reinterpret_cast<DummyService*>(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_3, ServiceKind::Stream));
201 REQUIRE(tt1->threadId == 1);
202 REQUIRE(tt2->threadId == 2);
203 REQUIRE(tt3->threadId == 3);
204 // Check that Context{1,1} throws, because we registerd it for a different data processor.
205 REQUIRE_THROWS_AS(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_1_1, ServiceKind::Stream), RuntimeErrorRef);
206
207 // Check that Context{0,0} throws.
208 REQUIRE_THROWS_AS(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_0, ServiceKind::Stream), RuntimeErrorRef);
209 registry.registerService({TypeIdHelpers::uniqueId<DummyService>()}, &t2_d1, ServiceKind::Stream, ServiceRegistry::Salt{3, 1}, "dummy-service", ServiceRegistry::SpecIndex{0});
210
211 auto tt2_dp1 = reinterpret_cast<DummyService*>(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, ServiceRegistry::Salt{3, 1}, ServiceKind::Stream));
212 REQUIRE(tt2_dp1->threadId == 2);
213
214 REQUIRE_THROWS_AS(registry.get({TypeIdHelpers::uniqueId<DummyService>()}, salt_1_1, ServiceKind::Stream), RuntimeErrorRef);
215}
216
217TEST_CASE("TestStreamServicesDoNotShareASlot")
218{
219 using namespace o2::framework;
220 ServiceRegistry registry;
221
222 ServiceSpec spec{.name = "dummy-service",
223 .uniqueId = CommonServices::simpleServiceId<DummyService>(),
224 .init = CommonServices::simpleServiceInit<DummyService, DummyService>(),
225 .configure = CommonServices::noConfiguration(),
226 .kind = ServiceKind::Stream};
227
229 fair::mq::ProgOptions options;
230 registry.declareService(spec, state, options, ServiceRegistry::globalDeviceSalt());
231
232 // One instance of the same service per stream, and more streams than a probe
233 // window is deep. The slot comes from the low bits of the type hash combined
234 // with the salt, so if the salt's streamId does not reach those bits every one
235 // of these lands on the same slot, and the first which does not fit in the
236 // window is refused outright.
237 constexpr short STREAMS = 32;
238 std::vector<DummyService> services(STREAMS);
239 for (short i = 0; i < STREAMS; ++i) {
240 services[i].threadId = i + 1;
241 }
242
243 for (short i = 0; i < STREAMS; ++i) {
244 // Refused registration is what a shared slot looks like from here: the
245 // window fills and the next one has nowhere to go.
246 REQUIRE_NOTHROW(registry.registerService({TypeIdHelpers::uniqueId<DummyService>()}, &services[i], ServiceKind::Stream,
247 ServiceRegistry::Salt{static_cast<short>(i + 1), 0}, "dummy-service",
249 }
250
251 // Every stream must get its own instance back, not a neighbour's.
252 for (short i = 0; i < STREAMS; ++i) {
253 auto* found = reinterpret_cast<DummyService*>(
254 registry.get({TypeIdHelpers::uniqueId<DummyService>()},
255 ServiceRegistry::Salt{static_cast<short>(i + 1), 0}, ServiceKind::Stream));
256 REQUIRE(found != nullptr);
257 CHECK(found->threadId == i + 1);
258 }
259}
260
261TEST_CASE("TestServiceRegistryCtor")
262{
263 using namespace o2::framework;
264 ServiceRegistry registry;
265 registry = ServiceRegistry();
266}
267
268TEST_CASE("TestServiceDeclaration")
269{
270 using namespace o2::framework;
271 ServiceRegistry registry;
273 fair::mq::ProgOptions options;
274 options.SetProperty("monitoring-backend", "no-op://");
275 options.SetProperty("infologger-mode", "no-op://");
276 options.SetProperty("infologger-severity", "info");
277 options.SetProperty("configuration", "command-line");
278
279 registry.declareService(CommonServices::callbacksSpec(), state, options);
280 REQUIRE(registry.active<CallbackService>(ServiceRegistry::globalDeviceSalt()) == true);
281 REQUIRE(registry.active<DummyService>(ServiceRegistry::globalDeviceSalt()) == false);
282}
283
284TEST_CASE("TestServiceOverride")
285{
286 using namespace o2::framework;
287 auto overrides = ServiceSpecHelpers::parseOverrides("foo:enable,bar:disable");
288 REQUIRE(overrides.size() == 2);
289 REQUIRE(overrides[0].name == "foo");
290 REQUIRE(overrides[0].active == true);
291 REQUIRE(overrides[1].name == "bar");
292 REQUIRE(overrides[1].active == false);
293
294 auto overrides2 = ServiceSpecHelpers::parseOverrides("foo:enable");
295 REQUIRE(overrides2.size() == 1);
296 REQUIRE(overrides[0].name == "foo");
297 REQUIRE(overrides[0].active == true);
298
299 REQUIRE_THROWS_AS(ServiceSpecHelpers::parseOverrides("foo:enabledisabl"), std::runtime_error);
300 REQUIRE_THROWS_AS(ServiceSpecHelpers::parseOverrides("foo"), std::runtime_error);
301 REQUIRE_THROWS_AS(ServiceSpecHelpers::parseOverrides("foo:"), std::runtime_error);
302 REQUIRE_THROWS_AS(ServiceSpecHelpers::parseOverrides("foo:a,"), std::runtime_error);
303 REQUIRE_THROWS_AS(ServiceSpecHelpers::parseOverrides("foo:,"), std::runtime_error);
304 REQUIRE(ServiceSpecHelpers::parseOverrides("").size() == 0);
305 REQUIRE(ServiceSpecHelpers::parseOverrides(nullptr).size() == 0);
306
307 auto overrides3 = ServiceSpecHelpers::parseOverrides("foo:disable,bar:enable,baz:enable");
308 ServiceSpecs originalServices{
309 {.name = "foo", .active = true},
310 {.name = "bar", .active = false},
311 };
312 REQUIRE(overrides3.size() == 3);
313 auto services = ServiceSpecHelpers::filterDisabled(originalServices, overrides3);
314 REQUIRE(services.size() == 1);
315 REQUIRE(services[0].name == "bar");
316 REQUIRE(services[0].active == true);
317}
benchmark::State & state
int32_t i
#define CHECK
GLsizeiptr size
Definition glcorearb.h:659
GLuint const GLchar * name
Definition glcorearb.h:781
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t0
Definition glcorearb.h:5034
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
Definition glcorearb.h:5034
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
std::vector< ServiceSpec > ServiceSpecs
void clean_all_runtime_errors()
Running state information of a given device.
Definition DeviceState.h:34
void declareService(ServiceSpec const &spec, DeviceState &state, fair::mq::ProgOptions &options, ServiceRegistry::Salt salt=ServiceRegistry::globalDeviceSalt())
void registerService(ServiceTypeHash typeHash, void *service, ServiceKind kind, Salt salt, char const *name=nullptr, ServiceRegistry::SpecIndex specIndex=SpecIndex{-1}) const
bool active(Salt salt) const
Check if service of type T is currently active.
void * get(ServiceTypeHash typeHash, Salt salt, ServiceKind kind, char const *name=nullptr) const
std::string name
Name of the service.
TEST_CASE("TestServiceRegistry")