Project
Loading...
Searching...
No Matches
DataAllocator.h
Go to the documentation of this file.
1// Copyright 2019-2026 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.
11#ifndef O2_FRAMEWORK_DATAALLOCATOR_H_
12#define O2_FRAMEWORK_DATAALLOCATOR_H_
13
17#include "Framework/Output.h"
18#include "Framework/OutputRef.h"
20#include "Framework/DataChunk.h"
24#include "Framework/Traits.h"
29
30#include "Headers/DataHeader.h"
31#include <TClass.h>
32
33#include <memory>
34#include <ranges>
35#include <vector>
36#include <map>
37#include <string>
38#include <utility>
39#include <type_traits>
40#include <utility>
41#include <cstddef>
42
43// Do not change this for a full inclusion of fair::mq::Device.
44#include <fairmq/FwdDecls.h>
45#include <fairmq/Version.h>
46#if (FAIRMQ_VERSION_DEC >= 111000)
47#include <fairmq/shmem/Message.h>
48#endif
49
50namespace arrow
51{
52class Schema;
53class Table;
54
55namespace ipc
56{
57class RecordBatchWriter;
58} // namespace ipc
59} // namespace arrow
60
61namespace o2::framework
62{
63struct ServiceRegistry;
64
70template <typename T>
72 using type = T;
73 T* ptr = nullptr;
74 std::function<void(T&)> callback = nullptr;
75 LifetimeHolder(T* ptr_) : ptr(ptr_),
76 callback(nullptr)
77 {
78 }
79 LifetimeHolder() = delete;
80 // Never copy it, because there is only one LifetimeHolder pointer
81 // created object.
85 {
86 this->ptr = other.ptr;
87 other.ptr = nullptr;
88 if (other.callback) {
89 this->callback = std::move(other.callback);
90 } else {
91 this->callback = nullptr;
92 }
93 other.callback = nullptr;
94 }
96 {
97 this->ptr = other.ptr;
98 other.ptr = nullptr;
99 if (other.callback) {
100 this->callback = std::move(other.callback);
101 } else {
102 this->callback = nullptr;
103 }
104 other.callback = nullptr;
105 return *this;
106 }
107
108 // On deletion we invoke the callback and then delete the object,
109 // when prensent.
111 {
112 release();
113 }
114
115 T* operator->() { return ptr; }
116 T& operator*() { return *ptr; }
117
118 // release the owned object, if any. This allows to
119 // invoke the callback early (e.g. for the Product<> case)
120 void release()
121 {
122 if (!ptr) {
123 return;
124 }
125
126 std::unique_ptr<T> released{ptr};
127 ptr = nullptr;
128 auto releaseCallback = std::move(callback);
129 if (!releaseCallback) {
130 return;
131 }
132 releaseCallback(*released);
133 }
134
135 // Delete the owned object without invoking the release callback. This is used
136 // when a partially filled object must be abandoned.
137 void discard()
138 {
139 if (!ptr) {
140 return;
141 }
142
143 callback = nullptr;
144 delete ptr;
145 ptr = nullptr;
146 }
147};
148
149template <typename T>
150concept VectorOfMessageableTypes = is_specialization_v<T, std::vector> &&
151 is_messageable<typename T::value_type>::value;
152
153template <typename T>
154concept ContiguousMessageablesRange = std::ranges::contiguous_range<T> &&
155 is_messageable<typename T::value_type>::value;
156
162{
163 public:
165 using AllowedOutputRoutes = std::vector<OutputRoute>;
170
171 template <typename T>
172 requires std::is_fundamental_v<T>
174 using value_type = T;
175 };
176
178
179 DataChunk& newChunk(const Output&, size_t);
180
181 inline DataChunk& newChunk(OutputRef&& ref, size_t size) { return newChunk(getOutputByBind(std::move(ref)), size); }
182
183 void adoptChunk(const Output&, char*, size_t, fair::mq::FreeFn*, void*);
184
185 // This method can be used to send a 0xdeadbeef message associated to a given
186 // output. The @a spec will be used to determine the channel to which the
187 // output will need to be sent, however the actual message will be empty
188 // and with subspecification 0xdeadbeef.
189 void cookDeadBeef(const Output& spec);
190
191 template <typename T, typename... Args>
192 requires is_specialization_v<T, o2::framework::DataAllocator::UninitializedVector>
193 decltype(auto) make(const Output& spec, Args... args)
194 {
195 auto& timingInfo = mRegistry.get<TimingInfo>();
196 auto& context = mRegistry.get<MessageContext>();
197
198 auto routeIndex = matchDataHeader(spec, timingInfo.timeslice);
199 // plain buffer as polymorphic spectator std::vector, which does not run constructors / destructors
200 using ValueType = typename T::value_type;
201
202 // Note: initial payload size is 0 and will be set by the context before sending
203 fair::mq::MessagePtr headerMessage = headerMessageFromOutput(spec, routeIndex, o2::header::gSerializationMethodNone, 0);
205 std::move(headerMessage), routeIndex, 0, std::forward<Args>(args)...)
206 .get();
207 }
208
209 template <typename T, typename... Args>
211 decltype(auto) make(const Output& spec, Args... args)
212 {
213 auto& timingInfo = mRegistry.get<TimingInfo>();
214 auto& context = mRegistry.get<MessageContext>();
215
216 auto routeIndex = matchDataHeader(spec, timingInfo.timeslice);
217 // this catches all std::vector objects with messageable value type before checking if is also
218 // has a root dictionary, so non-serialized transmission is preferred
219 using ValueType = typename T::value_type;
220
221 // Note: initial payload size is 0 and will be set by the context before sending
222 fair::mq::MessagePtr headerMessage = headerMessageFromOutput(spec, routeIndex, o2::header::gSerializationMethodNone, 0);
223 return context.add<MessageContext::VectorObject<ValueType>>(std::move(headerMessage), routeIndex, 0, std::forward<Args>(args)...).get();
224 }
225
226 template <typename T, typename... Args>
227 requires(!VectorOfMessageableTypes<T> && has_root_dictionary<T>::value == true && is_messageable<T>::value == false)
228 decltype(auto) make(const Output& spec, Args... args)
229 {
230 auto& timingInfo = mRegistry.get<TimingInfo>();
231 auto& context = mRegistry.get<MessageContext>();
232
233 auto routeIndex = matchDataHeader(spec, timingInfo.timeslice);
234 // Extended support for types implementing the Root ClassDef interface, both TObject
235 // derived types and others
237 fair::mq::MessagePtr headerMessage = headerMessageFromOutput(spec, routeIndex, o2::header::gSerializationMethodROOT, 0);
238
239 return context.add<typename enable_root_serialization<T>::object_type>(std::move(headerMessage), routeIndex, std::forward<Args>(args)...).get();
240 } else {
241 static_assert(enable_root_serialization<T>::value, "Please make sure you include RootMessageContext.h");
242 }
243 }
244
245 template <typename T, typename... Args>
246 requires std::is_base_of_v<std::string, T>
247 decltype(auto) make(const Output& spec, Args... args)
248 {
249 auto* s = new std::string(args...);
250 adopt(spec, s);
251 return *s;
252 }
253
254 template <typename T, typename... Args>
255 requires(requires { static_cast<struct TableBuilder>(std::declval<std::decay_t<T>>()); })
256 decltype(auto) make(const Output& spec, Args... args)
257 {
258 auto tb = std::move(LifetimeHolder<TableBuilder>(new std::decay_t<T>(args...)));
259 adopt(spec, tb);
260 return tb;
261 }
262
263 template <typename T, typename... Args>
264 requires(requires { static_cast<struct FragmentToBatch>(std::declval<std::decay_t<T>>()); })
265 decltype(auto) make(const Output& spec, Args... args)
266 {
267 auto f2b = std::move(LifetimeHolder<FragmentToBatch>(new std::decay_t<T>(args...)));
268 adopt(spec, f2b);
269 return f2b;
270 }
271
272 template <typename T>
273 requires is_messageable<T>::value && (!is_specialization_v<T, UninitializedVector>)
274 decltype(auto) make(const Output& spec)
275 {
276 return *reinterpret_cast<T*>(newChunk(spec, sizeof(T)).data());
277 }
278
279 template <typename T>
280 requires is_messageable<T>::value && (!is_specialization_v<T, UninitializedVector>)
281 decltype(auto) make(const Output& spec, std::integral auto nElements)
282 {
283 auto& timingInfo = mRegistry.get<TimingInfo>();
284 auto& context = mRegistry.get<MessageContext>();
285 auto routeIndex = matchDataHeader(spec, timingInfo.timeslice);
286
287 fair::mq::MessagePtr headerMessage = headerMessageFromOutput(spec, routeIndex, o2::header::gSerializationMethodNone, nElements * sizeof(T));
288 return context.add<MessageContext::SpanObject<T>>(std::move(headerMessage), routeIndex, 0, nElements).get();
289 }
290
291 template <typename T, typename Arg>
292 decltype(auto) make(const Output& spec, std::same_as<std::shared_ptr<arrow::Schema>> auto schema)
293 {
294 std::shared_ptr<arrow::ipc::RecordBatchWriter> writer;
295 create(spec, &writer, schema);
296 return writer;
297 }
298
301 void
302 adopt(const Output& spec, std::string*);
303
306 void
308
311 void
313
315 void
316 adopt(const Output& spec, std::shared_ptr<class arrow::Table>);
317
338 template <typename T>
339 requires(!std::ranges::contiguous_range<T> && is_messageable<T>::value)
340 void snapshot(const Output& spec, T const& object)
341 {
342 return snapshot(spec, std::span<T const>(&object, &object + 1));
343 }
344
345 void snapshot(const Output& spec, std::string_view const& object)
346 {
347 return snapshot(spec, std::span<char const>(object.data(), object.size()));
348 }
349
350 // This is for snapshotting a range of contiguous messageable types
351 template <typename T>
352 requires(ContiguousMessageablesRange<T> && !std::is_pointer_v<typename T::value_type>)
353 void snapshot(const Output& spec, T const& object)
354 {
355 auto& proxy = mRegistry.get<MessageContext>().proxy();
356 RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get<TimingInfo>().timeslice);
357 using ElementType = typename std::remove_pointer<typename T::value_type>::type;
358 // Serialize a snapshot of a std::vector of trivially copyable, non-polymorphic elements
359 // Note: in most cases it is better to use the `make` function und work with the provided
360 // reference object
361 constexpr auto elementSizeInBytes = sizeof(ElementType);
362 auto sizeInBytes = elementSizeInBytes * object.size();
363 fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex, sizeInBytes);
364
365 // vector of elements
366 if (object.data() && sizeInBytes) {
367 memcpy(payloadMessage->GetData(), object.data(), sizeInBytes);
368 }
369
370 addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodNone);
371 }
372
373 // A random access range of pointers we can serialise by storing the contens one after the other.
374 // On the receiving side you will have to retrieve it via a span
375 template <typename T>
376 requires(std::ranges::random_access_range<T> && is_messageable<typename std::remove_pointer_t<typename T::value_type>>::value && std::is_pointer_v<typename T::value_type>)
377 void snapshot(const Output& spec, T const& object)
378 {
379 auto& proxy = mRegistry.get<MessageContext>().proxy();
380 RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get<TimingInfo>().timeslice);
381 using ElementType = typename std::remove_pointer_t<typename T::value_type>;
382 // Serialize a snapshot of a std::vector of trivially copyable, non-polymorphic elements
383 // Note: in most cases it is better to use the `make` function und work with the provided
384 // reference object
385 constexpr auto elementSizeInBytes = sizeof(ElementType);
386 auto sizeInBytes = elementSizeInBytes * object.size();
387 fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex, sizeInBytes);
388
389 // serialize vector of pointers to elements
390 auto target = reinterpret_cast<unsigned char*>(payloadMessage->GetData());
391 for (auto const& pointer : object) {
392 memcpy(target, pointer, elementSizeInBytes);
393 target += elementSizeInBytes;
394 }
395
396 addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodNone);
397 }
398
399 // This is for a range where we can know upfront how many elements there are,
400 // so that we can preallocate the final size by simply multipling sizeof(T) x N elements
401 template <typename T>
402 requires(!std::ranges::contiguous_range<T> && std::ranges::sized_range<T> && has_messageable_value_type<T>::value)
403 void snapshot(const Output& spec, T const& object)
404 {
405 auto& proxy = mRegistry.get<MessageContext>().proxy();
406 RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get<TimingInfo>().timeslice);
407 // Serialize a snapshot of a std::container of trivially copyable, non-polymorphic elements
408 // Note: in most cases it is better to use the `make` function und work with the provided
409 // reference object
410 constexpr auto elementSizeInBytes = sizeof(typename T::value_type);
411 auto sizeInBytes = elementSizeInBytes * object.size();
412 fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex, sizeInBytes);
413
414 // serialize vector of pointers to elements
415 auto target = reinterpret_cast<unsigned char*>(payloadMessage->GetData());
416 for (auto const& entry : object) {
417 memcpy(target, (void*)&entry, elementSizeInBytes);
418 target += elementSizeInBytes;
419 }
420 addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodNone);
421 }
422
423 template <typename T>
424 requires(is_specialization_v<T, ROOTSerialized>)
425 void snapshot(const Output& spec, T const& object)
426 {
427 auto& proxy = mRegistry.get<MessageContext>().proxy();
428 RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get<TimingInfo>().timeslice);
429 // Serialize a snapshot of an object with root dictionary
430 fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex);
431 payloadMessage->Rebuild(4096, {64});
432 const TClass* cl = nullptr;
433 // Explicitely ROOT serialize a snapshot of object.
434 // An object wrapped into type `ROOTSerialized` is explicitely marked to be ROOT serialized
435 // and is expected to have a ROOT dictionary. Availability can not be checked at compile time
436 // for all cases.
437 using WrappedType = typename T::wrapped_type;
438
439 if (object.getHint() == nullptr) {
440 // get TClass info by wrapped type
441 cl = TClass::GetClass(typeid(WrappedType));
442 } else if (std::is_same<typename T::hint_type, TClass>::value) {
443 // the class info has been passed directly
444 cl = reinterpret_cast<const TClass*>(object.getHint());
445 } else if (std::is_same<typename T::hint_type, const char>::value) {
446 // get TClass info by optional name
447 cl = TClass::GetClass(reinterpret_cast<const char*>(object.getHint()));
448 }
449 if (has_root_dictionary<WrappedType>::value == false && cl == nullptr) {
450 if (std::is_same<typename T::hint_type, const char>::value) {
451 throw runtime_error_f("ROOT serialization not supported, dictionary not found for type %s",
452 reinterpret_cast<const char*>(object.getHint()));
453 } else {
454 throw runtime_error_f("ROOT serialization not supported, dictionary not found for type %s",
455 typeid(WrappedType).name());
456 }
457 }
458 typename root_serializer<T>::serializer().Serialize(*payloadMessage, &object(), cl);
459 addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodROOT);
460 }
461
462 template <typename T>
463 requires(!is_messageable<T>::value && !ContiguousMessageablesRange<T> && has_root_dictionary<T>::value && !is_specialization_v<T, ROOTSerialized>)
464 void snapshot(const Output& spec, T const& object)
465 {
466 auto& proxy = mRegistry.get<MessageContext>().proxy();
467 RouteIndex routeIndex = matchDataHeader(spec, mRegistry.get<TimingInfo>().timeslice);
468 // Serialize a snapshot of an object with root dictionary
469 fair::mq::MessagePtr payloadMessage = proxy.createOutputMessage(routeIndex);
470 payloadMessage->Rebuild(4096, {64});
471 typename root_serializer<T>::serializer().Serialize(*payloadMessage, &object, TClass::GetClass(typeid(T)));
472 addPartToContext(routeIndex, std::move(payloadMessage), spec, o2::header::gSerializationMethodROOT);
473 }
474
478 void snapshot(const Output& spec, const char* payload, size_t payloadSize,
480
483 void forwardPayload(const Output& spec, fair::mq::Message& inputPayload,
485
491 template <typename T, typename... Args>
492 decltype(auto) make(OutputRef&& ref, Args&&... args)
493 {
494 return make<T>(getOutputByBind(std::move(ref)), std::forward<Args>(args)...);
495 }
496
502 template <typename T>
503 void adopt(OutputRef&& ref, T* obj)
504 {
505 return adopt(getOutputByBind(std::move(ref)), obj);
506 }
507
508 // get the memory resource associated with an output
510 {
511 auto& timingInfo = mRegistry.get<TimingInfo>();
512 auto& proxy = mRegistry.get<FairMQDeviceProxy>();
513 RouteIndex routeIndex = matchDataHeader(spec, timingInfo.timeslice);
514 return *proxy.getOutputTransport(routeIndex);
515 }
516
517 // make a stl (pmr) vector
518 template <typename T, typename... Args>
519 o2::pmr::vector<T> makeVector(const Output& spec, Args&&... args)
520 {
521 o2::pmr::FairMQMemoryResource* targetResource = getMemoryResource(spec);
522 return o2::pmr::vector<T>{targetResource, std::forward<Args>(args)...};
523 }
524
525 struct CacheId {
526 int64_t value;
527 int64_t handle;
528 int64_t segment;
529 };
530
531 enum struct CacheStrategy : int {
532 Never = 0,
533 Always = 1
534 };
535
536 template <typename ContainerT>
537 CacheId adoptContainer(const Output& /*spec*/, ContainerT& /*container*/, CacheStrategy /* cache = false */, o2::header::SerializationMethod /* method = header::gSerializationMethodNone*/)
538 {
539 static_assert(always_static_assert_v<ContainerT>, "Container cannot be moved. Please make sure it is backed by a o2::pmr::FairMQMemoryResource");
540 return {0, 0, 0};
541 }
542
552 template <typename ContainerT>
554
557
560 void pruneFromCache(CacheId id);
561
567 template <typename... Args>
568 auto snapshot(OutputRef&& ref, Args&&... args)
569 {
570 return snapshot(getOutputByBind(std::move(ref)), std::forward<Args>(args)...);
571 }
572
574 bool isAllowed(Output const& query);
575
577 {
578 return mRegistry.get<MessageContext>().findMessageHeader(spec);
579 }
580
582 {
583 return mRegistry.get<MessageContext>().findMessageHeader(getOutputByBind(std::move(ref)));
584 }
585
587 {
588 return mRegistry.get<MessageContext>().findMessageHeaderStack(spec);
589 }
590
592 {
593 return mRegistry.get<MessageContext>().findMessageHeaderStack(getOutputByBind(std::move(ref)));
594 }
595
596 int countDeviceOutputs(bool excludeDPLOrigin = false)
597 {
598 return mRegistry.get<MessageContext>().countDeviceOutputs(excludeDPLOrigin);
599 }
600
601 private:
602 ServiceRegistryRef mRegistry;
603
604 RouteIndex matchDataHeader(const Output& spec, size_t timeframeId);
605 fair::mq::MessagePtr headerMessageFromOutput(Output const& spec, //
606 RouteIndex index, //
607 o2::header::SerializationMethod serializationMethod, //
608 size_t payloadSize); //
609
610 Output getOutputByBind(OutputRef&& ref);
611 void addPartToContext(RouteIndex routeIndex, fair::mq::MessagePtr&& payload,
612 const Output& spec,
613 o2::header::SerializationMethod serializationMethod);
614};
615
616template <typename ContainerT>
618{
619 // Find a matching channel, extract the message for it form the container
620 // and put it in the queue to be sent at the end of the processing
621 auto& timingInfo = mRegistry.get<TimingInfo>();
622 auto routeIndex = matchDataHeader(spec, timingInfo.timeslice);
623
624 auto& context = mRegistry.get<MessageContext>();
625 auto* transport = mRegistry.get<FairMQDeviceProxy>().getOutputTransport(routeIndex);
626 fair::mq::MessagePtr payloadMessage = o2::pmr::getMessage(std::forward<ContainerT>(container), *transport);
627 fair::mq::MessagePtr headerMessage = headerMessageFromOutput(spec, routeIndex, //
628 method, //
629 payloadMessage->GetSize() //
630 );
631
632 CacheId cacheId{0, 0, 0}; //
633 if (cache == CacheStrategy::Always) {
634 // The message will be shallow cloned in the cache. Since the
635 // clone is indistinguishable from the original, we can keep sending
636 // the original.
637 cacheId.value = context.addToCache(payloadMessage);
638#if (FAIRMQ_VERSION_DEC >= 111000)
639 auto meta = dynamic_cast<fair::mq::shmem::Message*>(payloadMessage.get())->GetMeta();
640 cacheId.handle = meta.fHandle;
641 cacheId.segment = meta.fSegmentId;
642#endif
643 }
644
645 context.add<MessageContext::TrivialObject>(std::move(headerMessage), std::move(payloadMessage), routeIndex);
646 return cacheId;
647}
648
649} // namespace o2::framework
650
651#endif // O2_FRAMEWORK_DATAALLOCATOR_H_
std::shared_ptr< arrow::Schema > schema
Type wrappers for enfording a specific serialization method.
static constexpr ServiceKind service_kind
::value &&!is_specialization_v< T, UninitializedVector > decltype(auto) make(const Output &spec, std::integral auto nElements)
void adopt(const Output &spec, std::string *)
o2::pmr::vector< T > makeVector(const Output &spec, Args &&... args)
void snapshot(const Output &spec, T const &object)
o2::pmr::FairMQMemoryResource * getMemoryResource(const Output &spec)
decltype(auto) make(OutputRef &&ref, Args &&... args)
DataChunk & newChunk(const Output &, size_t)
o2::header::DataHeader::SubSpecificationType SubSpecificationType
o2::header::Stack * findMessageHeaderStack(const Output &spec)
decltype(auto) make(const Output &spec, Args... args)
::value &&!is_specialization_v< T, UninitializedVector > decltype(auto) make(const Output &spec)
void snapshot(const Output &spec, std::string_view const &object)
decltype(auto) make(const Output &spec, Args... args)
auto snapshot(OutputRef &&ref, Args &&... args)
void adopt(const Output &spec, std::shared_ptr< class arrow::Table >)
Adopt an Arrow table and send it to all consumers of spec.
int countDeviceOutputs(bool excludeDPLOrigin=false)
o2::header::DataHeader * findMessageHeader(OutputRef &&ref)
void adoptFromCache(Output const &spec, CacheId id, header::SerializationMethod method=header::gSerializationMethodNone)
Adopt an already cached message, using an already provided CacheId.
decltype(auto) make(const Output &spec, Args... args)
std::vector< OutputRoute > AllowedOutputRoutes
decltype(auto) make(const Output &spec, Args... args)
void snapshot(const Output &spec, T const &object)
CacheId adoptContainer(const Output &, ContainerT &, CacheStrategy, o2::header::SerializationMethod)
void forwardPayload(const Output &spec, fair::mq::Message &inputPayload, o2::header::SerializationMethod serializationMethod=o2::header::gSerializationMethodNone)
void adoptChunk(const Output &, char *, size_t, fair::mq::FreeFn *, void *)
void snapshot(const Output &spec, T const &object)
bool isAllowed(Output const &query)
check if a certain output is allowed
void cookDeadBeef(const Output &spec)
o2::header::Stack * findMessageHeaderStack(OutputRef &&ref)
DataChunk & newChunk(OutputRef &&ref, size_t size)
decltype(auto) make(const Output &spec, Args... args)
decltype(auto) make(const Output &spec, std::same_as< std::shared_ptr< arrow::Schema > > auto schema)
void adopt(OutputRef &&ref, T *obj)
decltype(auto) make(const Output &spec, Args... args)
o2::header::DataHeader * findMessageHeader(const Output &spec)
void snapshot(const Output &spec, T const &object)
void snapshot(const Output &spec, T const &object)
void snapshot(const Output &spec, T const &object)
TrivialObject handles a message object.
GLenum void ** pointer
Definition glcorearb.h:805
GLuint entry
Definition glcorearb.h:5735
GLsizeiptr size
Definition glcorearb.h:659
GLuint index
Definition glcorearb.h:781
GLuint const GLchar * name
Definition glcorearb.h:781
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLenum target
Definition glcorearb.h:1641
GLboolean * data
Definition glcorearb.h:298
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLuint object
Definition glcorearb.h:4041
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
ServiceKind
The kind of service we are asking for.
RuntimeErrorRef runtime_error_f(const char *,...)
Descriptor< gSizeDataDescriptionString > DataDescription
Definition DataHeader.h:551
constexpr o2::header::SerializationMethod gSerializationMethodROOT
Definition DataHeader.h:328
constexpr o2::header::SerializationMethod gSerializationMethodNone
Definition DataHeader.h:327
Descriptor< gSizeDataOriginString > DataOrigin
Definition DataHeader.h:550
fair::mq::MessagePtr getMessage(ContainerT &&container, FairMQMemoryResource *targetResource=nullptr)
std::vector< T, fair::mq::pmr::polymorphic_allocator< T > > vector
fair::mq::MemoryResource FairMQMemoryResource
LifetimeHolder(const LifetimeHolder &)=delete
LifetimeHolder & operator=(LifetimeHolder &&other)
LifetimeHolder & operator=(const LifetimeHolder &)=delete
LifetimeHolder(LifetimeHolder &&other)
std::function< void(T &)> callback
the main header struct
Definition DataHeader.h:620
uint32_t SubSpecificationType
Definition DataHeader.h:622
a move-only header stack with serialized headers This is the flat buffer where all the headers in a m...
Definition Stack.h:33
VectorOfTObjectPtrs other