Project
Loading...
Searching...
No Matches
MessageContext.h
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.
11#ifndef O2_FRAMEWORK_MESSAGECONTEXT_H_
12#define O2_FRAMEWORK_MESSAGECONTEXT_H_
13
22
23#include "Headers/DataHeader.h"
24#include "Headers/Stack.h"
26
27#include <fairmq/Message.h>
28#include <fairmq/Parts.h>
29
30#include <cassert>
31#include <functional>
32#include <string>
33#include <type_traits>
34#include <unordered_map>
35#include <vector>
36
37#include <fairmq/FwdDecls.h>
38
39namespace o2::framework
40{
41
42template <typename T, typename = void>
43struct enable_root_serialization : std::false_type {
44 using debug_type = T;
45};
46
47template <typename T, typename = void>
48struct root_serializer : std::false_type {
49};
50
51struct Output;
52
54{
55 public:
56 enum class DispatchState {
60 };
61
63
64 // so far we are only using one instance per named channel
65 static constexpr int DefaultChannelIndex = 0;
66
68 : mProxy{proxy}
69 {
70 }
71
73 : mProxy{proxy}, mDispatchControl{dispatcher}
74 {
75 }
76
77 void init(DispatchControl&& dispatcher)
78 {
79 mDispatchControl = dispatcher;
80 }
81
82 // this is the virtual interface for context objects
84 {
85 public:
86 ContextObject() = delete;
87 ContextObject(fair::mq::MessagePtr&& headerMsg, fair::mq::MessagePtr&& payloadMsg, RouteIndex routeIndex)
88 : mParts{}, mRouteIndex{routeIndex}
89 {
90 mParts.AddPart(std::move(headerMsg));
91 mParts.AddPart(std::move(payloadMsg));
92 }
93
94 ContextObject(fair::mq::MessagePtr&& headerMsg, RouteIndex routeIndex)
95 : mParts{}, mRouteIndex{routeIndex}
96 {
97 mParts.AddPart(std::move(headerMsg));
98 }
99
100 virtual ~ContextObject() = default;
101
105 virtual fair::mq::Parts finalize()
106 {
107 fair::mq::Parts parts = std::move(mParts);
108 assert(parts.Size() == 2);
109 auto* header = o2::header::get<o2::header::DataHeader*>(parts.At(0)->GetData());
110 if (header == nullptr) {
111 throw std::logic_error("No valid header message found");
112 } else {
113 // o2::header::get returns const pointer, but here we can change the message
114 const_cast<o2::header::DataHeader*>(header)->payloadSize = parts.At(1)->GetSize();
115 }
116 // return value optimization returns by move
117 return parts;
118 }
119
121 [[nodiscard]] RouteIndex route() const
122 {
123 return mRouteIndex;
124 }
125
126 [[nodiscard]] bool empty() const
127 {
128 return mParts.Size() == 0;
129 }
130
132 {
133 // we would expect this function to be const but the fair::mq::Parts API does not allow this
134 if (empty() || mParts.At(0) == nullptr) {
135 return nullptr;
136 }
137 return o2::header::get<o2::header::DataHeader*>(mParts.At(0)->GetData());
138 }
139
141 {
142 if (empty() || mParts.At(0) == nullptr) {
143 return nullptr;
144 }
145 return o2::header::get<o2::framework::DataProcessingHeader*>(mParts.At(0)->GetData());
146 }
147
149 {
150 // we would expect this function to be const but the fair::mq::Parts API does not allow this
151 if (empty() || mParts.At(0) == nullptr) {
152 return nullptr;
153 }
154 return o2::header::get<o2::header::DataHeader*>(mParts.At(0)->GetData()) ? reinterpret_cast<o2::header::Stack*>(mParts.At(0)->GetData()) : nullptr;
155 }
156
157 protected:
158 fair::mq::Parts mParts;
160 };
161
164 {
165 public:
167 TrivialObject() = delete;
169 template <typename ContextType>
170 TrivialObject(ContextType* context, fair::mq::MessagePtr&& headerMsg, fair::mq::MessagePtr&& payloadMsg, RouteIndex routeIndex)
171 : ContextObject(std::forward<fair::mq::MessagePtr>(headerMsg), std::forward<fair::mq::MessagePtr>(payloadMsg), routeIndex)
172 {
173 }
175 template <typename ContextType, typename... Args>
176 TrivialObject(ContextType* context, fair::mq::MessagePtr&& headerMsg, RouteIndex routeIndex, int index, Args... args)
177 : ContextObject(std::forward<fair::mq::MessagePtr>(headerMsg), context->createMessage(routeIndex, index, std::forward<Args>(args)...), routeIndex)
178 {
179 }
180 ~TrivialObject() override = default;
181
182 auto* data()
183 {
184 assert(mParts.Size() == 2);
185 return mParts[1].GetData();
186 }
187 };
188
189 // A memory resource which can force a minimum alignment, so that
190 // the whole polymorphic allocator business is happy...
192 {
193 public:
194 AlignedMemoryResource(fair::mq::MemoryResource* other)
195 : mUpstream(other)
196 {
197 }
198
200 : mUpstream(other.mUpstream)
201 {
202 }
203
204 bool isValid()
205 {
206 return mUpstream != nullptr;
207 }
208 fair::mq::MessagePtr getMessage(void* p) override
209 {
210 return mUpstream->getMessage(p);
211 }
212
213 void* setMessage(fair::mq::MessagePtr fmm) override
214 {
215 return mUpstream->setMessage(std::move(fmm));
216 }
217
218 fair::mq::TransportFactory* getTransportFactory() noexcept override
219 {
220 return mUpstream->getTransportFactory();
221 }
222
223 [[nodiscard]] size_t getNumberOfMessages() const noexcept override
224 {
225 return mUpstream->getNumberOfMessages();
226 }
227
228 protected:
229 void* do_allocate(size_t bytes, size_t alignment) override
230 {
231 return mUpstream->allocate(bytes, alignment < 64 ? 64 : alignment);
232 }
233
234 void do_deallocate(void* p, size_t bytes, size_t alignment) override
235 {
236 return mUpstream->deallocate(p, bytes, alignment < 64 ? 64 : alignment);
237 }
238
239 [[nodiscard]] bool do_is_equal(const pmr::memory_resource& other) const noexcept override
240 {
241 return this == &other;
242 }
243
244 private:
245 fair::mq::MemoryResource* mUpstream = nullptr;
246 };
247
253 template <typename T>
255 {
256 public:
257 using value_type = typename T::value_type;
258 using return_type = T;
260 static_assert(std::is_base_of<o2::pmr::polymorphic_allocator<value_type>, typename T::allocator_type>::value, "container must have polymorphic allocator");
264 template <typename ContextType, typename... Args>
265 ContainerRefObject(ContextType* context, fair::mq::MessagePtr&& headerMsg, RouteIndex routeIndex, int index, Args&&... args)
266 : ContextObject(std::forward<fair::mq::MessagePtr>(headerMsg), routeIndex),
267 // the transport factory
268 mFactory{context->proxy().getOutputTransport(routeIndex)},
269 // the memory resource takes ownership of the message
270 mResource{mFactory ? AlignedMemoryResource(mFactory->GetMemoryResource()) : AlignedMemoryResource(nullptr)},
271 // create the vector with apropriate underlying memory resource for the message
272 mData{std::forward<Args>(args)..., pmr::polymorphic_allocator<value_type>(&mResource)}
273 {
274 // FIXME: drop this repeated check and make sure at initial setup of devices that everything is fine
275 // introduce error policy
276 if (mFactory == nullptr) {
277 throw runtime_error_f("failed to get transport factory for route %d", routeIndex);
278 }
279 if (mResource.isValid() == false) {
280 throw runtime_error_f("no memory resource for channel %d", routeIndex);
281 }
282 }
283 ~ContainerRefObject() override = default;
284
287 fair::mq::Parts finalize() final
288 {
289 assert(mParts.Size() == 1);
290 auto payloadMsg = o2::pmr::getMessage(std::move(mData));
291 mParts.AddPart(std::move(payloadMsg));
293 }
294
296 operator return_type&()
297 {
298 return mData;
299 }
300
303 {
304 return mData;
305 }
306
309 {
310 return mData.data();
311 }
312
313 private:
314 fair::mq::TransportFactory* mFactory = nullptr;
315 AlignedMemoryResource mResource;
316 buffer_type mData;
317 };
318
322 template <typename T, typename _BASE = ContainerRefObject<std::vector<T, o2::pmr::polymorphic_allocator<T>>>>
323 class VectorObject : public _BASE
324 {
325 public:
326 template <typename... Args>
327 VectorObject(Args&&... args) : _BASE(std::forward<Args>(args)...)
328 {
329 }
330 };
331
332 // SpanObject creates a trivial binary object for an array of elements of
333 // type T and holds a span over the elements
334 // FIXME: probably obsolete after introducing of vector with polymorphic_allocator
335 template <typename T>
337 {
338 public:
339 static_assert(is_messageable<T>::value == true, "unconsistent type");
340 using value_type = gsl::span<T>;
342 SpanObject() = delete;
344 template <typename ContextType>
345 SpanObject(ContextType* context, fair::mq::MessagePtr&& headerMsg, RouteIndex routeIndex, int index, size_t nElements)
346 : ContextObject(std::forward<fair::mq::MessagePtr>(headerMsg), routeIndex)
347 {
348 // create the span object for the memory of the payload message
349 // TODO: we probably also want to check consistency of the header message, i.e. payloadSize member
350 auto payloadMsg = context->createMessage(routeIndex, index, nElements * sizeof(T));
351 mValue = value_type(reinterpret_cast<T*>(payloadMsg->GetData()), nElements);
352 assert(mParts.Size() == 1);
353 mParts.AddPart(std::move(payloadMsg));
354 }
355 ~SpanObject() override = default;
356
357 operator value_type&()
358 {
359 return mValue;
360 }
361
363 {
364 return mValue;
365 }
366
367 private:
368 value_type mValue;
369 };
370
371 using Messages = std::vector<std::unique_ptr<ContextObject>>;
372
378 template <typename T, typename BASE = std::default_delete<T>>
379 class ScopeHook : public BASE
380 {
381 public:
382 using base = std::default_delete<T>;
384 ScopeHook() = default;
386 : mContext(context)
387 {
388 }
389 ~ScopeHook() = default;
390
391 // forbid assignment operator to prohibid changing the Deleter
392 // resource control property once used in the unique_ptr
393 self_type& operator=(const self_type&) = delete;
394
395 void operator()(T* ptr) const
396 {
397 if (!mContext) {
398 // TODO: decide whether this is an error or not
399 // can also check if the standard constructor can be dropped to make sure that
400 // the ScopeHook is always set up with a context
401 throw runtime_error("No context available to schedule the context object");
402 return base::operator()(ptr);
403 }
404 // keep the object alive and add to message list of the context
405 mContext->schedule(Messages::value_type(ptr));
406 }
407
408 private:
409 MessageContext* mContext = nullptr;
410 };
411
412 template <typename T>
413 using ContextObjectScope = std::unique_ptr<T, ScopeHook<T>>;
414
420 template <typename T, typename... Args>
421 auto& add(Args&&... args)
422 {
423 mMessages.push_back(std::move(make<T>(std::forward<Args>(args)...)));
424 // return a reference to the element in the vector of unique pointers
425 return *dynamic_cast<T*>(mMessages.back().get());
426 }
427
432 template <typename T, typename... Args>
433 Messages::value_type make(Args&&... args)
434 {
435 static_assert(std::is_base_of<ContextObject, T>::value == true, "type must inherit ContextObject interface");
436 return std::make_unique<T>(this, std::forward<Args>(args)...);
437 }
438
443 template <typename T, typename... Args>
445 {
446 ContextObjectScope<T> scope(dynamic_cast<T*>(make<T>(std::forward<Args>(args)...).release()), ScopeHook<T>(this));
447 return scope;
448 }
449
453 void schedule(Messages::value_type&& message);
454
456 {
457 // before starting iteration, message lists are merged
458 for (auto& message : mScheduledMessages) {
459 mMessages.emplace_back(std::move(message));
460 }
461 mScheduledMessages.clear();
462 return std::move(mMessages);
463 }
464
465 size_t size()
466 {
467 return mMessages.size();
468 }
469
473 void clear();
474
478 void discard();
479
481 {
482 return mProxy;
483 }
484
485 // Add a message to cache and returns a unique identifier for
486 // such cached message.
487 int64_t addToCache(std::unique_ptr<fair::mq::Message>& message);
488 // Clone a message from cache so that it can be added to the context
489 [[nodiscard]] std::unique_ptr<fair::mq::Message> cloneFromCache(int64_t id) const;
490 // Prune a message from cache
491 void pruneFromCache(int64_t id);
492
496 // FIXME: can that be const?
497 fair::mq::MessagePtr createMessage(RouteIndex routeIndex, int index, size_t size);
498 fair::mq::MessagePtr createMessage(RouteIndex routeIndex, int index, void* data, size_t size, fair::mq::FreeFn* ffn, void* hint);
499
503 [[nodiscard]] int countDeviceOutputs(bool excludeDPLOrigin = false) const;
504 void fakeDispatch() { mDispatchState = DispatchState::Dispatched; }
505 [[nodiscard]] DispatchState dispatchState() const { return mDispatchState; }
507 std::pair<o2::header::DataHeader*, o2::framework::DataProcessingHeader*> findMessageHeaders(const Output& spec);
508
509 private:
510 FairMQDeviceProxy& mProxy;
511 Messages mMessages;
512 Messages mScheduledMessages;
514 DispatchControl mDispatchControl;
516 std::unordered_map<int64_t, std::unique_ptr<fair::mq::Message>> mMessageCache;
517};
518} // namespace o2::framework
519#endif // O2_FRAMEWORK_MESSAGECONTEXT_H_
TBranch * ptr
AlignedMemoryResource(fair::mq::MemoryResource *other)
bool do_is_equal(const pmr::memory_resource &other) const noexcept override
void * do_allocate(size_t bytes, size_t alignment) override
AlignedMemoryResource(AlignedMemoryResource const &other)
void do_deallocate(void *p, size_t bytes, size_t alignment) override
void * setMessage(fair::mq::MessagePtr fmm) override
fair::mq::MessagePtr getMessage(void *p) override
size_t getNumberOfMessages() const noexcept override
fair::mq::TransportFactory * getTransportFactory() noexcept override
ContainerRefObject()=delete
default contructor forbidden, object always has to control message instances
fair::mq::Parts finalize() final
Finalize object and return parts by move This retrieves the actual message from the vector object and...
ContainerRefObject(ContextType *context, fair::mq::MessagePtr &&headerMsg, RouteIndex routeIndex, int index, Args &&... args)
constructor taking header message by move and creating the paypload message
return_type & get()
return reference to the handled vector object
value_type * data()
return data pointer of the handled vector object
ContextObject(fair::mq::MessagePtr &&headerMsg, fair::mq::MessagePtr &&payloadMsg, RouteIndex routeIndex)
o2::header::DataHeader const * header()
o2::framework::DataProcessingHeader const * dataProcessingHeader()
ContextObject(fair::mq::MessagePtr &&headerMsg, RouteIndex routeIndex)
virtual fair::mq::Parts finalize()
Finalize the object and return the parts by move This is the default method and can be overloaded by ...
RouteIndex route() const
return the channel name
self_type & operator=(const self_type &)=delete
SpanObject()=delete
default constructor forbidden, object alwasy has to control messages
SpanObject(ContextType *context, fair::mq::MessagePtr &&headerMsg, RouteIndex routeIndex, int index, size_t nElements)
constructor taking header message by move and creating the payload message for the span
TrivialObject handles a message object.
TrivialObject(ContextType *context, fair::mq::MessagePtr &&headerMsg, RouteIndex routeIndex, int index, Args... args)
constructor taking header message by move and creating the paypload message
TrivialObject()=delete
default contructor forbidden, object always has to control message instances
TrivialObject(ContextType *context, fair::mq::MessagePtr &&headerMsg, fair::mq::MessagePtr &&payloadMsg, RouteIndex routeIndex)
constructor consuming the header and payload messages for a given channel by move
void init(DispatchControl &&dispatcher)
o2::header::Stack * findMessageHeaderStack(const Output &spec)
Messages::value_type make(Args &&... args)
std::pair< o2::header::DataHeader *, o2::framework::DataProcessingHeader * > findMessageHeaders(const Output &spec)
static constexpr int DefaultChannelIndex
auto & add(Args &&... args)
std::unique_ptr< T, ScopeHook< T > > ContextObjectScope
void schedule(Messages::value_type &&message)
int countDeviceOutputs(bool excludeDPLOrigin=false) const
int64_t addToCache(std::unique_ptr< fair::mq::Message > &message)
MessageContext(FairMQDeviceProxy &proxy)
o2::header::DataHeader * findMessageHeader(const Output &spec)
return the headers of the 1st (from the end) matching message checking first in mMessages then in mSc...
MessageContext(FairMQDeviceProxy &proxy, DispatchControl &&dispatcher)
DispatchState dispatchState() const
ContextObjectScope< T > make_scoped(Args &&... args)
std::unique_ptr< fair::mq::Message > cloneFromCache(int64_t id) const
fair::mq::MessagePtr createMessage(RouteIndex routeIndex, int index, size_t size)
o2::framework::DataProcessingHeader * findMessageDataProcessingHeader(const Output &spec)
FairMQDeviceProxy & proxy()
static constexpr ServiceKind service_kind
std::vector< std::unique_ptr< ContextObject > > Messages
GLsizeiptr size
Definition glcorearb.h:659
GLuint index
Definition glcorearb.h:781
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLboolean * data
Definition glcorearb.h:298
GLuint GLsizei const GLchar * message
Definition glcorearb.h:2517
Definition ASoA.h:45
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
RuntimeErrorRef runtime_error(const char *)
ServiceKind
The kind of service we are asking for.
RuntimeErrorRef runtime_error_f(const char *,...)
fair::mq::MessagePtr getMessage(ContainerT &&container, FairMQMemoryResource *targetResource=nullptr)
fair::mq::MemoryResource FairMQMemoryResource
Control for the message dispatching within message context. Depending on dispatching policy,...
the main header struct
Definition DataHeader.h:620
a move-only header stack with serialized headers This is the flat buffer where all the headers in a m...
Definition Stack.h:33
uint32_t buffer_type
VectorOfTObjectPtrs other