Adopt FairMQMessage backed memory resource collection from AliceO2

Add a pmr interface to FairMQTransportFactory

refactor

Port the unit tests for MemoryResources

clang format
This commit is contained in:
mkrzewic 2018-07-30 18:39:30 +02:00 committed by Dennis Klein
parent 919193a1ad
commit 310b9647b5
9 changed files with 711 additions and 5 deletions

View File

@ -49,7 +49,7 @@ find_package(Threads REQUIRED)
if(BUILD_FAIRMQ) if(BUILD_FAIRMQ)
find_package2(PUBLIC Boost VERSION 1.64 REQUIRED find_package2(PUBLIC Boost VERSION 1.64 REQUIRED
COMPONENTS program_options thread system filesystem regex date_time signals COMPONENTS container program_options thread system filesystem regex date_time signals
) )
find_package2(PUBLIC FairLogger VERSION 1.2.0 REQUIRED) find_package2(PUBLIC FairLogger VERSION 1.2.0 REQUIRED)
find_package2(PRIVATE ZeroMQ VERSION 4.1.5 REQUIRED) find_package2(PRIVATE ZeroMQ VERSION 4.1.5 REQUIRED)

View File

@ -49,6 +49,8 @@ set(FAIRMQ_PUBLIC_HEADER_FILES
FairMQSocket.h FairMQSocket.h
FairMQStateMachine.h FairMQStateMachine.h
FairMQTransportFactory.h FairMQTransportFactory.h
MemoryResources.h
MemoryResourceTools.h
Tools.h Tools.h
Transports.h Transports.h
options/FairMQProgOptions.h options/FairMQProgOptions.h
@ -155,6 +157,7 @@ set(FAIRMQ_SOURCE_FILES
zeromq/FairMQUnmanagedRegionZMQ.cxx zeromq/FairMQUnmanagedRegionZMQ.cxx
zeromq/FairMQSocketZMQ.cxx zeromq/FairMQSocketZMQ.cxx
zeromq/FairMQTransportFactoryZMQ.cxx zeromq/FairMQTransportFactoryZMQ.cxx
MemoryResources.cxx
) )
if(BUILD_NANOMSG_TRANSPORT) if(BUILD_NANOMSG_TRANSPORT)
@ -232,6 +235,7 @@ endif()
target_link_libraries(${_target} target_link_libraries(${_target}
INTERFACE # only consumers link against interface dependencies INTERFACE # only consumers link against interface dependencies
Boost::container
PUBLIC # libFairMQ AND consumers of libFairMQ link aginst public dependencies PUBLIC # libFairMQ AND consumers of libFairMQ link aginst public dependencies
Threads::Threads Threads::Threads

View File

@ -9,11 +9,12 @@
#ifndef FAIRMQTRANSPORTFACTORY_H_ #ifndef FAIRMQTRANSPORTFACTORY_H_
#define FAIRMQTRANSPORTFACTORY_H_ #define FAIRMQTRANSPORTFACTORY_H_
#include <FairMQMessage.h>
#include <FairMQSocket.h>
#include <FairMQPoller.h>
#include <FairMQUnmanagedRegion.h>
#include <FairMQLogger.h> #include <FairMQLogger.h>
#include <FairMQMessage.h>
#include <FairMQPoller.h>
#include <FairMQSocket.h>
#include <FairMQUnmanagedRegion.h>
#include <fairmq/MemoryResources.h>
#include <fairmq/Transports.h> #include <fairmq/Transports.h>
#include <string> #include <string>
@ -30,6 +31,9 @@ class FairMQTransportFactory
/// Topology wide unique id /// Topology wide unique id
const std::string fkId; const std::string fkId;
/// The polymorphic memory resource associated with the transport
fair::mq::ChannelResource fMemoryResource{this};
public: public:
/// ctor /// ctor
/// @param id Topology wide unique id, usually the device id. /// @param id Topology wide unique id, usually the device id.
@ -37,6 +41,9 @@ class FairMQTransportFactory
auto GetId() const -> const std::string { return fkId; }; auto GetId() const -> const std::string { return fkId; };
/// Get a pointer to the associated polymorphic memory resource
fair::mq::ChannelResource* GetMemoryResource() { return &fMemoryResource; }
/// @brief Create empty FairMQMessage /// @brief Create empty FairMQMessage
/// @return pointer to FairMQMessage /// @return pointer to FairMQMessage
virtual FairMQMessagePtr CreateMessage() const = 0; virtual FairMQMessagePtr CreateMessage() const = 0;

View File

@ -0,0 +1,119 @@
/********************************************************************************
* Copyright (C) 2018 CERN and copyright holders of ALICE O2 *
* Copyright (C) 2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
/// @brief Tools for interfacing containers to the transport via polymorphic
/// allocators
///
/// @author Mikolaj Krzewicki, mkrzewic@cern.ch
#include <fairmq/FairMQTransportFactory.h>
#include <fairmq/MemoryResources.h>
namespace fair {
namespace mq {
using ByteSpectatorAllocator = SpectatorAllocator<fair::mq::byte>;
using BytePmrAllocator = boost::container::pmr::polymorphic_allocator<fair::mq::byte>;
//_________________________________________________________________________________________________
// return the message associated with the container or nullptr if it does not
// make sense (e.g. when
// we are just watching an existing message or when the container is not using
// FairMQMemoryResource
// as backend).
template<typename ContainerT>
// typename std::enable_if<
// std::is_base_of<
// boost::container::pmr::polymorphic_allocator<typename
// ContainerT::value_type>,
// typename ContainerT::allocator_type>::value == true,
// FairMQMessagePtr>::type
FairMQMessagePtr getMessage(ContainerT &&container_, FairMQMemoryResource *targetResource = nullptr)
{
auto container = std::move(container_);
auto alloc = container.get_allocator();
auto resource = dynamic_cast<FairMQMemoryResource *>(alloc.resource());
if (!resource && !targetResource) {
throw std::runtime_error("Neither the container or target resource specified");
}
size_t containerSizeBytes = container.size() * sizeof(typename ContainerT::value_type);
if ((!targetResource && resource)
|| (resource && targetResource && resource->is_equal(*targetResource))) {
auto message = resource->getMessage(static_cast<void *>(
const_cast<typename std::remove_const<typename ContainerT::value_type>::type *>(
container.data())));
if (message)
message->SetUsedSize(containerSizeBytes);
return std::move(message);
} else {
auto message = targetResource->getTransportFactory()->CreateMessage(containerSizeBytes);
std::memcpy(static_cast<fair::mq::byte *>(message->GetData()),
container.data(),
containerSizeBytes);
return std::move(message);
}
};
//_________________________________________________________________________________________________
/// Return a vector of const ElemT, resource must be kept alive throughout the
/// lifetime of the
/// container and associated message.
template<typename ElemT>
std::vector<const ElemT, boost::container::pmr::polymorphic_allocator<const ElemT>> adoptVector(
size_t nelem,
SpectatorMessageResource *resource)
{
return std::vector<const ElemT, SpectatorAllocator<const ElemT>>(
nelem, SpectatorAllocator<ElemT>(resource));
};
//_________________________________________________________________________________________________
/// Return a vector of const ElemT, takes ownership of the message, needs an
/// upstream global
/// ChannelResource to register the message.
template<typename ElemT>
std::vector<const ElemT, OwningMessageSpectatorAllocator<const ElemT>>
adoptVector(size_t nelem, ChannelResource *upstream, FairMQMessagePtr message)
{
return std::vector<const ElemT, OwningMessageSpectatorAllocator<const ElemT>>(
nelem,
OwningMessageSpectatorAllocator<const ElemT>(
MessageResource{std::move(message), upstream}));
};
//_________________________________________________________________________________________________
// TODO: this is C++14, converting it down to C++11 is too much work atm
// This returns a unique_ptr of const vector, does not allow modifications at
// the cost of pointer
// semantics for access.
// use auto or decltype to catch the return value (or use span)
// template<typename ElemT>
// auto adoptVector(size_t nelem, FairMQMessage* message)
//{
// using DataType = std::vector<ElemT, ByteSpectatorAllocator>;
//
// struct doubleDeleter
// {
// // kids: don't do this at home! (but here it's OK)
// // this stateful deleter allows a single unique_ptr to manage 2
// resources at the same time.
// std::unique_ptr<SpectatorMessageResource> extra;
// void operator()(const DataType* ptr) { delete ptr; }
// };
//
// using OutputType = std::unique_ptr<const DataType, doubleDeleter>;
//
// auto resource = std::make_unique<SpectatorMessageResource>(message);
// auto output = new DataType(nelem, ByteSpectatorAllocator{resource.get()});
// return OutputType(output, doubleDeleter{std::move(resource)});
//}
} /* namespace mq */
} /* namespace fair */

View File

@ -0,0 +1,25 @@
/********************************************************************************
* Copyright (C) 2018 CERN and copyright holders of ALICE O2 *
* Copyright (C) 2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
/// @brief Memory allocators and interfaces related to managing memory via the
/// trasport layer
///
/// @author Mikolaj Krzewicki, mkrzewic@cern.ch
#include <fairmq/FairMQTransportFactory.h>
#include <fairmq/MemoryResources.h>
void *fair::mq::ChannelResource::do_allocate(std::size_t bytes, std::size_t /*alignment*/)
{
FairMQMessagePtr message;
message = factory->CreateMessage(bytes);
void *addr = message->GetData();
messageMap[addr] = std::move(message);
return addr;
};

342
fairmq/MemoryResources.h Normal file
View File

@ -0,0 +1,342 @@
/********************************************************************************
* Copyright (C) 2018 CERN and copyright holders of ALICE O2 *
* Copyright (C) 2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
/// @brief Memory allocators and interfaces related to managing memory via the
/// trasport layer
///
/// @author Mikolaj Krzewicki, mkrzewic@cern.ch
#ifndef FAIR_MQ_MEMORY_RESOURCES_H
#define FAIR_MQ_MEMORY_RESOURCES_H
#include <fairmq/FairMQMessage.h>
class FairMQTransportFactory;
#include <boost/container/flat_map.hpp>
#include <boost/container/pmr/memory_resource.hpp>
#include <boost/container/pmr/monotonic_buffer_resource.hpp>
#include <boost/container/pmr/polymorphic_allocator.hpp>
#include <cstring>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
namespace fair {
namespace mq {
using byte = unsigned char;
/// All FairMQ related memory resources need to inherit from this interface
/// class for the
/// getMessage() api.
class FairMQMemoryResource : public boost::container::pmr::memory_resource
{
public:
/// return the message containing data associated with the pointer (to start
/// of
/// buffer), e.g. pointer returned by std::vector::data() return nullptr if
/// returning
/// a message does not make sense!
virtual FairMQMessagePtr getMessage(void *p) = 0;
virtual void *setMessage(FairMQMessagePtr) = 0;
virtual const FairMQTransportFactory *getTransportFactory() const noexcept = 0;
virtual size_t getNumberOfMessages() const noexcept = 0;
};
/// This is the allocator that interfaces to FairMQ memory management. All
/// allocations are
/// delegated to FairMQ so standard (e.g. STL) containers can construct their
/// stuff in
/// memory regions appropriate for the data channel configuration.
class ChannelResource : public FairMQMemoryResource
{
protected:
const FairMQTransportFactory *factory{nullptr};
// TODO: for now a map to keep track of allocations, something else would
// probably be
// faster, but for now this does not need to be fast.
boost::container::flat_map<void *, FairMQMessagePtr> messageMap;
public:
ChannelResource() = delete;
ChannelResource(const FairMQTransportFactory *_factory)
: FairMQMemoryResource()
, factory(_factory)
, messageMap()
{
if (!_factory) {
throw std::runtime_error("Tried to construct from a nullptr FairMQTransportFactory");
}
};
FairMQMessagePtr getMessage(void *p) override
{
auto mes = std::move(messageMap[p]);
messageMap.erase(p);
return mes;
}
void *setMessage(FairMQMessagePtr message) override
{
void *addr = message->GetData();
messageMap[addr] = std::move(message);
return addr;
}
const FairMQTransportFactory *getTransportFactory() const noexcept override { return factory; }
size_t getNumberOfMessages() const noexcept override { return messageMap.size(); }
protected:
void *do_allocate(std::size_t bytes, std::size_t alignment) override;
void do_deallocate(void *p, std::size_t /*bytes*/, std::size_t /*alignment*/) override
{
messageMap.erase(p);
};
bool do_is_equal(const boost::container::pmr::memory_resource &other) const noexcept override
{
return this == &other;
};
};
/// This memory resource only watches, does not allocate/deallocate anything.
/// In combination with the ByteSpectatorAllocator this is an alternative to
/// using span, as raw
/// memory (e.g. an existing buffer message) will be accessible with appropriate
/// container.
class SpectatorMessageResource : public FairMQMemoryResource
{
public:
SpectatorMessageResource() = default;
SpectatorMessageResource(const FairMQMessage *_message)
: message(_message)
{
}
FairMQMessagePtr getMessage(void * /*p*/) override { return nullptr; }
const FairMQTransportFactory *getTransportFactory() const noexcept override { return nullptr; }
size_t getNumberOfMessages() const noexcept override { return 0; }
void *setMessage(FairMQMessagePtr) override { return nullptr; }
protected:
const FairMQMessage *message;
void *do_allocate(std::size_t bytes, std::size_t /*alignment*/) override
{
if (message) {
if (bytes > message->GetSize()) {
throw std::bad_alloc();
}
return message->GetData();
} else {
return nullptr;
}
}
void do_deallocate(void * /*p*/, std::size_t /*bytes*/, std::size_t /*alignment*/) override
{
message = nullptr;
return;
}
bool do_is_equal(const memory_resource &other) const noexcept override
{
const SpectatorMessageResource *that =
dynamic_cast<const SpectatorMessageResource *>(&other);
if (!that) {
return false;
}
if (that->message == message) {
return true;
}
return false;
}
};
/// This memory resource only watches, does not allocate/deallocate anything.
/// Ownership of the message is taken. Meant to be used for transparent data
/// adoption in containers.
/// In combination with the SpectatorAllocator this is an alternative to using
/// span, as raw memory
/// (e.g. an existing buffer message) will be accessible with appropriate
/// container.
class MessageResource : public FairMQMemoryResource
{
public:
MessageResource() noexcept = delete;
MessageResource(const MessageResource &) noexcept = default;
MessageResource(MessageResource &&) noexcept = default;
MessageResource &operator=(const MessageResource &) = default;
MessageResource &operator=(MessageResource &&) = default;
MessageResource(FairMQMessagePtr message, FairMQMemoryResource *upstream)
: mUpstream{upstream}
, mMessageSize{message->GetSize()}
, mMessageData{mUpstream ? mUpstream->setMessage(std::move(message))
: throw std::runtime_error(
"MessageResource::MessageResource upstream is nullptr")}
{
}
FairMQMessagePtr getMessage(void *p) override { return mUpstream->getMessage(p); }
void *setMessage(FairMQMessagePtr message) override
{
return mUpstream->setMessage(std::move(message));
}
const FairMQTransportFactory *getTransportFactory() const noexcept override { return nullptr; }
size_t getNumberOfMessages() const noexcept override { return mMessageData ? 1 : 0; }
protected:
FairMQMemoryResource *mUpstream{nullptr};
size_t mMessageSize{0};
void *mMessageData{nullptr};
void *do_allocate(std::size_t bytes, std::size_t /*alignment*/) override
{
if (bytes > mMessageSize) {
throw std::bad_alloc();
}
return mMessageData;
}
void do_deallocate(void * /*p*/, std::size_t /*bytes*/, std::size_t /*alignment*/) override
{
getMessage(mMessageData); // let the message die.
return;
}
bool do_is_equal(const memory_resource & /*other*/) const noexcept override
{
// since this uniquely owns the message it can never be equal to anybody
// else
return false;
}
};
// This in general (as in STL) is a bad idea, but here it is safe to inherit
// from an allocator since
// we have no additional data and only override some methods so we don't get
// into slicing and other
// problems.
template<typename T>
class SpectatorAllocator : public boost::container::pmr::polymorphic_allocator<T>
{
public:
using boost::container::pmr::polymorphic_allocator<T>::polymorphic_allocator;
// skip default construction of empty elements
// this is important for two reasons: one: it allows us to adopt an existing
// buffer (e.g.
// incoming message) and quickly construct large vectors while skipping the
// element
// initialization.
template<class U>
void construct(U *)
{
}
// dont try to call destructors, makes no sense since resource is managed
// externally AND allowed
// types cannot have side effects
template<typename U>
void destroy(U *)
{
}
T *allocate(size_t size)
{
return reinterpret_cast<T *>(this->resource()->allocate(size * sizeof(T), 0));
}
void deallocate(T *ptr, size_t size)
{
this->resource()->deallocate(const_cast<typename std::remove_cv<T>::type *>(ptr), size);
}
};
/// This allocator has a pmr-like interface, but keeps the unique
/// MessageResource as internal state,
/// allowing full resource (associated message) management internally without
/// any global state.
template<typename T>
class OwningMessageSpectatorAllocator
{
public:
using value_type = T;
MessageResource mResource;
OwningMessageSpectatorAllocator() noexcept = default;
OwningMessageSpectatorAllocator(const OwningMessageSpectatorAllocator &) noexcept = default;
OwningMessageSpectatorAllocator(OwningMessageSpectatorAllocator &&) noexcept = default;
OwningMessageSpectatorAllocator(MessageResource &&resource) noexcept
: mResource{resource}
{
}
template<class U>
OwningMessageSpectatorAllocator(const OwningMessageSpectatorAllocator<U> &other) noexcept
: mResource(other.mResource)
{
}
OwningMessageSpectatorAllocator &operator=(const OwningMessageSpectatorAllocator &other)
{
mResource = other.mResource;
return *this;
}
OwningMessageSpectatorAllocator select_on_container_copy_construction() const
{
return OwningMessageSpectatorAllocator();
}
boost::container::pmr::memory_resource *resource() { return &mResource; }
// skip default construction of empty elements
// this is important for two reasons: one: it allows us to adopt an existing
// buffer (e.g.
// incoming message) and quickly construct large vectors while skipping the
// element
// initialization.
template<class U>
void construct(U *)
{
}
// dont try to call destructors, makes no sense since resource is managed
// externally AND allowed
// types cannot have side effects
template<typename U>
void destroy(U *)
{
}
T *allocate(size_t size)
{
return reinterpret_cast<T *>(mResource.allocate(size * sizeof(T), 0));
}
void deallocate(T *ptr, size_t size)
{
mResource.deallocate(const_cast<typename std::remove_cv<T>::type *>(ptr), size);
}
};
} /* namespace mq */
} /* namespace fair */
#endif /* FAIR_MQ_MEMORY_RESOURCES_H */

View File

@ -222,3 +222,14 @@ add_testsuite(FairMQ.Poller
TIMEOUT 10 TIMEOUT 10
${definitions} ${definitions}
) )
add_testsuite(FairMQ.MemoryResources
SOURCES
memory_resources/runner.cxx
memory_resources/_memory_resources.cxx
LINKS FairMQ
INCLUDES ${CMAKE_CURRENT_BINARY_DIR}
TIMEOUT 10
)

View File

@ -0,0 +1,179 @@
/********************************************************************************
* Copyright (C) 2018 Goethe University Frankfurt *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include <cstring>
#include <fairmq/FairMQTransportFactory.h>
#include <fairmq/MemoryResourceTools.h>
#include <gtest/gtest.h>
#include <vector>
namespace {
using namespace std;
using namespace fair::mq;
using factoryType = std::shared_ptr<FairMQTransportFactory>;
factoryType factoryZMQ = FairMQTransportFactory::CreateTransportFactory("zeromq");
factoryType factorySHM = FairMQTransportFactory::CreateTransportFactory("shmem");
struct testData
{
int i{1};
static int nallocated;
static int nallocations;
static int ndeallocations;
testData()
{
++nallocated;
++nallocations;
}
testData(const testData& in)
: i{in.i}
{
++nallocated;
++nallocations;
}
testData(const testData&& in)
: i{in.i}
{
++nallocated;
++nallocations;
}
testData(int in)
: i{in}
{
++nallocated;
++nallocations;
}
~testData()
{
--nallocated;
++ndeallocations;
}
};
int testData::nallocated = 0;
int testData::nallocations = 0;
int testData::ndeallocations = 0;
auto allocZMQ = factoryZMQ -> GetMemoryResource();
auto allocSHM = factorySHM -> GetMemoryResource();
TEST(MemoryResources, transportallocatormap_test)
{
EXPECT_TRUE(allocZMQ != nullptr && allocSHM != allocZMQ);
auto _tmp = factoryZMQ->GetMemoryResource();
EXPECT_TRUE(_tmp == allocZMQ);
}
using namespace boost::container::pmr;
TEST(MemoryResources, allocator_test)
{
testData::nallocations = 0;
testData::ndeallocations = 0;
{
std::vector<testData, polymorphic_allocator<testData>> v(
polymorphic_allocator<testData>{allocZMQ});
v.reserve(3);
EXPECT_TRUE(v.capacity() == 3);
EXPECT_TRUE(allocZMQ->getNumberOfMessages() == 1);
v.emplace_back(1);
v.emplace_back(2);
v.emplace_back(3);
EXPECT_TRUE((byte*)&(*v.end()) - (byte*)&(*v.begin()) == 3 * sizeof(testData));
EXPECT_TRUE(testData::nallocated == 3);
}
EXPECT_TRUE(testData::nallocated == 0);
EXPECT_TRUE(testData::nallocations == testData::ndeallocations);
testData::nallocations = 0;
testData::ndeallocations = 0;
{
std::vector<testData, SpectatorAllocator<testData>> v(
SpectatorAllocator<testData>{allocZMQ});
v.reserve(3);
EXPECT_TRUE(allocZMQ->getNumberOfMessages() == 1);
v.emplace_back(1);
v.emplace_back(2);
v.emplace_back(3);
EXPECT_TRUE(testData::nallocated == 3);
}
EXPECT_TRUE(testData::nallocated
== 3); // ByteSpectatorAllocator does not call dtors so nallocated remains at 3;
EXPECT_TRUE(allocZMQ->getNumberOfMessages() == 0);
}
TEST(MemoryResources, getMessage_test)
{
testData::nallocations = 0;
testData::ndeallocations = 0;
FairMQMessagePtr message{nullptr};
int* messageArray{nullptr};
// test message creation on the same channel it was allocated with
{
std::vector<testData, polymorphic_allocator<testData>> v(
polymorphic_allocator<testData>{allocZMQ});
v.emplace_back(1);
v.emplace_back(2);
v.emplace_back(3);
void* vectorBeginPtr = &v[0];
message = getMessage(std::move(v));
EXPECT_TRUE(message != nullptr);
EXPECT_TRUE(message->GetData() == vectorBeginPtr);
}
EXPECT_TRUE(message->GetSize() == 3 * sizeof(testData));
messageArray = static_cast<int*>(message->GetData());
EXPECT_TRUE(messageArray[0] == 1 && messageArray[1] == 2 && messageArray[2] == 3);
// test message creation on a different channel than it was allocated with
{
std::vector<testData, polymorphic_allocator<testData>> v(
polymorphic_allocator<testData>{allocZMQ});
v.emplace_back(4);
v.emplace_back(5);
v.emplace_back(6);
void* vectorBeginPtr = &v[0];
message = getMessage(std::move(v), allocSHM);
EXPECT_TRUE(message != nullptr);
EXPECT_TRUE(message->GetData() != vectorBeginPtr);
}
EXPECT_TRUE(message->GetSize() == 3 * sizeof(testData));
messageArray = static_cast<int*>(message->GetData());
EXPECT_TRUE(messageArray[0] == 4 && messageArray[1] == 5 && messageArray[2] == 6);
{
std::vector<testData, SpectatorAllocator<testData>> v(
SpectatorAllocator<testData>{allocSHM});
}
}
TEST(MemoryResources, adoptVector_test)
{
// Create a bogus message
auto message = factoryZMQ->CreateMessage(3 * sizeof(testData));
auto messageAddr = message.get();
testData tmpBuf[3] = {3, 2, 1};
std::memcpy(message->GetData(), tmpBuf, 3 * sizeof(testData));
auto adoptedOwner =
adoptVector<testData>(3, factoryZMQ->GetMemoryResource(), std::move(message));
EXPECT_TRUE(adoptedOwner[0].i == 3);
EXPECT_TRUE(adoptedOwner[1].i == 2);
EXPECT_TRUE(adoptedOwner[2].i == 1);
auto reclaimedMessage = getMessage(std::move(adoptedOwner));
EXPECT_TRUE(reclaimedMessage.get() == messageAddr);
EXPECT_TRUE(adoptedOwner.size() == 0);
}
} // namespace

View File

@ -0,0 +1,19 @@
/********************************************************************************
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include <TestEnvironment.h>
#include <gtest/gtest.h>
auto main(int argc, char** argv) -> int
{
::testing::InitGoogleTest(&argc, argv);
::testing::FLAGS_gtest_death_test_style = "threadsafe";
setenv("FAIRMQ_PATH", FAIRMQ_TEST_ENVIRONMENT, 0);
return RUN_ALL_TESTS();
}