Project
Loading...
Searching...
No Matches
MemoryResources.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
23
27
28#ifndef ALICEO2_MEMORY_RESOURCES_
29#define ALICEO2_MEMORY_RESOURCES_
30
31#include <boost/container/pmr/memory_resource.hpp>
32#include <boost/container/pmr/monotonic_buffer_resource.hpp>
33#include <boost/container/pmr/polymorphic_allocator.hpp>
34#include <cstring>
35#include <string>
36#include <type_traits>
37#include <utility>
38#include <vector>
39#include <unordered_map>
40#include <fairmq/Message.h>
41#include <fairmq/TransportFactory.h>
42#include <fairmq/MemoryResources.h>
43#include <fairmq/MemoryResourceTools.h>
44
45namespace o2::pmr
46{
47
48using FairMQMemoryResource = fair::mq::MemoryResource;
49using ChannelResource = fair::mq::ChannelResource;
50using namespace fair::mq::pmr;
51
52template <typename ContainerT>
53fair::mq::MessagePtr getMessage(ContainerT&& container, FairMQMemoryResource* targetResource = nullptr)
54{
55 return fair::mq::getMessage(std::forward<ContainerT>(container), targetResource);
56}
57
58//__________________________________________________________________________________________________
64{
65
66 public:
67 MessageResource() noexcept = delete;
68 MessageResource(const MessageResource&) noexcept = default;
69 MessageResource(MessageResource&&) noexcept = default;
70 MessageResource& operator=(const MessageResource&) = default;
71 MessageResource& operator=(MessageResource&&) = default;
72 MessageResource(fair::mq::MessagePtr message)
73 : mUpstream{message->GetTransport()->GetMemoryResource()},
74 mMessageSize{message->GetSize()},
75 mMessageData{mUpstream ? mUpstream->setMessage(std::move(message))
76 : throw std::runtime_error("MessageResource::MessageResource upstream is nullptr")}
77 {
78 }
79 fair::mq::MessagePtr getMessage(void* p) override { return mUpstream->getMessage(p); }
80 void* setMessage(fair::mq::MessagePtr message) override { return mUpstream->setMessage(std::move(message)); }
81 fair::mq::TransportFactory* getTransportFactory() noexcept override { return nullptr; }
82 size_t getNumberOfMessages() const noexcept override { return mMessageData ? 1 : 0; }
83
84 protected:
86 size_t mMessageSize{0};
87 void* mMessageData{nullptr};
88 bool initialImport{true};
89
90 void* do_allocate(std::size_t bytes, std::size_t alignment) override
91 {
92 if (initialImport) {
93 if (bytes > mMessageSize) {
94 throw std::bad_alloc();
95 }
96 initialImport = false;
97 return mMessageData;
98 } else {
99 return mUpstream->allocate(bytes, alignment < 64 ? 64 : alignment);
100 }
101 }
102 void do_deallocate(void* p, std::size_t bytes, std::size_t alignment) override
103 {
104 mUpstream->deallocate(p, bytes, alignment < 64 ? 64 : alignment);
105 return;
106 }
107 bool do_is_equal(const memory_resource& /*other*/) const noexcept override
108 {
109 // since this uniquely owns the message it can never be equal to anybody else
110 return false;
111 }
112};
113
114//__________________________________________________________________________________________________
115// A spectator pmr memory resource which only watches the memory of the underlying buffer, does not
116// carry out real allocation. It owns the underlying buffer which is destroyed on deallocation.
117template <typename BufferType>
119{
120 public:
121 using buffer_type = BufferType;
122
123 SpectatorMemoryResource() noexcept = delete;
128 ~SpectatorMemoryResource() noexcept override = default;
129
130 // the resource is the pointer managed by unique_ptr
131 template <typename T>
132 SpectatorMemoryResource(std::unique_ptr<T, typename buffer_type::deleter_type>&& buffer, size_t size)
133 : mBuffer{std::move(buffer)}, mPointer{mBuffer.get()}, mSize{size}
134 {
135 }
136
137 // the resource is the data of the vector managed by unique ptr
138 template <typename T>
139 SpectatorMemoryResource(std::unique_ptr<std::vector<T>, typename buffer_type::deleter_type>&& buffer)
140 : mBuffer{std::move(buffer)}, mPointer{mBuffer->data()}, mSize{mBuffer->size() * sizeof(T)}
141 {
142 }
143
144 // TODO: the underlying resource can be directly the vector or the read only buffer
145 protected:
146 void* do_allocate(std::size_t bytes, std::size_t /*alignment*/) override
147 {
148 if (mSize > 0) {
149 if (bytes > mSize) {
150 throw std::bad_alloc();
151 }
152 mSize = 0;
153 return mPointer;
154 }
155 throw std::runtime_error("Can not allocate: this memory resource is only supposed to provide spectator access to external buffer");
156 }
157
158 void do_deallocate(void* p, std::size_t /*bytes*/, std::size_t /*alignment*/) override
159 {
160 if (p == mPointer) {
161 mBuffer.reset();
162 mPointer = nullptr;
163 } else if (mPointer == nullptr) {
164 // there is an error in the logic flow, this should never be called more than once
165 throw std::logic_error("underlying controlled resource has been released already");
166 } else {
167 throw std::logic_error("this resource can only deallocate the controlled resource pointer");
168 }
169 }
170 bool do_is_equal(const memory_resource& /*other*/) const noexcept override
171 {
172 // uniquely owns the underlying resource, can never be equal to any other instance
173 return false;
174 }
175
176 private:
177 buffer_type mBuffer;
178 void* mPointer = nullptr;
179 size_t mSize = 0;
180};
181
182//__________________________________________________________________________________________________
183// This in general (as in STL) is a bad idea, but here it is safe to inherit from an allocator since we
184// have no additional data and only override some methods so we don't get into slicing and other problems.
185template <typename T>
186class SpectatorAllocator : public boost::container::pmr::polymorphic_allocator<T>
187{
188 public:
189 using boost::container::pmr::polymorphic_allocator<T>::polymorphic_allocator;
191
192 // skip default construction of empty elements
193 // this is important for two reasons: one: it allows us to adopt an existing buffer (e.g. incoming message) and
194 // quickly construct large vectors while skipping the element initialization.
195 template <class U>
196 void construct(U*)
197 {
198 }
199
200 // dont try to call destructors, makes no sense since resource is managed externally AND allowed
201 // types cannot have side effects
202 template <typename U>
203 void destroy(U*)
204 {
205 }
206
207 T* allocate(size_t size) { return reinterpret_cast<T*>(this->resource()->allocate(size * sizeof(T), 64)); }
208 void deallocate(T* ptr, size_t size)
209 {
210 this->resource()->deallocate(const_cast<typename std::remove_cv<T>::type*>(ptr), size);
211 }
212};
213
214//__________________________________________________________________________________________________
217template <typename T>
219{
220 public:
221 using value_type = T;
222
224
229
230 template <class U>
234
236 {
237 mResource = other.mResource;
238 return *this;
239 }
240
245
247
248 // skip default construction of empty elements
249 // this is important for two reasons: one: it allows us to adopt an existing buffer (e.g. incoming message) and
250 // quickly construct large vectors while skipping the element initialization.
251 template <class U>
252 void construct(U*)
253 {
254 }
255
256 // dont try to call destructors, makes no sense since resource is managed externally AND allowed
257 // types cannot have side effects
258 template <typename U>
259 void destroy(U*)
260 {
261 }
262
263 T* allocate(size_t size) { return reinterpret_cast<T*>(mResource.allocate(size * sizeof(T), 64)); }
264 void deallocate(T* ptr, size_t size)
265 {
266 mResource.deallocate(const_cast<typename std::remove_cv<T>::type*>(ptr), size);
267 }
268};
269
270// The NoConstructAllocator behaves like the normal pmr vector but does not call constructors / destructors
271template <typename T>
272class NoConstructAllocator : public boost::container::pmr::polymorphic_allocator<T>
273{
274 public:
275 using boost::container::pmr::polymorphic_allocator<T>::polymorphic_allocator;
277
278 template <typename... Args>
279 NoConstructAllocator(Args&&... args) : boost::container::pmr::polymorphic_allocator<T>(std::forward<Args>(args)...)
280 {
281 }
282
283 // skip default construction of empty elements
284 // this is important for two reasons: one: it allows us to adopt an existing buffer (e.g. incoming message) and
285 // quickly construct large vectors while skipping the element initialization.
286 template <class U>
287 void construct(U*)
288 {
289 }
290
291 // dont try to call destructors, makes no sense since resource is managed externally AND allowed
292 // types cannot have side effects
293 template <typename U>
294 void destroy(U*)
295 {
296 }
297};
298
299//__________________________________________________________________________________________________
300//__________________________________________________________________________________________________
301//__________________________________________________________________________________________________
302//__________________________________________________________________________________________________
303
305using BytePmrAllocator = boost::container::pmr::polymorphic_allocator<std::byte>;
306template <class T>
307using vector = std::vector<T, o2::pmr::polymorphic_allocator<T>>;
308
309//__________________________________________________________________________________________________
311template <typename ElemT>
312auto adoptVector(size_t nelem, fair::mq::MessagePtr message)
313{
314 static_assert(std::is_trivially_destructible<ElemT>::value);
315 return std::vector<ElemT, OwningMessageSpectatorAllocator<ElemT>>(
317};
318
319//__________________________________________________________________________________________________
321inline static FairMQMemoryResource* getTransportAllocator(fair::mq::TransportFactory* factory)
322{
323 return *factory;
324}
325
326} // namespace o2::pmr
327
328#endif
TBranch * ptr
const auto & getMessage()
void * setMessage(fair::mq::MessagePtr message) override
MessageResource() noexcept=delete
fair::mq::TransportFactory * getTransportFactory() noexcept override
void do_deallocate(void *p, std::size_t bytes, std::size_t alignment) override
void * do_allocate(std::size_t bytes, std::size_t alignment) override
FairMQMemoryResource * mUpstream
fair::mq::MessagePtr getMessage(void *p) override
bool do_is_equal(const memory_resource &) const noexcept override
size_t getNumberOfMessages() const noexcept override
std::true_type propagate_on_container_move_assignment
NoConstructAllocator(Args &&... args)
OwningMessageSpectatorAllocator(const OwningMessageSpectatorAllocator< U > &other) noexcept
OwningMessageSpectatorAllocator() noexcept=default
OwningMessageSpectatorAllocator & operator=(const OwningMessageSpectatorAllocator &other)
boost::container::pmr::memory_resource * resource()
OwningMessageSpectatorAllocator select_on_container_copy_construction() const
void deallocate(T *ptr, size_t size)
std::true_type propagate_on_container_move_assignment
void * do_allocate(std::size_t bytes, std::size_t) override
bool do_is_equal(const memory_resource &) const noexcept override
SpectatorMemoryResource() noexcept=delete
SpectatorMemoryResource(std::unique_ptr< std::vector< T >, typename buffer_type::deleter_type > &&buffer)
void do_deallocate(void *p, std::size_t, std::size_t) override
GLuint buffer
Definition glcorearb.h:655
GLsizeiptr size
Definition glcorearb.h:659
GLboolean * data
Definition glcorearb.h:298
GLuint GLsizei const GLchar * message
Definition glcorearb.h:2517
O2 memory allocators and interfaces related to managing memory via the trasport layer.
boost::container::pmr::polymorphic_allocator< std::byte > BytePmrAllocator
auto adoptVector(size_t nelem, fair::mq::MessagePtr message)
Return a std::vector spanned over the contents of the message, takes ownership of the message.
fair::mq::ChannelResource ChannelResource
std::vector< T, o2::pmr::polymorphic_allocator< T > > vector
Defining DataPointCompositeObject explicitly as copiable.
uint32_t buffer_type
VectorOfTObjectPtrs other