From 4ec3610181f4bcd0f23c47f2ab0afd6123d22c3e Mon Sep 17 00:00:00 2001 From: AdolfoMartinez <49362510+adolfomarver@users.noreply.github.com> Date: Tue, 24 Mar 2020 07:19:11 +0100 Subject: [PATCH] Shared-memory transport implementation (#992) * shared_mem_transport interface classes * build errors solved * Refs #6942. Send-refactor * SharedMemTransport refactoring * SharedMemTransportInterface removed * SharedMemTransport interface v0.1 * fields added to SharedMemTransportDescriptor * Shared Memory transport unit testing * test outgoing port is open * Refs #6486. SharedMemoryManager::Port::Readers are now Listeners. * Refs #6942. SharedMemTransport receives LocatorsIterator * Refs #7022. SharedMemManager integration * Refs #7022. SharedMemTransport test pass * Refs #7022. UUIDs generator. * Refs #7022. Fix compilation on Windows. * Refs #7022. Fix Tests in Windows. * Refs #7114. Try clean port before open * Refs #7114. Helloworld working with shared-memory transport * Refs #7114. Healthy check mecanism * Refs #7114. Example HelloWorldExampleSharedMem added. * Refs #7114. Reliability tests added. * Refs #7114. Traces added. * Refs #7114. Code review. * Refs #7114. memset when creating segment * Refs #7114. XML profile for SharedMemTransport. * Refs #7237. UUIDs performance improvements. * Refs #7237. Bugfix in healthy_check mecanism. * Refs #7237. Bugfix in SharedMemTest (Windows). * Refs #7237: Add SHM best effort latency test * Refs #7237. Compilation warning removed. * Refs #7237. Simple throughput tests added to shared-mem and udpv4. * Refs #7237. Avoid copy to shared-memory when destination locators empty. * Refs #7237. dev/shm files clean-up * Refs #7237. Interprocess notify asynchronous * Refs #7237. Bugfix: Big data wasn't supported. * Refs #7364. Big datagrams (>64K) support in RTPS layer. * Refs #7364. Big samples in HelloWorldExampleSharedMem. * Refs #7409 Fix: locator_kind not checked in SHM send. * Refs #7409. Transport selection when SHM & UDP available. * Refs #7409 Bugs fixed. * Refs #7409. Fix UDP+SHM vs UDP. * Refs #7409. recv_buffer_size for SHM decoupled from max_message_size. * Refs #7409. HelloWorldExampleSharedMem configurable message size. * Refs #7409. HelloWorldExampleSharedMem Both transports active. * Refs #7409. Refactor SHMEM -> SHM. * Refs #7409. Warning removal. * Refs #7237. SHM Throughput test added to cmake. * Refs #7237. SHM transport default_port_queue_capacity set to 512. * Refs #7479. Warning removal. * Refs #7479. SharedMem-RingBuffer tests added. * Refs #7479. SharedMemTransportTests send-timeout 100(ms) => 100(us). * Refs #7409. Max message size limits to 64KB when participant is secure. * Refs #7479. SHM & Security blackbox tests added * Refs #6558. Remove warnings & errors in Windows. * Refs #6558. Remove errors for V140 toolset. * Refs #6558. Disable some tests. * Refs #6558. ShareMemTransportTests - log::Flush(). * Refs #6558. Clag warnings removed & more logError/Warning. * Refs #6558. More clang warnings removed. * Refs #6558. Refactor SHM. * Refs #6558. Windows shared-memory environment initialization. * Refs #6558. Fix compilation error on Linux, Mac. * Refs #7479. SHM Blackbox tests. * Refs #7479. NOMINMAX added for MSVC to avoid compilation errors. * Refs #7479. Compilation error in MAC with clang solved. * Refs #7479. Enable SHM Generic emulation on Windows & MAC. * Refs #7479. Latency test reliable_shm added. * Refs #7479. Remove inexplicable warning with VC(v140 toolset). * Refs #7479. Remove valgrind errors. * Refs #7237. Performance throughput improvement. * Refs #7237. Performance Latency improvement. * Refs #7237. Uncrustify. * Refs #7237. Allocate Buffer node & data in only one allocation. * Refs #7479. Fix SharedMemTest mutex busy deletion. * Refs #7479 Fix Test:SHMTransportTests.dead_listener_port_recover. * Refs #7699. Supporting SHM transport with secure participants. * Refs #7699. Refactor & Windows compilation errors solved. * Refs #6558. rtps_dump_file added to SharedMemTransportDescriptor. * Refs #7684. max_message_size SHM 64K to improve latency. * Refs #7684. maxMessageSize for SHM configurable in XML. * Refs #7517. Requested changes applied. * Refs #7517. Warnings fix. * Refs #7856. SHM unit tests failing under valgrind. * Refs #6558. Requested changes applied. * Refs #7839. Packet hex dump to file for SHM transport. * Refs #7839. Fix warnings & SHM dump_file test failure on MAC. * Refs #6645. Valgrind suppression for sem_open added. * Refs #6645. Fix CMake wrong path for some files of SharedMem unit tests. * Refs #6645. Added --gen-suppressions=all * Refs #7517. Removed unused method on ReaderProxyData. * Refs #7517. Ignore shared memory locators from other vendors. * Revert "Refs #6645. Added --gen-suppressions=all" This reverts commit 8e5d72d7c3012979ecb421e086d794df0e289ee7. Co-authored-by: Iker Luengo Co-authored-by: EduPonz Co-authored-by: Miguel Company --- .../C++/HelloWorldExampleSharedMem/.gitignore | 2 + .../HelloWorldExampleSharedMem/CMakeLists.txt | 59 + .../HelloWorldExampleSharedMem/HelloWorld.cxx | 255 ++++ .../HelloWorldExampleSharedMem/HelloWorld.h | 244 ++++ .../HelloWorldExampleSharedMem/HelloWorld.idl | 6 + .../HelloWorldPubSubTypes.cxx | 152 ++ .../HelloWorldPubSubTypes.h | 67 + .../HelloWorldPublisher.cpp | 190 +++ .../HelloWorldPublisher.h | 74 + .../HelloWorldSubscriber.cpp | 148 ++ .../HelloWorldSubscriber.h | 77 + .../HelloWorld_main.cpp | 120 ++ .../C++/HelloWorldExampleSharedMem/README.txt | 6 + examples/CMakeLists.txt | 1 + .../rtps/builtin/data/ParticipantProxyData.h | 3 +- .../rtps/builtin/data/ReaderProxyData.h | 14 +- .../rtps/builtin/data/WriterProxyData.h | 3 +- include/fastdds/rtps/common/Locator.h | 16 +- .../fastdds/rtps/common/LocatorSelector.hpp | 10 +- .../fastdds/rtps/common/RemoteLocators.hpp | 14 + include/fastdds/rtps/network/NetworkFactory.h | 6 +- .../fastdds/rtps/network/ReceiverResource.h | 6 + include/fastdds/rtps/reader/StatefulReader.h | 4 +- .../rtps/transport/TCPTransportInterface.h | 7 +- .../rtps/transport/TransportInterface.h | 5 + .../rtps/transport/UDPTransportInterface.h | 5 + .../shared_mem/SharedMemTransportDescriptor.h | 144 ++ include/fastrtps/xmlparser/XMLParser.h | 4 + include/fastrtps/xmlparser/XMLParserCommon.h | 9 + resources/xsd/fastRTPS_profiles.xsd | 13 + src/cpp/CMakeLists.txt | 5 + .../publisher/PublisherImpl.cpp | 2 +- .../fastrtps_deprecated/utils/IPFinder.cpp | 1 + src/cpp/rtps/RTPSDomain.cpp | 26 +- .../builtin/data/ParticipantProxyData.cpp | 48 +- .../rtps/builtin/data/ProxyDataFilters.hpp | 115 ++ src/cpp/rtps/builtin/data/ReaderProxyData.cpp | 39 +- src/cpp/rtps/builtin/data/WriterProxyData.cpp | 40 +- .../rtps/builtin/discovery/endpoint/EDP.cpp | 8 +- .../discovery/endpoint/EDPSimpleListeners.cpp | 6 +- .../discovery/participant/PDPListener.cpp | 7 +- .../participant/PDPServerListener.cpp | 4 +- src/cpp/rtps/network/NetworkFactory.cpp | 15 +- src/cpp/rtps/network/ReceiverResource.cpp | 9 +- .../rtps/participant/RTPSParticipantImpl.cpp | 34 +- .../rtps/participant/RTPSParticipantImpl.h | 8 + .../MultiProducerConsumerRingBuffer.hpp | 403 ++++++ .../rtps/transport/shared_mem/SHMLocator.hpp | 85 ++ .../shared_mem/SharedMemChannelResource.hpp | 201 +++ .../transport/shared_mem/SharedMemGlobal.hpp | 619 ++++++++ .../transport/shared_mem/SharedMemLog.hpp | 412 ++++++ .../transport/shared_mem/SharedMemManager.hpp | 593 ++++++++ .../transport/shared_mem/SharedMemSegment.hpp | 276 ++++ .../shared_mem/SharedMemSenderResource.hpp | 89 ++ .../shared_mem/SharedMemTransport.cpp | 641 +++++++++ .../transport/shared_mem/SharedMemTransport.h | 269 ++++ .../transport/shared_mem/SharedMemUUID.hpp | 207 +++ .../test_SharedMemChannelResource.hpp | 80 ++ .../shared_mem/test_SharedMemTransport.cpp | 88 ++ .../shared_mem/test_SharedMemTransport.h | 55 + .../test_SharedMemTransportDescriptor.h | 48 + src/cpp/rtps/xmlparser/XMLParser.cpp | 122 +- src/cpp/rtps/xmlparser/XMLParserCommon.cpp | 9 + src/cpp/utils/Host.hpp | 81 ++ test/blackbox/BlackboxTestsSecurity.cpp | 147 +- test/blackbox/BlackboxTestsTransportSHM.cpp | 349 +++++ .../fastdds/rtps/network/ReceiverResource.h | 5 +- .../shared_mem/SharedMemTransportDescriptor.h | 154 ++ test/performance/latency/CMakeLists.txt | 2 + .../xml/interprocess_best_effort_shm.xml | 105 ++ .../latency/xml/interprocess_reliable_shm.xml | 104 ++ test/performance/throughput/CMakeLists.txt | 2 + .../throughput/payloads_demands.csv | 2 + .../xml/interprocess_best_effort_shm.xml | 84 ++ .../xml/interprocess_reliable_shm.xml | 81 ++ test/unittest/dynamic_types/CMakeLists.txt | 1 + .../rtps/network/NetworkFactoryTests.cpp | 12 +- .../rtps/network/mock/MockTransport.h | 6 +- .../security/authentication/CMakeLists.txt | 5 +- test/unittest/transport/CMakeLists.txt | 37 + test/unittest/transport/SharedMemTests.cpp | 1270 +++++++++++++++++ test/unittest/transport/TCPv6Tests.cpp | 2 +- test/unittest/transport/UDPv4Tests.cpp | 65 + .../transport/mock/SharedMemGlobalMock.hpp | 77 + test/unittest/xmlparser/CMakeLists.txt | 5 + .../SHM_transport_descriptors_config.xml | 18 + .../xmlparser/XMLProfileParserTests.cpp | 25 + .../include/boost/config/abi/msvc_prefix.hpp | 22 + .../include/boost/config/abi/msvc_suffix.hpp | 8 + .../boost/include/boost/config/abi_prefix.hpp | 25 + .../boost/include/boost/config/abi_suffix.hpp | 27 + .../include/boost/date_time/dst_rules.hpp | 391 +++++ .../posix_time/date_duration_operators.hpp | 114 ++ .../date_time/posix_time/posix_time_types.hpp | 55 + .../date_time/posix_time/time_period.hpp | 29 + .../include/boost/date_time/time_iterator.hpp | 52 + .../include/boost/thread/thread_time.hpp | 55 + thirdparty/boost/include/boostconfig.hpp | 121 +- .../test/ThirdpartyBoostCompile_test.cpp | 3 +- valgrind.supp | 5 + 100 files changed, 9663 insertions(+), 111 deletions(-) create mode 100644 examples/C++/HelloWorldExampleSharedMem/.gitignore create mode 100644 examples/C++/HelloWorldExampleSharedMem/CMakeLists.txt create mode 100644 examples/C++/HelloWorldExampleSharedMem/HelloWorld.cxx create mode 100644 examples/C++/HelloWorldExampleSharedMem/HelloWorld.h create mode 100644 examples/C++/HelloWorldExampleSharedMem/HelloWorld.idl create mode 100644 examples/C++/HelloWorldExampleSharedMem/HelloWorldPubSubTypes.cxx create mode 100644 examples/C++/HelloWorldExampleSharedMem/HelloWorldPubSubTypes.h create mode 100644 examples/C++/HelloWorldExampleSharedMem/HelloWorldPublisher.cpp create mode 100644 examples/C++/HelloWorldExampleSharedMem/HelloWorldPublisher.h create mode 100644 examples/C++/HelloWorldExampleSharedMem/HelloWorldSubscriber.cpp create mode 100644 examples/C++/HelloWorldExampleSharedMem/HelloWorldSubscriber.h create mode 100644 examples/C++/HelloWorldExampleSharedMem/HelloWorld_main.cpp create mode 100644 examples/C++/HelloWorldExampleSharedMem/README.txt create mode 100644 include/fastdds/rtps/transport/shared_mem/SharedMemTransportDescriptor.h create mode 100644 src/cpp/rtps/builtin/data/ProxyDataFilters.hpp create mode 100644 src/cpp/rtps/transport/shared_mem/MultiProducerConsumerRingBuffer.hpp create mode 100644 src/cpp/rtps/transport/shared_mem/SHMLocator.hpp create mode 100644 src/cpp/rtps/transport/shared_mem/SharedMemChannelResource.hpp create mode 100644 src/cpp/rtps/transport/shared_mem/SharedMemGlobal.hpp create mode 100644 src/cpp/rtps/transport/shared_mem/SharedMemLog.hpp create mode 100644 src/cpp/rtps/transport/shared_mem/SharedMemManager.hpp create mode 100644 src/cpp/rtps/transport/shared_mem/SharedMemSegment.hpp create mode 100644 src/cpp/rtps/transport/shared_mem/SharedMemSenderResource.hpp create mode 100644 src/cpp/rtps/transport/shared_mem/SharedMemTransport.cpp create mode 100644 src/cpp/rtps/transport/shared_mem/SharedMemTransport.h create mode 100644 src/cpp/rtps/transport/shared_mem/SharedMemUUID.hpp create mode 100644 src/cpp/rtps/transport/shared_mem/test_SharedMemChannelResource.hpp create mode 100644 src/cpp/rtps/transport/shared_mem/test_SharedMemTransport.cpp create mode 100644 src/cpp/rtps/transport/shared_mem/test_SharedMemTransport.h create mode 100644 src/cpp/rtps/transport/shared_mem/test_SharedMemTransportDescriptor.h create mode 100644 src/cpp/utils/Host.hpp create mode 100644 test/blackbox/BlackboxTestsTransportSHM.cpp create mode 100644 test/mock/rtps/SharedMemTransportDescriptor/fastdds/rtps/transport/shared_mem/SharedMemTransportDescriptor.h create mode 100644 test/performance/latency/xml/interprocess_best_effort_shm.xml create mode 100644 test/performance/latency/xml/interprocess_reliable_shm.xml create mode 100644 test/performance/throughput/xml/interprocess_best_effort_shm.xml create mode 100644 test/performance/throughput/xml/interprocess_reliable_shm.xml create mode 100644 test/unittest/transport/SharedMemTests.cpp create mode 100644 test/unittest/transport/mock/SharedMemGlobalMock.hpp create mode 100644 test/unittest/xmlparser/SHM_transport_descriptors_config.xml create mode 100644 thirdparty/boost/include/boost/config/abi/msvc_prefix.hpp create mode 100644 thirdparty/boost/include/boost/config/abi/msvc_suffix.hpp create mode 100644 thirdparty/boost/include/boost/config/abi_prefix.hpp create mode 100644 thirdparty/boost/include/boost/config/abi_suffix.hpp create mode 100644 thirdparty/boost/include/boost/date_time/dst_rules.hpp create mode 100644 thirdparty/boost/include/boost/date_time/posix_time/date_duration_operators.hpp create mode 100644 thirdparty/boost/include/boost/date_time/posix_time/posix_time_types.hpp create mode 100644 thirdparty/boost/include/boost/date_time/posix_time/time_period.hpp create mode 100644 thirdparty/boost/include/boost/date_time/time_iterator.hpp create mode 100644 thirdparty/boost/include/boost/thread/thread_time.hpp diff --git a/examples/C++/HelloWorldExampleSharedMem/.gitignore b/examples/C++/HelloWorldExampleSharedMem/.gitignore new file mode 100644 index 00000000000..ffc5570a9c3 --- /dev/null +++ b/examples/C++/HelloWorldExampleSharedMem/.gitignore @@ -0,0 +1,2 @@ +bin +output diff --git a/examples/C++/HelloWorldExampleSharedMem/CMakeLists.txt b/examples/C++/HelloWorldExampleSharedMem/CMakeLists.txt new file mode 100644 index 00000000000..8db3be59aaf --- /dev/null +++ b/examples/C++/HelloWorldExampleSharedMem/CMakeLists.txt @@ -0,0 +1,59 @@ +# Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 2.8.12) + +if(NOT CMAKE_VERSION VERSION_LESS 3.0) + cmake_policy(SET CMP0048 NEW) +endif() + +project(HelloWorldExampleSharedMem) + +# Find requirements +if(NOT fastcdr_FOUND) + find_package(fastcdr REQUIRED) +endif() + +if(NOT foonathan_memory_FOUND) + find_package(foonathan_memory REQUIRED) +endif() + +if(NOT fastrtps_FOUND) + find_package(fastrtps REQUIRED) +endif() + +# Set C++11 +include(CheckCXXCompilerFlag) +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG OR + CMAKE_CXX_COMPILER_ID MATCHES "Clang") + check_cxx_compiler_flag(-std=c++11 SUPPORTS_CXX11) + if(SUPPORTS_CXX11) + add_compile_options(-std=c++11) + else() + message(FATAL_ERROR "Compiler doesn't support C++11") + endif() +endif() + +message(STATUS "Configuring HelloWorld example...") +file(GLOB HELLOWORLD_EXAMPLE_SOURCES_CXX "*.cxx") +file(GLOB HELLOWORLD_EXAMPLE_SOURCES_CPP "*.cpp") + +if(WIN32) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +add_executable(HelloWorldExampleSharedMem ${HELLOWORLD_EXAMPLE_SOURCES_CXX} ${HELLOWORLD_EXAMPLE_SOURCES_CPP}) +target_link_libraries(HelloWorldExampleSharedMem fastrtps fastcdr foonathan_memory) +install(TARGETS HelloWorldExampleSharedMem + RUNTIME DESTINATION examples/C++/HelloWorldExampleSharedMem/${BIN_INSTALL_DIR}) diff --git a/examples/C++/HelloWorldExampleSharedMem/HelloWorld.cxx b/examples/C++/HelloWorldExampleSharedMem/HelloWorld.cxx new file mode 100644 index 00000000000..277733d11ec --- /dev/null +++ b/examples/C++/HelloWorldExampleSharedMem/HelloWorld.cxx @@ -0,0 +1,255 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file HelloWorld.cpp + * This source file contains the definition of the described types in the IDL file. + * + * This file was generated by the tool gen. + */ + +#ifdef _WIN32 +// Remove linker warning LNK4221 on Visual Studio +namespace { char dummy; } +#endif + +#include "HelloWorld.h" +#include + +#include +using namespace eprosima::fastcdr::exception; + +#include + +HelloWorld::HelloWorld() +{ + // m_index com.eprosima.idl.parser.typecode.PrimitiveTypeCode@185d8b6 + m_index = 0; + // m_message com.eprosima.idl.parser.typecode.StringTypeCode@67784306 + m_message =""; + // m_data com.eprosima.idl.parser.typecode.ArrayTypeCode@335eadca + memset(&m_data, 0, DATA_SIZE); + +} + +HelloWorld::~HelloWorld() +{ +} + +HelloWorld::HelloWorld( + const HelloWorld& x) +{ + m_index = x.m_index; + m_message = x.m_message; + m_data = x.m_data; +} + +HelloWorld::HelloWorld( + HelloWorld&& x) +{ + m_index = x.m_index; + m_message = std::move(x.m_message); + m_data = std::move(x.m_data); +} + +HelloWorld& HelloWorld::operator=( + const HelloWorld& x) +{ + + m_index = x.m_index; + m_message = x.m_message; + m_data = x.m_data; + + return *this; +} + +HelloWorld& HelloWorld::operator=( + HelloWorld&& x) +{ + + m_index = x.m_index; + m_message = std::move(x.m_message); + m_data = std::move(x.m_data); + + return *this; +} + +size_t HelloWorld::getMaxCdrSerializedSize( + size_t current_alignment) +{ + size_t initial_alignment = current_alignment; + + + current_alignment += 4 + eprosima::fastcdr::Cdr::alignment(current_alignment, 4); + + current_alignment += 4 + eprosima::fastcdr::Cdr::alignment(current_alignment, 4) + 255 + 1; + + current_alignment += ((DATA_SIZE) * 1) + eprosima::fastcdr::Cdr::alignment(current_alignment, 1); + + return current_alignment - initial_alignment; +} + +size_t HelloWorld::getCdrSerializedSize( + const HelloWorld& data, + size_t current_alignment) +{ + (void)data; + size_t initial_alignment = current_alignment; + + current_alignment += 4 + eprosima::fastcdr::Cdr::alignment(current_alignment, 4); + + current_alignment += 4 + eprosima::fastcdr::Cdr::alignment(current_alignment, 4) + data.message().size() + 1; + + current_alignment += DATA_SIZE + eprosima::fastcdr::Cdr::alignment(current_alignment, 1); + + return current_alignment - initial_alignment; +} + +void HelloWorld::serialize( + eprosima::fastcdr::Cdr& scdr) const +{ + scdr << m_index; + scdr << m_message; + scdr << m_data; +} + +void HelloWorld::deserialize( + eprosima::fastcdr::Cdr& dcdr) +{ + dcdr >> m_index; + dcdr >> m_message; + dcdr >> m_data; +} + +/*! + * @brief This function sets a value in member index + * @param _index New value for member index + */ +void HelloWorld::index( + uint32_t _index) +{ + m_index = _index; +} + +/*! + * @brief This function returns the value of member index + * @return Value of member index + */ +uint32_t HelloWorld::index() const +{ + return m_index; +} + +/*! + * @brief This function returns a reference to member index + * @return Reference to member index + */ +uint32_t& HelloWorld::index() +{ + return m_index; +} + +/*! + * @brief This function copies the value in member message + * @param _message New value to be copied in member message + */ +void HelloWorld::message( + const std::string& _message) +{ + m_message = _message; +} + +/*! + * @brief This function moves the value in member message + * @param _message New value to be moved in member message + */ +void HelloWorld::message( + std::string&& _message) +{ + m_message = std::move(_message); +} + +/*! + * @brief This function returns a constant reference to member message + * @return Constant reference to member message + */ +const std::string& HelloWorld::message() const +{ + return m_message; +} + +/*! + * @brief This function returns a reference to member message + * @return Reference to member message + */ +std::string& HelloWorld::message() +{ + return m_message; +} +/*! + * @brief This function copies the value in member data + * @param _data New value to be copied in member data + */ +void HelloWorld::data( + const std::array& _data) +{ + m_data = _data; +} + +/*! + * @brief This function moves the value in member data + * @param _data New value to be moved in member data + */ +void HelloWorld::data( + std::array&& _data) +{ + m_data = std::move(_data); +} + +/*! + * @brief This function returns a constant reference to member data + * @return Constant reference to member data + */ +const std::array& HelloWorld::data() const +{ + return m_data; +} + +/*! + * @brief This function returns a reference to member data + * @return Reference to member data + */ +std::array& HelloWorld::data() +{ + return m_data; +} + +size_t HelloWorld::getKeyMaxCdrSerializedSize( + size_t current_alignment) +{ + size_t current_align = current_alignment; + + return current_align; +} + +bool HelloWorld::isKeyDefined() +{ + return false; +} + +void HelloWorld::serializeKey( + eprosima::fastcdr::Cdr& scdr) const +{ + (void) scdr; +} diff --git a/examples/C++/HelloWorldExampleSharedMem/HelloWorld.h b/examples/C++/HelloWorldExampleSharedMem/HelloWorld.h new file mode 100644 index 00000000000..c63ec9d0cad --- /dev/null +++ b/examples/C++/HelloWorldExampleSharedMem/HelloWorld.h @@ -0,0 +1,244 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file HelloWorld.h + * This header file contains the declaration of the described types in the IDL file. + * + * This file was generated by the tool gen. + */ + +#ifndef _HELLOWORLD_H_ +#define _HELLOWORLD_H_ + +#define DATA_SIZE (1024*1024) + +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#if defined(EPROSIMA_USER_DLL_EXPORT) +#define eProsima_user_DllExport __declspec( dllexport ) +#else +#define eProsima_user_DllExport +#endif +#else +#define eProsima_user_DllExport +#endif + +#if defined(_WIN32) +#if defined(EPROSIMA_USER_DLL_EXPORT) +#if defined(HelloWorld_SOURCE) +#define HelloWorld_DllAPI __declspec( dllexport ) +#else +#define HelloWorld_DllAPI __declspec( dllimport ) +#endif // HelloWorld_SOURCE +#else +#define HelloWorld_DllAPI +#endif +#else +#define HelloWorld_DllAPI +#endif // _WIN32 + +namespace eprosima { +namespace fastcdr { +class Cdr; +} +} + + +/*! + * @brief This class represents the structure HelloWorld defined by the user in the IDL file. + * @ingroup HELLOWORLD + */ +class HelloWorld +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport HelloWorld(); + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~HelloWorld(); + + /*! + * @brief Copy constructor. + * @param x Reference to the object HelloWorld that will be copied. + */ + eProsima_user_DllExport HelloWorld( + const HelloWorld& x); + + /*! + * @brief Move constructor. + * @param x Reference to the object HelloWorld that will be copied. + */ + eProsima_user_DllExport HelloWorld( + HelloWorld&& x); + + /*! + * @brief Copy assignment. + * @param x Reference to the object HelloWorld that will be copied. + */ + eProsima_user_DllExport HelloWorld& operator=( + const HelloWorld& x); + + /*! + * @brief Move assignment. + * @param x Reference to the object HelloWorld that will be copied. + */ + eProsima_user_DllExport HelloWorld& operator=( + HelloWorld&& x); + + /*! + * @brief This function sets a value in member index + * @param _index New value for member index + */ + eProsima_user_DllExport void index( + uint32_t _index); + + /*! + * @brief This function returns the value of member index + * @return Value of member index + */ + eProsima_user_DllExport uint32_t index() const; + + /*! + * @brief This function returns a reference to member index + * @return Reference to member index + */ + eProsima_user_DllExport uint32_t& index(); + + /*! + * @brief This function copies the value in member message + * @param _message New value to be copied in member message + */ + eProsima_user_DllExport void message( + const std::string& _message); + + /*! + * @brief This function moves the value in member message + * @param _message New value to be moved in member message + */ + eProsima_user_DllExport void message( + std::string&& _message); + + /*! + * @brief This function returns a constant reference to member message + * @return Constant reference to member message + */ + eProsima_user_DllExport const std::string& message() const; + + /*! + * @brief This function returns a reference to member message + * @return Reference to member message + */ + eProsima_user_DllExport std::string& message(); + /*! + * @brief This function copies the value in member data + * @param _data New value to be copied in member data + */ + eProsima_user_DllExport void data( + const std::array& _data); + + /*! + * @brief This function moves the value in member data + * @param _data New value to be moved in member data + */ + eProsima_user_DllExport void data( + std::array&& _data); + + /*! + * @brief This function returns a constant reference to member data + * @return Constant reference to member data + */ + eProsima_user_DllExport const std::array& data() const; + + /*! + * @brief This function returns a reference to member data + * @return Reference to member data + */ + eProsima_user_DllExport std::array& data(); + + /*! + * @brief This function returns the maximum serialized size of an object + * depending on the buffer alignment. + * @param current_alignment Buffer alignment. + * @return Maximum serialized size. + */ + eProsima_user_DllExport static size_t getMaxCdrSerializedSize( + size_t current_alignment = 0); + + /*! + * @brief This function returns the serialized size of a data depending on the buffer alignment. + * @param data Data which is calculated its serialized size. + * @param current_alignment Buffer alignment. + * @return Serialized size. + */ + eProsima_user_DllExport static size_t getCdrSerializedSize( + const HelloWorld& data, + size_t current_alignment = 0); + + /*! + * @brief This function serializes an object using CDR serialization. + * @param cdr CDR serialization object. + */ + eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& cdr) const; + + /*! + * @brief This function deserializes an object using CDR serialization. + * @param cdr CDR serialization object. + */ + eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr); + + + + /*! + * @brief This function returns the maximum serialized size of the Key of an object + * depending on the buffer alignment. + * @param current_alignment Buffer alignment. + * @return Maximum serialized size. + */ + eProsima_user_DllExport static size_t getKeyMaxCdrSerializedSize( + size_t current_alignment = 0); + + /*! + * @brief This function tells you if the Key has been defined for this type + */ + eProsima_user_DllExport static bool isKeyDefined(); + + /*! + * @brief This function serializes the key members of an object using CDR serialization. + * @param cdr CDR serialization object. + */ + eProsima_user_DllExport void serializeKey( + eprosima::fastcdr::Cdr& cdr) const; + +private: + + uint32_t m_index; + std::string m_message; + std::array m_data; +}; + +#endif // _HELLOWORLD_H_ \ No newline at end of file diff --git a/examples/C++/HelloWorldExampleSharedMem/HelloWorld.idl b/examples/C++/HelloWorldExampleSharedMem/HelloWorld.idl new file mode 100644 index 00000000000..9799dca5984 --- /dev/null +++ b/examples/C++/HelloWorldExampleSharedMem/HelloWorld.idl @@ -0,0 +1,6 @@ +struct HelloWorld +{ + unsigned long index; + string message; + char data[1024*1024]; +}; diff --git a/examples/C++/HelloWorldExampleSharedMem/HelloWorldPubSubTypes.cxx b/examples/C++/HelloWorldExampleSharedMem/HelloWorldPubSubTypes.cxx new file mode 100644 index 00000000000..9e7aceb5129 --- /dev/null +++ b/examples/C++/HelloWorldExampleSharedMem/HelloWorldPubSubTypes.cxx @@ -0,0 +1,152 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file HelloWorldPubSubTypes.cpp + * This header file contains the implementation of the serialization functions. + * + * This file was generated by the tool fastcdrgen. + */ + + +#include +#include + +#include "HelloWorldPubSubTypes.h" + +using namespace eprosima::fastrtps; +using namespace eprosima::fastrtps::rtps; + +HelloWorldPubSubType::HelloWorldPubSubType() +{ + setName("HelloWorldSharedMem"); + m_typeSize = static_cast(HelloWorld::getMaxCdrSerializedSize()) + 4 /*encapsulation*/; + m_isGetKeyDefined = HelloWorld::isKeyDefined(); + size_t keyLength = HelloWorld::getKeyMaxCdrSerializedSize()>16 ? HelloWorld::getKeyMaxCdrSerializedSize() : 16; + m_keyBuffer = reinterpret_cast(malloc(keyLength)); + memset(m_keyBuffer, 0, keyLength); +} + +HelloWorldPubSubType::~HelloWorldPubSubType() +{ + if (m_keyBuffer!=nullptr) + { + free(m_keyBuffer); + } +} + +bool HelloWorldPubSubType::serialize( + void* data, + SerializedPayload_t* payload) +{ + HelloWorld* p_type = static_cast(data); + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload->data), payload->max_size); // Object that manages the raw buffer. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + eprosima::fastcdr::Cdr::DDS_CDR); // Object that serializes the data. + payload->encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + // Serialize encapsulation + ser.serialize_encapsulation(); + + try + { + p_type->serialize(ser); // Serialize the object: + } + catch (eprosima::fastcdr::exception::NotEnoughMemoryException& /*exception*/) + { + return false; + } + + payload->length = static_cast(ser.getSerializedDataLength()); //Get the serialized length + return true; +} + +bool HelloWorldPubSubType::deserialize( + SerializedPayload_t* payload, + void* data) +{ + HelloWorld* p_type = static_cast(data); //Convert DATA to pointer of your type + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload->data), payload->length); // Object that manages the raw buffer. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + eprosima::fastcdr::Cdr::DDS_CDR); // Object that deserializes the data. + // Deserialize encapsulation. + deser.read_encapsulation(); + payload->encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + try + { + p_type->deserialize(deser); //Deserialize the object: + } + catch (eprosima::fastcdr::exception::NotEnoughMemoryException& /*exception*/) + { + return false; + } + + return true; +} + +std::function HelloWorldPubSubType::getSerializedSizeProvider( + void* data) +{ + return [data]() -> uint32_t + { + return static_cast(type::getCdrSerializedSize(*static_cast(data))) + + 4 /*encapsulation*/; + }; +} + +void* HelloWorldPubSubType::createData() +{ + return reinterpret_cast(new HelloWorld()); +} + +void HelloWorldPubSubType::deleteData( + void* data) +{ + delete(reinterpret_cast(data)); +} + +bool HelloWorldPubSubType::getKey( + void* data, + InstanceHandle_t* handle, + bool force_md5) +{ + if (!m_isGetKeyDefined) + { + return false; + } + HelloWorld* p_type = static_cast(data); + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(m_keyBuffer), + HelloWorld::getKeyMaxCdrSerializedSize()); // Object that manages the raw buffer. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::BIG_ENDIANNESS); // Object that serializes the data. + p_type->serializeKey(ser); + if (force_md5 || HelloWorld::getKeyMaxCdrSerializedSize()>16) + { + m_md5.init(); + m_md5.update(m_keyBuffer, static_cast(ser.getSerializedDataLength())); + m_md5.finalize(); + for (uint8_t i = 0; i<16; ++i) + { + handle->value[i] = m_md5.digest[i]; + } + } + else + { + for (uint8_t i = 0; i<16; ++i) + { + handle->value[i] = m_keyBuffer[i]; + } + } + return true; +} + diff --git a/examples/C++/HelloWorldExampleSharedMem/HelloWorldPubSubTypes.h b/examples/C++/HelloWorldExampleSharedMem/HelloWorldPubSubTypes.h new file mode 100644 index 00000000000..68efea74db0 --- /dev/null +++ b/examples/C++/HelloWorldExampleSharedMem/HelloWorldPubSubTypes.h @@ -0,0 +1,67 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file HelloWorldPubSubTypes.h + * This header file contains the declaration of the serialization functions. + * + * This file was generated by the tool fastcdrgen. + */ + + +#ifndef _HELLOWORLD_PUBSUBTYPES_H_ +#define _HELLOWORLD_PUBSUBTYPES_H_ + +#include +#include + +#include "HelloWorld.h" + +#if !defined(GEN_API_VER) || (GEN_API_VER != 1) +#error Generated HelloWorld is not compatible with current installed Fast-RTPS. Please, regenerate it with fastrtpsgen. +#endif + +/*! + * @brief This class represents the TopicDataType of the type HelloWorld defined by the user in the IDL file. + * @ingroup HELLOWORLD + */ +class HelloWorldPubSubType : public eprosima::fastrtps::TopicDataType +{ +public: + + typedef HelloWorld type; + + eProsima_user_DllExport HelloWorldPubSubType(); + + eProsima_user_DllExport virtual ~HelloWorldPubSubType(); + eProsima_user_DllExport virtual bool serialize( + void* data, + eprosima::fastrtps::rtps::SerializedPayload_t* payload) override; + eProsima_user_DllExport virtual bool deserialize( + eprosima::fastrtps::rtps::SerializedPayload_t* payload, + void* data) override; + eProsima_user_DllExport virtual std::function getSerializedSizeProvider( + void* data) override; + eProsima_user_DllExport virtual bool getKey( + void* data, + eprosima::fastrtps::rtps::InstanceHandle_t* ihandle, + bool force_md5 = false) override; + eProsima_user_DllExport virtual void* createData() override; + eProsima_user_DllExport virtual void deleteData( + void* data) override; + MD5 m_md5; + unsigned char* m_keyBuffer; +}; + +#endif // _HELLOWORLD_PUBSUBTYPES_H_ \ No newline at end of file diff --git a/examples/C++/HelloWorldExampleSharedMem/HelloWorldPublisher.cpp b/examples/C++/HelloWorldExampleSharedMem/HelloWorldPublisher.cpp new file mode 100644 index 00000000000..c9636f75c88 --- /dev/null +++ b/examples/C++/HelloWorldExampleSharedMem/HelloWorldPublisher.cpp @@ -0,0 +1,190 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file HelloWorldPublisher.cpp + * + */ + +#include "HelloWorldPublisher.h" +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace eprosima::fastrtps; +using namespace eprosima::fastrtps::rtps; +using namespace eprosima::fastdds::rtps; + +HelloWorldPublisher::HelloWorldPublisher() + : mp_participant(nullptr) + , mp_publisher(nullptr) +{ +} + +bool HelloWorldPublisher::init() +{ + m_Hello = std::make_shared(); + m_Hello->index(0); + m_Hello->message("HelloWorld"); + ParticipantAttributes PParam; + PParam.rtps.builtin.discovery_config.discoveryProtocol = DiscoveryProtocol_t::SIMPLE; + PParam.rtps.builtin.discovery_config.use_SIMPLE_EndpointDiscoveryProtocol = true; + PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationReaderANDSubscriptionWriter = true; + PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationWriterANDSubscriptionReader = true; + PParam.rtps.builtin.domainId = 66; + PParam.rtps.builtin.discovery_config.leaseDuration = c_TimeInfinite; + + PParam.rtps.setName("Participant_pub"); + + // SharedMem transport configuration + PParam.rtps.useBuiltinTransports = false; + + auto shm_transport = std::make_shared(); + const uint32_t segment_size = 2*1024*1024; + shm_transport->segment_size(segment_size, segment_size); + PParam.rtps.userTransports.push_back(shm_transport); + + // UDP + auto udp_transport = std::make_shared(); + //udp_transport->interfaceWhiteList.push_back("127.0.0.1"); + PParam.rtps.userTransports.push_back(udp_transport); + + mp_participant = Domain::createParticipant(PParam); + + if (mp_participant==nullptr) + { + return false; + } + //REGISTER THE TYPE + + Domain::registerType(mp_participant,&m_type); + + //CREATE THE PUBLISHER + PublisherAttributes Wparam; + Wparam.topic.topicKind = NO_KEY; + Wparam.topic.topicDataType = "HelloWorldSharedMem"; + Wparam.topic.topicName = "HelloWorldSharedMemTopic"; + Wparam.topic.historyQos.kind = KEEP_LAST_HISTORY_QOS; + Wparam.topic.historyQos.depth = 30; + Wparam.topic.resourceLimitsQos.max_samples = 50; + Wparam.topic.resourceLimitsQos.allocated_samples = 20; + Wparam.times.heartbeatPeriod.seconds = 2; + Wparam.times.heartbeatPeriod.nanosec = 200*1000*1000; + Wparam.qos.m_reliability.kind = RELIABLE_RELIABILITY_QOS; + Wparam.qos.m_publishMode.kind = ASYNCHRONOUS_PUBLISH_MODE; + mp_publisher = Domain::createPublisher(mp_participant,Wparam,(PublisherListener*)&m_listener); + if (mp_publisher == nullptr) + { + return false; + } + + return true; +} + +HelloWorldPublisher::~HelloWorldPublisher() +{ + // TODO Auto-generated destructor stub + Domain::removeParticipant(mp_participant); +} + +void HelloWorldPublisher::PubListener::onPublicationMatched( + Publisher* /*pub*/, + MatchingInfo& info) +{ + if (info.status == MATCHED_MATCHING) + { + n_matched++; + firstConnected = true; + std::cout << "Publisher matched"<message() << " with index: " << m_Hello->index() << " SENT" << std::endl; + } + std::this_thread::sleep_for(std::chrono::milliseconds(sleep)); + } + } + else + { + for (uint32_t i = 0; imessage() << " with index: " << m_Hello->index() << " SENT"<0) + { + m_Hello->index(m_Hello->index() + 1); + size_t data_size = m_Hello->data().size(); + std::string s = "BigData" + std::to_string(m_Hello->index() % 10); + strcpy(&m_Hello->data()[data_size - s.length() - 1], s.c_str()); + + mp_publisher->write((void*)m_Hello.get()); + + return true; + } + return false; +} diff --git a/examples/C++/HelloWorldExampleSharedMem/HelloWorldPublisher.h b/examples/C++/HelloWorldExampleSharedMem/HelloWorldPublisher.h new file mode 100644 index 00000000000..fdc207d4b52 --- /dev/null +++ b/examples/C++/HelloWorldExampleSharedMem/HelloWorldPublisher.h @@ -0,0 +1,74 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file HelloWorldPublisher.h + * + */ + +#ifndef HELLOWORLDPUBLISHER_H_ +#define HELLOWORLDPUBLISHER_H_ + +#include "HelloWorldPubSubTypes.h" + +#include +#include +#include + + +#include "HelloWorld.h" + +class HelloWorldPublisher +{ +public: + + HelloWorldPublisher(); + virtual ~HelloWorldPublisher(); + //!Initialize + bool init(); + //!Publish a sample + bool publish( + bool waitForListener = true); + //!Run for number samples + void run( + uint32_t number, + uint32_t sleep); + +private: + + std::shared_ptr m_Hello; + eprosima::fastrtps::Participant* mp_participant; + eprosima::fastrtps::Publisher* mp_publisher; + bool stop; + class PubListener : public eprosima::fastrtps::PublisherListener + { +public: + + PubListener() + : n_matched(0) + ,firstConnected(false){}; + ~PubListener(){}; + void onPublicationMatched( + eprosima::fastrtps::Publisher* pub, + eprosima::fastrtps::rtps::MatchingInfo& info); + int n_matched; + bool firstConnected; + } m_listener; + void runThread( + uint32_t number, + uint32_t sleep); + HelloWorldPubSubType m_type; +}; + +#endif /* HELLOWORLDPUBLISHER_H_ */ diff --git a/examples/C++/HelloWorldExampleSharedMem/HelloWorldSubscriber.cpp b/examples/C++/HelloWorldExampleSharedMem/HelloWorldSubscriber.cpp new file mode 100644 index 00000000000..cec68f8ab60 --- /dev/null +++ b/examples/C++/HelloWorldExampleSharedMem/HelloWorldSubscriber.cpp @@ -0,0 +1,148 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file HelloWorldSubscriber.cpp + * + */ + +#include "HelloWorldSubscriber.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace eprosima::fastrtps; +using namespace eprosima::fastrtps::rtps; +using namespace eprosima::fastdds::rtps; + +HelloWorldSubscriber::HelloWorldSubscriber() + : mp_participant(nullptr) + ,mp_subscriber(nullptr) +{ +} + +bool HelloWorldSubscriber::init() +{ + ParticipantAttributes PParam; + PParam.rtps.builtin.discovery_config.discoveryProtocol = DiscoveryProtocol_t::SIMPLE; + PParam.rtps.builtin.discovery_config.use_SIMPLE_EndpointDiscoveryProtocol = true; + PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationReaderANDSubscriptionWriter = true; + PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationWriterANDSubscriptionReader = true; + PParam.rtps.builtin.domainId = 66; + PParam.rtps.builtin.discovery_config.leaseDuration = c_TimeInfinite; + PParam.rtps.setName("Participant_sub"); + + // SharedMem transport configuration + PParam.rtps.useBuiltinTransports = false; + + auto sm_transport = std::make_shared(); + sm_transport->segment_size(2*1024*1024, 2 * 1024 * 1024); + PParam.rtps.userTransports.push_back(sm_transport); + + // UDP + auto udp_transport = std::make_shared(); + //udp_transport->interfaceWhiteList.push_back("127.0.0.1"); + PParam.rtps.userTransports.push_back(udp_transport); + + mp_participant = Domain::createParticipant(PParam); + if (mp_participant==nullptr) + { + return false; + } + + //REGISTER THE TYPE + + + Domain::registerType(mp_participant,&m_type); + //CREATE THE SUBSCRIBER + SubscriberAttributes Rparam; + Rparam.topic.topicKind = NO_KEY; + Rparam.topic.topicDataType = "HelloWorldSharedMem"; + Rparam.topic.topicName = "HelloWorldSharedMemTopic"; + Rparam.topic.historyQos.kind = KEEP_LAST_HISTORY_QOS; + Rparam.topic.historyQos.depth = 30; + Rparam.topic.resourceLimitsQos.max_samples = 50; + Rparam.topic.resourceLimitsQos.allocated_samples = 20; + Rparam.qos.m_reliability.kind = RELIABLE_RELIABILITY_QOS; + Rparam.qos.m_durability.kind = TRANSIENT_LOCAL_DURABILITY_QOS; + + mp_subscriber = Domain::createSubscriber(mp_participant,Rparam,(SubscriberListener*)&m_listener); + + if (mp_subscriber == nullptr) + { + return false; + } + + + return true; +} + +HelloWorldSubscriber::~HelloWorldSubscriber() { + // TODO Auto-generated destructor stub + Domain::removeParticipant(mp_participant); +} + +void HelloWorldSubscriber::SubListener::onSubscriptionMatched( + Subscriber* /*sub*/, + MatchingInfo& info) +{ + if (info.status == MATCHED_MATCHING) + { + n_matched++; + std::cout << "Subscriber matched"<takeNextData((void*)m_Hello.get(), &m_info)) + { + if (m_info.sampleKind == ALIVE) + { + this->n_samples++; + const size_t data_size = m_Hello->data().size(); + // Print your structure data here. + std::cout << "Message " << m_Hello->message() << " " << m_Hello->index() + << " RECEIVED With " << data_size << "(bytes) of Data. DataEnd = " << + (char*)&m_Hello->data()[data_size-9] + << std::endl; + } + } +} + +void HelloWorldSubscriber::run() +{ + std::cout << "Subscriber running. Please press enter to stop the Subscriber" << std::endl; + std::cin.ignore(); +} + +void HelloWorldSubscriber::run( + uint32_t number) +{ + std::cout << "Subscriber running until " << number << "samples have been received" << std::endl; + while (number > this->m_listener.n_samples) + { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } +} diff --git a/examples/C++/HelloWorldExampleSharedMem/HelloWorldSubscriber.h b/examples/C++/HelloWorldExampleSharedMem/HelloWorldSubscriber.h new file mode 100644 index 00000000000..e6cc078f7f6 --- /dev/null +++ b/examples/C++/HelloWorldExampleSharedMem/HelloWorldSubscriber.h @@ -0,0 +1,77 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file HelloWorldSubscriber.h + * + */ + +#ifndef HELLOWORLDSUBSCRIBER_H_ +#define HELLOWORLDSUBSCRIBER_H_ + +#include "HelloWorldPubSubTypes.h" + +#include +#include +#include +#include + +#include "HelloWorld.h" + +class HelloWorldSubscriber +{ +public: + + HelloWorldSubscriber(); + virtual ~HelloWorldSubscriber(); + //!Initialize the subscriber + bool init(); + //!RUN the subscriber + void run(); + //!Run the subscriber until number samples have been recevied. + void run( + uint32_t number); + +private: + + eprosima::fastrtps::Participant* mp_participant; + eprosima::fastrtps::Subscriber* mp_subscriber; + +public: + + class SubListener : public eprosima::fastrtps::SubscriberListener + { + public: + + SubListener() + : n_matched(0) + , n_samples(0) { m_Hello = std::make_shared(); }; + ~SubListener(){}; + void onSubscriptionMatched( + eprosima::fastrtps::Subscriber* sub, + eprosima::fastrtps::rtps::MatchingInfo& info); + void onNewDataMessage( + eprosima::fastrtps::Subscriber* sub); + std::shared_ptr m_Hello; + eprosima::fastrtps::SampleInfo_t m_info; + int n_matched; + uint32_t n_samples; + } m_listener; + +private: + + HelloWorldPubSubType m_type; +}; + +#endif /* HELLOWORLDSUBSCRIBER_H_ */ diff --git a/examples/C++/HelloWorldExampleSharedMem/HelloWorld_main.cpp b/examples/C++/HelloWorldExampleSharedMem/HelloWorld_main.cpp new file mode 100644 index 00000000000..e5522447b58 --- /dev/null +++ b/examples/C++/HelloWorldExampleSharedMem/HelloWorld_main.cpp @@ -0,0 +1,120 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file HelloWorld_main.cpp + * + */ + +#include "HelloWorldPublisher.h" +#include "HelloWorldSubscriber.h" + +#include +#include + +using namespace eprosima; +using namespace fastrtps; +using namespace rtps; +int main( + int argc, + char** argv) +{ + Log::SetVerbosity(Log::Warning); + //Log::SetCategoryFilter(std::regex("RTPS_EDP_MATCH|RTPS_PDP_DISCOVERY|RTPS_PARTICIPANT_LISTEN|SHM")); + + + std::cout << "Starting "<< std::endl; + int type = 1; + int count = 10; + long sleep = 100; + if (argc > 1) + { + if (strcmp(argv[1],"publisher")==0) + { + type = 1; + if (argc >= 3) + { + count = atoi(argv[2]); + if (argc == 4) + { + sleep = atoi(argv[3]); + } + } + } + else if (strcmp(argv[1],"subscriber")==0) + { + type = 2; + } + else if (strcmp(argv[1], "both") == 0) + { + type = 3; + } + } + else + { + std::cout << "publisher, subscriber or both argument needed" << std::endl; + Log::Reset(); + return 0; + } + + switch (type) + { + case 1: + { + HelloWorldPublisher mypub; + if (mypub.init()) + { + mypub.run(count, sleep); + } + break; + } + case 2: + { + HelloWorldSubscriber mysub; + if (mysub.init()) + { + mysub.run(); + } + break; + } + case 3: + { + std::thread thread_sub([] + { + HelloWorldSubscriber mysub; + if (mysub.init()) + { + mysub.run(); + } + }); + + std::thread thread_pub([&] + { + HelloWorldPublisher mypub; + if (mypub.init()) + { + mypub.run(count, sleep); + } + }); + + thread_sub.join(); + thread_pub.join(); + + break; + } + } + Domain::stopAll(); + Log::Reset(); + return 0; +} diff --git a/examples/C++/HelloWorldExampleSharedMem/README.txt b/examples/C++/HelloWorldExampleSharedMem/README.txt new file mode 100644 index 00000000000..2303e5d5dd9 --- /dev/null +++ b/examples/C++/HelloWorldExampleSharedMem/README.txt @@ -0,0 +1,6 @@ +To launch this test open two different consoles: + +In the first one launch: HelloWorldExample publisher (or HelloWorldExample.exe publisher on windows). +In the second one: HelloWorldExample subscriber. + + diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 21dbdd53de5..23bf59ae8a7 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -19,6 +19,7 @@ add_subdirectory(C++/OwnershipStrengthQoSExample) add_subdirectory(C++/DynamicHelloWorldExample) add_subdirectory(C++/HelloWorldExample) add_subdirectory(C++/HelloWorldExampleTCP) +add_subdirectory(C++/HelloWorldExampleSharedMem) add_subdirectory(C++/Filtering) add_subdirectory(C++/UserDefinedTransportExample) add_subdirectory(C++/FlowControlExample) diff --git a/include/fastdds/rtps/builtin/data/ParticipantProxyData.h b/include/fastdds/rtps/builtin/data/ParticipantProxyData.h index 6c9550d1065..edc46ae51af 100644 --- a/include/fastdds/rtps/builtin/data/ParticipantProxyData.h +++ b/include/fastdds/rtps/builtin/data/ParticipantProxyData.h @@ -174,7 +174,8 @@ class ParticipantProxyData bool readFromCDRMessage( CDRMessage_t* msg, bool use_encapsulation, - const NetworkFactory& network); + const NetworkFactory& network, + bool is_shm_transport_available); //! Clear the data (restore to default state). void clear(); diff --git a/include/fastdds/rtps/builtin/data/ReaderProxyData.h b/include/fastdds/rtps/builtin/data/ReaderProxyData.h index 195b77dd0e5..6a41a490f97 100644 --- a/include/fastdds/rtps/builtin/data/ReaderProxyData.h +++ b/include/fastdds/rtps/builtin/data/ReaderProxyData.h @@ -360,14 +360,16 @@ class ReaderProxyData bool write_encapsulation) const; /** - * Read the information from a CDRMessage_t. The position of hte message must be in the beggining on the parameter list. - * @param msg Pointer to the message. - * @param network Reference to network factory for locator validation and transformation - * @return true on success - */ + * Read the information from a CDRMessage_t. The position of the message must be in the beggining on the parameter list. + * @param msg Pointer to the message. + * @param network Reference to network factory for locator validation and transformation + * @param is_shm_transport_possible Indicates wether the Reader is reachable by SHM. + * @return true on success + */ RTPS_DllAPI bool readFromCDRMessage( CDRMessage_t* msg, - const NetworkFactory& network); + const NetworkFactory& network, + bool is_shm_transport_available); //! bool m_expectsInlineQos; diff --git a/include/fastdds/rtps/builtin/data/WriterProxyData.h b/include/fastdds/rtps/builtin/data/WriterProxyData.h index 08324f14345..03211f0655b 100644 --- a/include/fastdds/rtps/builtin/data/WriterProxyData.h +++ b/include/fastdds/rtps/builtin/data/WriterProxyData.h @@ -414,7 +414,8 @@ class WriterProxyData //!Read a parameter list from a CDRMessage_t. RTPS_DllAPI bool readFromCDRMessage( CDRMessage_t* msg, - const NetworkFactory& network); + const NetworkFactory& network, + bool is_shm_transport_possible); private: diff --git a/include/fastdds/rtps/common/Locator.h b/include/fastdds/rtps/common/Locator.h index 148cd084e8a..792cbf26334 100644 --- a/include/fastdds/rtps/common/Locator.h +++ b/include/fastdds/rtps/common/Locator.h @@ -44,6 +44,7 @@ namespace rtps { #define LOCATOR_KIND_UDPv6 2 #define LOCATOR_KIND_TCPv4 4 #define LOCATOR_KIND_TCPv6 8 +#define LOCATOR_KIND_SHM 16 //!@brief Class Locator_t, uniquely identifies a communication channel for a particular transport. //For example, an address+port combination in the case of UDP. @@ -58,6 +59,7 @@ class RTPS_DllAPI Locator_t * LOCATOR_KIND_UDPv6 * LOCATOR_KIND_TCPv4 * LOCATOR_KIND_TCPv6 + * LOCATOR_KIND_SHM */ int32_t kind; uint32_t port; @@ -201,6 +203,18 @@ inline std::ostream& operator<<(std::ostream& output, const Locator_t& loc) } output << ":" << loc.port; } + else if (loc.kind == LOCATOR_KIND_SHM) + { + if (loc.address[0] == 'M') + { + output << "SHM:M" << loc.port; + } + else + { + output << "SHM:" << loc.port; + } + } + return output; } @@ -208,7 +222,7 @@ typedef std::vector::iterator LocatorListIterator; typedef std::vector::const_iterator LocatorListConstIterator; /** - * Provides a Locator's iterator interface that can by used by different Locator's + * Provides a Locator's iterator interface that can be used by different Locator's * containers */ class LocatorsIterator diff --git a/include/fastdds/rtps/common/LocatorSelector.hpp b/include/fastdds/rtps/common/LocatorSelector.hpp index 5d5b7734e64..3037c39d39e 100644 --- a/include/fastdds/rtps/common/LocatorSelector.hpp +++ b/include/fastdds/rtps/common/LocatorSelector.hpp @@ -349,11 +349,11 @@ class LocatorSelector } } - iterator(const iterator& other) - : locator_selector_(other.locator_selector_) - , current_(other.current_) - { - } + iterator(const iterator& other) + : locator_selector_(other.locator_selector_) + , current_(other.current_) + { + } iterator& operator++() { diff --git a/include/fastdds/rtps/common/RemoteLocators.hpp b/include/fastdds/rtps/common/RemoteLocators.hpp index a728e46aecd..e735c5fb325 100644 --- a/include/fastdds/rtps/common/RemoteLocators.hpp +++ b/include/fastdds/rtps/common/RemoteLocators.hpp @@ -121,6 +121,20 @@ struct RemoteLocatorList ResourceLimitedVector multicast; }; +inline std::ostream& operator<<(std::ostream& output, const RemoteLocatorList& remote_locators) +{ + for (auto it = remote_locators.multicast.begin(); it != remote_locators.multicast.end(); ++it) + { + output << *it << ","; + } + + for (auto it = remote_locators.unicast.begin(); it != remote_locators.unicast.end(); ++it) + { + output << *it << ","; + } + return output; +} + } /* namespace rtps */ } /* namespace fastrtps */ } /* namespace eprosima */ diff --git a/include/fastdds/rtps/network/NetworkFactory.h b/include/fastdds/rtps/network/NetworkFactory.h index 505005000c5..5fc121e4406 100644 --- a/include/fastdds/rtps/network/NetworkFactory.h +++ b/include/fastdds/rtps/network/NetworkFactory.h @@ -74,13 +74,13 @@ class NetworkFactory * Walks over the list of transports, opening every possible channel that we can listen to * from the given locator, and returns a vector of Receiver Resources for this goal. * @param local Locator from which to listen. - * @param maxMsgSize Maximum size of the message. * @param returned_resources_list List that will be filled with the created ReceiverResources. + * @param receiver_max_message_size Max message size allowed by the message receiver. */ bool BuildReceiverResources( Locator_t& local, - uint32_t maxMsgSize, - std::vector>& returned_resources_list); + std::vector>& returned_resources_list, + uint32_t receiver_max_message_size); void NormalizeLocators(LocatorList_t& locators); diff --git a/include/fastdds/rtps/network/ReceiverResource.h b/include/fastdds/rtps/network/ReceiverResource.h index a5c5303c6b6..7f4504d851d 100644 --- a/include/fastdds/rtps/network/ReceiverResource.h +++ b/include/fastdds/rtps/network/ReceiverResource.h @@ -73,6 +73,11 @@ class ReceiverResource : public fastdds::rtps::TransportReceiverInterface */ void disable(); + inline uint32_t max_message_size() const + { + return max_message_size_; + } + /** * Resources can only be transfered through move semantics. Copy, assignment, and * construction outside of the factory are forbidden. @@ -93,6 +98,7 @@ class ReceiverResource : public fastdds::rtps::TransportReceiverInterface std::mutex mtx; MessageReceiver* receiver; + uint32_t max_message_size_; }; } // namespace rtps diff --git a/include/fastdds/rtps/reader/StatefulReader.h b/include/fastdds/rtps/reader/StatefulReader.h index 3a4b3fd957d..724fa48cdca 100644 --- a/include/fastdds/rtps/reader/StatefulReader.h +++ b/include/fastdds/rtps/reader/StatefulReader.h @@ -245,8 +245,8 @@ class StatefulReader : public RTPSReader */ bool send_sync_nts( CDRMessage_t* message, - const Locators& locators_begin, - const Locators& locators_end, + const Locators& locators_begin, + const Locators& locators_end, std::chrono::steady_clock::time_point& max_blocking_time_point); private: diff --git a/include/fastdds/rtps/transport/TCPTransportInterface.h b/include/fastdds/rtps/transport/TCPTransportInterface.h index 7107c17405a..233c1a4d3c2 100644 --- a/include/fastdds/rtps/transport/TCPTransportInterface.h +++ b/include/fastdds/rtps/transport/TCPTransportInterface.h @@ -241,7 +241,7 @@ class TCPTransportInterface : public TransportInterface virtual bool IsLocatorSupported(const fastrtps::rtps::Locator_t&) const override; //! Checks whether there are open and bound sockets for the given port. - bool is_output_channel_open_for(const fastrtps::rtps::Locator_t&) const ; + bool is_output_channel_open_for(const fastrtps::rtps::Locator_t&) const; /** Opens an input channel to receive incomming connections. * If there is an existing channel it registers the receiver resource. @@ -384,6 +384,11 @@ class TCPTransportInterface : public TransportInterface fastrtps::rtps::Locator_t &locator, uint32_t well_known_port) const override; + virtual uint32_t max_recv_buffer_size() const override + { + return configuration()->maxMessageSize; + } + void DeleteSocket(TCPChannelResource *channelResource); virtual const TCPTransportDescriptor* configuration() const = 0; diff --git a/include/fastdds/rtps/transport/TransportInterface.h b/include/fastdds/rtps/transport/TransportInterface.h index 61b2c9821d9..8c77e870210 100644 --- a/include/fastdds/rtps/transport/TransportInterface.h +++ b/include/fastdds/rtps/transport/TransportInterface.h @@ -172,6 +172,11 @@ class TransportInterface fastrtps::rtps::Locator_t& locator, uint32_t well_known_port) const = 0; + /** + * @return The maximum datagram size for reception supported by the transport + */ + virtual uint32_t max_recv_buffer_size() const = 0; + /** * Shutdown method to close the connections of the transports. */ diff --git a/include/fastdds/rtps/transport/UDPTransportInterface.h b/include/fastdds/rtps/transport/UDPTransportInterface.h index 28edb63a8a7..ebda5c093e9 100644 --- a/include/fastdds/rtps/transport/UDPTransportInterface.h +++ b/include/fastdds/rtps/transport/UDPTransportInterface.h @@ -134,6 +134,11 @@ class UDPTransportInterface : public TransportInterface virtual bool fillUnicastLocator(fastrtps::rtps::Locator_t &locator, uint32_t well_known_port) const override; + virtual uint32_t max_recv_buffer_size() const override + { + return configuration()->maxMessageSize; + } + protected: friend class UDPChannelResource; diff --git a/include/fastdds/rtps/transport/shared_mem/SharedMemTransportDescriptor.h b/include/fastdds/rtps/transport/shared_mem/SharedMemTransportDescriptor.h new file mode 100644 index 00000000000..b0b9df81eb1 --- /dev/null +++ b/include/fastdds/rtps/transport/shared_mem/SharedMemTransportDescriptor.h @@ -0,0 +1,144 @@ +// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_SHAREDMEM_TRANSPORT_DESCRIPTOR_ +#define _FASTDDS_SHAREDMEM_TRANSPORT_DESCRIPTOR_ + +#include "fastdds/rtps/transport/TransportDescriptorInterface.h" + +namespace eprosima { +namespace fastdds { +namespace rtps { + +class TransportInterface; + +/** + * Shared memory transport configuration + * + * @ingroup TRANSPORT_MODULE + */ +typedef struct SharedMemTransportDescriptor : public TransportDescriptorInterface +{ + virtual ~SharedMemTransportDescriptor() + { + + } + + virtual TransportInterface* create_transport() const override; + uint32_t min_send_buffer_size() const override + { + return 0; + } + + enum class OverflowPolicy + { + DISCARD, + FAIL + }; + + RTPS_DllAPI SharedMemTransportDescriptor(); + + RTPS_DllAPI SharedMemTransportDescriptor( + const SharedMemTransportDescriptor& t); + + RTPS_DllAPI uint32_t segment_size() const + { + return segment_size_; + } + + /** + * Sets the segment_size and the max_message_size. + * max_message must be <= segment_size + * @param [in] segment_size in bytes. + * @param [in] max_message_size in bytes. + */ + RTPS_DllAPI void segment_size( + uint32_t segment_size, + uint32_t max_message_size) + { + segment_size_ = segment_size; + maxMessageSize = max_message_size; + } + + RTPS_DllAPI OverflowPolicy port_overflow_policy() const + { + return port_overflow_policy_; + } + + RTPS_DllAPI void port_overflow_policy( + OverflowPolicy port_overflow_policy) + { + port_overflow_policy_ = port_overflow_policy; + } + + RTPS_DllAPI uint32_t port_queue_capacity() const + { + return port_queue_capacity_; + } + + RTPS_DllAPI void port_queue_capacity( + uint32_t port_queue_capacity) + { + port_queue_capacity_ = port_queue_capacity; + } + + RTPS_DllAPI OverflowPolicy segment_overflow_policy() const + { + return segment_overflow_policy_; + } + + RTPS_DllAPI void segment_overflow_policy( + OverflowPolicy segment_overflow_policy) + { + segment_overflow_policy_ = segment_overflow_policy; + } + + RTPS_DllAPI uint32_t healthy_check_timeout_ms() const + { + return healthy_check_timeout_ms_; + } + + RTPS_DllAPI void healthy_check_timeout_ms( + uint32_t healthy_check_timeout_ms) + { + healthy_check_timeout_ms_ = healthy_check_timeout_ms; + } + + RTPS_DllAPI std::string rtps_dump_file() const + { + return rtps_dump_file_; + } + + RTPS_DllAPI void rtps_dump_file( + const std::string& rtps_dump_file) + { + rtps_dump_file_ = rtps_dump_file; + } + +private: + + uint32_t segment_size_; + uint32_t port_queue_capacity_; + OverflowPolicy port_overflow_policy_; + OverflowPolicy segment_overflow_policy_; + uint32_t healthy_check_timeout_ms_; + std::string rtps_dump_file_; + +}SharedMemTransportDescriptor; + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_SHAREDMEM_TRANSPORT_DESCRIPTOR_ diff --git a/include/fastrtps/xmlparser/XMLParser.h b/include/fastrtps/xmlparser/XMLParser.h index 800a636b7cf..c7dc2ec4d89 100644 --- a/include/fastrtps/xmlparser/XMLParser.h +++ b/include/fastrtps/xmlparser/XMLParser.h @@ -200,6 +200,10 @@ class XMLParser tinyxml2::XMLElement* p_root, sp_transport_t p_transport); + RTPS_DllAPI static XMLP_ret parseXMLCommonSharedMemTransportData( + tinyxml2::XMLElement* p_root, + sp_transport_t p_transport); + RTPS_DllAPI static XMLP_ret parse_tls_config( tinyxml2::XMLElement* p_root, sp_transport_t tcp_transport); diff --git a/include/fastrtps/xmlparser/XMLParserCommon.h b/include/fastrtps/xmlparser/XMLParserCommon.h index a6183121488..458e10556fd 100644 --- a/include/fastrtps/xmlparser/XMLParserCommon.h +++ b/include/fastrtps/xmlparser/XMLParserCommon.h @@ -71,6 +71,14 @@ extern const char* METADATA_LOGICAL_PORT; extern const char* LISTENING_PORTS; extern const char* CALCULATE_CRC; extern const char* CHECK_CRC; +extern const char* SEGMENT_SIZE; +extern const char* PORT_QUEUE_CAPACITY; +extern const char* PORT_OVERFLOW_POLICY; +extern const char* SEGMENT_OVERFLOW_POLICY; +extern const char* HEALTHY_CHECK_TIMEOUT_MS; +extern const char* DISCARD; +extern const char* FAIL; +extern const char* RTPS_DUMP_FILE; // IntraprocessDeliveryType extern const char* OFF; @@ -161,6 +169,7 @@ extern const char* UDPv4; extern const char* UDPv6; extern const char* TCPv4; extern const char* TCPv6; +extern const char* SHM; extern const char* INIT_ACKNACK_DELAY; extern const char* HEARTB_RESP_DELAY; extern const char* INIT_HEARTB_DELAY; diff --git a/resources/xsd/fastRTPS_profiles.xsd b/resources/xsd/fastRTPS_profiles.xsd index d3d3dabdac1..3a6278c58ca 100644 --- a/resources/xsd/fastRTPS_profiles.xsd +++ b/resources/xsd/fastRTPS_profiles.xsd @@ -453,6 +453,13 @@ + + + + + + + @@ -793,6 +800,12 @@ + + + + + + diff --git a/src/cpp/CMakeLists.txt b/src/cpp/CMakeLists.txt index f69d6a87caf..097527c62c6 100644 --- a/src/cpp/CMakeLists.txt +++ b/src/cpp/CMakeLists.txt @@ -26,6 +26,8 @@ file(GLOB_RECURSE ALL_HEADERS ${PROJECT_SOURCE_DIR}/src/cpp/**/*.hpp ) +add_definitions(-DNOMINMAX) + endif(WIN32) set(${PROJECT_NAME}_source_files @@ -115,10 +117,12 @@ set(${PROJECT_NAME}_source_files rtps/transport/UDPv4Transport.cpp rtps/transport/TCPTransportInterface.cpp rtps/transport/UDPTransportInterface.cpp + rtps/transport/shared_mem/SharedMemTransport.cpp rtps/transport/TCPv4Transport.cpp rtps/transport/UDPv6Transport.cpp rtps/transport/TCPv6Transport.cpp rtps/transport/test_UDPv4Transport.cpp + rtps/transport/shared_mem/test_SharedMemTransport.cpp rtps/transport/tcp/TCPControlMessage.cpp rtps/transport/tcp/RTCPMessageManager.cpp @@ -338,6 +342,7 @@ elseif(NOT EPROSIMA_INSTALLER) $<$,$>>:_WIN32_WINNT=0x0601> $<$,$>:SQLITE_OS_WINRT> $<$,$>,$>>:ASIO_DISABLE_STD_STRING_VIEW> + $<$:_ENABLE_ATOMIC_ALIGNMENT_FIX> ) # Define public headers diff --git a/src/cpp/fastrtps_deprecated/publisher/PublisherImpl.cpp b/src/cpp/fastrtps_deprecated/publisher/PublisherImpl.cpp index ada148957dc..e93e5cdd876 100644 --- a/src/cpp/fastrtps_deprecated/publisher/PublisherImpl.cpp +++ b/src/cpp/fastrtps_deprecated/publisher/PublisherImpl.cpp @@ -197,7 +197,7 @@ bool PublisherImpl::create_new_change_with_params( // If it is big data, fragment it. if (ch->serializedPayload.length > final_high_mark_for_frag) { - /// Fragment the data. + // Fragment the data. // Set the fragment size to the cachechange. ch->setFragmentSize(static_cast( (std::min)(final_high_mark_for_frag, RTPSMessageGroup::get_max_fragment_payload_size()))); diff --git a/src/cpp/fastrtps_deprecated/utils/IPFinder.cpp b/src/cpp/fastrtps_deprecated/utils/IPFinder.cpp index 8df1c6fa820..b164e17b251 100644 --- a/src/cpp/fastrtps_deprecated/utils/IPFinder.cpp +++ b/src/cpp/fastrtps_deprecated/utils/IPFinder.cpp @@ -21,6 +21,7 @@ #include #if defined(_WIN32) +#pragma comment(lib, "Iphlpapi.lib") #include #include #include diff --git a/src/cpp/rtps/RTPSDomain.cpp b/src/cpp/rtps/RTPSDomain.cpp index 34e87053e77..982e71e919d 100644 --- a/src/cpp/rtps/RTPSDomain.cpp +++ b/src/cpp/rtps/RTPSDomain.cpp @@ -39,6 +39,7 @@ #include #include "RTPSDomainImpl.hpp" +#include #include #include @@ -141,27 +142,10 @@ RTPSParticipant* RTPSDomain::createParticipant( guidP.value[0] = c_VendorId_eProsima[0]; guidP.value[1] = c_VendorId_eProsima[1]; - if (loc.size() > 0) - { - MD5 md5; - for (auto& l : loc) - { - md5.update(l.address, sizeof(l.address)); - } - md5.finalize(); - uint16_t hostid = 0; - for (size_t i = 0; i < sizeof(md5.digest); i += 2) - { - hostid ^= ((md5.digest[i] << 8) | md5.digest[i + 1]); - } - guidP.value[2] = octet(hostid); - guidP.value[3] = octet(hostid >> 8); - } - else - { - guidP.value[2] = 127; - guidP.value[3] = 1; - } + uint16_t host_id = Host::get().id(); + guidP.value[2] = octet(host_id); + guidP.value[3] = octet(host_id >> 8); + guidP.value[4] = octet(pid); guidP.value[5] = octet(pid >> 8); guidP.value[6] = octet(pid >> 16); diff --git a/src/cpp/rtps/builtin/data/ParticipantProxyData.cpp b/src/cpp/rtps/builtin/data/ParticipantProxyData.cpp index 5745885b883..86fc5a2f9a0 100644 --- a/src/cpp/rtps/builtin/data/ParticipantProxyData.cpp +++ b/src/cpp/rtps/builtin/data/ParticipantProxyData.cpp @@ -21,10 +21,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -35,7 +37,6 @@ using namespace eprosima::fastrtps; - namespace eprosima { namespace fastrtps { namespace rtps { @@ -365,9 +366,17 @@ bool ParticipantProxyData::writeToCDRMessage( bool ParticipantProxyData::readFromCDRMessage( CDRMessage_t* msg, bool use_encapsulation, - const NetworkFactory& network) + const NetworkFactory& network, + bool is_shm_transport_available) { - auto param_process = [this, &network](CDRMessage_t* msg, const ParameterId_t& pid, uint16_t plength) + bool are_shm_metatraffic_locators_present = false; + bool are_shm_default_locators_present = false; + bool is_shm_transport_possible = false; + + auto param_process = [this, &network, &is_shm_transport_possible, + &are_shm_metatraffic_locators_present, + &are_shm_default_locators_present, + &is_shm_transport_available](CDRMessage_t* msg, const ParameterId_t& pid, uint16_t plength) { switch (pid) { @@ -410,6 +419,7 @@ bool ParticipantProxyData::readFromCDRMessage( m_VendorId[0] = p.vendorId[0]; m_VendorId[1] = p.vendorId[1]; + is_shm_transport_available &= (m_VendorId == c_VendorId_eProsima); break; } case fastdds::dds::PID_EXPECTS_INLINE_QOS: @@ -446,7 +456,13 @@ bool ParticipantProxyData::readFromCDRMessage( Locator_t temp_locator; if (network.transform_remote_locator(p.locator, temp_locator)) { - metatraffic_locators.add_multicast_locator(temp_locator); + ProxyDataFilters::filter_locators( + is_shm_transport_available, + &is_shm_transport_possible, + &are_shm_metatraffic_locators_present, + &metatraffic_locators, + temp_locator, + false); } break; } @@ -461,7 +477,13 @@ bool ParticipantProxyData::readFromCDRMessage( Locator_t temp_locator; if (network.transform_remote_locator(p.locator, temp_locator)) { - metatraffic_locators.add_unicast_locator(temp_locator); + ProxyDataFilters::filter_locators( + is_shm_transport_available, + &is_shm_transport_possible, + &are_shm_metatraffic_locators_present, + &metatraffic_locators, + temp_locator, + true); } break; } @@ -476,7 +498,13 @@ bool ParticipantProxyData::readFromCDRMessage( Locator_t temp_locator; if (network.transform_remote_locator(p.locator, temp_locator)) { - default_locators.add_unicast_locator(temp_locator); + ProxyDataFilters::filter_locators( + is_shm_transport_available, + &is_shm_transport_possible, + &are_shm_default_locators_present, + &default_locators, + temp_locator, + true); } break; } @@ -491,7 +519,13 @@ bool ParticipantProxyData::readFromCDRMessage( Locator_t temp_locator; if (network.transform_remote_locator(p.locator, temp_locator)) { - default_locators.add_multicast_locator(temp_locator); + ProxyDataFilters::filter_locators( + is_shm_transport_available, + &is_shm_transport_possible, + &are_shm_default_locators_present, + &default_locators, + temp_locator, + false); } break; } diff --git a/src/cpp/rtps/builtin/data/ProxyDataFilters.hpp b/src/cpp/rtps/builtin/data/ProxyDataFilters.hpp new file mode 100644 index 00000000000..f4ab1f484fc --- /dev/null +++ b/src/cpp/rtps/builtin/data/ProxyDataFilters.hpp @@ -0,0 +1,115 @@ +// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_RTPS_BUILTIN_DATA_PROXYDATAFILTERS_H_ +#define _FASTDDS_RTPS_BUILTIN_DATA_PROXYDATAFILTERS_H_ + +#include +#include + +namespace eprosima { +namespace fastrtps { +namespace rtps { + +/** + * Contains filtering functions for ProxyData structures + */ +class ProxyDataFilters +{ +public: + + /** + * As locator are parsed, when a CDR encapsulated proxydata message is received, + * this function decides wether SHM communication is possible, in that case only + * SHM locators are stored in the target_locator_list. If SHM communication is + * not possible SHM locators are not stored in the list. + * @param[in] is_shm_transport_available Indicates wether the participant has SHM transport enabled. + * @param[in/out] is_shm_transport_possible Is true when at least a SHM locator from the local host has + * been parsed + * @param[in/out] are_shm_locators_present True when SHM locators has been parsed + * @param[in/out] target_locators_list List where parsed locators are stored + * @param[in] temp_locator New locator to parse + * @param[in] is_unicast true if temp_locator is unicast, false if it is multicast + */ + static void filter_locators( + bool is_shm_transport_available, + bool* is_shm_transport_possible, + bool* are_shm_locators_present, + RemoteLocatorList* target_locators_list, + const Locator_t& temp_locator, + bool is_unicast) + { + using SHMLocator = eprosima::fastdds::rtps::SHMLocator; + + if (is_shm_transport_available && !(*is_shm_transport_possible) ) + { + *is_shm_transport_possible = SHMLocator::is_shm_and_from_this_host(temp_locator); + } + + if (*is_shm_transport_possible) + { + if (temp_locator.kind == LOCATOR_KIND_SHM) + { + // First SHM locator + if (!(*are_shm_locators_present)) + { + // Remove previously added locators from other transports + target_locators_list->unicast.clear(); + target_locators_list->multicast.clear(); + *are_shm_locators_present = true; + } + + if (is_unicast) + { + target_locators_list->add_unicast_locator(temp_locator); + } + else + { + target_locators_list->add_multicast_locator(temp_locator); + } + } + else if (!(*are_shm_locators_present)) + { + if (is_unicast) + { + target_locators_list->add_unicast_locator(temp_locator); + } + else + { + target_locators_list->add_multicast_locator(temp_locator); + } + } + } + else + { + if (temp_locator.kind != LOCATOR_KIND_SHM) + { + if (is_unicast) + { + target_locators_list->add_unicast_locator(temp_locator); + } + else + { + target_locators_list->add_multicast_locator(temp_locator); + } + } + } + } +}; + +} /* namespace rtps */ +} /* namespace fastrtps */ +} /* namespace eprosima */ + +#endif // _FASTDDS_RTPS_BUILTIN_DATA_PROXYDATAFILTERS_H_ diff --git a/src/cpp/rtps/builtin/data/ReaderProxyData.cpp b/src/cpp/rtps/builtin/data/ReaderProxyData.cpp index 07d7acdd3a1..c6fb218ddb0 100644 --- a/src/cpp/rtps/builtin/data/ReaderProxyData.cpp +++ b/src/cpp/rtps/builtin/data/ReaderProxyData.cpp @@ -21,6 +21,7 @@ #include #include #include +#include namespace eprosima { namespace fastrtps { @@ -539,12 +540,30 @@ bool ReaderProxyData::writeToCDRMessage( bool ReaderProxyData::readFromCDRMessage( CDRMessage_t* msg, - const NetworkFactory& network) + const NetworkFactory& network, + bool is_shm_transport_available) { - auto param_process = [this, &network](CDRMessage_t* msg, const ParameterId_t& pid, uint16_t plength) + bool are_shm_default_locators_present = false; + bool is_shm_transport_possible = false; + + auto param_process = [this, &network, + &is_shm_transport_available, + &is_shm_transport_possible, + &are_shm_default_locators_present](CDRMessage_t* msg, const ParameterId_t& pid, uint16_t plength) { switch (pid) { + case fastdds::dds::PID_VENDORID: + { + ParameterVendorId_t p(pid, plength); + if (!p.readFromCDRMessage(msg, plength)) + { + return false; + } + + is_shm_transport_available &= (p.vendorId == c_VendorId_eProsima); + break; + } case fastdds::dds::PID_DURABILITY: { if (!m_qos.m_durability.readFromCDRMessage(msg, plength)) @@ -723,7 +742,13 @@ bool ReaderProxyData::readFromCDRMessage( Locator_t temp_locator; if (network.transform_remote_locator(p.locator, temp_locator)) { - remote_locators_.add_unicast_locator(temp_locator); + ProxyDataFilters::filter_locators( + is_shm_transport_available, + &is_shm_transport_possible, + &are_shm_default_locators_present, + &remote_locators_, + temp_locator, + true); } break; } @@ -738,7 +763,13 @@ bool ReaderProxyData::readFromCDRMessage( Locator_t temp_locator; if (network.transform_remote_locator(p.locator, temp_locator)) { - remote_locators_.add_multicast_locator(temp_locator); + ProxyDataFilters::filter_locators( + is_shm_transport_available, + &is_shm_transport_possible, + &are_shm_default_locators_present, + &remote_locators_, + temp_locator, + false); } break; } diff --git a/src/cpp/rtps/builtin/data/WriterProxyData.cpp b/src/cpp/rtps/builtin/data/WriterProxyData.cpp index 978847d4999..67ec2e5bd8d 100644 --- a/src/cpp/rtps/builtin/data/WriterProxyData.cpp +++ b/src/cpp/rtps/builtin/data/WriterProxyData.cpp @@ -25,6 +25,8 @@ #include +#include + namespace eprosima { namespace fastrtps { namespace rtps { @@ -553,12 +555,30 @@ bool WriterProxyData::writeToCDRMessage( bool WriterProxyData::readFromCDRMessage( CDRMessage_t* msg, - const NetworkFactory& network) + const NetworkFactory& network, + bool is_shm_transport_available) { - auto param_process = [this, &network](CDRMessage_t* msg, const ParameterId_t& pid, uint16_t plength) + bool are_shm_default_locators_present = false; + bool is_shm_transport_possible = false; + + auto param_process = [this, &network, + &is_shm_transport_available, + &is_shm_transport_possible, + &are_shm_default_locators_present](CDRMessage_t* msg, const ParameterId_t& pid, uint16_t plength) { switch (pid) { + case fastdds::dds::PID_VENDORID: + { + ParameterVendorId_t p(pid, plength); + if (!p.readFromCDRMessage(msg, plength)) + { + return false; + } + + is_shm_transport_available &= (p.vendorId == c_VendorId_eProsima); + break; + } case fastdds::dds::PID_DURABILITY: { if (!m_qos.m_durability.readFromCDRMessage(msg, plength)) @@ -756,7 +776,13 @@ bool WriterProxyData::readFromCDRMessage( Locator_t temp_locator; if (network.transform_remote_locator(p.locator, temp_locator)) { - remote_locators_.add_unicast_locator(temp_locator); + ProxyDataFilters::filter_locators( + is_shm_transport_available, + &is_shm_transport_possible, + &are_shm_default_locators_present, + &remote_locators_, + temp_locator, + true); } break; } @@ -771,7 +797,13 @@ bool WriterProxyData::readFromCDRMessage( Locator_t temp_locator; if (network.transform_remote_locator(p.locator, temp_locator)) { - remote_locators_.add_multicast_locator(temp_locator); + ProxyDataFilters::filter_locators( + is_shm_transport_available, + &is_shm_transport_possible, + &are_shm_default_locators_present, + &remote_locators_, + temp_locator, + false); } break; } diff --git a/src/cpp/rtps/builtin/discovery/endpoint/EDP.cpp b/src/cpp/rtps/builtin/discovery/endpoint/EDP.cpp index 28893214e60..57d1dad13bc 100644 --- a/src/cpp/rtps/builtin/discovery/endpoint/EDP.cpp +++ b/src/cpp/rtps/builtin/discovery/endpoint/EDP.cpp @@ -912,7 +912,7 @@ bool EDP::pairingReader( #else if (R->matched_writer_add(*wdatait)) { - logInfo(RTPS_EDP, "Valid Matching to writerProxy: " << writer_guid); + logInfo(RTPS_EDP_MATCH, "WP:" << wdatait->guid() << " match R:" << R->getGuid() << ". RLoc:" << wdatait->remote_locators()); //MATCHED AND ADDED CORRECTLY: if (R->getListener() != nullptr) { @@ -994,7 +994,7 @@ bool EDP::pairingWriter( #else if (W->matched_reader_add(*rdatait)) { - logInfo(RTPS_EDP, "Valid Matching to readerProxy: " << reader_guid); + logInfo(RTPS_EDP_MATCH, "RP:" << rdatait->guid() << " match W:" << W->getGuid() << ". WLoc:" << rdatait->remote_locators()); //MATCHED AND ADDED CORRECTLY: if (W->getListener() != nullptr) { @@ -1072,7 +1072,7 @@ bool EDP::pairing_reader_proxy_with_any_local_writer( #else if ((*wit)->matched_reader_add(*rdata)) { - logInfo(RTPS_EDP, "Valid Matching to local writer: " << writerGUID.entityId); + logInfo(RTPS_EDP_MATCH, "RP:" << rdata->guid() << " match W:" << (*wit)->getGuid() << ". RLoc:" << rdata->remote_locators()); //MATCHED AND ADDED CORRECTLY: if ((*wit)->getListener() != nullptr) { @@ -1252,7 +1252,7 @@ bool EDP::pairing_writer_proxy_with_any_local_reader( #else if ((*rit)->matched_writer_add(*wdata)) { - logInfo(RTPS_EDP, "Valid Matching to local reader: " << readerGUID.entityId); + logInfo(RTPS_EDP_MATCH, "WP:" << wdata->guid() << " match R:" << (*rit)->getGuid() << ". WLoc:" << wdata->remote_locators()); //MATCHED AND ADDED CORRECTLY: if ((*rit)->getListener() != nullptr) { diff --git a/src/cpp/rtps/builtin/discovery/endpoint/EDPSimpleListeners.cpp b/src/cpp/rtps/builtin/discovery/endpoint/EDPSimpleListeners.cpp index 7311f2f1690..51358954376 100644 --- a/src/cpp/rtps/builtin/discovery/endpoint/EDPSimpleListeners.cpp +++ b/src/cpp/rtps/builtin/discovery/endpoint/EDPSimpleListeners.cpp @@ -52,7 +52,8 @@ void EDPBasePUBListener::add_writer_from_change( //LOAD INFORMATION IN DESTINATION WRITER PROXY DATA const NetworkFactory& network = edp->mp_RTPSParticipant->network_factory(); CDRMessage_t tempMsg(change->serializedPayload); - if (temp_writer_data_.readFromCDRMessage(&tempMsg, network)) + if (temp_writer_data_.readFromCDRMessage(&tempMsg, network, + edp->mp_RTPSParticipant->has_shm_transport())) { change->instanceHandle = temp_writer_data_.key(); if (temp_writer_data_.guid().guidPrefix == edp->mp_RTPSParticipant->getGuid().guidPrefix) @@ -155,7 +156,8 @@ void EDPBaseSUBListener::add_reader_from_change( //LOAD INFORMATION IN TEMPORAL WRITER PROXY DATA const NetworkFactory& network = edp->mp_RTPSParticipant->network_factory(); CDRMessage_t tempMsg(change->serializedPayload); - if (temp_reader_data_.readFromCDRMessage(&tempMsg, network)) + if (temp_reader_data_.readFromCDRMessage(&tempMsg, network, + edp->mp_RTPSParticipant->has_shm_transport())) { change->instanceHandle = temp_reader_data_.key(); if (temp_reader_data_.guid().guidPrefix == edp->mp_RTPSParticipant->getGuid().guidPrefix) diff --git a/src/cpp/rtps/builtin/discovery/participant/PDPListener.cpp b/src/cpp/rtps/builtin/discovery/participant/PDPListener.cpp index 1bf9a8b49b1..8fe92aee7b3 100644 --- a/src/cpp/rtps/builtin/discovery/participant/PDPListener.cpp +++ b/src/cpp/rtps/builtin/discovery/participant/PDPListener.cpp @@ -99,7 +99,8 @@ void PDPListener::onNewCacheChangeAdded( // Load information on temp_participant_data_ CDRMessage_t msg(change->serializedPayload); temp_participant_data_.clear(); - if (temp_participant_data_.readFromCDRMessage(&msg, true, parent_pdp_->getRTPSParticipant()->network_factory())) + if (temp_participant_data_.readFromCDRMessage(&msg, true, parent_pdp_->getRTPSParticipant()->network_factory(), + parent_pdp_->getRTPSParticipant()->has_shm_transport())) { // After correctly reading it change->instanceHandle = temp_participant_data_.m_key; @@ -128,6 +129,8 @@ void PDPListener::onNewCacheChangeAdded( reader->getMutex().unlock(); lock.unlock(); + logInfo(RTPS_PDP_DISCOVERY, "New participant " << pdata->m_guid << " at " << "MTTLoc: " << pdata->metatraffic_locators << " DefLoc:" << pdata->default_locators); + parent_pdp_->announceParticipantState(false); parent_pdp_->assignRemoteEndpoints(pdata); } @@ -139,6 +142,8 @@ void PDPListener::onNewCacheChangeAdded( reader->getMutex().unlock(); lock.unlock(); + logInfo(RTPS_PDP_DISCOVERY, "Update participant " << pdata->m_guid << " at " << "MTTLoc: " << pdata->metatraffic_locators << " DefLoc:" << pdata->default_locators); + if (parent_pdp_->updateInfoMatchesEDP()) { parent_pdp_->mp_EDP->assignRemoteEndpoints(*pdata); diff --git a/src/cpp/rtps/builtin/discovery/participant/PDPServerListener.cpp b/src/cpp/rtps/builtin/discovery/participant/PDPServerListener.cpp index e10a0a2fbf0..c62b3bc3ea0 100644 --- a/src/cpp/rtps/builtin/discovery/participant/PDPServerListener.cpp +++ b/src/cpp/rtps/builtin/discovery/participant/PDPServerListener.cpp @@ -91,7 +91,9 @@ void PDPServerListener::onNewCacheChangeAdded( // Load information on local_data CDRMessage_t msg(change->serializedPayload); - if(local_data.readFromCDRMessage(&msg, true, parent_pdp_->getRTPSParticipant()->network_factory())) + if(local_data.readFromCDRMessage(&msg, true, + parent_pdp_->getRTPSParticipant()->network_factory(), + parent_pdp_->getRTPSParticipant()->has_shm_transport())) { change->instanceHandle = local_data.m_key; guid = local_data.m_guid; diff --git a/src/cpp/rtps/network/NetworkFactory.cpp b/src/cpp/rtps/network/NetworkFactory.cpp index eaaf4d50b32..a6e5cb2be4c 100644 --- a/src/cpp/rtps/network/NetworkFactory.cpp +++ b/src/cpp/rtps/network/NetworkFactory.cpp @@ -30,7 +30,7 @@ namespace rtps{ using SendResourceList = fastdds::rtps::SendResourceList; -NetworkFactory::NetworkFactory() : maxMessageSizeBetweenTransports_(0), +NetworkFactory::NetworkFactory() : maxMessageSizeBetweenTransports_(std::numeric_limits::max()), minSendBufferSize_(std::numeric_limits::max()) { } @@ -49,8 +49,9 @@ bool NetworkFactory::build_send_resources( return returned_value; } -bool NetworkFactory::BuildReceiverResources(Locator_t& local, uint32_t maxMsgSize, - std::vector>& returned_resources_list) +bool NetworkFactory::BuildReceiverResources(Locator_t& local, + std::vector>& returned_resources_list, + uint32_t receiver_max_message_size) { bool returnedValue = false; for (auto& transport : mRegisteredTransports) @@ -59,8 +60,12 @@ bool NetworkFactory::BuildReceiverResources(Locator_t& local, uint32_t maxMsgSiz { if (!transport->IsInputChannelOpen(local)) { + uint32_t max_recv_buffer_size = (std::min)( + transport->max_recv_buffer_size(), + receiver_max_message_size); + std::shared_ptr newReceiverResource = std::shared_ptr( - new ReceiverResource(*transport, local, maxMsgSize)); + new ReceiverResource(*transport, local, max_recv_buffer_size)); if (newReceiverResource->mValid) { @@ -90,7 +95,7 @@ bool NetworkFactory::RegisterTransport(const TransportDescriptorInterface* descr if(wasRegistered) { - if(descriptor->max_message_size() > maxMessageSizeBetweenTransports_) + if(descriptor->max_message_size() < maxMessageSizeBetweenTransports_) maxMessageSizeBetweenTransports_ = descriptor->max_message_size(); if(minSendBufferSize < minSendBufferSize_) diff --git a/src/cpp/rtps/network/ReceiverResource.cpp b/src/cpp/rtps/network/ReceiverResource.cpp index 2833c8fdc6d..108d84133e7 100644 --- a/src/cpp/rtps/network/ReceiverResource.cpp +++ b/src/cpp/rtps/network/ReceiverResource.cpp @@ -26,15 +26,19 @@ namespace eprosima{ namespace fastrtps{ namespace rtps{ -ReceiverResource::ReceiverResource(TransportInterface& transport, const Locator_t& locator, uint32_t max_size) +ReceiverResource::ReceiverResource( + TransportInterface& transport, + const Locator_t& locator, + uint32_t max_recv_buffer_size) : Cleanup(nullptr) , LocatorMapsToManagedChannel(nullptr) , mValid(false) , mtx() , receiver(nullptr) + , max_message_size_(max_recv_buffer_size) { // Internal channel is opened and assigned to this resource. - mValid = transport.OpenInputChannel(locator, this, max_size); + mValid = transport.OpenInputChannel(locator, this, max_message_size_); if (!mValid) { return; // Invalid resource to be discarded by the factory. @@ -54,6 +58,7 @@ ReceiverResource::ReceiverResource(ReceiverResource&& rValueResource) rValueResource.receiver = nullptr; mValid = rValueResource.mValid; rValueResource.mValid = false; + max_message_size_ = rValueResource.max_message_size_; } bool ReceiverResource::SupportsLocator(const Locator_t& localLocator) diff --git a/src/cpp/rtps/participant/RTPSParticipantImpl.cpp b/src/cpp/rtps/participant/RTPSParticipantImpl.cpp index 7161b2ef2ff..9792f6771d4 100644 --- a/src/cpp/rtps/participant/RTPSParticipantImpl.cpp +++ b/src/cpp/rtps/participant/RTPSParticipantImpl.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include @@ -112,6 +113,7 @@ RTPSParticipantImpl::RTPSParticipantImpl( , mp_userParticipant(par) , mp_mutex(new std::recursive_mutex()) , is_intraprocess_only_(should_be_intraprocess_only(PParam)) + , has_shm_transport_(false) { // Builtin transport by default if (PParam.useBuiltinTransports) @@ -147,6 +149,8 @@ RTPSParticipantImpl::RTPSParticipantImpl( for (const auto& transportDescriptor : PParam.userTransports) { m_network_Factory.RegisterTransport(transportDescriptor.get()); + + has_shm_transport_ |= (dynamic_cast(transportDescriptor.get()) != nullptr); } mp_userParticipant->mp_impl = this; @@ -955,10 +959,18 @@ void RTPSParticipantImpl::createReceiverResources( { std::vector > newItemsBuffer; - uint32_t size = m_network_Factory.get_max_message_size_between_transports(); +#if HAVE_SECURITY + // An auxilary buffer is needed in the ReceiverResource to to decrypt the message, + // that imposes a limit in the received messages size even if the transport allows (uint32_t) messages size. + uint32_t max_receiver_buffer_size = + is_secure() ? std::numeric_limits::max() : std::numeric_limits::max(); +#else + uint32_t max_receiver_buffer_size = std::numeric_limits::max(); +#endif + for (auto it_loc = Locator_list.begin(); it_loc != Locator_list.end(); ++it_loc) { - bool ret = m_network_Factory.BuildReceiverResources(*it_loc, size, newItemsBuffer); + bool ret = m_network_Factory.BuildReceiverResources(*it_loc, newItemsBuffer, max_receiver_buffer_size); if (!ret && ApplyMutation) { uint32_t tries = 0; @@ -966,7 +978,7 @@ void RTPSParticipantImpl::createReceiverResources( { tries++; *it_loc = applyLocatorAdaptRule(*it_loc); - ret = m_network_Factory.BuildReceiverResources(*it_loc, size, newItemsBuffer); + ret = m_network_Factory.BuildReceiverResources(*it_loc, newItemsBuffer, max_receiver_buffer_size); } } @@ -976,7 +988,7 @@ void RTPSParticipantImpl::createReceiverResources( //Push the new items into the ReceiverResource buffer m_receiverResourcelist.emplace_back(*it_buffer); //Create and init the MessageReceiver - auto mr = new MessageReceiver(this, size); + auto mr = new MessageReceiver(this, (*it_buffer)->max_message_size()); m_receiverResourcelist.back().mp_receiver = mr; //Start reception if (RegisterReceiver) @@ -1211,7 +1223,19 @@ void RTPSParticipantImpl::assert_remote_participant_liveliness( uint32_t RTPSParticipantImpl::getMaxMessageSize() const { - return m_network_Factory.get_max_message_size_between_transports(); +#if HAVE_SECURITY + // An auxilary buffer is needed in the ReceiverResource to to decrypt the message, + // that imposes a limit in the received messages size even if the transport allows (uint32_t) messages size. + // So the sender limits also its size. + uint32_t max_receiver_buffer_size = + is_secure() ? std::numeric_limits::max() : std::numeric_limits::max(); +#else + uint32_t max_receiver_buffer_size = std::numeric_limits::max(); +#endif + + return (std::min)( + m_network_Factory.get_max_message_size_between_transports(), + max_receiver_buffer_size); } uint32_t RTPSParticipantImpl::getMaxDataSize() diff --git a/src/cpp/rtps/participant/RTPSParticipantImpl.h b/src/cpp/rtps/participant/RTPSParticipantImpl.h index 1c67a256abd..e95decf85ee 100644 --- a/src/cpp/rtps/participant/RTPSParticipantImpl.h +++ b/src/cpp/rtps/participant/RTPSParticipantImpl.h @@ -383,6 +383,11 @@ class RTPSParticipantImpl return m_network_Factory; } + inline bool has_shm_transport() + { + return has_shm_transport_; + } + uint32_t get_min_network_send_buffer_size() { return m_network_Factory.get_min_send_buffer_size(); @@ -566,6 +571,9 @@ class RTPSParticipantImpl security::ParticipantSecurityAttributes security_attributes_; #endif + //! Indicates whether the participant has shared-memory transport + bool has_shm_transport_; + /** * Get persistence service from factory, using endpoint attributes (or participant * attributes if endpoint does not define a persistence service config) diff --git a/src/cpp/rtps/transport/shared_mem/MultiProducerConsumerRingBuffer.hpp b/src/cpp/rtps/transport/shared_mem/MultiProducerConsumerRingBuffer.hpp new file mode 100644 index 00000000000..c812b85a942 --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/MultiProducerConsumerRingBuffer.hpp @@ -0,0 +1,403 @@ +// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_SHAREDMEM_MPC_RINGBUFFER_ +#define _FASTDDS_SHAREDMEM_MPC_RINGBUFFER_ + +#include +#include +#include + +namespace eprosima { +namespace fastdds { +namespace rtps { + +/** + * Ring buffer capable of multiple producers / multiple consumers. + * Read / Write operations are lock-free. + * Data is organized in a fixed number of Cells of the same type. + * Consumers (listeners) must be registered to access the cells. + * When a Cell is pushed to the buffer, a counter for that cell is initialized with number + * of listeners registered in the buffer in that moment. The Cell will be freed + * when all listeners have poped the cell. + */ +template +class MultiProducerConsumerRingBuffer +{ +public: + + class Cell + { + public: + + const T& data() const + { + return data_; + } + + void data( + const T& data) + { + data_ = data; + } + + uint32_t ref_counter() const + { + return ref_counter_.load(std::memory_order_relaxed); + } + + friend class MultiProducerConsumerRingBuffer; + + std::atomic ref_counter_; + T data_; + }; + + class Listener + { + public: + + Listener( + MultiProducerConsumerRingBuffer& buffer, + uint32_t write_p) + : buffer_(buffer) + , read_p_(write_p) + { + } + + ~Listener() + { + buffer_.unregister_listener(*this); + } + + /** + * @returns the Cell at the read pointer or nullptr if the buffer is empty + */ + Cell* head() + { + auto pointer = buffer_.node_->pointer_.load(std::memory_order_relaxed); + + // If local read_pointer and write_pointer are equal => buffer is empty for this listener + if (read_p_ == pointer.write_p ) + { + return nullptr; + } + + auto cell = &buffer_.cells_[get_pointer_value(read_p_)]; + + return cell->ref_counter() != 0 ? cell : nullptr; + } + + /** + * Decreases the ref_counter of the head cell, + * if the counter reaches 0 the cell becomes dirty + * and free_cells are incremented + * @return true if the cell ref_counter is 0 after pop + * @throw int if buffer is empty + */ + bool pop() + { + auto cell = head(); + + if (!cell) + { + throw std::runtime_error("Buffer empty"); + } + + auto counter = cell->ref_counter_.fetch_sub(1); + assert(counter > 0); + + // If all the listeners have read the cell + if (counter == 1) + { + // Increase the free cells => increase the global read pointer + auto pointer = buffer_.node_->pointer_.load(std::memory_order_relaxed); + while (!buffer_.node_->pointer_.compare_exchange_weak(pointer, + { pointer.write_p, pointer.free_cells + 1 }, + std::memory_order_release, + std::memory_order_relaxed)) + { + } + } + + // Increase the local read pointer + read_p_ = buffer_.inc_pointer(read_p_); + + return (counter == 1); + } + + private: + + MultiProducerConsumerRingBuffer& buffer_; + uint32_t read_p_; + }; + + struct Pointer + { + uint32_t write_p; + uint32_t free_cells; + }; + + struct RegisterPushLock + { + uint32_t pushing_count; + uint32_t registering_flag; + }; + + struct Node + { + std::atomic pointer_; + uint32_t total_cells_; + + uint32_t registered_listeners_; + std::atomic register_push_lock_; + }; + + MultiProducerConsumerRingBuffer( + Cell* cells_base, + uint32_t total_cells) + : cells_(cells_base) + , is_node_owned_(true) + { + if (total_cells > (1u << 31u)) + { + throw std::runtime_error("total_cells out of range"); + } + + node_ = new Node(); + init_node(node_, total_cells); + + // Init cells + for (Cell* cell = &cells_[0]; cell < &cells_[total_cells]; cell++) + { + cell->ref_counter_.store(0, std::memory_order_relaxed); + } + } + + MultiProducerConsumerRingBuffer( + Cell* cells_base, + Node* node) + : cells_(cells_base) + , is_node_owned_(false) + { + node_ = node; + } + + ~MultiProducerConsumerRingBuffer() + { + if (is_node_owned_) + { + delete node_; + } + } + + /** + * Push a new element into the buffer initializing the cell's ref_counter, + * @return true if there are listeners registered, false if no listeners => buffer not enqueued + * @throw std::runtime_error if the buffer is full + */ + bool push( + const T& data) + { + lock_registering(); + + // If no listeners the buffer is dropped + if (node_->registered_listeners_ == 0) + { + unlock_registering(); + return false; + } + + auto pointer = node_->pointer_.load(std::memory_order_relaxed); + + // if free cells, increase the write pointer and decrease the free cells + while (pointer.free_cells > 0 && + !node_->pointer_.compare_exchange_weak(pointer, {inc_pointer(pointer.write_p), pointer.free_cells-1}, + std::memory_order_release, + std::memory_order_relaxed)) + { + } + + unlock_registering(); + + + if (pointer.free_cells == 0) + { + unlock_registering(); + throw std::runtime_error("Buffer full"); + } + + auto& cell = cells_[get_pointer_value(pointer.write_p)]; + + cell.data(data); + cell.ref_counter_.store(node_->registered_listeners_, std::memory_order_release); + + unlock_registering(); + + return true; + } + + bool is_buffer_full() + { + return (node_->pointer_.load(std::memory_order_relaxed).free_cells == 0); + } + + bool is_buffer_empty() + { + return (node_->pointer_.load(std::memory_order_relaxed).free_cells == node_->total_cells_); + } + + /** + * Register a new listener (consumer) + * The new listener's read pointer is equal to the ring-buffer write pointer at the registering moment. + * @return A shared_ptr to the listener. + * The listener will be unregistered when shared_ptr is destroyed. + */ + std::shared_ptr register_listener() + { + lock_pushing(); + + // The new listener's read pointer is the current write pointer + auto listener = std::make_shared(*this, node_->pointer_.load(std::memory_order_relaxed).write_p); + + node_->registered_listeners_++; + + unlock_pushing(); + + return listener; + } + + static void init_node( + Node* node, + uint32_t total_cells) + { + if (total_cells > static_cast((1 << 31))) + { + throw std::runtime_error("total_cells out of range"); + } + + node->total_cells_ = total_cells; + node->registered_listeners_ = 0; + node->register_push_lock_.store({0, false}); + node->pointer_.store({0,total_cells}, std::memory_order_relaxed); + } + +private: + + Node* node_; + Cell* cells_; + bool is_node_owned_; + + static uint32_t get_pointer_value( + uint32_t pointer) + { + // Bit 31 is loop_flag, 0-30 are value + return pointer & 0x7FFFFFFF; + } + + uint32_t inc_pointer( + const uint32_t pointer) + { + uint32_t value = pointer & 0x7FFFFFFF; + uint32_t loop_flag = pointer >> 31; + + value = (value + 1) % node_->total_cells_; + + if (value == 0) + { + loop_flag ^= 1; + } + + // Bit 31 is loop_flag, 0-30 are value + return (loop_flag << 31) | value; + } + + /** + * Called by the writters to lock listener's registering while writer pushes + */ + void lock_registering() + { + auto register_push = node_->register_push_lock_.load(std::memory_order_relaxed); + // Increase the pushing_count (only possible if registering_flag == false) + while (!node_->register_push_lock_.compare_exchange_weak(register_push, {register_push.pushing_count+1, false}, + std::memory_order_acquire, + std::memory_order_relaxed)); + } + + /** + * Called by the writters to unlock listener's registering + */ + void unlock_registering() + { + auto register_push = node_->register_push_lock_.load(std::memory_order_relaxed); + assert(!register_push.registering_flag); // Opssss. + // Decrease the pushing_count + while (!node_->register_push_lock_.compare_exchange_weak(register_push, {register_push.pushing_count-1, false}, + std::memory_order_release, + std::memory_order_relaxed)); + } + + /** + * Called by a listener when registering to lock writters pushing + */ + void lock_pushing() + { + RegisterPushLock register_push = {0, false}; + // Registering a listener... pushing_count must be 0 + while (!node_->register_push_lock_.compare_exchange_weak(register_push, {0, true}, + std::memory_order_acquire, + std::memory_order_relaxed)); + } + + /** + * Called by a listener when registering finish to unlock writters pushing + */ + void unlock_pushing() + { + RegisterPushLock register_push = {0, true}; + // Registering a listener... pushing_count must be 0 + while (!node_->register_push_lock_.compare_exchange_weak(register_push, {0, false}, + std::memory_order_release, + std::memory_order_relaxed)); + } + + void unregister_listener( + Listener& listener) + { + lock_pushing(); + + try + { + // Forces to decrement the ref_counters for cells available to the listener. + // A exception will break the loop + while (1) + { + listener.pop(); + } + + } + catch (const std::exception&) + { + } + + node_->registered_listeners_--; + + unlock_pushing(); + } +}; + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_SHAREDMEM_MPC_RINGBUFFER_ \ No newline at end of file diff --git a/src/cpp/rtps/transport/shared_mem/SHMLocator.hpp b/src/cpp/rtps/transport/shared_mem/SHMLocator.hpp new file mode 100644 index 00000000000..5a1e21a67a9 --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/SHMLocator.hpp @@ -0,0 +1,85 @@ +// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_SHMLOCATOR_H_ +#define _FASTDDS_SHMLOCATOR_H_ + +#include +#include + +namespace eprosima { +namespace fastdds { +namespace rtps { + +/** + * Define operations for shared-memory locators + */ +class SHMLocator +{ +public: + + enum class Type + { + UNICAST, + MULTICAST + }; + + /** + * Generate a shared-memory locator for the local host. + * @param port Locator's shared-memory port. + * @param type Indicates whether the locator is unicast or multicast. + * @return The created shared-memory locator. + */ + static fastrtps::rtps::Locator_t create_locator( + uint32_t port, + Type type) + { + using namespace fastrtps::rtps; + + Locator_t locator(LOCATOR_KIND_SHM, port); + + locator.get_address()[0] = (type == Type::UNICAST) ? 'U' : 'M'; + + auto host_id = Host::get().id(); + locator.get_address()[1] = octet(host_id); + locator.get_address()[2] = octet(host_id >> 8); + + return locator; + } + + /** + * Check whether a given locator is shared-memory kind and belongs to this host + * @param locator Locator to check + * @return boolean + */ + static bool is_shm_and_from_this_host(const fastrtps::rtps::Locator_t& locator) + { + using namespace fastrtps::rtps; + + if(locator.kind == LOCATOR_KIND_SHM) + { + auto host_id = Host::get().id(); + + return locator.address[1] == octet(host_id) && locator.address[2] == octet(host_id >> 8); + } + + return false; + } +}; + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_SHMLOCATOR_H_ diff --git a/src/cpp/rtps/transport/shared_mem/SharedMemChannelResource.hpp b/src/cpp/rtps/transport/shared_mem/SharedMemChannelResource.hpp new file mode 100644 index 00000000000..a15e9684054 --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/SharedMemChannelResource.hpp @@ -0,0 +1,201 @@ +// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_SHAREDMEM_CHANNEL_RESOURCE_ +#define _FASTDDS_SHAREDMEM_CHANNEL_RESOURCE_ + +#include +#include +#include + +#include +#include + +namespace eprosima { +namespace fastdds { +namespace rtps { + +class SharedMemChannelResource : public ChannelResource +{ +public: + + using Log = fastdds::dds::Log; + + SharedMemChannelResource( + std::shared_ptr listener, + const fastrtps::rtps::Locator_t& locator, + TransportReceiverInterface* receiver, + const std::string& dump_file) + : ChannelResource() + , message_receiver_(receiver) + , listener_(listener) + , only_multicast_purpose_(false) + , locator_(locator) + { + if (!dump_file.empty()) + { + auto packets_file_consumer = std::unique_ptr( + new SHMPacketFileConsumer(dump_file)); + + packet_logger_ = std::make_shared >(); + packet_logger_->RegisterConsumer(std::move(packets_file_consumer)); + } + + thread(std::thread(&SharedMemChannelResource::perform_listen_operation, this, locator)); + } + + virtual ~SharedMemChannelResource() override + { + message_receiver_ = nullptr; + } + + void only_multicast_purpose( + const bool value) + { + only_multicast_purpose_ = value; + } + + bool& only_multicast_purpose() + { + return only_multicast_purpose_; + } + + bool only_multicast_purpose() const + { + return only_multicast_purpose_; + } + + inline void message_receiver( + TransportReceiverInterface* receiver) + { + message_receiver_ = receiver; + } + + inline TransportReceiverInterface* message_receiver() + { + return message_receiver_; + } + + inline virtual void disable() override + { + ChannelResource::disable(); + } + + const fastrtps::rtps::Locator_t& locator() const + { + return locator_; + } + + void release() + { + listener_->close(); + } + +private: + + /** + * Function to be called from a new thread, which takes cares of performing a blocking receive + * operation on the ReceiveResource + * @param input_locator - Locator that triggered the creation of the resource + */ + void perform_listen_operation( + fastrtps::rtps::Locator_t input_locator) + { + fastrtps::rtps::Locator_t remote_locator; + + while (alive()) + { + // Blocking receive. + std::shared_ptr message; + + if (!(message = Receive(remote_locator)) ) + { + continue; + } + + if (packet_logger_) + { + packet_logger_->QueueLog({packet_logger_->now(), input_locator, remote_locator, message}); + } + + // Processes the data through the CDR Message interface. + if (message_receiver() != nullptr) + { + message_receiver()->OnDataReceived( + static_cast(message->data()), + message->size(), + input_locator, remote_locator); + } + else if (alive()) + { + logWarning(RTPS_MSG_IN, "Received Message, but no receiver attached"); + } + + // Forces message release before waiting for the next + message.reset(); + } + + message_receiver(nullptr); + } + +protected: + + /** + * Blocking Receive from the specified channel. + */ + virtual std::shared_ptr Receive( + fastrtps::rtps::Locator_t& remote_locator) + { + (void)remote_locator; + + try + { + return listener_->pop(); + } + catch (const std::exception& error) + { + (void)error; + logWarning(RTPS_MSG_OUT, "Error receiving data: " << error.what() << " - " << message_receiver() + << " (" << this << ")"); + return nullptr; + } + } + +private: + + TransportReceiverInterface* message_receiver_; //Associated Readers/Writers inside of MessageReceiver + + // Allows dumping of received packets to a file + std::shared_ptr> packet_logger_; + +protected: + + std::shared_ptr listener_; + +private: + + bool only_multicast_purpose_; + fastrtps::rtps::Locator_t locator_; + + SharedMemChannelResource( + const SharedMemChannelResource&) = delete; + SharedMemChannelResource& operator=( + const SharedMemChannelResource&) = delete; +}; + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_SHAREDMEM_CHANNEL_RESOURCE_ diff --git a/src/cpp/rtps/transport/shared_mem/SharedMemGlobal.hpp b/src/cpp/rtps/transport/shared_mem/SharedMemGlobal.hpp new file mode 100644 index 00000000000..1cb1a76b090 --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/SharedMemGlobal.hpp @@ -0,0 +1,619 @@ +// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_SHAREDMEM_GLOBAL_H_ +#define _FASTDDS_SHAREDMEM_GLOBAL_H_ + +#include +#include +#include + +#include +#include + +#define THREADID "(ID:" << std::this_thread::get_id() <<") " + +namespace eprosima { +namespace fastdds { +namespace rtps { + +/** + * This class defines the global resources for shared-memory communication. + * Mainly the shared-memory ports and its operations. + */ +class SharedMemGlobal +{ +public: + + SharedMemGlobal( + const std::string& domain_name) + : domain_name_(domain_name) + { + + } + + ~SharedMemGlobal() + { + + } + + /** + * Identifies a data buffer given its segment_id (shared-memory segment global_name) + * and offset inside the segment + */ + struct BufferDescriptor + { + SharedMemSegment::Id source_segment_id; + SharedMemSegment::offset buffer_node_offset; + }; + + typedef MultiProducerConsumerRingBuffer::Listener Listener; + typedef MultiProducerConsumerRingBuffer::Cell PortCell; + + struct PortNode + { + UUID<8> uuid; + uint32_t port_id; + SharedMemSegment::condition_variable empty_cv; + SharedMemSegment::mutex empty_cv_mutex; + SharedMemSegment::offset buffer; + SharedMemSegment::offset buffer_node; + std::atomic ref_counter; + uint32_t waiting_count; + uint32_t check_awaken_count; + uint32_t check_id; + bool is_port_ok; + bool is_opened_read_exclusive; + bool is_opened_for_reading; + }; + + /** + * A shared-memory port is a communication channel where data can be written / read. + * A port has a port_id and a global name derived from the port_id and the domain. + * System processes can open a port by knowing its name. + */ + class Port + { + friend class MockPortSharedMemGlobal; + + private: + + std::unique_ptr port_segment_; + + PortNode* node_; + + std::unique_ptr > buffer_; + + uint64_t overflows_count_; + + bool was_check_thread_detached_; + + bool check_all_waiting_threads_alive( + uint32_t time_out_ms) + { + bool is_check_ok = false; + + { + std::lock_guard lock_empty(node_->empty_cv_mutex); + + if (!node_->is_port_ok) + { + throw std::runtime_error("Previous check failed"); + } + + node_->check_id++; + node_->check_awaken_count = node_->waiting_count; + + node_->empty_cv.notify_all(); + } + + auto start = std::chrono::high_resolution_clock::now(); + + do + { + std::this_thread::yield(); + is_check_ok = node_->check_awaken_count == 0; + } + while (!is_check_ok && + std::chrono::high_resolution_clock::now() < start + std::chrono::milliseconds(time_out_ms)); + + return is_check_ok; + } + + inline void notify_unicast( + bool was_buffer_empty_before_push) + { + if (was_buffer_empty_before_push) + { + node_->empty_cv.notify_one(); + } + } + + inline void notify_multicast() + { + node_->empty_cv.notify_all(); + } + + public: + + /** + * Defines open sharing mode of a shared-memory port: + * + * ReadShared (multiple listeners / multiple writers): Once a port is opened ReadShared cannot be opened ReadExclusive. + * + * ReadExclusive (one listener / multipler writers): Once a port is opened ReadExclusive cannot be opened ReadShared. + * + * Write (multiple writers): A port can always be opened for writing. + */ + enum class OpenMode + { + ReadShared, + ReadExclusive, + Write + }; + + Port( + std::unique_ptr&& port_segment, + PortNode* node) + : port_segment_(std::move(port_segment)) + , node_(node) + , overflows_count_(0) + { + auto buffer_base = static_cast::Cell*>( + port_segment_->get_address_from_offset(node_->buffer)); + + auto buffer_node = static_cast::Node*>( + port_segment_->get_address_from_offset(node_->buffer_node)); + + buffer_ = std::unique_ptr>( + new MultiProducerConsumerRingBuffer(buffer_base, buffer_node)); + + node_->ref_counter.fetch_add(1); + } + + ~Port() + { + if (node_->ref_counter.fetch_sub(1) == 1) + { + auto segment_name = port_segment_->name(); + + logInfo(RTPS_TRANSPORT_SHM, THREADID << "Port " << node_->port_id + << segment_name.c_str() << " removed." << "overflows_count " + << overflows_count_); + + if (overflows_count_) + { + logWarning(RTPS_TRANSPORT_SHM, "Port " << node_->port_id + << segment_name.c_str() << " had overflows_count " + << overflows_count_); + } + + port_segment_.reset(); + + SharedMemSegment::remove(segment_name.c_str()); + SharedMemSegment::named_mutex::remove((segment_name + "_mutex").c_str()); + } + } + + /** + * Try to enqueue a buffer descriptor in the port. + * If the port queue is full returns inmediatelly with false value. + * @param[in] buffer_descriptor buffer descriptor to be enqueued + * @param[out] listeners_active false if no active listeners => buffer not enqueued + * @return false in overflow case, true otherwise. + */ + bool try_push( + const BufferDescriptor& buffer_descriptor, + bool* listeners_active) + { + std::unique_lock lock_empty(node_->empty_cv_mutex); + + try + { + bool was_opened_as_unicast_port = node_->is_opened_read_exclusive; + bool was_buffer_empty_before_push = buffer_->is_buffer_empty(); + bool was_someone_listening = (node_->waiting_count > 0); + + *listeners_active = buffer_->push(buffer_descriptor); + + lock_empty.unlock(); + + if (was_someone_listening) + { + if (was_opened_as_unicast_port) + { + notify_unicast(was_buffer_empty_before_push); + } + else + { + notify_multicast(); + } + } + + return true; + } + catch (const std::exception&) + { + lock_empty.unlock(); + overflows_count_++; + } + return false; + } + + /** + * Waits while the port is empty and listener is not closed + * @param[in] listener reference to the listener that will wait for an incoming buffer descriptor. + * @param[in] is_listener_closed this reference can become true in the middle of the waiting process, + * if that happens wait is aborted. + */ + void wait_pop( + Listener& listener, + const std::atomic& is_listener_closed) + { + std::unique_lock lock(node_->empty_cv_mutex); + + uint32_t check_id = node_->check_id; + + node_->waiting_count++; + + do + { + node_->empty_cv.wait(lock, [&] { + return is_listener_closed.load() || listener.head() != nullptr || check_id != node_->check_id; + }); + + if (check_id != node_->check_id) + { + node_->check_awaken_count--; + check_id = node_->check_id; + + if (listener.head()) + { + break; + } + } + else + { + break; + } + } while (1); + + node_->waiting_count--; + } + + /** + * Set the caller's 'is_closed' flag (protecting empty_cv_mutex) and + * forces wake-up all listeners on this port. + * This function is used when destroying a listener waiting for messages + * in the port. + * @param is_listener_closed pointer to the atomic is_closed flag of the Listener object. + */ + void close_listener( + std::atomic* is_listener_closed) + { + { + std::lock_guard lock(node_->empty_cv_mutex); + is_listener_closed->exchange(true); + } + + node_->empty_cv.notify_all(); + } + + /** + * Removes the head buffer-descriptor from the listener's queue + * @param [in] listener reference to the listener that will pop the buffer descriptor. + * @param [out] was_cell_freed is true if the port's cell is freed because all listeners has poped the cell + * @throw std::runtime_error if buffer is empty + */ + void pop( + Listener& listener, + bool& was_cell_freed) + { + was_cell_freed = listener.pop(); + } + + /** + * Register a new listener + * The new listener's read pointer is equal to the ring-buffer write pointer at the registering moment. + * @return A shared_ptr to the listener. + * The listener will be unregistered when shared_ptr is destroyed. + */ + std::shared_ptr create_listener() + { + return buffer_->register_listener(); + } + + bool was_check_thread_detached() + { + return was_check_thread_detached_; + } + + /** + * Performs a check of the opened port. + * When a process crashes with a port opened the port can be leave inoperative. + * @param [in] healthy_check_timeout_ms max timeout (milliseconds) allowed for the whole + * healthy check operation. + * @throw std::exception if the port is inoperative. + */ + void healthy_check( + uint32_t healthy_check_timeout_ms) + { + std::shared_ptr is_check_ok = std::make_shared(false); + + was_check_thread_detached_ = false; + + std::shared_ptr notify_check_done_mutex = std::make_shared(); + + std::shared_ptr notify_check_done_cv = + std::make_shared(); + + std::shared_ptr is_check_done_received = std::make_shared(false); + + std::thread check_thread([=] + { + try + { + *is_check_ok = check_all_waiting_threads_alive(healthy_check_timeout_ms); + + { + std::lock_guard lock_received(*notify_check_done_mutex); + *is_check_done_received = true; + } + + notify_check_done_cv->notify_one(); + } + catch (std::exception&) + { + *is_check_ok = false; + } + }); + + std::unique_lock lock(*notify_check_done_mutex); + + if (!notify_check_done_cv->wait_for(lock, + std::chrono::milliseconds(healthy_check_timeout_ms), + [&] { return *is_check_done_received; })) + { + node_->is_port_ok = false; + was_check_thread_detached_ = true; + check_thread.detach(); + throw std::runtime_error("healthy_check timeout"); + } + + check_thread.join(); + + if (!(*is_check_ok)) + { + node_->is_port_ok = false; + throw std::runtime_error("healthy_check failed"); + } + } + + }; // Port + + /** + * Open a shared-memory port. If the port doesn't exist in the system a port with port_id is created, + * otherwise the existing port is opened. + * + * @param [in] port_id Identifies the port + * @param [in] max_buffer_descriptors Capacity of the port (only used if the port is created) + * @param [in] healthy_check_timeout_ms Timeout for healthy check test + * @param [in] open_mode Can be ReadShared, ReadExclusive or Write (see Port::OpenMode enum). + * + * @remarks This function performs a test to validate whether the existing port is OK, if the test + * goes wrong the existing port is removed from shared-memory and a new port is created. + */ + std::shared_ptr open_port( + uint32_t port_id, + uint32_t max_buffer_descriptors, + uint32_t healthy_check_timeout_ms, + Port::OpenMode open_mode = Port::OpenMode::ReadShared) + { + std::shared_ptr port; + + auto port_segment_name = domain_name_ + "_port" + std::to_string(port_id); + + logInfo(RTPS_TRANSPORT_SHM, THREADID << "Opening " << port_segment_name); + + std::unique_ptr port_mutex = + SharedMemSegment::open_or_create_and_lock_named_mutex(port_segment_name + "_mutex"); + + std::unique_lock port_lock(*port_mutex, std::adopt_lock); + + try + { + // Try to open + auto port_segment = std::unique_ptr( + new SharedMemSegment(boost::interprocess::open_only, port_segment_name.c_str())); + + SharedMemGlobal::PortNode* port_node; + + try + { + port_node = port_segment->get().find("port_node").first; + port = std::make_shared(std::move(port_segment), port_node); + } + catch (std::exception&) + { + logWarning(RTPS_TRANSPORT_SHM, THREADID << "Port " + << port_id << " Couldn't find port_node "); + + SharedMemSegment::remove(port_segment_name.c_str()); + + logWarning(RTPS_TRANSPORT_SHM, THREADID << "Port " + << port_id << " Removed."); + + throw; + } + + try + { + port->healthy_check(healthy_check_timeout_ms); + + if ( (port_node->is_opened_read_exclusive && open_mode != Port::OpenMode::Write) || + (port_node->is_opened_for_reading && open_mode == Port::OpenMode::ReadExclusive)) + { + logError(RTPS_TRANSPORT_SHM, THREADID << "Couln't open Port " + << port_node->port_id << " (" << port_node->uuid.to_string() << + ") for reading because is exclusive"); + + port.reset(); + } + else + { + port_node->is_opened_read_exclusive = (open_mode == Port::OpenMode::ReadExclusive); + port_node->is_opened_for_reading |= (open_mode == Port::OpenMode::ReadShared); + + logInfo(RTPS_TRANSPORT_SHM, THREADID << "Port " + << port_node->port_id << " (" << port_node->uuid.to_string() << + ") Opened"); + } + } + catch (std::exception&) + { + auto port_uuid = port_node->uuid.to_string(); + + // Healthy check left a thread blocked at port resources + // So we leave port_segment unmanaged, better to leak memory than a crash + if (port->was_check_thread_detached()) + { + // Release owership + port_segment.release(); + + logWarning(RTPS_TRANSPORT_SHM, THREADID << "Existing Port " + << port_id << " (" << port_uuid << + ") NOT Healthy (check_thread detached)."); + } + else + { + logWarning(RTPS_TRANSPORT_SHM, THREADID << "Existing Port " + << port_id << " (" << port_uuid << ") NOT Healthy."); + } + + SharedMemSegment::remove(port_segment_name.c_str()); + + logWarning(RTPS_TRANSPORT_SHM, THREADID << "Port " + << port_id << " (" << port_uuid << ") Removed."); + + throw; + } + } + catch (std::exception&) + { + // Doesn't exist => create it + // The segment will contain the node, the buffer and the internal allocator structures (512bytes estimated) + uint32_t extra = 512; + uint32_t segment_size = sizeof(PortNode) + sizeof(PortCell) * max_buffer_descriptors; + + try + { + auto port_segment = std::unique_ptr( + new SharedMemSegment(boost::interprocess::create_only, port_segment_name.c_str(), + segment_size + extra)); + + // Memset the whole segment to zero in order to force physical map of the buffer + auto payload = port_segment->get().allocate(segment_size); + memset(payload, 0, segment_size); + port_segment->get().deallocate(payload); + + port = init_port(port_id, port_segment, max_buffer_descriptors, open_mode); + } + catch (std::exception& e) + { + logError(RTPS_TRANSPORT_SHM, "Failed to create port segment " << port_segment_name + << ": " << e.what()); + + throw; + } + } + + if (port == nullptr) + { + throw std::runtime_error("Coulnd't open port "); + } + + return port; + } + +private: + + std::string domain_name_; + + std::shared_ptr init_port( + uint32_t port_id, + std::unique_ptr& segment, + uint32_t max_buffer_descriptors, + Port::OpenMode open_mode) + { + std::shared_ptr port; + PortNode* port_node = nullptr; + MultiProducerConsumerRingBuffer::Node* buffer_node = nullptr; + + try + { + // Port node allocation + port_node = segment->get().construct("port_node")(); + port_node->port_id = port_id; + port_node->is_port_ok = true; + UUID<8>::generate(port_node->uuid); + port_node->waiting_count = 0; + port_node->check_awaken_count = 0; + port_node->check_id = 0; + port_node->is_opened_read_exclusive = (open_mode == Port::OpenMode::ReadExclusive); + port_node->is_opened_for_reading = (open_mode != Port::OpenMode::Write); + + // Buffer cells allocation + port_node->buffer = + segment->get_offset_from_address( + segment->get().construct::Cell>( + boost::interprocess::anonymous_instance)[max_buffer_descriptors]()); + + // Buffer node allocation + buffer_node = segment->get().construct::Node>( + boost::interprocess::anonymous_instance)(); + + MultiProducerConsumerRingBuffer::init_node(buffer_node, max_buffer_descriptors); + + port_node->buffer_node = segment->get_offset_from_address(buffer_node); + + port = std::make_shared(std::move(segment), port_node); + + logInfo(RTPS_TRANSPORT_SHM, THREADID << "Port " + << port_node->port_id << " (" << port_node->uuid.to_string() << + ") Created."); + } + catch (const std::exception&) + { + if (port_node) + { + segment->get().destroy_ptr(port_node); + } + + if (buffer_node) + { + segment->get().destroy_ptr(buffer_node); + } + + throw; + } + + return port; + } +}; + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_SHAREDMEM_GLOBAL_H_ \ No newline at end of file diff --git a/src/cpp/rtps/transport/shared_mem/SharedMemLog.hpp b/src/cpp/rtps/transport/shared_mem/SharedMemLog.hpp new file mode 100644 index 00000000000..9f2617d9ed4 --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/SharedMemLog.hpp @@ -0,0 +1,412 @@ +// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_SHAREDMEM_LOG_H_ +#define _FASTDDS_SHAREDMEM_LOG_H_ + +#include +#include +#include + +namespace eprosima { +namespace fastdds { +namespace rtps { + +class SHMPacketFileLogger +{ +private: + + uint16_t dump_id_ = 0; + FILE* f_; + std::unique_ptr f_mutex_; + +public: + + SHMPacketFileLogger( + const std::string& filename, + uint16_t dump_id) + : dump_id_(dump_id) + { +#if defined( _MSC_VER ) + // In Windows, shared-access specification is needed + f_ = _fsopen(filename.c_str(), "a", _SH_DENYNO); +#else + f_ = fopen(filename.c_str(), "a"); +#endif + + if (f_ != nullptr) + { + std::hash hash_fn; + size_t filename_hash = hash_fn(filename); + std::string mutex_name; + + try + { + mutex_name = "log" + std::to_string(filename_hash) + "mtx"; + + f_mutex_ = SharedMemSegment::open_or_create_and_lock_named_mutex(mutex_name); + + f_mutex_->unlock(); + + } + catch (const std::exception& e) + { + logError(RTPS_TRANSPORT_SHM, "Failed to open/create interprocess mutex for packet_file_log: " + << filename << " named: " << mutex_name << " with err: "<< e.what()); + + fclose(f_); + f_ = nullptr; + } + } + else + { + logError(RTPS_TRANSPORT_SHM, "Failed to open packet_file_log: " << filename); + } + } + + virtual ~SHMPacketFileLogger() + { + if (f_) + { + fclose(f_); + } + } + + void dump_packet( + const std::string timestamp, + const fastrtps::rtps::Locator_t& from, + const fastrtps::rtps::Locator_t& to, + const fastrtps::rtps::octet* buf, + const uint32_t len) + { + try + { + if (f_ != NULL ) + { + std::lock_guard interprocess_file_lock(*f_mutex_); + + uint32_t ipSize = len + 28; + uint32_t udpSize = len + 8; + + // Timestamp in format '%H:%M:%S.' + fprintf(f_, "%s ", timestamp.c_str()); + + // IP header + fprintf(f_, "000000 45 00 %02x %02x %02x %02x 00 00 11 11 00 00\n", (ipSize >> 8) & 0xFF, ipSize & 0xFF, + (dump_id_ >> 8) & 0xFF, dump_id_ & 0xFF); + + if (from.kind == 1 && fastrtps::rtps::IsAddressDefined(from)) + { + fprintf(f_, "00000c %02x %02x %02x %02x\n", from.address[12], from.address[13], from.address[14], + from.address[15]); + } + else + { + std::stringstream ss; + ss << std::this_thread::get_id(); + uint32_t thread_id = std::atoi(ss.str().c_str()); + auto addr = reinterpret_cast(&thread_id); + fprintf(f_, "00000c %02x %02x %02x %02x\n", addr[0], addr[1], addr[2], addr[3]); + } + + if (to.kind == 1 && fastrtps::rtps::IsAddressDefined(to)) + { + fprintf(f_, "000010 %02x %02x %02x %02x\n", to.address[12], to.address[13], to.address[14], + to.address[15]); + } + else + { + fprintf(f_, "000010 %02x %02x %02x %02x\n", 0, 0, 0, 0); + } + + // UDP header + fprintf(f_, "000014 %02x %02x %02x %02x\n", (from.port >> 8) & 0xFF, from.port & 0xFF, + (to.port >> 8) & 0xFF, + to.port & 0xFF); + fprintf(f_, "000018 %02x %02x 00 00", (udpSize >> 8) & 0xFF, udpSize & 0xFF); + + // Data + for (uint32_t i = 0; i < len; i++) + { + if ((i & 15) == 0) + { + fprintf(f_, "\n%06x", i + 28); + } + fprintf(f_, " %02x", buf[i]); + } + + fprintf(f_, "\n\n"); + fflush(f_); + } + } + catch (const std::exception&) + { + logError(RTPS_TRANSPORT_SHM, "Failed to lock interprocess mutex packet_file_log"); + return; + } + } +}; + +class SHMPacketFileConsumer +{ +public: + + struct Pkt + { + std::string timestamp; + fastrtps::rtps::Locator_t from; + fastrtps::rtps::Locator_t to; + std::shared_ptr buffer; + }; + + SHMPacketFileConsumer( + const std::string& filename) + : file_logger_(filename, 1) + { + } + + void Consume( + const Pkt& packet) + { + file_logger_.dump_packet(packet.timestamp, packet.from, packet.to, + static_cast(packet.buffer->data()), packet.buffer->size()); + } + +private: + + SHMPacketFileLogger file_logger_; +}; + + +/** + * PacketLogger + */ +template +class PacketsLog +{ +public: + + ~PacketsLog() + { + Flush(); + KillThread(); + } + + //! Returns the logging engine to configuration defaults. + void Reset() + { + std::unique_lock configGuard(resources_.config_mutex); + resources_.consumers.clear(); + } + + void RegisterConsumer( + std::unique_ptr&& consumer) + { + std::unique_lock guard(resources_.config_mutex); + resources_.consumers.emplace_back(std::move(consumer)); + } + + void ClearConsumers() + { + std::unique_lock working(resources_.cv_mutex); + resources_.cv.wait(working, + [&]() + { + return resources_.logs.BothEmpty(); + }); + std::unique_lock guard(resources_.config_mutex); + resources_.consumers.clear(); + } + + //! Waits until no more log info is availabel + void Flush() + { + std::unique_lock guard(resources_.cv_mutex); + + if (!resources_.logging && !resources_.logging_thread) + { + // already killed + return; + } + + /* Flush() two steps strategy: + + I must assure Log::Run swaps the queues because only swapping the queues the background content + will be consumed (first Run() loop). + + Then, I must assure the new front queue content is consumed (second Run() loop). + */ + + int last_loop = -1; + + for (int i = 0; i < 2; ++i) + { + resources_.cv.wait(guard, + [&]() + { + /* I must avoid: + + the two calls be processed without an intermediate Run() loop (by using last_loop sequence number) + + deadlock by absence of Run() loop activity (by using BothEmpty() call) + */ + return !resources_.logging || + ( resources_.logs.Empty() && + ( last_loop != resources_.current_loop || resources_.logs.BothEmpty()) ); + }); + + last_loop = resources_.current_loop; + + } + } + + //! Stops the logging thread. It will re-launch on the next call to a successful log macro. + void KillThread() + { + { + std::unique_lock guard(resources_.cv_mutex); + resources_.logging = false; + resources_.work = false; + } + + if (resources_.logging_thread) + { + resources_.cv.notify_all(); + // The #ifdef workaround here is due to an unsolved MSVC bug, which Microsoft has announced + // they have no intention of solving: https://connect.microsoft.com/VisualStudio/feedback/details/747145 + // Each VS version deals with post-main deallocation of threads in a very different way. + #if !defined(_WIN32) || defined(FASTRTPS_STATIC_LINK) || _MSC_VER >= 1800 + resources_.logging_thread->join(); + #endif + resources_.logging_thread.reset(); + } + } + + // Note: In VS2013, if you're linking this class statically, you will have to call KillThread before leaving + // main, due to an unsolved MSVC bug. + + void QueueLog( + const typename TPacketConsumer::Pkt& packet) + { + { + std::unique_lock guard(resources_.cv_mutex); + if (!resources_.logging && !resources_.logging_thread) + { + resources_.logging = true; + resources_.logging_thread.reset(new std::thread(&PacketsLog::run, this)); + } + } + + resources_.logs.Push(packet); + { + std::unique_lock guard(resources_.cv_mutex); + resources_.work = true; + } + resources_.cv.notify_all(); + } + + std::string now() + { + std::stringstream stream; + auto now = std::chrono::system_clock::now(); + std::time_t now_c = std::chrono::system_clock::to_time_t(now); + std::chrono::system_clock::duration tp = now.time_since_epoch(); + tp -= std::chrono::duration_cast(tp); + auto ms = static_cast(tp / std::chrono::milliseconds(1)); + + #if defined(_WIN32) + struct tm timeinfo; + localtime_s(&timeinfo, &now_c); + stream << std::put_time(&timeinfo, "%T") << "." << std::setw(3) << std::setfill('0') << ms << " "; + //#elif defined(__clang__) && !defined(std::put_time) // TODO arm64 doesn't seem to support std::put_time + // (void)now_c; + // (void)ms; + #else + stream << std::put_time(localtime(&now_c), "%T") << "." << std::setw(3) << std::setfill('0') << ms << " "; + #endif + return stream.str(); + } + +private: + + struct Resources + { + eprosima::fastrtps::DBQueue logs; + std::vector > consumers; + std::unique_ptr logging_thread; + + // Condition variable segment. + std::condition_variable cv; + std::mutex cv_mutex; + bool logging; + bool work; + int current_loop; + + // Context configuration. + std::mutex config_mutex; + + Resources() + : logging(false) + ,work(false) + ,current_loop(0) + { + } + }; + + Resources resources_; + + void run() + { + std::unique_lock guard(resources_.cv_mutex); + + while (resources_.logging) + { + resources_.cv.wait(guard, + [&]() + { + return !resources_.logging || resources_.work; + }); + + resources_.work = false; + + guard.unlock(); + { + resources_.logs.Swap(); + while (!resources_.logs.Empty()) + { + std::unique_lock configGuard(resources_.config_mutex); + for (auto& consumer : resources_.consumers) + { + consumer->Consume(resources_.logs.Front()); + } + + resources_.logs.Pop(); + } + } + guard.lock(); + + // avoid overflow + if (++resources_.current_loop > 10000) + { + resources_.current_loop = 0; + } + + resources_.cv.notify_all(); + } + } +}; + +} // eprosima +} // fastdds +} // rtps + +#endif // _FASTDDS_SHAREDMEM_LOG_H_ \ No newline at end of file diff --git a/src/cpp/rtps/transport/shared_mem/SharedMemManager.hpp b/src/cpp/rtps/transport/shared_mem/SharedMemManager.hpp new file mode 100644 index 00000000000..92529ab828e --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/SharedMemManager.hpp @@ -0,0 +1,593 @@ +// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_SHAREDMEM_MANAGER_H_ +#define _FASTDDS_SHAREDMEM_MANAGER_H_ + +#include +#include +#include + +#include + +namespace eprosima { +namespace fastdds { +namespace rtps { + +using Log = fastdds::dds::Log; + +/** + * Provides functionality for the application to: + * + * Open shared-memory ports. + * Create shared memory segments. + */ +class SharedMemManager +{ +private: + + struct BufferNode + { + struct + { + std::atomic ref_count; + uint32_t data_size; + }header; + uint8_t data[1]; + }; + + struct SegmentNode + { + std::atomic ref_count; + std::atomic free_bytes; + }; + +public: + + SharedMemManager( + const std::string& domain_name) + : global_segment_(domain_name) + { + per_allocation_extra_size_ = + SharedMemSegment::compute_per_allocation_extra_size(std::alignment_of::value); + } + + class Buffer + { + protected: + + virtual ~Buffer() = default; + + public: + + virtual void* data() = 0; + virtual uint32_t size() = 0; + }; + + class SharedMemBuffer : public Buffer + { + public: + + SharedMemBuffer( + std::shared_ptr& segment, + const SharedMemSegment::Id& segment_id, + BufferNode* buffer_node, + SegmentNode* segment_node) + : segment_(segment) + , segment_id_(segment_id) + , buffer_node_(buffer_node) + , segment_node_(segment_node) + { + increase_ref(); + } + + ~SharedMemBuffer() override + { + decrease_ref(); + } + + void* data() override + { + return buffer_node_->data; + } + + uint32_t size() override + { + return buffer_node_->header.data_size; + } + + SharedMemSegment::offset node_offset() + { + return segment_->get_offset_from_address(buffer_node_); + } + + SharedMemSegment::Id segment_id() + { + return segment_id_; + } + + void increase_ref() + { + buffer_node_->header.ref_count.fetch_add(1); + } + + void decrease_ref() + { + int32_t buffer_size = buffer_node_->header.data_size; + + // Last reference to the buffer + if (buffer_node_->header.ref_count.fetch_sub(1) == 1) + { + // Anotate the new free space + segment_node_->free_bytes.fetch_add(buffer_size); + } + } + + private: + + std::shared_ptr segment_; + SharedMemSegment::Id segment_id_; + BufferNode* buffer_node_; + SegmentNode* segment_node_; + }; + + /** + * Handle a shared-memory segment + * Allows buffer allocation / deallocation + */ + class Segment + { + public: + + Segment( + uint32_t size, + uint32_t payload_size) + : segment_id_() + , overflows_count_(0) + , max_payload_size_(payload_size) + { + segment_id_.generate(); + + auto segment_name = segment_id_.to_string(); + + SharedMemSegment::remove(segment_name.c_str()); + + try + { + segment_ = std::unique_ptr( + new SharedMemSegment(boost::interprocess::create_only, segment_name.c_str(), size)); + } + catch (const std::exception& e) + { + logError(RTPS_TRANSPORT_SHM, "Failed to create segment " << segment_name + << ": " << e.what()); + + throw; + } + + // Init the segment node + segment_node_ = segment_->get().construct("segment_node")(); + segment_node_->ref_count.exchange(1); + segment_node_->free_bytes.exchange(payload_size); + } + + ~Segment() + { + if (segment_node_->ref_count.fetch_sub(1) == 1) + { + segment_.reset(); + SharedMemSegment::remove(segment_id_.to_string().c_str()); + } + + if (overflows_count_) + { + logWarning(RTPS_TRANSPORT_SHM, + "Segment " << segment_id_.to_string().c_str() + << " closed. It had " << "overflows_count " + << overflows_count_); + } + } + + SharedMemSegment::Id id() + { + return segment_id_; + } + + std::shared_ptr alloc_buffer( + uint32_t size, + const std::chrono::steady_clock::time_point& max_blocking_time_point) + { + std::lock_guard lock(alloc_mutex_); + + wait_for_avaible_space(size, max_blocking_time_point); + + release_unused_buffers(); + + void* data = nullptr; + BufferNode* buffer_node = nullptr; + std::shared_ptr new_buffer; + + try + { + buffer_node = static_cast( + segment_->get().allocate_aligned(sizeof(BufferNode) + size, + std::alignment_of::value)); + + buffer_node->header.data_size = size; + buffer_node->header.ref_count.store(0, std::memory_order_relaxed); + + // TODO(Adolfo) : Dynamic allocation. Use foonathan to convert it to static allocation + new_buffer = std::make_shared(segment_, segment_id_, buffer_node, segment_node_); + + // TODO(Adolfo) : Dynamic allocation. Use foonathan to convert it to static allocation + allocated_nodes_.push_back(buffer_node); + + segment_node_->free_bytes.fetch_sub(size); + } + catch (const std::exception&) + { + if (buffer_node) + { + release_buffer(buffer_node); + } + else if (data) + { + segment_->get().deallocate(data); + } + + overflows_count_++; + + throw; + } + + return new_buffer; + } + + private: + + SegmentNode* segment_node_; + std::list allocated_nodes_; + std::mutex alloc_mutex_; + std::shared_ptr segment_; + SharedMemSegment::Id segment_id_; + uint64_t overflows_count_; + uint32_t max_payload_size_; + + void release_buffer( + BufferNode* buffer_node) + { + segment_->get().deallocate(buffer_node); + } + + void release_unused_buffers() + { + auto node_it = allocated_nodes_.begin(); + while (node_it != allocated_nodes_.end()) + { + if ((*node_it)->header.ref_count.load() == 0) + { + release_buffer(*node_it); + + allocated_nodes_.erase(node_it++); + } + else + { + node_it++; + } + } + } + + void wait_for_avaible_space( + uint32_t size, + const std::chrono::steady_clock::time_point& max_blocking_time_point) + { + SharedMemSegment::spin_wait spin_wait; + + // Not enough avaible space + while ( segment_node_->free_bytes.load(std::memory_order_relaxed) < size && + std::chrono::steady_clock::now() < max_blocking_time_point ) + { + if (size > max_payload_size_) + { + throw std::runtime_error("buffer bigger than whole segment size"); + } + + // Optimized active wait. We're in overflow with timeout case. + // Much higher throughput is achive with active wait over shared-memory + // vs interprocess_mutex + interprocess_cv. + spin_wait.yield(); + } + + if (segment_node_->free_bytes.load(std::memory_order_relaxed) < size) + { + throw std::runtime_error("allocation timeout"); + } + } + + + }; // Segment + + class Port; + + /** + * Listen to descriptors pushed to a port. + * Provides an interface to wait and access to the data referenced by the descriptors + */ + class Listener + { + public: + + Listener( + SharedMemManager& shared_mem_manager, + std::shared_ptr port) + : global_port_(port) + , shared_mem_manager_(shared_mem_manager) + , is_closed_(false) + { + global_listener_ = global_port_->create_listener(); + } + + /** + * Extract the first buffer enqued in the port. + * If the queue is empty, blocks until a buffer is pushed + * to the port. + * @remark Multithread not supported. + */ + std::shared_ptr pop() + { + bool was_cell_freed; + std::shared_ptr buffer_ref; + + SharedMemGlobal::PortCell* head_cell = nullptr; + + while ( !is_closed_.load() && nullptr == (head_cell = global_listener_->head()) ) + { + // Wait until threre's data to pop + global_port_->wait_pop(*global_listener_, is_closed_); + } + + if (!head_cell) + { + return nullptr; + } + + SharedMemGlobal::BufferDescriptor buffer_descriptor = head_cell->data(); + + SegmentNode* segment_node; + auto segment = shared_mem_manager_.find_segment(buffer_descriptor.source_segment_id, &segment_node); + auto buffer_node = + static_cast(segment->get_address_from_offset(buffer_descriptor.buffer_node_offset)); + + // TODO(Adolfo) : Dynamic allocation. Use foonathan to convert it to static allocation + buffer_ref = std::make_shared(segment, buffer_descriptor.source_segment_id, buffer_node, + segment_node); + + // If the cell has been read by all listeners + global_port_->pop(*global_listener_, was_cell_freed); + + if (was_cell_freed) + { + buffer_node->header.ref_count.fetch_sub(1); + } + + return buffer_ref; + } + + /** + * Unblock a thread blocked in pop() call, not allowing pop() to block again, + */ + void close() + { + // Just in case a thread is blocked in pop() function + global_port_->close_listener(&is_closed_); + } + + private: + + std::shared_ptr global_port_; + + std::shared_ptr global_listener_; + + SharedMemManager& shared_mem_manager_; + + std::atomic is_closed_; + }; // Listener + + /** + * Allows to push buffers and create listeners of a shared-memory port, + */ + class Port + { + public: + + Port( + SharedMemManager& shared_mem_manager, + std::shared_ptr port) + : shared_mem_manager_(shared_mem_manager) + , global_port_(port) + { + } + + /** + * Try to enqueue a buffer in the port. + * @returns false If the port's queue is full so buffer couldn't be enqueued. + */ + bool try_push( + const std::shared_ptr& buffer) + { + assert(std::dynamic_pointer_cast(buffer)); + + SharedMemBuffer* shared_mem_buffer = std::static_pointer_cast(buffer).get(); + shared_mem_buffer->increase_ref(); + + bool ret; + bool are_listeners_active = false; + + try + { + ret = global_port_->try_push({shared_mem_buffer->segment_id(), shared_mem_buffer->node_offset()}, + &are_listeners_active); + + if (!are_listeners_active) + { + shared_mem_buffer->decrease_ref(); + } + } + catch (std::exception&) + { + shared_mem_buffer->decrease_ref(); + throw; + } + + return ret; + } + + std::shared_ptr create_listener() + { + return std::make_shared(shared_mem_manager_, global_port_); + } + + private: + + SharedMemManager& shared_mem_manager_; + + std::shared_ptr global_port_; + + }; // Port + + /** + * Creates a shared-memory segment + * @param size size of the segment + * @param max_buffers maximum, at a time, allocated buffers + * @return A shared_ptr to the segment + */ + std::shared_ptr create_segment( + uint32_t size, + uint32_t max_allocations) + { + // Every buffer allocated implies two internal allocations, node and payload. + // Every internal allocation consumes 'per_allocation_extra_size_' bytes + uint32_t allocation_extra_size = sizeof(SegmentNode) + per_allocation_extra_size_ + + max_allocations * ((sizeof(BufferNode) + per_allocation_extra_size_) + per_allocation_extra_size_); + + return std::make_shared(size + allocation_extra_size, size); + } + + std::shared_ptr open_port( + uint32_t port_id, + uint32_t max_descriptors, + uint32_t healthy_check_timeout_ms, + SharedMemGlobal::Port::OpenMode open_mode = SharedMemGlobal::Port::OpenMode::ReadShared) + { + return std::make_shared(*this, + global_segment_.open_port(port_id, max_descriptors, healthy_check_timeout_ms, open_mode)); + } + + private: + + /** + * Controls life-cycle of a remote segment + */ + class SegmentWrapper + { + public: + + SegmentWrapper() + { + + } + + SegmentWrapper( + std::shared_ptr segment_, + SharedMemSegment::Id segment_id) + : segment_(segment_) + , segment_id_(segment_id) + { + segment_node_ = segment_->get().find("segment_node").first; + segment_node_->ref_count.fetch_add(1); + } + + SegmentWrapper& operator=( + SegmentWrapper&& other) + { + segment_ = other.segment_; + segment_id_ = other.segment_id_; + segment_node_ = other.segment_node_; + + other.segment_.reset(); + + return *this; + } + + ~SegmentWrapper() + { + if (segment_ != nullptr && segment_node_->ref_count.fetch_sub(1) == 1) + { + segment_.reset(); + SharedMemSegment::remove(segment_id_.to_string().c_str()); + } + } + + std::shared_ptr segment() { return segment_; } + SegmentNode* segment_node() { return segment_node_; } + + private: + + std::shared_ptr segment_; + SharedMemSegment::Id segment_id_; + SegmentNode* segment_node_; + }; + + uint32_t per_allocation_extra_size_; + + std::unordered_map > ids_segments_; + std::mutex ids_segments_mutex_; + + SharedMemGlobal global_segment_; + + std::shared_ptr find_segment( + SharedMemSegment::Id id, + SegmentNode** segment_node) + { + std::lock_guard lock(ids_segments_mutex_); + + std::shared_ptr segment; + + // TODO (Adolfo): Garbage collector for opened but unused segments???? + + try + { + SegmentWrapper& segment_wrapper = ids_segments_.at(id.get()); + segment = segment_wrapper.segment(); + *segment_node = segment_wrapper.segment_node(); + } + catch (std::out_of_range&) + { + segment = std::make_shared(boost::interprocess::open_only, id.to_string()); + SegmentWrapper segment_wrapper(segment, id); + + *segment_node = segment_wrapper.segment_node(); + ids_segments_[id.get()] = std::move(segment_wrapper); + } + + return segment; + } +}; + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_SHAREDMEM_MANAGER_H_ \ No newline at end of file diff --git a/src/cpp/rtps/transport/shared_mem/SharedMemSegment.hpp b/src/cpp/rtps/transport/shared_mem/SharedMemSegment.hpp new file mode 100644 index 00000000000..4d94a532b4f --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/SharedMemSegment.hpp @@ -0,0 +1,276 @@ +// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_SHAREDMEM_SEGMENT_H_ +#define _FASTDDS_SHAREDMEM_SEGMENT_H_ + +#include +#include +#include +#include +#include +#include +#include + + +#include "SharedMemUUID.hpp" + +namespace eprosima { +namespace fastdds { +namespace rtps { + +using Log = fastdds::dds::Log; + +/** + * Provides shared memory functionallity abstrating from + * lower level layers + */ +class SharedMemSegment +{ +public: + + typedef std::ptrdiff_t offset; + typedef boost::interprocess::interprocess_condition condition_variable; + typedef boost::interprocess::interprocess_mutex mutex; + typedef boost::interprocess::named_mutex named_mutex; + typedef boost::interprocess::spin_wait spin_wait; + + static constexpr boost::interprocess::open_only_t open_only = boost::interprocess::open_only_t(); + static constexpr boost::interprocess::create_only_t create_only = boost::interprocess::create_only_t(); + static constexpr boost::interprocess::open_or_create_t open_or_create = boost::interprocess::open_or_create_t(); + + // Boost memory manager needs extra memory to maintain its structures, + // as these structures are shared they are stored in the segment. + // TODO(Adolfo): Further analysis to determine the perfect value for this extra segment size + static constexpr uint32_t EXTRA_SEGMENT_SIZE = 512; + + SharedMemSegment( + boost::interprocess::create_only_t, + const std::string& name, + size_t size) + : segment_(boost::interprocess::create_only, name.c_str(), size + EXTRA_SEGMENT_SIZE) + , name_(name) + { + } + + SharedMemSegment( + boost::interprocess::open_only_t, + const std::string& name) + : segment_(boost::interprocess::open_only, name.c_str()) + , name_(name) + { + } + + SharedMemSegment( + boost::interprocess::open_or_create_t, + const std::string& name, + size_t size) + : segment_(boost::interprocess::create_only, name.c_str(), size) + , name_(name) + { + } + + void* get_address_from_offset( + SharedMemSegment::offset offset) const + { + return segment_.get_address_from_handle(offset); + } + + SharedMemSegment::offset get_offset_from_address( + void* address) const + { + return segment_.get_handle_from_address(address); + } + + boost::interprocess::managed_shared_memory& get() { return segment_;} + + static void remove( + const std::string& name) + { + boost::interprocess::shared_memory_object::remove(name.c_str()); + } + + std::string name() + { + return name_; + } + + /** + * Estimates the extra segment space required for an allocation + */ + static uint32_t compute_per_allocation_extra_size( + size_t allocation_alignment) + { + Id uuid; + + try + { + static uint32_t extra_size = 0; + + if (extra_size == 0) + { + uuid.generate(); + + SharedMemEnvironment::get().init(); + + { + boost::interprocess::managed_shared_memory + test_segment(boost::interprocess::create_only, uuid.to_string().c_str(), + (std::max)((size_t)1024, allocation_alignment* 4)); + + auto m1 = test_segment.get_free_memory(); + test_segment.allocate_aligned(1, allocation_alignment); + auto m2 = test_segment.get_free_memory(); + extra_size = static_cast(m1 - m2); + } + + boost::interprocess::shared_memory_object::remove(uuid.to_string().c_str()); + } + + return extra_size; + + } + catch (const std::exception& e) + { + logError(RTPS_TRANSPORT_SHM, "Failed to create segment " << uuid.to_string() + << ": " << e.what()); + + throw; + } + } + + static std::unique_ptr open_or_create_and_lock_named_mutex( + const std::string& mutex_name) + { + std::unique_ptr named_mutex; + + // Todo(Adolfo) : Dataraces could occur, this algorithm has to be improved + + named_mutex = std::unique_ptr( + new SharedMemSegment::named_mutex(boost::interprocess::open_or_create, mutex_name.c_str())); + + boost::posix_time::ptime wait_time + = boost::posix_time::microsec_clock::universal_time() + + boost::posix_time::milliseconds(BOOST_INTERPROCESS_TIMEOUT_WHEN_LOCKING_DURATION_MS*2); + if (!named_mutex->timed_lock(wait_time)) + { + // Interprocess mutex timeout when locking. Possible deadlock: owner died without unlocking? + // try to remove and create again + SharedMemSegment::named_mutex::remove(mutex_name.c_str()); + + named_mutex = std::unique_ptr( + new SharedMemSegment::named_mutex(boost::interprocess::open_or_create, mutex_name.c_str())); + + if (!named_mutex->try_lock()) + { + throw std::runtime_error("Couldn't create name_mutex: " + mutex_name); + } + } + + return named_mutex; + } + + static std::unique_ptr open_named_mutex( + const std::string& mutex_name) + { + std::unique_ptr named_mutex; + + // Todo(Adolfo) : Dataraces could occur, this algorithm has to be improved + + named_mutex = std::unique_ptr( + new SharedMemSegment::named_mutex(boost::interprocess::open_only, mutex_name.c_str())); + + return named_mutex; + } + + /** + * Unique ID of the memory segment + */ + class Id + { + public: + + typedef UUID<16> type; + + Id() + { + } + + Id( + const Id& other) + { + uuid_ = other.uuid_; + } + + Id( + const type& uuid) + { + uuid_ = uuid; + } + + void generate() + { + type::generate(uuid_); + } + + const type& get() const + { + return uuid_; + } + + bool operator == ( + const Id& other) const + { + return uuid_ == other.uuid_; + } + + std::string to_string() + { + return uuid_.to_string(); + } + + static const Id null() + { + return Id(type(type::null_t())); + } + +private: + + type uuid_; + + }; // Id + +private: + + class EnvironmentInitializer + { +public: + + EnvironmentInitializer() + { + SharedMemEnvironment::get().init(); + } + } shared_mem_environment_initializer_; + + boost::interprocess::managed_shared_memory segment_; + + std::string name_; +}; + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_SHAREDMEM_SEGMENT_H_ + diff --git a/src/cpp/rtps/transport/shared_mem/SharedMemSenderResource.hpp b/src/cpp/rtps/transport/shared_mem/SharedMemSenderResource.hpp new file mode 100644 index 00000000000..97cb6254770 --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/SharedMemSenderResource.hpp @@ -0,0 +1,89 @@ +// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_SHAREDMEM_SENDERRESOURCE_HPP_ +#define _FASTDDS_SHAREDMEM_SENDERRESOURCE_HPP_ + +#include +#include + +namespace eprosima { +namespace fastdds { +namespace rtps { + +class SharedMemSenderResource : public fastrtps::rtps::SenderResource +{ +public: + + SharedMemSenderResource( + SharedMemTransport& transport) + : fastrtps::rtps::SenderResource(transport.kind()) + { + // Implementation functions are bound to the right transport parameters + clean_up = []() + { + // No cleanup is required + }; + + send_lambda_ = [&transport] ( + const fastrtps::rtps::octet* data, + uint32_t dataSize, + fastrtps::rtps::LocatorsIterator* destination_locators_begin, + fastrtps::rtps::LocatorsIterator* destination_locators_end, + const std::chrono::steady_clock::time_point& max_blocking_time_point) -> bool + { + return transport.send(data, dataSize, destination_locators_begin, destination_locators_end, + max_blocking_time_point); + }; + + } + + virtual ~SharedMemSenderResource() + { + if (clean_up) + { + clean_up(); + } + } + + static SharedMemSenderResource* cast( + TransportInterface& transport, + SenderResource* sender_resource) + { + SharedMemSenderResource* returned_resource = nullptr; + + if (sender_resource->kind() == transport.kind()) + { + returned_resource = dynamic_cast(sender_resource); + } + + return returned_resource; + } + +private: + + SharedMemSenderResource() = delete; + + SharedMemSenderResource( + const SenderResource&) = delete; + + SharedMemSenderResource& operator=( + const SenderResource&) = delete; +}; + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_SHAREDMEM_SENDERRESOURCE_HPP_ diff --git a/src/cpp/rtps/transport/shared_mem/SharedMemTransport.cpp b/src/cpp/rtps/transport/shared_mem/SharedMemTransport.cpp new file mode 100644 index 00000000000..98ab0468307 --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/SharedMemTransport.cpp @@ -0,0 +1,641 @@ +// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define SHM_MANAGER_DOMAIN ("fastrtps") + +using namespace std; + +using namespace eprosima; +using namespace eprosima::fastdds; +using namespace eprosima::fastdds::rtps; + +using Locator_t = fastrtps::rtps::Locator_t; +using LocatorList_t = fastrtps::rtps::LocatorList_t; +using Log = dds::Log; +using octet = fastrtps::rtps::octet; +using SenderResource = fastrtps::rtps::SenderResource; +using LocatorSelectorEntry = fastrtps::rtps::LocatorSelectorEntry; +using LocatorSelector = fastrtps::rtps::LocatorSelector; +using PortParameters = fastrtps::rtps::PortParameters; + +//********************************************************* +// SharedMemTransportDescriptor +//********************************************************* + +SharedMemTransportDescriptor::SharedMemTransportDescriptor() + : TransportDescriptorInterface(SharedMemTransport::default_segment_size, s_maximumInitialPeersRange) + , segment_size_(SharedMemTransport::default_segment_size) + , port_queue_capacity_(SharedMemTransport::default_port_queue_capacity) + , port_overflow_policy_(SharedMemTransport::default_overflow_policy) + , segment_overflow_policy_(SharedMemTransport::default_overflow_policy) + , healthy_check_timeout_ms_(SharedMemTransport::default_healthy_check_timeout_ms) + , rtps_dump_file_("") +{ + maxMessageSize = segment_size_; +} + +SharedMemTransportDescriptor::SharedMemTransportDescriptor( + const SharedMemTransportDescriptor& t) + : TransportDescriptorInterface(t.segment_size_, s_maximumInitialPeersRange) + , segment_size_(t.segment_size_) + , port_queue_capacity_(t.port_queue_capacity_) + , port_overflow_policy_(t.port_overflow_policy_) + , segment_overflow_policy_(t.segment_overflow_policy_) + , healthy_check_timeout_ms_(t.healthy_check_timeout_ms_) + , rtps_dump_file_(t.rtps_dump_file_) +{ + maxMessageSize = t.segment_size_; +} + +TransportInterface* SharedMemTransportDescriptor::create_transport() const +{ + return new SharedMemTransport(*this); +} + +//********************************************************* +// SharedMemTransport +//********************************************************* + +SharedMemTransport::SharedMemTransport( + const SharedMemTransportDescriptor& descriptor) + : TransportInterface(LOCATOR_KIND_SHM) + , configuration_(descriptor) +{ + +} + +SharedMemTransport::SharedMemTransport() + : TransportInterface(LOCATOR_KIND_SHM) +{ +} + +SharedMemTransport::~SharedMemTransport() +{ + clean(); +} + +bool SharedMemTransport::getDefaultMetatrafficMulticastLocators( + LocatorList_t& locators, + uint32_t metatraffic_multicast_port) const +{ + locators.push_back(SHMLocator::create_locator(metatraffic_multicast_port, SHMLocator::Type::MULTICAST)); + + return true; +} + +bool SharedMemTransport::getDefaultMetatrafficUnicastLocators( + LocatorList_t& locators, + uint32_t metatraffic_unicast_port) const +{ + locators.push_back(SHMLocator::create_locator(metatraffic_unicast_port, SHMLocator::Type::UNICAST)); + + return true; +} + +bool SharedMemTransport::getDefaultUnicastLocators( + LocatorList_t& locators, + uint32_t unicast_port) const +{ + auto locator = SHMLocator::create_locator(unicast_port, SHMLocator::Type::UNICAST); + + fillUnicastLocator(locator, unicast_port); + locators.push_back(locator); + + return true; +} + +void SharedMemTransport::AddDefaultOutputLocator( + LocatorList_t& defaultList) +{ + (void)defaultList; +} + +const SharedMemTransportDescriptor* SharedMemTransport::configuration() const +{ + return &configuration_; +} + +bool SharedMemTransport::OpenInputChannel( + const Locator_t& locator, + TransportReceiverInterface* receiver, + uint32_t maxMsgSize) +{ + std::unique_lock scopedLock(input_channels_mutex_); + + if (!IsLocatorSupported(locator)) + { + return false; + } + + if (!IsInputChannelOpen(locator)) + { + try + { + auto channel_resource = CreateInputChannelResource(locator, maxMsgSize, receiver); + input_channels_.push_back(channel_resource); + } + catch (std::exception& e) + { + (void)e; + + logInfo(RTPS_MSG_OUT, std::string("CreateInputChannelResource failed for port ") + << locator.port << " msg: " << e.what()); + return false; + } + } + + return true; +} + +bool SharedMemTransport::is_locator_allowed( + const Locator_t& locator) const +{ + return IsLocatorSupported(locator); +} + +LocatorList_t SharedMemTransport::NormalizeLocator( + const Locator_t& locator) +{ + LocatorList_t list; + + list.push_back(locator); + + return list; +} + +bool SharedMemTransport::is_local_locator( + const Locator_t& locator) const +{ + assert(locator.kind == LOCATOR_KIND_SHM); + (void)locator; + + return true; +} + +void SharedMemTransport::clean() +{ + assert(input_channels_.size() == 0); +} + +bool SharedMemTransport::CloseInputChannel( + const Locator_t& locator) +{ + std::lock_guard lock(input_channels_mutex_); + + for (auto it = input_channels_.begin(); it != input_channels_.end(); it++) + { + if ( (*it)->locator() == locator) + { + (*it)->disable(); + (*it)->release(); + (*it)->clear(); + delete (*it); + input_channels_.erase(it); + + return true; + } + } + + return false; +} + +bool SharedMemTransport::DoInputLocatorsMatch( + const Locator_t& left, + const Locator_t& right) const +{ + return left.kind == right.kind && left.port == right.port; +} + +bool SharedMemTransport::init() +{ + try + { + switch (configuration_.port_overflow_policy()) + { + case SharedMemTransportDescriptor::OverflowPolicy::DISCARD: + push_lambda_ = &SharedMemTransport::push_discard; + break; + case SharedMemTransportDescriptor::OverflowPolicy::FAIL: + push_lambda_ = &SharedMemTransport::push_fail; + break; + default: + throw std::runtime_error("unknown port_overflow_policy"); + } + + switch (configuration_.segment_overflow_policy()) + { + case SharedMemTransportDescriptor::OverflowPolicy::DISCARD: + case SharedMemTransportDescriptor::OverflowPolicy::FAIL: + break; + default: + throw std::runtime_error("unknown port_overflow_policy"); + } + + shared_mem_manager_ = std::make_shared(SHM_MANAGER_DOMAIN); + shared_mem_segment_ = shared_mem_manager_->create_segment(configuration_.segment_size(), + configuration_.port_queue_capacity()); + + // Memset the whole segment to zero in order to force physical map of the buffer + auto buffer = shared_mem_segment_->alloc_buffer(configuration_.segment_size(), + (std::chrono::steady_clock::now()+std::chrono::milliseconds(100))); + memset(buffer->data(), 0, configuration_.segment_size()); + buffer.reset(); + + if (!configuration_.rtps_dump_file().empty()) + { + auto packets_file_consumer = std::unique_ptr( + new SHMPacketFileConsumer(configuration_.rtps_dump_file())); + + packet_logger_ = std::make_shared >(); + packet_logger_->RegisterConsumer(std::move(packets_file_consumer)); + } + } + catch (std::exception& e) + { + logError(RTPS_MSG_OUT, e.what()); + return false; + } + + return true; +} + +bool SharedMemTransport::IsInputChannelOpen( + const Locator_t& locator) const +{ + std::lock_guard lock(input_channels_mutex_); + + return IsLocatorSupported(locator) && (std::find_if( + input_channels_.begin(), input_channels_.end(), + [&](const SharedMemChannelResource* resource) { + return locator == resource->locator(); + }) != input_channels_.end()); +} + +bool SharedMemTransport::IsLocatorSupported( + const Locator_t& locator) const +{ + return locator.kind == transport_kind_; +} + +SharedMemChannelResource* SharedMemTransport::CreateInputChannelResource( + const Locator_t& locator, + uint32_t maxMsgSize, + TransportReceiverInterface* receiver) +{ + (void) maxMsgSize; + + // Multicast locators implies ReadShared (Multiple readers) ports. + auto open_mode = locator.address[0] == 'M' ? SharedMemGlobal::Port::OpenMode::ReadShared : + SharedMemGlobal::Port::OpenMode::ReadExclusive; + + return new SharedMemChannelResource( + shared_mem_manager_->open_port( + locator.port, + configuration_.port_queue_capacity(), + configuration_.healthy_check_timeout_ms(), + open_mode)->create_listener(), + locator, + receiver, + configuration_.rtps_dump_file()); +} + +bool SharedMemTransport::OpenOutputChannel( + SendResourceList& sender_resource_list, + const Locator_t& locator) +{ + if (!IsLocatorSupported(locator)) + { + return false; + } + + // We try to find a SenderResource that can be reuse to this locator. + // Note: This is done in this level because if we do in NetworkFactory level, we have to mantain what transport + // already reuses a SenderResource. + for (auto& sender_resource : sender_resource_list) + { + SharedMemSenderResource* sm_sender_resource = SharedMemSenderResource::cast(*this, sender_resource.get()); + + if (sm_sender_resource) + { + return true; + } + } + + try + { + sender_resource_list.emplace_back( + static_cast(new SharedMemSenderResource(*this))); + } + catch (std::exception& e) + { + logError(RTPS_MSG_OUT, "SharedMemTransport error opening port " << std::to_string(locator.port) + << " with msg: " << e.what()); + + return false; + } + + return true; +} + +Locator_t SharedMemTransport::RemoteToMainLocal( + const Locator_t& remote) const +{ + if (!IsLocatorSupported(remote)) + { + return false; + } + + Locator_t mainLocal(remote); + mainLocal.set_Invalid_Address(); + return mainLocal; +} + +bool SharedMemTransport::transform_remote_locator( + const Locator_t& remote_locator, + Locator_t& result_locator) const +{ + if (IsLocatorSupported(remote_locator)) + { + result_locator = remote_locator; + + return true; + } + + return false; +} + +std::shared_ptr SharedMemTransport::copy_to_shared_buffer( + const octet* send_buffer, + uint32_t send_buffer_size, + const std::chrono::steady_clock::time_point& max_blocking_time_point) +{ + assert(shared_mem_segment_); + + std::shared_ptr shared_buffer = + shared_mem_segment_->alloc_buffer(send_buffer_size, max_blocking_time_point); + + memcpy(shared_buffer->data(), send_buffer, send_buffer_size); + + return shared_buffer; +} + +bool SharedMemTransport::send( + const octet* send_buffer, + uint32_t send_buffer_size, + fastrtps::rtps::LocatorsIterator* destination_locators_begin, + fastrtps::rtps::LocatorsIterator* destination_locators_end, + const std::chrono::steady_clock::time_point& max_blocking_time_point) +{ + fastrtps::rtps::LocatorsIterator& it = *destination_locators_begin; + + bool ret = true; + + std::shared_ptr shared_buffer; + + try + { + while (it != *destination_locators_end) + { + if (IsLocatorSupported(*it)) + { + // Only copy the first time + if (shared_buffer == nullptr) + { + shared_buffer = copy_to_shared_buffer(send_buffer, send_buffer_size, max_blocking_time_point); + } + + ret &= send(shared_buffer, *it); + + if (packet_logger_ && ret) + { + packet_logger_->QueueLog({packet_logger_->now(), Locator_t(), * it, shared_buffer}); + } + } + + ++it; + } + } + catch (const std::exception& e) + { + logWarning(RTPS_MSG_OUT, e.what()); + + // Segment overflow with discard policy doesn't return error. + if (!shared_buffer && + configuration_.segment_overflow_policy() == SharedMemTransportDescriptor::OverflowPolicy::DISCARD) + { + ret = true; + } + else + { + ret = false; + } + } + + return ret; + +} + +std::shared_ptr SharedMemTransport::find_port( + uint32_t port_id) +{ + try + { + return opened_ports_.at(port_id); + } + catch (const std::out_of_range&) + { + // The port is not opened + std::shared_ptr port = shared_mem_manager_-> + open_port(port_id, configuration_.port_queue_capacity(), configuration_.healthy_check_timeout_ms(), + SharedMemGlobal::Port::OpenMode::Write); + opened_ports_[port_id] = port; + + return port; + } +} + +bool SharedMemTransport::push_discard( + const std::shared_ptr& buffer, + const Locator_t& remote_locator) +{ + try + { + if (!find_port(remote_locator.port)->try_push(buffer)) + { + logWarning(RTPS_MSG_OUT, "Port " << remote_locator.port << " full. Buffer dropped"); + } + } + catch (const std::exception& error) + { + logWarning(RTPS_MSG_OUT, error.what()); + return false; + } + + return true; +} + +bool SharedMemTransport::push_fail( + const std::shared_ptr& buffer, + const Locator_t& remote_locator) +{ + try + { + return find_port(remote_locator.port)->try_push(buffer); + } + catch (const std::exception& error) + { + logWarning(RTPS_MSG_OUT, error.what()); + return false; + } +} + +bool SharedMemTransport::send( + const std::shared_ptr& buffer, + const Locator_t& remote_locator) +{ + if (!push_lambda_(*this, buffer, remote_locator)) + { + return false; + } + + logInfo(RTPS_MSG_OUT, + "(ID:" << std::this_thread::get_id() <<") " << "SharedMemTransport: " << buffer->size() << " bytes to port " << + remote_locator.port); + + return true; +} + +/** + * Invalidate all selector entries containing certain multicast locator. + * + * This function will process all entries from 'index' onwards and, if any + * of them has 'locator' on its multicast list, will invalidate them + * (i.e. their 'transport_should_process' flag will be changed to false). + * + * If this function returns true, the locator received should be selected. + * + * @param entries Selector entries collection to process + * @param index Starting index to process + * @param locator Locator to be searched + * + * @return true when at least one entry was invalidated, false otherwise + */ +void SharedMemTransport::select_locators( + LocatorSelector& selector) const +{ + fastrtps::ResourceLimitedVector& entries = selector.transport_starts(); + + for (size_t i = 0; i < entries.size(); ++i) + { + LocatorSelectorEntry* entry = entries[i]; + if (entry->transport_should_process) + { + bool selected = false; + + // With shared-memory transport using multicast vs unicast is not an advantage + // because no copies are saved. So no multicast locators are selected + for (size_t j = 0; j < entry->unicast.size(); ++j) + { + if (IsLocatorSupported(entry->unicast[j]) && !selector.is_selected(entry->unicast[j])) + { + entry->state.unicast.push_back(j); + selected = true; + } + } + + // Select this entry if necessary + if (selected) + { + selector.select(i); + } + } + } +} + +bool SharedMemTransport::fillMetatrafficMulticastLocator( + Locator_t& locator, + uint32_t metatraffic_multicast_port) const +{ + if (locator.port == 0) + { + locator.port = metatraffic_multicast_port; + } + + return true; +} + +bool SharedMemTransport::fillMetatrafficUnicastLocator( + Locator_t& locator, + uint32_t metatraffic_unicast_port) const +{ + if (locator.port == 0) + { + locator.port = metatraffic_unicast_port; + } + return true; +} + +bool SharedMemTransport::configureInitialPeerLocator( + Locator_t& locator, + const PortParameters& port_params, + uint32_t domainId, + LocatorList_t& list) const +{ + if (locator.port == 0) + { + for (uint32_t i = 0; i < configuration()->maxInitialPeersRange; ++i) + { + Locator_t auxloc(locator); + auxloc.port = port_params.getUnicastPort(domainId, i); + + list.push_back(auxloc); + } + } + else + { + list.push_back(locator); + } + + return true; +} + +bool SharedMemTransport::fillUnicastLocator( + Locator_t& locator, + uint32_t well_known_port) const +{ + if (locator.port == 0) + { + locator.port = well_known_port; + } + + return true; +} diff --git a/src/cpp/rtps/transport/shared_mem/SharedMemTransport.h b/src/cpp/rtps/transport/shared_mem/SharedMemTransport.h new file mode 100644 index 00000000000..5ce6e3ae0a2 --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/SharedMemTransport.h @@ -0,0 +1,269 @@ +// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_SHAREDMEM_TRANSPORT_H_ +#define _FASTDDS_SHAREDMEM_TRANSPORT_H_ + +#include +#include + +#include +#include + +namespace eprosima { +namespace fastdds { +namespace rtps { + +class SharedMemChannelResource; + +/** + * Shared memory transport implementation. + * + * - Opening an output channel by passing a locator will prepare the output channel. + * The opening of the shared memory port will be done when a send operation is issued, based on + * the port indicated in the destination locator. + * + * - Opening an input channel by passing a locator will open a shared memory port and a listener attached + * to the port. + * + * @ingroup TRANSPORT_MODULE + */ +class SharedMemTransport : public TransportInterface +{ +public: + + static const uint32_t maximum_message_size = (std::numeric_limits::max)(); + static const uint32_t default_segment_size = 262144; + static const uint32_t default_port_queue_capacity = 512; + static const SharedMemTransportDescriptor::OverflowPolicy default_overflow_policy = + SharedMemTransportDescriptor::OverflowPolicy::DISCARD; + static const uint32_t default_healthy_check_timeout_ms = 1000; + + RTPS_DllAPI SharedMemTransport( + const SharedMemTransportDescriptor&); + void clean(); + const SharedMemTransportDescriptor* configuration() const; + + bool init() override; + + virtual ~SharedMemTransport() override; + + /** + * Starts listening on the specified port, and if the specified address is in the + * multicast range, it joins the specified multicast group, + */ + bool OpenInputChannel( + const fastrtps::rtps::Locator_t&, + TransportReceiverInterface*, uint32_t) override; + + //! Removes the listening socket for the specified port. + bool CloseInputChannel( + const fastrtps::rtps::Locator_t&) override; + + //! Checks whether there are open and bound sockets for the given port. + bool IsInputChannelOpen( + const fastrtps::rtps::Locator_t&) const override; + + //! Reports whether Locators correspond to the same port. + bool DoInputLocatorsMatch( + const fastrtps::rtps::Locator_t&, + const fastrtps::rtps::Locator_t&) const override; + + //! Checks for TCP kinds. + bool IsLocatorSupported( + const fastrtps::rtps::Locator_t&) const override; + + //! Opens a socket on the given address and port (as long as they are white listed). + bool OpenOutputChannel( + SendResourceList& sender_resource_list, + const fastrtps::rtps::Locator_t&) override; + + /** + * Converts a given remote locator (that is, a locator referring to a remote + * destination) to the main local locator whose channel can write to that + * destination. In this case it will return a 0.0.0.0 address on that port. + */ + fastrtps::rtps::Locator_t RemoteToMainLocal( + const fastrtps::rtps::Locator_t&) const override; + + /** + * Transforms a remote locator into a locator optimized for local communications. + * + * If the remote locator corresponds to one of the local interfaces, it is converted + * to the corresponding local address. + * + * @param [in] remote_locator Locator to be converted. + * @param [out] result_locator Converted locator. + * + * @return false if the input locator is not supported/allowed by this transport, true otherwise. + */ + bool transform_remote_locator( + const fastrtps::rtps::Locator_t& remote_locator, + fastrtps::rtps::Locator_t& result_locator) const override; + + fastrtps::rtps::LocatorList_t NormalizeLocator( + const fastrtps::rtps::Locator_t& locator) override; + + bool is_local_locator( + const fastrtps::rtps::Locator_t& locator) const override; + + TransportDescriptorInterface* get_configuration() override { return &configuration_; } + + void AddDefaultOutputLocator( + fastrtps::rtps::LocatorList_t& defaultList) override; + + bool getDefaultMetatrafficMulticastLocators( + fastrtps::rtps::LocatorList_t& locators, + uint32_t metatraffic_multicast_port) const override; + + bool getDefaultMetatrafficUnicastLocators( + fastrtps::rtps::LocatorList_t& locators, + uint32_t metatraffic_unicast_port) const override; + + bool getDefaultUnicastLocators( + fastrtps::rtps::LocatorList_t& locators, + uint32_t unicast_port) const override; + + /** + * Blocking Send through the specified channel. In both modes, using a localLocator of 0.0.0.0 will + * send through all whitelisted interfaces provided the channel is open. + * @param send_buffer Slice into the raw data to send. + * @param send_buffer_size Size of the raw data. It will be used as a bounds check for the previous argument. + * It must not exceed the send_buffer_size fed to this class during construction. + * @param socket channel we're sending from. + * @param remote_locator Locator describing the remote destination we're sending to. + * @param only_multicast_purpose + * @param timeout Maximum time this function will block + */ + virtual bool send( + const fastrtps::rtps::octet* send_buffer, + uint32_t send_buffer_size, + fastrtps::rtps::LocatorsIterator* destination_locators_begin, + fastrtps::rtps::LocatorsIterator* destination_locators_end, + const std::chrono::steady_clock::time_point& max_blocking_time_point); + + /** + * Performs the locator selection algorithm for this transport. + * + * It basically consists of the following steps + * - selector.transport_starts is called + * - transport handles the selection state of each locator + * - if a locator from an entry is selected, selector.select is called for that entry + * + * In the case of UDP, multicast locators are selected when present in more than one entry, + * otherwise unicast locators are selected. + * + * @param [in, out] selector Locator selector. + */ + void select_locators( + fastrtps::rtps::LocatorSelector& selector) const override; + + bool fillMetatrafficMulticastLocator( + fastrtps::rtps::Locator_t& locator, + uint32_t metatraffic_multicast_port) const override; + + bool fillMetatrafficUnicastLocator( + fastrtps::rtps::Locator_t& locator, + uint32_t metatraffic_unicast_port) const override; + + bool configureInitialPeerLocator( + fastrtps::rtps::Locator_t& locator, + const fastrtps::rtps::PortParameters& port_params, + uint32_t domainId, + fastrtps::rtps::LocatorList_t& list) const override; + + bool fillUnicastLocator( + fastrtps::rtps::Locator_t& locator, + uint32_t well_known_port) const override; + + uint32_t max_recv_buffer_size() const override + { + return (std::numeric_limits::max)(); + } + +private: + + //! Constructor with no descriptor is necessary for implementations derived from this class. + SharedMemTransport(); + + SharedMemTransportDescriptor configuration_; + + //! Checks for whether locator is allowed. + bool is_locator_allowed( + const fastrtps::rtps::Locator_t&) const override; + +protected: + + std::shared_ptr shared_mem_manager_; + +private: + + std::map > opened_ports_; + + mutable std::recursive_mutex input_channels_mutex_; + + std::vector input_channels_; + + std::shared_ptr shared_mem_segment_; + + std::shared_ptr> packet_logger_; + + friend class SharedMemChannelResource; + +protected: + + /** + * Creates an input channel + * @param locator Listening locator + * @param max_msg_size Maximum message size supported by the channel + * @throw std::exception& If the channel cannot be created + */ + virtual SharedMemChannelResource* CreateInputChannelResource( + const fastrtps::rtps::Locator_t& locator, + uint32_t max_msg_size, + TransportReceiverInterface* receiver); + +private: + + std::shared_ptr copy_to_shared_buffer( + const fastrtps::rtps::octet* send_buffer, + uint32_t send_buffer_size, + const std::chrono::steady_clock::time_point& max_blocking_time_point); + + bool send( + const std::shared_ptr& buffer, + const fastrtps::rtps::Locator_t& remote_locator); + + std::shared_ptr find_port( + uint32_t port_id); + + bool push_fail( + const std::shared_ptr& buffer, + const fastrtps::rtps::Locator_t& remote_locator); + + bool push_discard( + const std::shared_ptr& buffer, + const fastrtps::rtps::Locator_t& remote_locator); + + std::function& buffer, + const fastrtps::rtps::Locator_t& remote_locator)> push_lambda_; +}; + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_SHAREDMEM_TRANSPORT_H_ diff --git a/src/cpp/rtps/transport/shared_mem/SharedMemUUID.hpp b/src/cpp/rtps/transport/shared_mem/SharedMemUUID.hpp new file mode 100644 index 00000000000..ad7d46627a0 --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/SharedMemUUID.hpp @@ -0,0 +1,207 @@ +// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_SHAREDMEM_UUID_H_ +#define _FASTDDS_SHAREDMEM_UUID_H_ + +#ifdef _MSC_VER +#include +#endif + +#include +#include +#include +#include +#include +#include + +namespace eprosima { +namespace fastdds { +namespace rtps { + +/** + * UUIDs Generator Singleton + */ +class UUIDGen +{ +public: + + UUIDGen() + : last_gen_time_(0) + { + + } + + static UUIDGen& instance() + { + static UUIDGen singleton_uuid_gen; + return singleton_uuid_gen; + } + + /** + * Generates UUIDs guaranteed to be unique only inside the same machine. + * @param uuid pointer to an array of bytes to store the uuid. + * @param len lenght in bytes of the uuid. + * @remarks Thread-safe. + */ + void generate( + uint8_t* uuid, + size_t len) + { + uint64_t now = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + + { + // Two IDs cannot be generated in this process at the same exact time + std::lock_guard lock(gen_mutex_); + + while (now == last_gen_time_) + { + std::this_thread::sleep_for(std::chrono::microseconds(1)); + now = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + } + + last_gen_time_ = now; + } + + // Mersenne twister generator + std::mt19937 gen; + + // The seed is derived from the ProcessID and the steady clock + std::array seq; + uint64_t pid = this_process_pid(); + seq[0] = static_cast(pid); + seq[1] = static_cast(pid>>32); + seq[2] = static_cast(now); + seq[3] = static_cast(now>>32); + + std::seed_seq seed_seq(seq.begin(), seq.end()); + gen.seed(seed_seq); + + std::uniform_int_distribution<> dis(0, 255); + + for (size_t i = 0; i < len; i++) + { + uuid[i] = static_cast(dis(gen)); + } + } + +private: + + std::mutex gen_mutex_; + uint64_t last_gen_time_; + + static uint64_t this_process_pid() + { +#ifdef _MSC_VER + return GetCurrentProcessId(); +#else // POSIX + return static_cast(getpid()); +#endif + } +}; + +template +class UUID +{ +public: + + struct null_t {}; + static constexpr null_t null = null_t(); + + UUID() + { + } + + UUID( + const null_t&) + { + memset(uuid_, 0, SIZE); + } + + UUID& operator =( + const UUID& other) + { + memcpy(uuid_, other.uuid_, sizeof(uuid_)); + + return *this; + } + + static void generate( + UUID& other) + { + UUIDGen::instance().generate(other.uuid_, sizeof(other.uuid_)); + } + + uint8_t* get() + { + return uuid_; + } + + size_t size() + { + return sizeof(uuid_); + } + + size_t hash() const + { + return *reinterpret_cast(uuid_); + } + + std::string to_string() const + { + // TODO(Adolfo): This function should be allocation free + std::stringstream ss; + + for (size_t i=0; i < sizeof(uuid_); i++ ) + { + std::stringstream hex_ss; + hex_ss << std::hex << static_cast(uuid_[i]); + std::string hex = hex_ss.str(); + ss << (hex.length() < 2 ? '0' + hex : hex); + } + + return ss.str(); + } + + bool operator == ( + const UUID& other) const + { + return 0 == memcmp(uuid_, other.uuid_, sizeof(uuid_)); + } + +private: + + uint8_t uuid_[SIZE]; + +}; + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +namespace std { +template <> +struct hash > +{ + std::size_t operator()( + const eprosima::fastdds::rtps::UUID<16>& k) const + { + return k.hash(); + } +}; + +} // namespace std + + +#endif // _FASTDDS_SHAREDMEM_UUID_H_ \ No newline at end of file diff --git a/src/cpp/rtps/transport/shared_mem/test_SharedMemChannelResource.hpp b/src/cpp/rtps/transport/shared_mem/test_SharedMemChannelResource.hpp new file mode 100644 index 00000000000..ad57faef436 --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/test_SharedMemChannelResource.hpp @@ -0,0 +1,80 @@ +// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_TEST_SHAREDMEM_CHANNEL_RESOURCE_ +#define _FASTDDS_TEST_SHAREDMEM_CHANNEL_RESOURCE_ + +#include + +namespace eprosima { +namespace fastdds { +namespace rtps { + +class test_SharedMemChannelResource : public SharedMemChannelResource +{ +public: + + using Log = fastdds::dds::Log; + + test_SharedMemChannelResource( + std::shared_ptr listener, + const fastrtps::rtps::Locator_t& locator, + TransportReceiverInterface* receiver, + uint32_t big_buffer_size, + uint32_t* big_buffer_size_count) + : SharedMemChannelResource(listener, locator, receiver, std::string()) + , big_buffer_size_(big_buffer_size) + , big_buffer_size_count_(big_buffer_size_count) + { + } + +private: + + uint32_t big_buffer_size_; + uint32_t* big_buffer_size_count_; + + /** + * Blocking Receive from the specified channel. + */ + std::shared_ptr Receive( + fastrtps::rtps::Locator_t& remote_locator) override + { + (void)remote_locator; + + try + { + auto ret = listener_->pop(); + + if (ret && ret->size() >= big_buffer_size_) + { + (*big_buffer_size_count_)++; + } + + return ret; + } + catch (const std::exception& error) + { + (void)error; + logWarning(RTPS_MSG_OUT, "Error receiving data: " << error.what() << " - " << message_receiver() + << " (" << this << ")"); + return nullptr; + } + } +}; + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_TEST_SHAREDMEM_CHANNEL_RESOURCE_ diff --git a/src/cpp/rtps/transport/shared_mem/test_SharedMemTransport.cpp b/src/cpp/rtps/transport/shared_mem/test_SharedMemTransport.cpp new file mode 100644 index 00000000000..aa1064fdf28 --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/test_SharedMemTransport.cpp @@ -0,0 +1,88 @@ +// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +using namespace eprosima; +using namespace eprosima::fastdds; +using namespace eprosima::fastdds::rtps; + +using Locator_t = fastrtps::rtps::Locator_t; + +test_SharedMemTransportDescriptor::test_SharedMemTransportDescriptor() + : SharedMemTransportDescriptor() +{ + big_buffer_size_ = std::numeric_limits::max(); + big_buffer_size_count_ = nullptr; +} + +test_SharedMemTransportDescriptor::test_SharedMemTransportDescriptor( + const test_SharedMemTransportDescriptor& t) + : SharedMemTransportDescriptor(t) +{ +} + +test_SharedMemTransport::test_SharedMemTransport( + const test_SharedMemTransportDescriptor& t) + : SharedMemTransport(t) +{ + big_buffer_size_ = t.big_buffer_size_; + big_buffer_size_count_ = t.big_buffer_size_count_; +} + +TransportInterface* test_SharedMemTransportDescriptor::create_transport() const +{ + return new test_SharedMemTransport(*this); +} + +bool test_SharedMemTransport::send( + const fastrtps::rtps::octet* send_buffer, + uint32_t send_buffer_size, + fastrtps::rtps::LocatorsIterator* destination_locators_begin, + fastrtps::rtps::LocatorsIterator* destination_locators_end, + const std::chrono::steady_clock::time_point& max_blocking_time_point) +{ + if (send_buffer_size >= big_buffer_size_) + { + (*big_buffer_size_count_)++; + } + + return SharedMemTransport::send(send_buffer, send_buffer_size, destination_locators_begin, + destination_locators_end, max_blocking_time_point); +} + +SharedMemChannelResource* test_SharedMemTransport::CreateInputChannelResource( + const Locator_t& locator, + uint32_t maxMsgSize, + TransportReceiverInterface* receiver) +{ + (void) maxMsgSize; + + // Multicast locators implies ReadShared (Multiple readers) ports. + auto open_mode = locator.address[0] == 'M' ? SharedMemGlobal::Port::OpenMode::ReadShared : + SharedMemGlobal::Port::OpenMode::ReadExclusive; + + return new test_SharedMemChannelResource( + shared_mem_manager_->open_port( + locator.port, + configuration()->port_queue_capacity(), + configuration()->healthy_check_timeout_ms(), + open_mode)->create_listener(), + locator, + receiver, + big_buffer_size_, + big_buffer_size_count_); +} \ No newline at end of file diff --git a/src/cpp/rtps/transport/shared_mem/test_SharedMemTransport.h b/src/cpp/rtps/transport/shared_mem/test_SharedMemTransport.h new file mode 100644 index 00000000000..f3a2efdfa8e --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/test_SharedMemTransport.h @@ -0,0 +1,55 @@ +// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_TEST_SHAREDMEM_TRANSPORT_H_ +#define _FASTDDS_TEST_SHAREDMEM_TRANSPORT_H_ + +#include +#include + +namespace eprosima { +namespace fastdds { +namespace rtps { + +class test_SharedMemTransport : public SharedMemTransport +{ +public: + + RTPS_DllAPI test_SharedMemTransport( + const test_SharedMemTransportDescriptor&); + + bool send( + const fastrtps::rtps::octet* send_buffer, + uint32_t send_buffer_size, + fastrtps::rtps::LocatorsIterator* destination_locators_begin, + fastrtps::rtps::LocatorsIterator* destination_locators_end, + const std::chrono::steady_clock::time_point& max_blocking_time_point) override; + + SharedMemChannelResource* CreateInputChannelResource( + const fastrtps::rtps::Locator_t& locator, + uint32_t max_msg_size, + TransportReceiverInterface* receiver) override; + +private: + + uint32_t big_buffer_size_; + uint32_t* big_buffer_size_count_; +}; + + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_TEST_SHAREDMEM_TRANSPORT_H_ diff --git a/src/cpp/rtps/transport/shared_mem/test_SharedMemTransportDescriptor.h b/src/cpp/rtps/transport/shared_mem/test_SharedMemTransportDescriptor.h new file mode 100644 index 00000000000..04df12d305d --- /dev/null +++ b/src/cpp/rtps/transport/shared_mem/test_SharedMemTransportDescriptor.h @@ -0,0 +1,48 @@ +// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_TEST_SHAREDMEM_TRANSPORT_DESCRIPTOR_ +#define _FASTDDS_TEST_SHAREDMEM_TRANSPORT_DESCRIPTOR_ + +#include + +namespace eprosima { +namespace fastdds { +namespace rtps { + +/** + * Shared memory transport configuration + * + * @ingroup TRANSPORT_MODULE + */ +typedef struct test_SharedMemTransportDescriptor : public SharedMemTransportDescriptor +{ + virtual ~test_SharedMemTransportDescriptor() {} + + RTPS_DllAPI test_SharedMemTransportDescriptor(); + RTPS_DllAPI test_SharedMemTransportDescriptor( + const test_SharedMemTransportDescriptor& t); + + virtual TransportInterface* create_transport() const override; + + uint32_t big_buffer_size_; + uint32_t* big_buffer_size_count_; + +}test_SharedMemTransportDescriptor; + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +#endif //_FASTDDS_TEST_SHAREDMEM_TRANSPORT_DESCRIPTOR_ diff --git a/src/cpp/rtps/xmlparser/XMLParser.cpp b/src/cpp/rtps/xmlparser/XMLParser.cpp index daab77e410f..7eb8a0e9d14 100644 --- a/src/cpp/rtps/xmlparser/XMLParser.cpp +++ b/src/cpp/rtps/xmlparser/XMLParser.cpp @@ -15,12 +15,12 @@ #include #include #include -#include #include #include #include #include +#include #include @@ -359,6 +359,15 @@ XMLP_ret XMLParser::parseXMLTransportData(tinyxml2::XMLElement* p_root) return ret; } } + else if(sType == SHM) + { + pDescriptor = std::make_shared(); + ret = parseXMLCommonSharedMemTransportData(p_root, pDescriptor); + if (ret != XMLP_ret::XML_OK) + { + return ret; + } + } else { logError(XMLPARSER, "Invalid transport type: '" << sType << "'"); @@ -439,7 +448,7 @@ XMLP_ret XMLParser::parseXMLCommonTransportData(tinyxml2::XMLElement* p_root, sp uint32_t uSize = 0; if (XMLP_ret::XML_OK != getXMLUint(p_aux0, &uSize, 0)) return XMLP_ret::XML_ERROR; - pDesc->maxMessageSize = uSize; + std::dynamic_pointer_cast(p_transport)->maxMessageSize = uSize; } else if (strcmp(name, MAX_INITIAL_PEERS_RANGE) == 0) { @@ -479,7 +488,11 @@ XMLP_ret XMLParser::parseXMLCommonTransportData(tinyxml2::XMLElement* p_root, sp strcmp(name, LOGICAL_PORT_INCREMENT) == 0 || strcmp(name, LISTENING_PORTS) == 0 || strcmp(name, CALCULATE_CRC) == 0 || strcmp(name, CHECK_CRC) == 0 || strcmp(name, ENABLE_TCP_NODELAY) == 0 || strcmp(name, TLS) == 0 || - strcmp(name, NON_BLOCKING_SEND) == 0 ) + strcmp(name, NON_BLOCKING_SEND) == 0 || + strcmp(name, SEGMENT_SIZE) == 0 || strcmp(name, PORT_QUEUE_CAPACITY) == 0 || + strcmp(name, PORT_OVERFLOW_POLICY) == 0 || strcmp(name, SEGMENT_OVERFLOW_POLICY) == 0 || + strcmp(name, HEALTHY_CHECK_TIMEOUT_MS) == 0 || strcmp(name, HEALTHY_CHECK_TIMEOUT_MS) == 0 || + strcmp(name, RTPS_DUMP_FILE) == 0) { // Parsed outside of this method } @@ -632,6 +645,109 @@ XMLP_ret XMLParser::parseXMLCommonTCPTransportData(tinyxml2::XMLElement* p_root, return ret; } +XMLP_ret XMLParser::parseXMLCommonSharedMemTransportData(tinyxml2::XMLElement* p_root, sp_transport_t p_transport) +{ + /* + + + + + + + + + + + */ + + XMLP_ret ret = XMLP_ret::XML_OK; + std::shared_ptr transport_descriptor = + std::dynamic_pointer_cast(p_transport); + if (transport_descriptor != nullptr) + { + tinyxml2::XMLElement *p_aux0 = nullptr; + const char* name = nullptr; + for (p_aux0 = p_root->FirstChildElement(); p_aux0 != nullptr; p_aux0 = p_aux0->NextSiblingElement()) + { + uint32_t aux; + name = p_aux0->Name(); + if (strcmp(name, SEGMENT_SIZE) == 0) + { + if (XMLP_ret::XML_OK != getXMLUint(p_aux0, &aux, 0)) + return XMLP_ret::XML_ERROR; + transport_descriptor->segment_size(static_cast(aux), static_cast(aux)); + } + else if (strcmp(name, PORT_QUEUE_CAPACITY) == 0) + { + if (XMLP_ret::XML_OK != getXMLUint(p_aux0, &aux, 0)) + return XMLP_ret::XML_ERROR; + transport_descriptor->port_queue_capacity(static_cast(aux)); + } + else if (strcmp(name, HEALTHY_CHECK_TIMEOUT_MS) == 0) + { + if (XMLP_ret::XML_OK != getXMLUint(p_aux0, &aux, 0)) + return XMLP_ret::XML_ERROR; + transport_descriptor->healthy_check_timeout_ms(static_cast(aux)); + } + else if (strcmp(name, PORT_OVERFLOW_POLICY) == 0) + { + std::string str; + if (XMLP_ret::XML_OK != getXMLString(p_aux0, &str, 0)) + return XMLP_ret::XML_ERROR; + if(str == DISCARD) + { + transport_descriptor->port_overflow_policy(fastdds::rtps::SharedMemTransportDescriptor::OverflowPolicy::DISCARD); + } + else if(str == FAIL) + { + transport_descriptor->port_overflow_policy(fastdds::rtps::SharedMemTransportDescriptor::OverflowPolicy::FAIL); + } + else + return XMLP_ret::XML_ERROR; + } + else if (strcmp(name, SEGMENT_OVERFLOW_POLICY) == 0) + { + std::string str; + if (XMLP_ret::XML_OK != getXMLString(p_aux0, &str, 0)) + return XMLP_ret::XML_ERROR; + if(str == DISCARD) + { + transport_descriptor->segment_overflow_policy(fastdds::rtps::SharedMemTransportDescriptor::OverflowPolicy::DISCARD); + } + else if(str == FAIL) + { + transport_descriptor->segment_overflow_policy(fastdds::rtps::SharedMemTransportDescriptor::OverflowPolicy::FAIL); + } + else + return XMLP_ret::XML_ERROR; + } + else if (strcmp(name, RTPS_DUMP_FILE) == 0) + { + std::string str; + if (XMLP_ret::XML_OK != getXMLString(p_aux0, &str, 0)) + return XMLP_ret::XML_ERROR; + transport_descriptor->rtps_dump_file(str); + } + else if (strcmp(name, TRANSPORT_ID) == 0 || strcmp(name, TYPE) == 0 || strcmp(name, MAX_MESSAGE_SIZE) == 0) + { + // Parsed Outside of this method + } + else + { + logError(XMLPARSER, "Invalid element found into 'rtpsTransportDescriptorType'. Name: " << name); + return XMLP_ret::XML_ERROR; + } + } + } + else + { + logError(XMLPARSER, "Error parsing SharedMem Transport data"); + ret = XMLP_ret::XML_ERROR; + } + + return ret; +} + XMLP_ret XMLParser::parse_tls_config( tinyxml2::XMLElement* p_root, sp_transport_t tcp_transport) diff --git a/src/cpp/rtps/xmlparser/XMLParserCommon.cpp b/src/cpp/rtps/xmlparser/XMLParserCommon.cpp index abb84cfabd4..0069f9b0731 100644 --- a/src/cpp/rtps/xmlparser/XMLParserCommon.cpp +++ b/src/cpp/rtps/xmlparser/XMLParserCommon.cpp @@ -55,6 +55,14 @@ const char* METADATA_LOGICAL_PORT = "metadata_logical_port"; const char* LISTENING_PORTS = "listening_ports"; const char* CALCULATE_CRC = "calculate_crc"; const char* CHECK_CRC = "check_crc"; +const char* SEGMENT_SIZE = "segment_size"; +const char* PORT_QUEUE_CAPACITY = "port_queue_capacity"; +const char* PORT_OVERFLOW_POLICY = "port_overflow_policy"; +const char* SEGMENT_OVERFLOW_POLICY = "segment_overflow_policy"; +const char* HEALTHY_CHECK_TIMEOUT_MS = "healthy_check_timeout_ms"; +const char* DISCARD = "DISCARD"; +const char* FAIL = "FAIL"; +const char* RTPS_DUMP_FILE = "rtps_dump_file"; const char* OFF = "OFF"; const char* USER_DATA_ONLY = "USER_DATA_ONLY"; @@ -142,6 +150,7 @@ const char* UDPv4 = "UDPv4"; const char* UDPv6 = "UDPv6"; const char* TCPv4 = "TCPv4"; const char* TCPv6 = "TCPv6"; +const char* SHM = "SHM"; const char* INIT_ACKNACK_DELAY = "initialAcknackDelay"; const char* HEARTB_RESP_DELAY = "heartbeatResponseDelay"; const char* INIT_HEARTB_DELAY = "initialHeartbeatDelay"; diff --git a/src/cpp/utils/Host.hpp b/src/cpp/utils/Host.hpp new file mode 100644 index 00000000000..a261bfaf465 --- /dev/null +++ b/src/cpp/utils/Host.hpp @@ -0,0 +1,81 @@ +// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef UTILS_HOST_HPP_ +#define UTILS_HOST_HPP_ + +#include +#include + +namespace eprosima { + +/** + * This singleton generates a host_id based on system interfaces ip addresses + */ +class Host +{ +public: + + Host() + { + // Compute the host id + fastrtps::rtps::LocatorList_t loc; + fastrtps::rtps::IPFinder::getIP4Address(&loc); + + { + if (loc.size() > 0) + { + MD5 md5; + for (auto& l : loc) + { + md5.update(l.address, sizeof(l.address)); + } + md5.finalize(); + id_ = 0; + for (size_t i = 0; i < sizeof(md5.digest); i += 2) + { + id_ ^= ((md5.digest[i] << 8) | md5.digest[i + 1]); + } + } + else + { + reinterpret_cast(id_)[0] = 127; + reinterpret_cast(id_)[1] = 1; + } + } + } + + ~Host() + { + } + + inline uint16_t id() const + { + return id_; + } + + static Host& get() + { + static Host singleton; + return singleton; + } + +private: + + uint16_t id_; +}; + +} // eprosima + +#endif // UTILS_HOST_HPP_ \ No newline at end of file diff --git a/test/blackbox/BlackboxTestsSecurity.cpp b/test/blackbox/BlackboxTestsSecurity.cpp index a731a566cca..3d238477f04 100644 --- a/test/blackbox/BlackboxTestsSecurity.cpp +++ b/test/blackbox/BlackboxTestsSecurity.cpp @@ -24,6 +24,7 @@ #include #include +#include using namespace eprosima::fastrtps; using namespace eprosima::fastrtps::rtps; @@ -322,7 +323,151 @@ TEST_P(Security, BuiltinAuthenticationAndCryptoPlugin_besteffort_rtps_ok) reader.block_for_at_least(2); } -TEST_P(Security, BuiltinAuthenticationAndCryptoPlugin_reliable_rtps_ok) +TEST_P(Security, BuiltinAuthenticationAndCryptoPlugin_shm_transport_ok) +{ + PubSubReader reader(TEST_TOPIC_NAME); + PubSubWriter writer(TEST_TOPIC_NAME); + + auto shm_transport = std::make_shared(); + auto udp_transport = std::make_shared(); + const uint32_t segment_size = 1024 * 1024; + shm_transport->segment_size(segment_size, segment_size); + reader.disable_builtin_transport(); + reader.add_user_transport_to_pparams(shm_transport); + writer.disable_builtin_transport(); + writer.add_user_transport_to_pparams(shm_transport); + + PropertyPolicy pub_property_policy, sub_property_policy; + + sub_property_policy.properties().emplace_back(Property("dds.sec.auth.plugin", + "builtin.PKI-DH")); + sub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_ca", + "file://" + std::string(certs_path) + "/maincacert.pem")); + sub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_certificate", + "file://" + std::string(certs_path) + "/mainsubcert.pem")); + sub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.private_key", + "file://" + std::string(certs_path) + "/mainsubkey.pem")); + sub_property_policy.properties().emplace_back(Property("dds.sec.crypto.plugin", + "builtin.AES-GCM-GMAC")); + sub_property_policy.properties().emplace_back("rtps.participant.rtps_protection_kind", "ENCRYPT"); + + reader.history_depth(10). + property_policy(sub_property_policy).init(); + + ASSERT_TRUE(reader.isInitialized()); + + pub_property_policy.properties().emplace_back(Property("dds.sec.auth.plugin", + "builtin.PKI-DH")); + pub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_ca", + "file://" + std::string(certs_path) + "/maincacert.pem")); + pub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_certificate", + "file://" + std::string(certs_path) + "/mainpubcert.pem")); + pub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.private_key", + "file://" + std::string(certs_path) + "/mainpubkey.pem")); + pub_property_policy.properties().emplace_back(Property("dds.sec.crypto.plugin", + "builtin.AES-GCM-GMAC")); + pub_property_policy.properties().emplace_back("rtps.participant.rtps_protection_kind", "ENCRYPT"); + + writer.history_depth(10). + reliability(eprosima::fastrtps::BEST_EFFORT_RELIABILITY_QOS). + property_policy(pub_property_policy).init(); + + ASSERT_TRUE(writer.isInitialized()); + + // Wait for authorization + reader.waitAuthorized(); + writer.waitAuthorized(); + + // Wait for discovery. + writer.wait_discovery(); + reader.wait_discovery(); + + auto data = default_helloworld_data_generator(); + + reader.startReception(data); + + // Send data + writer.send(data); + // In this test all data should be sent. + ASSERT_TRUE(data.empty()); + // Block reader until reception finished or timeout. + reader.block_for_at_least(2); +} + +TEST_P(Security, BuiltinAuthenticationAndCryptoPlugin_shm_udp_transport_ok) +{ + PubSubReader reader(TEST_TOPIC_NAME); + PubSubWriter writer(TEST_TOPIC_NAME); + + auto shm_transport = std::make_shared(); + auto udp_transport = std::make_shared(); + const uint32_t segment_size = 1024 * 1024; + shm_transport->segment_size(segment_size, segment_size); + reader.disable_builtin_transport(); + reader.add_user_transport_to_pparams(shm_transport); + reader.add_user_transport_to_pparams(udp_transport); + writer.disable_builtin_transport(); + writer.add_user_transport_to_pparams(shm_transport); + writer.add_user_transport_to_pparams(udp_transport); + + PropertyPolicy pub_property_policy, sub_property_policy; + + sub_property_policy.properties().emplace_back(Property("dds.sec.auth.plugin", + "builtin.PKI-DH")); + sub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_ca", + "file://" + std::string(certs_path) + "/maincacert.pem")); + sub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_certificate", + "file://" + std::string(certs_path) + "/mainsubcert.pem")); + sub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.private_key", + "file://" + std::string(certs_path) + "/mainsubkey.pem")); + sub_property_policy.properties().emplace_back(Property("dds.sec.crypto.plugin", + "builtin.AES-GCM-GMAC")); + sub_property_policy.properties().emplace_back("rtps.participant.rtps_protection_kind", "ENCRYPT"); + + reader.history_depth(10). + property_policy(sub_property_policy).init(); + + ASSERT_TRUE(reader.isInitialized()); + + pub_property_policy.properties().emplace_back(Property("dds.sec.auth.plugin", + "builtin.PKI-DH")); + pub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_ca", + "file://" + std::string(certs_path) + "/maincacert.pem")); + pub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.identity_certificate", + "file://" + std::string(certs_path) + "/mainpubcert.pem")); + pub_property_policy.properties().emplace_back(Property("dds.sec.auth.builtin.PKI-DH.private_key", + "file://" + std::string(certs_path) + "/mainpubkey.pem")); + pub_property_policy.properties().emplace_back(Property("dds.sec.crypto.plugin", + "builtin.AES-GCM-GMAC")); + pub_property_policy.properties().emplace_back("rtps.participant.rtps_protection_kind", "ENCRYPT"); + + writer.history_depth(10). + reliability(eprosima::fastrtps::BEST_EFFORT_RELIABILITY_QOS). + property_policy(pub_property_policy).init(); + + ASSERT_TRUE(writer.isInitialized()); + + // Wait for authorization + reader.waitAuthorized(); + writer.waitAuthorized(); + + // Wait for discovery. + writer.wait_discovery(); + reader.wait_discovery(); + + auto data = default_helloworld_data_generator(); + + reader.startReception(data); + + // Send data + writer.send(data); + // In this test all data should be sent. + ASSERT_TRUE(data.empty()); + // Block reader until reception finished or timeout. + reader.block_for_at_least(2); +} + +TEST(Security, BuiltinAuthenticationAndCryptoPlugin_reliable_rtps_ok) { PubSubReader reader(TEST_TOPIC_NAME); PubSubWriter writer(TEST_TOPIC_NAME); diff --git a/test/blackbox/BlackboxTestsTransportSHM.cpp b/test/blackbox/BlackboxTestsTransportSHM.cpp new file mode 100644 index 00000000000..99590adda9d --- /dev/null +++ b/test/blackbox/BlackboxTestsTransportSHM.cpp @@ -0,0 +1,349 @@ +// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "BlackboxTests.hpp" + +#include "PubSubReader.hpp" +#include "PubSubWriter.hpp" + +#include + +#include +#include "../cpp/rtps/transport/shared_mem/test_SharedMemTransportDescriptor.h" + +using namespace eprosima::fastrtps; + +using SharedMemTransportDescriptor = eprosima::fastdds::rtps::SharedMemTransportDescriptor; +using test_SharedMemTransportDescriptor = eprosima::fastdds::rtps::test_SharedMemTransportDescriptor; + +TEST(SHM, TransportPubSub) +{ + PubSubReader reader(TEST_TOPIC_NAME); + PubSubWriter writer(TEST_TOPIC_NAME); + + // Number of samples written by writer + uint32_t writer_samples = 15; + + auto testTransport = std::make_shared(); + + writer.reliability(eprosima::fastrtps::RELIABLE_RELIABILITY_QOS); + writer.disable_builtin_transport(). + add_user_transport_to_pparams(testTransport).init(); + + reader.reliability(eprosima::fastrtps::RELIABLE_RELIABILITY_QOS); + reader.disable_builtin_transport(). + add_user_transport_to_pparams(testTransport).init(); + + ASSERT_TRUE(reader.isInitialized()); + ASSERT_TRUE(writer.isInitialized()); + + // Wait for discovery. + writer.wait_discovery(); + reader.wait_discovery(); + + std::list data = default_helloworld_data_generator(writer_samples); + reader.startReception(data); + // Send data + writer.send(data); + // In this test all data should be sent. + ASSERT_TRUE(data.empty()); + // Block reader until reception finished or timeout. + reader.block_for_all(); + + // Destroy the writer participant. + writer.destroy(); + + // Check that reader receives the unmatched. + reader.wait_participant_undiscovery(); +} + +TEST(SHM, Test300KFragmentation) +{ + PubSubReader reader(TEST_TOPIC_NAME); + PubSubWriter writer(TEST_TOPIC_NAME); + + auto data = default_data300kb_data_generator(1); + auto data_size = data.front().data().size(); + + auto shm_transport = std::make_shared(); + const uint32_t segment_size = static_cast(data_size * 3 / 4); + shm_transport->segment_size(segment_size, segment_size); + + writer + .asynchronously(eprosima::fastrtps::ASYNCHRONOUS_PUBLISH_MODE) + .reliability(eprosima::fastrtps::RELIABLE_RELIABILITY_QOS) + .disable_builtin_transport() + .add_user_transport_to_pparams(shm_transport) + .init(); + + uint32_t big_buffers_count = 0; + shm_transport->big_buffer_size_ = shm_transport->segment_size()/3; + shm_transport->big_buffer_size_count_ = &big_buffers_count; + + reader + .reliability(eprosima::fastrtps::RELIABLE_RELIABILITY_QOS) + .disable_builtin_transport() + .add_user_transport_to_pparams(shm_transport) + .init(); + + ASSERT_TRUE(reader.isInitialized()); + ASSERT_TRUE(writer.isInitialized()); + + // Wait for discovery. + writer.wait_discovery(); + reader.wait_discovery(); + + reader.startReception(data); + // Send data with some interval, to let async writer thread send samples + writer.send(data, 300); + + // In this test all data should be sent. + ASSERT_TRUE(data.empty()); + // Block reader until reception finished or timeout. + reader.block_for_all(); + + ASSERT_EQ(big_buffers_count, 2u); + + // Destroy the writer participant. + writer.destroy(); + + // Check that reader receives the unmatched. + reader.wait_participant_undiscovery(); +} + +TEST(SHM, Test300KNoFragmentation) +{ + PubSubReader reader(TEST_TOPIC_NAME); + PubSubWriter writer(TEST_TOPIC_NAME); + + auto data = default_data300kb_data_generator(1); + auto data_size = data.front().data().size(); + + auto shm_transport = std::make_shared(); + const uint32_t segment_size = 1024 * 1024; + shm_transport->segment_size(segment_size, segment_size); + + writer + .asynchronously(eprosima::fastrtps::ASYNCHRONOUS_PUBLISH_MODE) + .reliability(eprosima::fastrtps::RELIABLE_RELIABILITY_QOS) + .disable_builtin_transport() + .add_user_transport_to_pparams(shm_transport) + .init(); + + uint32_t big_buffers_count = 0; + shm_transport->big_buffer_size_ = static_cast(data_size); + shm_transport->big_buffer_size_count_ = &big_buffers_count; + + reader + .reliability(eprosima::fastrtps::RELIABLE_RELIABILITY_QOS) + .disable_builtin_transport() + .add_user_transport_to_pparams(shm_transport) + .init(); + + ASSERT_TRUE(reader.isInitialized()); + ASSERT_TRUE(writer.isInitialized()); + + // Wait for discovery. + writer.wait_discovery(); + reader.wait_discovery(); + + reader.startReception(data); + // Send data with some interval, to let async writer thread send samples + writer.send(data, 300); + + // In this test all data should be sent. + ASSERT_TRUE(data.empty()); + // Block reader until reception finished or timeout. + reader.block_for_all(); + + ASSERT_EQ(big_buffers_count, 1u); + + // Destroy the writer participant. + writer.destroy(); + + // Check that reader receives the unmatched. + reader.wait_participant_undiscovery(); +} + +TEST(SHM, SHM_UDP_300KFragmentation) +{ + PubSubReader reader(TEST_TOPIC_NAME); + PubSubWriter writer(TEST_TOPIC_NAME); + + // Number of samples written by writer + uint32_t writer_samples = 1; + + auto shm_transport = std::make_shared(); + const uint32_t segment_size = 1024 * 1024; + shm_transport->segment_size(segment_size, segment_size); + + auto udp_transport = std::make_shared(); + + writer.asynchronously(eprosima::fastrtps::ASYNCHRONOUS_PUBLISH_MODE); + writer.reliability(eprosima::fastrtps::RELIABLE_RELIABILITY_QOS); + writer + .disable_builtin_transport() + .add_user_transport_to_pparams(shm_transport) + .add_user_transport_to_pparams(udp_transport) + .init(); + + uint32_t big_buffers_count = 0; + shm_transport->big_buffer_size_ = 32*1024; // 32K + shm_transport->big_buffer_size_count_ = &big_buffers_count; + + reader.reliability(eprosima::fastrtps::RELIABLE_RELIABILITY_QOS); + reader + .disable_builtin_transport() + .add_user_transport_to_pparams(shm_transport) + .add_user_transport_to_pparams(udp_transport) + .init(); + + ASSERT_TRUE(reader.isInitialized()); + ASSERT_TRUE(writer.isInitialized()); + + // Wait for discovery. + writer.wait_discovery(); + reader.wait_discovery(); + + auto data = default_data300kb_data_generator(writer_samples); + auto data_size = data.front().data().size(); + reader.startReception(data); + // Send data with some interval, to let async writer thread send samples + writer.send(data, 300); + + // In this test all data should be sent. + ASSERT_TRUE(data.empty()); + // Block reader until reception finished or timeout. + reader.block_for_all(); + + ASSERT_EQ(big_buffers_count, std::ceil(data_size / (float)udp_transport->maxMessageSize)); + + // Destroy the writer participant. + writer.destroy(); + + // Check that reader receives the unmatched. + reader.wait_participant_undiscovery(); +} + +TEST(SHM, UDPvsSHM_UDP) +{ + PubSubReader reader(TEST_TOPIC_NAME); + PubSubWriter writer(TEST_TOPIC_NAME); + + // Number of samples written by writer + uint32_t writer_samples = 1; + + auto shm_transport = std::make_shared(); + const uint32_t segment_size = 1024 * 1024; + shm_transport->segment_size(segment_size, segment_size); + + auto udp_transport = std::make_shared(); + + writer.asynchronously(eprosima::fastrtps::ASYNCHRONOUS_PUBLISH_MODE); + writer.reliability(eprosima::fastrtps::RELIABLE_RELIABILITY_QOS); + writer + .disable_builtin_transport() + .add_user_transport_to_pparams(udp_transport) + .init(); + + uint32_t big_buffers_count = 0; + shm_transport->big_buffer_size_ = 32*1024; // 32K + shm_transport->big_buffer_size_count_ = &big_buffers_count; + + reader.reliability(eprosima::fastrtps::RELIABLE_RELIABILITY_QOS); + reader + .disable_builtin_transport() + .add_user_transport_to_pparams(shm_transport) + .add_user_transport_to_pparams(udp_transport) + .init(); + + ASSERT_TRUE(reader.isInitialized()); + ASSERT_TRUE(writer.isInitialized()); + + // Wait for discovery. + writer.wait_discovery(); + reader.wait_discovery(); + + auto data = default_data300kb_data_generator(writer_samples); + reader.startReception(data); + // Send data with some interval, to let async writer thread send samples + writer.send(data, 300); + + // In this test all data should be sent. + ASSERT_TRUE(data.empty()); + // Block reader until reception finished or timeout. + reader.block_for_all(); + + ASSERT_EQ(big_buffers_count, 0u); + + // Destroy the writer participant. + writer.destroy(); + + // Check that reader receives the unmatched. + reader.wait_participant_undiscovery(); +} + +TEST(SHM, SHM_UDPvsUDP) +{ + PubSubReader reader(TEST_TOPIC_NAME); + PubSubWriter writer(TEST_TOPIC_NAME); + + // Number of samples written by writer + uint32_t writer_samples = 1; + + auto shm_transport = std::make_shared(); + const uint32_t segment_size = 1024 * 1024; + shm_transport->segment_size(segment_size, segment_size); + + auto udp_transport = std::make_shared(); + + writer.asynchronously(eprosima::fastrtps::ASYNCHRONOUS_PUBLISH_MODE); + writer.reliability(eprosima::fastrtps::RELIABLE_RELIABILITY_QOS); + writer + .disable_builtin_transport() + .add_user_transport_to_pparams(shm_transport) + .add_user_transport_to_pparams(udp_transport) + .init(); + + reader.reliability(eprosima::fastrtps::RELIABLE_RELIABILITY_QOS); + reader + .disable_builtin_transport() + .add_user_transport_to_pparams(udp_transport) + .init(); + + ASSERT_TRUE(reader.isInitialized()); + ASSERT_TRUE(writer.isInitialized()); + + // Wait for discovery. + writer.wait_discovery(); + reader.wait_discovery(); + + auto data = default_data300kb_data_generator(writer_samples); + reader.startReception(data); + // Send data with some interval, to let async writer thread send samples + writer.send(data, 300); + + // In this test all data should be sent. + ASSERT_TRUE(data.empty()); + // Block reader until reception finished or timeout. + reader.block_for_all(); + + // Destroy the writer participant. + writer.destroy(); + + // Check that reader receives the unmatched. + reader.wait_participant_undiscovery(); +} + + diff --git a/test/mock/rtps/ReceiverResource/fastdds/rtps/network/ReceiverResource.h b/test/mock/rtps/ReceiverResource/fastdds/rtps/network/ReceiverResource.h index 6678aeea343..aedc5fe27f1 100644 --- a/test/mock/rtps/ReceiverResource/fastdds/rtps/network/ReceiverResource.h +++ b/test/mock/rtps/ReceiverResource/fastdds/rtps/network/ReceiverResource.h @@ -66,9 +66,10 @@ class ReceiverResource : public TransportReceiverInterface bool checkReaders(EntityId_t) { return false; } protected: ReceiverResource(TransportInterface& transport, - const Locator_t& locator, uint32_t maxMsgSize) + const Locator_t& locator, + uint32_t max_recv_buffer_size) : mValid(false) - , m_maxMsgSize(maxMsgSize) + , m_maxMsgSize(max_recv_buffer_size) { mValid = transport.OpenInputChannel(locator, this, m_maxMsgSize); if (!mValid) diff --git a/test/mock/rtps/SharedMemTransportDescriptor/fastdds/rtps/transport/shared_mem/SharedMemTransportDescriptor.h b/test/mock/rtps/SharedMemTransportDescriptor/fastdds/rtps/transport/shared_mem/SharedMemTransportDescriptor.h new file mode 100644 index 00000000000..64d25fce1d8 --- /dev/null +++ b/test/mock/rtps/SharedMemTransportDescriptor/fastdds/rtps/transport/shared_mem/SharedMemTransportDescriptor.h @@ -0,0 +1,154 @@ +// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_SHAREDMEM_TRANSPORT_DESCRIPTOR_ +#define _FASTDDS_SHAREDMEM_TRANSPORT_DESCRIPTOR_ + +#include "fastdds/rtps/transport/TransportDescriptorInterface.h" + +namespace eprosima { +namespace fastdds { +namespace rtps { + +class TransportInterface; + +/** + * Shared memory transport configuration + * + * @ingroup TRANSPORT_MODULE + */ +typedef struct SharedMemTransportDescriptor : public TransportDescriptorInterface +{ + virtual ~SharedMemTransportDescriptor() + { + + } + + + RTPS_DllAPI SharedMemTransportDescriptor() + : TransportDescriptorInterface(0, 0) + { + + } + + RTPS_DllAPI SharedMemTransportDescriptor( + const SharedMemTransportDescriptor& t) + : TransportDescriptorInterface(t) + { + (void)t; + } + + virtual TransportInterface* create_transport() const override + { + return nullptr; + } + + uint32_t min_send_buffer_size() const override { return 0; } + + enum class OverflowPolicy + { + DISCARD, + FAIL + }; + + RTPS_DllAPI uint32_t segment_size() const + { + return segment_size_; + } + + /** + * Sets the segment_size and the max_message_size. + * max_message must be <= segment_size + * @param [in] segment_size in bytes. + * @param [in] max_message_size in bytes. + */ + RTPS_DllAPI void segment_size( + uint32_t segment_size, + uint32_t max_message_size) + { + segment_size_ = segment_size; + maxMessageSize = std::min(segment_size, max_message_size); + } + + RTPS_DllAPI OverflowPolicy port_overflow_policy() const + { + return port_overflow_policy_; + } + + RTPS_DllAPI void port_overflow_policy( + OverflowPolicy port_overflow_policy) + { + port_overflow_policy_ = port_overflow_policy; + } + + RTPS_DllAPI uint32_t port_queue_capacity() const + { + return port_queue_capacity_; + } + + RTPS_DllAPI void port_queue_capacity( + uint32_t port_queue_capacity) + { + port_queue_capacity_ = port_queue_capacity; + } + + RTPS_DllAPI OverflowPolicy segment_overflow_policy() const + { + return segment_overflow_policy_; + } + + RTPS_DllAPI void segment_overflow_policy( + OverflowPolicy segment_overflow_policy) + { + segment_overflow_policy_ = segment_overflow_policy; + } + + RTPS_DllAPI uint32_t healthy_check_timeout_ms() const + { + return healthy_check_timeout_ms_; + } + + RTPS_DllAPI void healthy_check_timeout_ms( + uint32_t healthy_check_timeout_ms) + { + healthy_check_timeout_ms_ = healthy_check_timeout_ms; + } + + RTPS_DllAPI std::string rtps_dump_file() const + { + return rtps_dump_file_; + } + + RTPS_DllAPI void rtps_dump_file( + const std::string& rtps_dump_file) + { + rtps_dump_file_ = rtps_dump_file; + } + +private: + + uint32_t segment_size_; + uint32_t port_queue_capacity_; + OverflowPolicy port_overflow_policy_; + OverflowPolicy segment_overflow_policy_; + uint32_t healthy_check_timeout_ms_; + std::string rtps_dump_file_; + +}SharedMemTransportDescriptor; + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_SHAREDMEM_TRANSPORT_DESCRIPTOR_ diff --git a/test/performance/latency/CMakeLists.txt b/test/performance/latency/CMakeLists.txt index b7ffe1bc5f9..6bc1de13167 100644 --- a/test/performance/latency/CMakeLists.txt +++ b/test/performance/latency/CMakeLists.txt @@ -40,6 +40,8 @@ set( interprocess_reliable interprocess_best_effort_tcp interprocess_reliable_tcp + interprocess_best_effort_shm + interprocess_reliable_shm ) ########################################################################### diff --git a/test/performance/latency/xml/interprocess_best_effort_shm.xml b/test/performance/latency/xml/interprocess_best_effort_shm.xml new file mode 100644 index 00000000000..e85e726e496 --- /dev/null +++ b/test/performance/latency/xml/interprocess_best_effort_shm.xml @@ -0,0 +1,105 @@ + + + + + + + publisher_transport + SHM + + + + + + + 231 + + latency_test_publisher + + publisher_transport + + false + + + + + latency_interprocess_pub2sub + LatencyType + NO_KEY + + + + BEST_EFFORT + + + VOLATILE + + + + + + latency_interprocess_sub2pub + LatencyType + NO_KEY + + + + BEST_EFFORT + + + VOLATILE + + + + + + + + subscriber_transport + SHM + + + + + + 231 + + latency_test_subscriber + + subscriber_transport + + false + + + + + latency_interprocess_sub2pub + LatencyType + NO_KEY + + + + BEST_EFFORT + + + VOLATILE + + + + + + latency_interprocess_pub2sub + LatencyType + NO_KEY + + + + BEST_EFFORT + + + VOLATILE + + + + + diff --git a/test/performance/latency/xml/interprocess_reliable_shm.xml b/test/performance/latency/xml/interprocess_reliable_shm.xml new file mode 100644 index 00000000000..717ac4f7929 --- /dev/null +++ b/test/performance/latency/xml/interprocess_reliable_shm.xml @@ -0,0 +1,104 @@ + + + + + + + publisher_transport + SHM + + + + + + 231 + + latency_test_publisher + + publisher_transport + + false + + + + + latency_interprocess_pub2sub + LatencyType + NO_KEY + + + + RELIABLE + + + VOLATILE + + + + + + latency_interprocess_sub2pub + LatencyType + NO_KEY + + + + RELIABLE + + + VOLATILE + + + + + + + + subscriber_transport + SHM + + + + + + 231 + + latency_test_subscriber + + subscriber_transport + + false + + + + + latency_interprocess_sub2pub + LatencyType + NO_KEY + + + + RELIABLE + + + VOLATILE + + + + + + latency_interprocess_pub2sub + LatencyType + NO_KEY + + + + RELIABLE + + + VOLATILE + + + + + \ No newline at end of file diff --git a/test/performance/throughput/CMakeLists.txt b/test/performance/throughput/CMakeLists.txt index d109f2faf52..53f08fb0da1 100644 --- a/test/performance/throughput/CMakeLists.txt +++ b/test/performance/throughput/CMakeLists.txt @@ -41,6 +41,8 @@ set( interprocess_reliable interprocess_best_effort_tcp interprocess_reliable_tcp + interprocess_best_effort_shm + interprocess_reliable_shm ) ########################################################################### diff --git a/test/performance/throughput/payloads_demands.csv b/test/performance/throughput/payloads_demands.csv index de48b845769..2af0fcb16bd 100644 --- a/test/performance/throughput/payloads_demands.csv +++ b/test/performance/throughput/payloads_demands.csv @@ -1 +1,3 @@ +16;1000 1024;1000 +16384;1000 diff --git a/test/performance/throughput/xml/interprocess_best_effort_shm.xml b/test/performance/throughput/xml/interprocess_best_effort_shm.xml new file mode 100644 index 00000000000..7b4c1ad1b5a --- /dev/null +++ b/test/performance/throughput/xml/interprocess_best_effort_shm.xml @@ -0,0 +1,84 @@ + + + + + OFF + + + + + throughput_publisher_shm_transport + SHM + + + + throughput_subscriber_shm_transport + SHM + + + + + + + + 120 + + + throughput_publisher_shm_transport + + false + throughput_test_publisher + + + + + + + 120 + + + throughput_subscriber_shm_transport + + false + throughput_test_subscriber + + + + + + + throughput_interprocess + ThroughputType + NO_KEY + + KEEP_ALL + + + + + BEST_EFFORT + + + VOLATILE + + + + + + + + throughput_interprocess + ThroughputType + NO_KEY + + KEEP_ALL + + + + + BEST_EFFORT + + + + + diff --git a/test/performance/throughput/xml/interprocess_reliable_shm.xml b/test/performance/throughput/xml/interprocess_reliable_shm.xml new file mode 100644 index 00000000000..7f00e26e76c --- /dev/null +++ b/test/performance/throughput/xml/interprocess_reliable_shm.xml @@ -0,0 +1,81 @@ + + + + + + + throughput_publisher_shm_transport + SHM + + + + throughput_subscriber_shm_transport + SHM + + + + + + + + 222 + + + throughput_publisher_shm_transport + + false + throughput_test_publisher + + + + + + + 222 + + + throughput_subscriber_shm_transport + + false + throughput_test_subscriber + + + + + + + throughput_interprocess + ThroughputType + NO_KEY + + KEEP_ALL + + + + + RELIABLE + + + VOLATILE + + + + + + + + throughput_interprocess + ThroughputType + NO_KEY + + KEEP_ALL + + + + + RELIABLE + + + + + diff --git a/test/unittest/dynamic_types/CMakeLists.txt b/test/unittest/dynamic_types/CMakeLists.txt index b4cebe4e4c4..bb55d59b80b 100644 --- a/test/unittest/dynamic_types/CMakeLists.txt +++ b/test/unittest/dynamic_types/CMakeLists.txt @@ -111,6 +111,7 @@ if(NOT ((MSVC OR MSVC_IDE) AND EPROSIMA_INSTALLER)) ${PROJECT_SOURCE_DIR}/test/mock/rtps/UDPTransportDescriptor ${PROJECT_SOURCE_DIR}/test/mock/rtps/UDPv4TransportDescriptor ${PROJECT_SOURCE_DIR}/test/mock/rtps/UDPv6TransportDescriptor + ${PROJECT_SOURCE_DIR}/test/mock/rtps/SharedMemTransportDescriptor ${TINYXML2_INCLUDE_DIR} ${PROJECT_SOURCE_DIR}/include ${PROJECT_BINARY_DIR}/include ) diff --git a/test/unittest/rtps/network/NetworkFactoryTests.cpp b/test/unittest/rtps/network/NetworkFactoryTests.cpp index ccd8ac22a71..bd9f4b58ca7 100644 --- a/test/unittest/rtps/network/NetworkFactoryTests.cpp +++ b/test/unittest/rtps/network/NetworkFactoryTests.cpp @@ -83,7 +83,7 @@ TEST_F(NetworkTests, BuildReceiverResource_returns_receive_resource_for_a_kind_c // When std::vector> resources; - bool ret = networkFactoryUnderTest.BuildReceiverResources(kindCompatibleLocator, 0x8FFF, resources); + bool ret = networkFactoryUnderTest.BuildReceiverResources(kindCompatibleLocator, resources, 0x8FFF); // Then ASSERT_TRUE(ret); @@ -102,7 +102,7 @@ TEST_F(NetworkTests, BuildReceiverResource_returns_multiple_resources_if_multipl // When std::vector> resources; - bool ret = networkFactoryUnderTest.BuildReceiverResources(locatorCompatibleWithTwoTransports, 0x8FFF, resources); + bool ret = networkFactoryUnderTest.BuildReceiverResources(locatorCompatibleWithTwoTransports, resources, 0x8FFF); // Then ASSERT_TRUE(ret); @@ -158,7 +158,7 @@ TEST_F(NetworkTests, creating_receive_resource_from_locator_opens_channels_mappe // When std::vector> resources; - bool ret = networkFactoryUnderTest.BuildReceiverResources(locator, 0x8FFF, resources); + bool ret = networkFactoryUnderTest.BuildReceiverResources(locator, resources, 0x8FFF); ASSERT_TRUE(ret); @@ -199,7 +199,7 @@ TEST_F(NetworkTests, destroying_a_receive_resource_will_close_all_channels_mappe locator.kind = ArbitraryKind; std::vector> resources; - bool ret = networkFactoryUnderTest.BuildReceiverResources(locator, 0x8FFF, resources); + bool ret = networkFactoryUnderTest.BuildReceiverResources(locator, resources, 0x8FFF); ASSERT_TRUE(ret); // When @@ -261,7 +261,7 @@ TEST_F(NetworkTests, A_receiver_resource_accurately_reports_whether_it_supports_ Locator_t locator; locator.kind = ArbitraryKind; std::vector> resources; - bool ret = networkFactoryUnderTest.BuildReceiverResources(locator, 0x8FFF, resources); + bool ret = networkFactoryUnderTest.BuildReceiverResources(locator, resources, 0x8FFF); ASSERT_TRUE(ret); auto& resource = resources.back(); @@ -335,7 +335,7 @@ TEST_F(NetworkTests, A_Receiver_Resource_will_always_receive_through_its_origina Locator_t locator; locator.kind = ArbitraryKind; std::vector> resources; - bool ret = networkFactoryUnderTest.BuildReceiverResources(locator, 0x8FFF, resources); + bool ret = networkFactoryUnderTest.BuildReceiverResources(locator, resources); ASSERT_TRUE(ret); auto& receiverResource = resources.back(); diff --git a/test/unittest/rtps/network/mock/MockTransport.h b/test/unittest/rtps/network/mock/MockTransport.h index 5ab59087e86..0596cb4fd5c 100644 --- a/test/unittest/rtps/network/mock/MockTransport.h +++ b/test/unittest/rtps/network/mock/MockTransport.h @@ -125,6 +125,11 @@ class MockTransport : public TransportInterface Locator_t &, uint32_t) const override { return true; } + virtual uint32_t max_recv_buffer_size() const override + { + return 0x8FFF; + } + //Helpers and message record typedef struct { @@ -137,7 +142,6 @@ class MockTransport : public TransportInterface std::vector mockMessagesSent; // For the mock, port + direction tuples will have a 1:1 relatonship with channels - typedef uint32_t Port; std::vector mockOpenInputChannels; diff --git a/test/unittest/security/authentication/CMakeLists.txt b/test/unittest/security/authentication/CMakeLists.txt index 4cffd92e847..7e4ab72300d 100644 --- a/test/unittest/security/authentication/CMakeLists.txt +++ b/test/unittest/security/authentication/CMakeLists.txt @@ -50,6 +50,9 @@ if(NOT ((MSVC OR MSVC_IDE) AND EPROSIMA_INSTALLER)) ${PROJECT_SOURCE_DIR}/src/cpp/fastrtps_deprecated/security/authentication/PKIIdentityHandle.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastrtps_deprecated/security/authentication/PKIHandshakeHandle.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastrtps_deprecated/security/OpenSSLInit.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/fastrtps_deprecated/utils/md5.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/fastrtps_deprecated/utils/IPFinder.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/fastrtps_deprecated/utils/IPLocator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BuiltinPKIDHTests.cpp) target_compile_definitions(BuiltinPKIDH PRIVATE FASTRTPS_NO_LIB) target_include_directories(BuiltinPKIDH PRIVATE @@ -60,7 +63,7 @@ if(NOT ((MSVC OR MSVC_IDE) AND EPROSIMA_INSTALLER)) ${PROJECT_SOURCE_DIR}/include ${PROJECT_BINARY_DIR}/include ${PROJECT_SOURCE_DIR}/src/cpp ) - target_link_libraries(BuiltinPKIDH ${GTEST_LIBRARIES} ${OPENSSL_LIBRARIES} foonathan_memory) + target_link_libraries(BuiltinPKIDH ${GTEST_LIBRARIES} ${OPENSSL_LIBRARIES} foonathan_memory $<$:ws2_32>) add_gtest(BuiltinPKIDH SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/BuiltinPKIDHTests.cpp ENVIRONMENTS "CERTS_PATH=${PROJECT_SOURCE_DIR}/test/certs") diff --git a/test/unittest/transport/CMakeLists.txt b/test/unittest/transport/CMakeLists.txt index d16a69815dc..2fa58d317df 100644 --- a/test/unittest/transport/CMakeLists.txt +++ b/test/unittest/transport/CMakeLists.txt @@ -176,6 +176,20 @@ if(NOT ((MSVC OR MSVC_IDE) AND EPROSIMA_INSTALLER)) ${PROJECT_SOURCE_DIR}/src/cpp/rtps/common/Time_t.cpp ) + set(SHAREDMEMTESTS_SOURCE + SharedMemTests.cpp + mock/MockReceiverResource.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/log/Log.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/log/StdoutConsumer.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/rtps/transport/shared_mem/SharedMemTransport.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/rtps/transport/ChannelResource.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/fastrtps_deprecated/utils/IPFinder.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/fastrtps_deprecated/utils/IPLocator.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/rtps/network/NetworkFactory.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/rtps/common/Time_t.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/fastrtps_deprecated/utils/md5.cpp + ) + include_directories(mock/) add_executable(UDPv4Tests ${UDPV4TESTS_SOURCE}) @@ -270,5 +284,28 @@ if(NOT ((MSVC OR MSVC_IDE) AND EPROSIMA_INSTALLER)) target_link_libraries(TCPv4Tests ${PRIVACY} fastcdr) endif() add_gtest(TCPv4Tests SOURCES ${TCPV4TESTS_SOURCE}) + + add_executable(SharedMemTests ${SHAREDMEMTESTS_SOURCE}) + + target_compile_definitions(SharedMemTests PRIVATE FASTRTPS_NO_LIB + $<$:_ENABLE_ATOMIC_ALIGNMENT_FIX>) + + target_include_directories(SharedMemTests PRIVATE + ${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR}/test/mock/rtps/MessageReceiver + ${PROJECT_SOURCE_DIR}/test/mock/rtps/ReceiverResource + ${PROJECT_SOURCE_DIR}/include ${PROJECT_BINARY_DIR}/include + ${PROJECT_SOURCE_DIR}/src/cpp + ${THIRDPARTY_BOOST_INCLUDE_DIR} + ) + target_link_libraries(SharedMemTests ${GTEST_LIBRARIES} ${MOCKS} + $<$:OpenSSL::SSL$OpenSSL::Crypto> + ${THIRDPARTY_BOOST_LINK_LIBS}) + if(MSVC OR MSVC_IDE) + target_link_libraries(SharedMemTests ${PRIVACY} iphlpapi Shlwapi ) + else() + target_link_libraries(SharedMemTests ${PRIVACY} ) + endif() + add_gtest(SharedMemTests SOURCES ${SHAREDMEMTESTS_SOURCE}) endif() endif() diff --git a/test/unittest/transport/SharedMemTests.cpp b/test/unittest/transport/SharedMemTests.cpp new file mode 100644 index 00000000000..6e74bb5f3a5 --- /dev/null +++ b/test/unittest/transport/SharedMemTests.cpp @@ -0,0 +1,1270 @@ +// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include "../../../src/cpp/rtps/transport/shared_mem/SharedMemSenderResource.hpp" +#include "../../../src/cpp/rtps/transport/shared_mem/SharedMemManager.hpp" +#include "../../../src/cpp/rtps/transport/shared_mem/SharedMemGlobal.hpp" +#include "../../../src/cpp/rtps/transport/shared_mem/MultiProducerConsumerRingBuffer.hpp" + +#include +#include +#include +#include +#include +#include + +using namespace eprosima::fastrtps; +using namespace eprosima::fastrtps::rtps; +using namespace eprosima::fastdds; +using namespace eprosima::fastdds::rtps; + +#if defined(_WIN32) +#define GET_PID _getpid +#else +#define GET_PID getpid +#endif + +static uint16_t g_default_port = 0; +static uint16_t g_output_port = 0; +static uint16_t g_input_port = 0; + +uint16_t get_port( + uint16_t offset) +{ + uint16_t port = static_cast(GET_PID()); + + if (offset > port) + { + port += offset; + } + + return port; +} + +class SHMTransportTests : public ::testing::Test +{ +public: + + SHMTransportTests() + { + eprosima::fastdds::dds::Log::SetVerbosity(eprosima::fastdds::dds::Log::Kind::Info); + } + + ~SHMTransportTests() + { + eprosima::fastdds::dds::Log::Flush(); + eprosima::fastdds::dds::Log::KillThread(); + } + + SharedMemTransportDescriptor descriptor; +}; + +class SHMRingBuffer : public ::testing::Test +{ +protected: + + struct MyData + { + uint32_t thread_number; + uint32_t counter; + }; + + std::unique_ptr::Cell[]> cells_; + std::unique_ptr > ring_buffer_; + uint32_t buffer_size_; + + SHMRingBuffer() + : buffer_size_((std::max)((unsigned int) 4, std::thread::hardware_concurrency())) + { + } + + void SetUp() override + { + cells_ = std::unique_ptr::Cell[]>( + new MultiProducerConsumerRingBuffer::Cell[buffer_size_]); + + ring_buffer_ = + std::unique_ptr >(new MultiProducerConsumerRingBuffer( + cells_.get(), buffer_size_)); + } + + void TearDown() override + { + ring_buffer_.reset(); + cells_.reset(); + } +}; + +class SHMRingBufferMultiThread + : public SHMRingBuffer, + public testing::WithParamInterface > +{ + +}; + +TEST_F(SHMRingBuffer, test_read_write_bounds) +{ + auto listener = ring_buffer_->register_listener(); + + for (uint32_t i = 0; ipush({0,i}); + } + + ASSERT_THROW(ring_buffer_->push({0,(std::numeric_limits::max)()}), std::exception); + + for (uint32_t i = 0; ihead()->data().counter, i); + listener->pop(); + } + + ASSERT_THROW(listener->pop(), std::exception); +} + +TEST_F(SHMRingBuffer, circular_pointer) +{ + uint32_t r = 0; + uint32_t w = 0; + uint32_t i = 0; + + auto listener = ring_buffer_->register_listener(); + + // Buffer full + for (; ipush({0,w++}); + } + + i = (i % buffer_size_); + ASSERT_EQ(i, 0u); + + // Another cicle + for (; ihead()->data().counter, r++); + listener->pop(); + ring_buffer_->push({0,w++}); + } + + i = (i % buffer_size_); + ASSERT_EQ(i, 0u); + + // Flush the buffer + for (; ihead()->data().counter, r++); + listener->pop(); + } + + // Is empty + ASSERT_THROW(listener->pop(), std::exception); +} + +TEST_F(SHMRingBuffer, one_listener_reads_all) +{ + auto listener1 = ring_buffer_->register_listener(); + auto listener2 = ring_buffer_->register_listener(); + + for (uint32_t i=0; ipush({0,i}); + } + + for (uint32_t i=0; ipop(); + } + + ASSERT_EQ(listener1->head(), nullptr); +} + +TEST_F(SHMRingBuffer, listeners_register_unregister) +{ + // 0 Must be discarted because no listeners + ring_buffer_->push({0,0}); + + auto listener1 = ring_buffer_->register_listener(); + // 1 Must be only read by listener1 + ring_buffer_->push({0,1}); + + auto listener2 = ring_buffer_->register_listener(); + // 2 Must be read by listener1 and listener 2 + ring_buffer_->push({0,2}); + + // 3 + ring_buffer_->push({0,3}); + + ASSERT_EQ(listener1->head()->data().counter, 1u); + ASSERT_EQ(listener2->head()->data().counter, 2u); + + listener1->pop(); // 1:1 + ASSERT_EQ(listener1->head()->data().counter, 2u); + + listener2->pop(); // 2:2 + + // Listener 1 must decrease ref_counter of 2 and 3 in its destructor + listener1.reset(); + + // 4 + ring_buffer_->push({0,4}); + + ASSERT_EQ(listener2->head()->data().counter, 3u); + listener2->pop(); // 3 + + ASSERT_EQ(listener2->head()->data().counter, 4u); + listener2->pop(); +} + +TEST_P(SHMRingBufferMultiThread, multiple_writers_listeners) +{ + const uint32_t elements_to_push = buffer_size_ * std::get<1>(GetParam()); + std::vector threads; + std::atomic ready_listeners; + ready_listeners.store(0); + + uint32_t num_listeners_writters = std::get<0>(GetParam()); + uint32_t num_register_unregister = std::get<2>(GetParam()); + + for (uint32_t i = 0; i < num_listeners_writters; i++) + { + // Listeners + threads.emplace_back( + std::thread( + [&]() + { + std::vector read_counters(num_listeners_writters, (std::numeric_limits::max)()); + MultiProducerConsumerRingBuffer::Cell* cell; + + auto listener = ring_buffer_->register_listener(); + ready_listeners.fetch_add(1); + + do + { + // poll until there's data + while (nullptr == (cell = listener->head())); + + ASSERT_EQ(++read_counters[cell->data().thread_number], cell->data().counter); + + listener->pop(); + + } while (cell->data().counter != elements_to_push-1); + + })); + } + + // Wait until all listeners ready + while (ready_listeners.load(std::memory_order_relaxed) != num_listeners_writters) + { + std::this_thread::yield(); + } + + for (uint32_t i = 0; i < num_listeners_writters; i++) + { + // Writers + threads.emplace_back( + std::thread( + [&](uint32_t thread_number) + { + for (uint32_t c = 0; c < elements_to_push; c++) + { + bool success = false; + while (!success) + { + try + { + ring_buffer_->push({thread_number, c}); + success = true; + } + catch (const std::exception&) + { + } + } + } + }, i)); + } + + for (uint32_t i = 0; i < num_register_unregister; i++) + { + // Register-Unregister + threads.emplace_back( + std::thread( + [&]() + { + auto listener = ring_buffer_->register_listener(); + + // Reads two times the size values + for (uint32_t i=0; ihead()); + listener->pop(); + } + + // Unregister + listener.reset(); + + })); + } + + for (auto& thread : threads) + { + if (thread.joinable()) + { + thread.join(); + } + } +} + +TEST_F(SHMTransportTests, locators_with_kind_16_supported) +{ + // Given + SharedMemTransport transportUnderTest(descriptor); + ASSERT_TRUE(transportUnderTest.init()); + + Locator_t supportedLocator; + supportedLocator.kind = LOCATOR_KIND_SHM; + Locator_t unsupportedLocatorTcpv4; + unsupportedLocatorTcpv4.kind = LOCATOR_KIND_TCPv4; + Locator_t unsupportedLocatorTcpv6; + unsupportedLocatorTcpv6.kind = LOCATOR_KIND_TCPv6; + Locator_t unsupportedLocatorUdpv4; + unsupportedLocatorUdpv4.kind = LOCATOR_KIND_UDPv4; + Locator_t unsupportedLocatorUdpv6; + unsupportedLocatorUdpv6.kind = LOCATOR_KIND_UDPv6; + + // Then + ASSERT_TRUE(transportUnderTest.IsLocatorSupported(supportedLocator)); + ASSERT_FALSE(transportUnderTest.IsLocatorSupported(unsupportedLocatorTcpv4)); + ASSERT_FALSE(transportUnderTest.IsLocatorSupported(unsupportedLocatorTcpv6)); + ASSERT_FALSE(transportUnderTest.IsLocatorSupported(unsupportedLocatorUdpv4)); + ASSERT_FALSE(transportUnderTest.IsLocatorSupported(unsupportedLocatorUdpv6)); +} + +TEST_F(SHMTransportTests, opening_and_closing_input_channel) +{ + // Given + SharedMemTransport transportUnderTest(descriptor); + ASSERT_TRUE(transportUnderTest.init()); + + Locator_t genericInputChannelLocator; + genericInputChannelLocator.kind = LOCATOR_KIND_SHM; + genericInputChannelLocator.port = g_input_port; // listen port + + // Then + ASSERT_FALSE (transportUnderTest.IsInputChannelOpen(genericInputChannelLocator)); + ASSERT_TRUE (transportUnderTest.OpenInputChannel(genericInputChannelLocator, nullptr, 0x00FF)); + ASSERT_TRUE (transportUnderTest.IsInputChannelOpen(genericInputChannelLocator)); + ASSERT_TRUE (transportUnderTest.CloseInputChannel(genericInputChannelLocator)); + ASSERT_FALSE (transportUnderTest.IsInputChannelOpen(genericInputChannelLocator)); + ASSERT_FALSE (transportUnderTest.CloseInputChannel(genericInputChannelLocator)); +} + +TEST_F(SHMTransportTests, closing_input_channel_leaves_other_channels_unclosed) +{ + // Given + SharedMemTransport transportUnderTest(descriptor); + ASSERT_TRUE(transportUnderTest.init()); + + Locator_t genericInputChannelLocator; + genericInputChannelLocator.kind = LOCATOR_KIND_SHM; + genericInputChannelLocator.port = g_input_port; // listen port + + Locator_t otherInputChannelLocator; + otherInputChannelLocator.kind = LOCATOR_KIND_SHM; + otherInputChannelLocator.port = g_input_port + 1; // listen port + + // Then + ASSERT_TRUE (transportUnderTest.OpenInputChannel(genericInputChannelLocator, nullptr, 0x00FF)); + ASSERT_TRUE (transportUnderTest.OpenInputChannel(otherInputChannelLocator, nullptr, 0x00FF)); + ASSERT_TRUE (transportUnderTest.IsInputChannelOpen(genericInputChannelLocator)); + ASSERT_TRUE (transportUnderTest.IsInputChannelOpen(otherInputChannelLocator)); + ASSERT_TRUE (transportUnderTest.CloseInputChannel(genericInputChannelLocator)); + ASSERT_FALSE (transportUnderTest.IsInputChannelOpen(genericInputChannelLocator)); + ASSERT_TRUE (transportUnderTest.IsInputChannelOpen(otherInputChannelLocator)); + ASSERT_FALSE (transportUnderTest.CloseInputChannel(genericInputChannelLocator)); + ASSERT_FALSE (transportUnderTest.IsInputChannelOpen(genericInputChannelLocator)); + ASSERT_TRUE (transportUnderTest.IsInputChannelOpen(otherInputChannelLocator)); + ASSERT_TRUE (transportUnderTest.CloseInputChannel(otherInputChannelLocator)); + ASSERT_FALSE (transportUnderTest.IsInputChannelOpen(genericInputChannelLocator)); + ASSERT_FALSE (transportUnderTest.IsInputChannelOpen(otherInputChannelLocator)); +} + +TEST_F(SHMTransportTests, RemoteToMainLocal_returns_input_locator) +{ + // Given + SharedMemTransport transportUnderTest(descriptor); + ASSERT_TRUE(transportUnderTest.init()); + + Locator_t remote_locator; + remote_locator.kind = LOCATOR_KIND_SHM; + remote_locator.port = g_default_port; + + // When + Locator_t mainLocalLocator = transportUnderTest.RemoteToMainLocal(remote_locator); + + // Then + ASSERT_EQ(mainLocalLocator, remote_locator); +} + +TEST_F(SHMTransportTests, transform_remote_locator_returns_input_locator) +{ + // Given + SharedMemTransport transportUnderTest(descriptor); + ASSERT_TRUE(transportUnderTest.init()); + + Locator_t remote_locator; + remote_locator.kind = LOCATOR_KIND_SHM; + remote_locator.port = g_default_port; + + // Then + Locator_t otherLocator; + ASSERT_TRUE(transportUnderTest.transform_remote_locator(remote_locator, otherLocator)); + ASSERT_EQ(otherLocator, remote_locator); +} + +TEST_F(SHMTransportTests, all_shared_mem_locators_are_local) +{ + // Given + SharedMemTransport transportUnderTest(descriptor); + ASSERT_TRUE(transportUnderTest.init()); + + Locator_t shared_mem_locator; + shared_mem_locator.kind = LOCATOR_KIND_SHM; + shared_mem_locator.port = g_default_port; + + // Then + ASSERT_TRUE(transportUnderTest.is_local_locator(shared_mem_locator)); +} + +TEST_F(SHMTransportTests, match_if_port_and_address_matches) +{ + // Given + SharedMemTransport transportUnderTest(descriptor); + ASSERT_TRUE(transportUnderTest.init()); + + Locator_t locatorAlpha; + locatorAlpha.kind = LOCATOR_KIND_SHM; + locatorAlpha.port = g_default_port; + + Locator_t locatorBeta; + locatorBeta.kind = LOCATOR_KIND_SHM; + locatorBeta.port = g_default_port; + + // Then + ASSERT_TRUE(transportUnderTest.DoInputLocatorsMatch(locatorAlpha, locatorBeta)); + + locatorBeta.port = g_default_port + 1; + // Then + ASSERT_FALSE(transportUnderTest.DoInputLocatorsMatch(locatorAlpha, locatorBeta)); +} + +TEST_F(SHMTransportTests, send_and_receive_between_ports) +{ + SharedMemTransport transportUnderTest(descriptor); + ASSERT_TRUE(transportUnderTest.init()); + + Locator_t unicastLocator; + unicastLocator.kind = LOCATOR_KIND_SHM; + unicastLocator.port = g_default_port; + + Locator_t outputChannelLocator; + outputChannelLocator.kind = LOCATOR_KIND_SHM; + outputChannelLocator.port = g_default_port + 1; + + Semaphore sem; + MockReceiverResource receiver(transportUnderTest, unicastLocator); + MockMessageReceiver* msg_recv = dynamic_cast(receiver.CreateMessageReceiver()); + + eprosima::fastrtps::rtps::SendResourceList send_resource_list; + ASSERT_TRUE(transportUnderTest.OpenOutputChannel(send_resource_list, outputChannelLocator)); + ASSERT_FALSE(send_resource_list.empty()); + ASSERT_TRUE(transportUnderTest.IsInputChannelOpen(unicastLocator)); + octet message[5] = { 'H','e','l','l','o' }; + + std::function recCallback = [&]() + { + EXPECT_EQ(memcmp(message, msg_recv->data, 5), 0); + sem.post(); + }; + msg_recv->setCallback(recCallback); + + LocatorList_t locator_list; + locator_list.push_back(unicastLocator); + + auto sendThreadFunction = [&]() + { + Locators locators_begin(locator_list.begin()); + Locators locators_end(locator_list.end()); + + EXPECT_TRUE(send_resource_list.at(0)->send(message, 5, &locators_begin, &locators_end, + (std::chrono::steady_clock::now()+ std::chrono::microseconds(100)))); + }; + + std::unique_ptr sender_thread; + sender_thread.reset(new std::thread(sendThreadFunction)); + + sem.wait(); + sender_thread->join(); +} + +TEST_F(SHMTransportTests, port_and_segment_overflow_fail) +{ + SharedMemTransportDescriptor my_descriptor; + + my_descriptor.port_overflow_policy(SharedMemTransportDescriptor::OverflowPolicy::FAIL); + my_descriptor.segment_overflow_policy(SharedMemTransportDescriptor::OverflowPolicy::FAIL); + my_descriptor.segment_size(16, 16); + my_descriptor.port_queue_capacity(4); + + SharedMemTransport transportUnderTest(my_descriptor); + ASSERT_TRUE(transportUnderTest.init()); + + Locator_t unicastLocator; + unicastLocator.kind = LOCATOR_KIND_SHM; + unicastLocator.port = g_default_port; + + Semaphore sem; + MockReceiverResource receiver(transportUnderTest, unicastLocator); + MockMessageReceiver* msg_recv = dynamic_cast(receiver.CreateMessageReceiver()); + + uint32_t messages_received = 0; + std::function recCallback = [&]() + { + // At the second message block + if (messages_received > 0) + { + messages_received++; + sem.wait(); + } + else + { + messages_received++; + } + }; + msg_recv->setCallback(recCallback); + + Locator_t outputChannelLocator; + outputChannelLocator.kind = LOCATOR_KIND_SHM; + outputChannelLocator.port = g_default_port + 1; + + eprosima::fastrtps::rtps::SendResourceList send_resource_list; + ASSERT_TRUE(transportUnderTest.OpenOutputChannel(send_resource_list, outputChannelLocator)); + ASSERT_FALSE(send_resource_list.empty()); + octet message[4] = { 'H','e','l','l'}; + + LocatorList_t locator_list; + locator_list.push_back(unicastLocator); + + { + Locators locators_begin(locator_list.begin()); + Locators locators_end(locator_list.end()); + // Internally the segment is bigger than "my_descriptor.segment_size" so a bigger buffer is tried + // to cause segment overflow + octet message_big[4096] = { 'H','e','l','l'}; + + ASSERT_FALSE(send_resource_list.at(0)->send(message_big, sizeof(message_big), &locators_begin, &locators_end, + (std::chrono::steady_clock::now()+ std::chrono::microseconds(100)))); + } + + // At least 4 msgs of 4 bytes are allowed + for (int i=0; i<4; i++) + { + Locators locators_begin(locator_list.begin()); + Locators locators_end(locator_list.end()); + + // At least 4 msgs of 4 bytes are allowed + ASSERT_TRUE(send_resource_list.at(0)->send(message, sizeof(message), &locators_begin, &locators_end, + (std::chrono::steady_clock::now() + std::chrono::microseconds(100)))); + } + + // Wait until the receiver get the first message + while (messages_received == 0) + { + std::this_thread::yield(); + } + + // The receiver has poped a message so now 3 messages are in the + // port's queue + + // Push a 4th should go well + { + Locators locators_begin(locator_list.begin()); + Locators locators_end(locator_list.end()); + + ASSERT_TRUE(send_resource_list.at(0)->send(message, sizeof(message), &locators_begin, &locators_end, + (std::chrono::steady_clock::now()+ std::chrono::microseconds(100)))); + } + + // Push a 5th will cause port overflow + { + Locators locators_begin(locator_list.begin()); + Locators locators_end(locator_list.end()); + + ASSERT_FALSE(send_resource_list.at(0)->send(message, sizeof(message), &locators_begin, &locators_end, + (std::chrono::steady_clock::now()+ std::chrono::microseconds(100)))); + } + + sem.disable(); +} + +TEST_F(SHMTransportTests, port_and_segment_overflow_discard) +{ + SharedMemTransportDescriptor my_descriptor; + + my_descriptor.port_overflow_policy(SharedMemTransportDescriptor::OverflowPolicy::DISCARD); + my_descriptor.segment_overflow_policy(SharedMemTransportDescriptor::OverflowPolicy::DISCARD); + my_descriptor.segment_size(16, 16); + my_descriptor.port_queue_capacity(4); + + SharedMemTransport transportUnderTest(my_descriptor); + ASSERT_TRUE(transportUnderTest.init()); + + Locator_t unicastLocator; + unicastLocator.kind = LOCATOR_KIND_SHM; + unicastLocator.port = g_default_port; + + Semaphore sem; + MockReceiverResource receiver(transportUnderTest, unicastLocator); + MockMessageReceiver* msg_recv = dynamic_cast(receiver.CreateMessageReceiver()); + + bool is_first_message_received = false; + std::function recCallback = [&]() + { + is_first_message_received = true; + sem.wait(); + }; + msg_recv->setCallback(recCallback); + + Locator_t outputChannelLocator; + outputChannelLocator.kind = LOCATOR_KIND_SHM; + outputChannelLocator.port = g_default_port + 1; + + eprosima::fastrtps::rtps::SendResourceList send_resource_list; + ASSERT_TRUE(transportUnderTest.OpenOutputChannel(send_resource_list, outputChannelLocator)); + ASSERT_FALSE(send_resource_list.empty()); + octet message[4] = { 'H','e','l','l'}; + + LocatorList_t locator_list; + locator_list.push_back(unicastLocator); + + { + Locators locators_begin(locator_list.begin()); + Locators locators_end(locator_list.end()); + // Internally the segment is bigger than "my_descriptor.segment_size" so a bigger buffer is tried + // to cause segment overflow + octet message_big[4096] = { 'H','e','l','l'}; + + EXPECT_TRUE(send_resource_list.at(0)->send(message_big, sizeof(message_big), &locators_begin, &locators_end, + (std::chrono::steady_clock::now()+ std::chrono::microseconds(100)))); + } + + // At least 4 msgs of 4 bytes are allowed + for (int i=0; i<4; i++) + { + Locators locators_begin(locator_list.begin()); + Locators locators_end(locator_list.end()); + + // At least 4 msgs of 4 bytes are allowed + EXPECT_TRUE(send_resource_list.at(0)->send(message, sizeof(message), &locators_begin, &locators_end, + (std::chrono::steady_clock::now()+ std::chrono::microseconds(100)))); + } + + // Wait until the receiver get the first message + while (!is_first_message_received) + { + std::this_thread::yield(); + } + + // The receiver has poped a message so now 3 messages are in the + // port's queue + + // Push a 4th should go well + { + Locators locators_begin(locator_list.begin()); + Locators locators_end(locator_list.end()); + + EXPECT_TRUE(send_resource_list.at(0)->send(message, sizeof(message), &locators_begin, &locators_end, + (std::chrono::steady_clock::now()+ std::chrono::microseconds(100)))); + } + + // Push a 5th will not cause overflow + { + Locators locators_begin(locator_list.begin()); + Locators locators_end(locator_list.end()); + + EXPECT_TRUE(send_resource_list.at(0)->send(message, sizeof(message), &locators_begin, &locators_end, + (std::chrono::steady_clock::now()+ std::chrono::microseconds(100)))); + } + + sem.disable(); +} + +TEST_F(SHMTransportTests, port_mutex_deadlock_recover) +{ + const std::string domain_name("SHMTransportTests"); + + SharedMemGlobal shared_mem_global(domain_name); + MockPortSharedMemGlobal port_mocker; + + port_mocker.remove_port_mutex(domain_name, 0); + + auto global_port = shared_mem_global.open_port(0, 1, 1000); + + Semaphore sem_lock_done; + Semaphore sem_end_thread_locker; + std::thread thread_locker([&] + { + // lock has to be done in another thread because + // boost::inteprocess_named_mutex and interprocess_mutex are recursive in Win32 + auto port_mutex = port_mocker.get_port_mutex(domain_name, 0); + ASSERT_TRUE(port_mutex->try_lock()); + sem_lock_done.post(); + sem_end_thread_locker.wait(); + } + ); + + sem_lock_done.wait(); + + auto port_mutex = port_mocker.get_port_mutex(domain_name, 0); + ASSERT_FALSE(port_mutex->try_lock()); + + auto global_port2 = shared_mem_global.open_port(0, 1, 1000); + + ASSERT_NO_THROW(global_port2->healthy_check(1000)); + + sem_end_thread_locker.post(); + thread_locker.join(); +} + +TEST_F(SHMTransportTests, empty_cv_mutex_deadlocked_try_push) +{ + const std::string domain_name("SHMTransportTests"); + + SharedMemGlobal shared_mem_global(domain_name); + MockPortSharedMemGlobal port_mocker; + + auto global_port = shared_mem_global.open_port(0, 1, 1000); + + Semaphore sem_lock_done; + Semaphore sem_end_thread_locker; + std::thread thread_locker([&] + { + // lock has to be done in another thread because + // boost::inteprocess_named_mutex and interprocess_mutex are recursive in Win32 + ASSERT_TRUE(port_mocker.lock_empty_cv_mutex(*global_port)); + sem_lock_done.post(); + sem_end_thread_locker.wait(); + } + ); + + sem_lock_done.wait(); + + ASSERT_FALSE(port_mocker.lock_empty_cv_mutex(*global_port)); + + bool listerner_active; + SharedMemGlobal::BufferDescriptor foo; + ASSERT_THROW(global_port->try_push(foo, &listerner_active), std::exception); + + ASSERT_THROW(global_port->healthy_check(1000), std::exception); + + sem_end_thread_locker.post(); + thread_locker.join(); +} + +TEST_F(SHMTransportTests, dead_listener_port_recover) +{ + const std::string domain_name("SHMTransportTests"); + + SharedMemGlobal shared_mem_global(domain_name); + auto deadlocked_port = shared_mem_global.open_port(0, 1, 1000); + auto deadlocked_listener = deadlocked_port->create_listener(); + + // Simulates a deadlocked wait_pop + std::atomic_bool is_listener_closed(false); + std::thread thread_wait_deadlock([&] + { + MockPortSharedMemGlobal port_mocker; + port_mocker.wait_pop_deadlock(*deadlocked_port, *deadlocked_listener, is_listener_closed); + (void)port_mocker; // Removes an inexplicable warning when compiling with VC(v140 toolset) + }); + + // Assert the thread is waiting + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Open the deadlocked port + auto port = shared_mem_global.open_port(0, 1, 1000); + auto listener = port->create_listener(); + bool listerners_active; + SharedMemSegment::Id random_id; + random_id.generate(); + SharedMemGlobal::BufferDescriptor foo = {random_id, 0}; + ASSERT_TRUE(port->try_push(foo, &listerners_active)); + ASSERT_TRUE(listerners_active); + ASSERT_TRUE(listener->head() != nullptr); + ASSERT_TRUE(listener->head()->data().source_segment_id == random_id); + ASSERT_TRUE(listener->pop()); + + deadlocked_port->close_listener(&is_listener_closed); + thread_wait_deadlock.join(); +} + +/*TEST_F(SHMTransportTests, simple_latency) + { + int num_samples = 1000; + char data[16] = { "" }; + + std::thread thread_subscriber([&] + { + SharedMemManager shared_mem_manager("SHMTransportTests"); + auto port_pub_to_sub = shared_mem_manager.open_port(0, 64, 1000); + auto port_sub_to_pub = shared_mem_manager.open_port(1, 64, 1000); + auto listener_sub = port_pub_to_sub->create_listener(); + + auto segment = shared_mem_manager.create_segment(sizeof(data)*64,64); + int i = num_samples; + + do + { + auto recv_sample = listener_sub->pop(); + + auto sample_to_send = segment->alloc_buffer(sizeof(data)); + memcpy(sample_to_send->data(), data, sizeof(data)); + ASSERT_TRUE(port_sub_to_pub->try_push(sample_to_send)); + } while (--i); + }); + + std::thread thread_publisher([&] + { + SharedMemManager shared_mem_manager("SHMTransportTests"); + auto port_pub_to_sub = shared_mem_manager.open_port(0, 64, 1000); + auto port_sub_to_pub = shared_mem_manager.open_port(1, 64, 1000); + auto listener_pub = port_sub_to_pub->create_listener(); + + auto segment = shared_mem_manager.create_segment(sizeof(data) * 64, 64); + int i = num_samples; + + std::chrono::high_resolution_clock::rep total_times = 0; + std::chrono::high_resolution_clock::rep min_time = (std::numeric_limits::max)(); + std::chrono::high_resolution_clock::rep max_time = (std::numeric_limits::min)(); + + while (i--) + { + auto t0 = std::chrono::high_resolution_clock::now(); + + auto sample_to_send = segment->alloc_buffer(sizeof(data)); + memcpy(sample_to_send->data(), data, sizeof(data)); + ASSERT_TRUE(port_pub_to_sub->try_push(sample_to_send)); + sample_to_send.reset(); + + auto recv_sample = listener_pub->pop(); + + auto t1 = std::chrono::high_resolution_clock::now(); + + auto t = std::chrono::duration_cast(t1 - t0).count(); + if (t < min_time) + { + min_time = t; + } + if (t > max_time) + { + max_time = t; + } + total_times += t; + } + + printf("LatencyTest for %d samples. Avg = %.3f(us) Min = %.3f(us) Max = %.3f(us)\n", num_samples, total_times / (num_samples*1000.0), min_time/1000.0, max_time/1000.0); + }); + + thread_subscriber.join(); + thread_publisher.join(); + }*/ + +/*TEST_F(SHMTransportTests, simple_latency2) + { + int num_samples = 1000; + octet data[16] = { "" }; + + Locator_t sub_locator; + sub_locator.kind = LOCATOR_KIND_SHM; + sub_locator.port = 0; + + Locator_t pub_locator; + pub_locator.kind = LOCATOR_KIND_SHM; + pub_locator.port = 1; + + SharedMemTransportDescriptor my_descriptor; + + std::thread thread_subscriber([&] + { + SharedMemTransport transport(my_descriptor); + ASSERT_TRUE(transport.init()); + + Semaphore sem; + MockReceiverResource receiver(transport, sub_locator); + MockMessageReceiver* msg_recv = dynamic_cast(receiver.CreateMessageReceiver()); + + int samples_to_receive = num_samples; + + LocatorList_t send_locators_list; + send_locators_list.push_back(pub_locator); + + eprosima::fastrtps::rtps::SendResourceList send_resource_list; + ASSERT_TRUE(transport.OpenOutputChannel(send_resource_list, pub_locator)); + + std::function sub_callback = [&]() + { + Locators locators_begin(send_locators_list.begin()); + Locators locators_end(send_locators_list.end()); + + EXPECT_TRUE(send_resource_list.at(0)->send(data, sizeof(data), &locators_begin, &locators_end, + (std::chrono::steady_clock::now() + std::chrono::milliseconds(100)))); + + if (--samples_to_receive == 0) + { + sem.post(); + } + }; + + msg_recv->setCallback(sub_callback); + + sem.wait(); + }); + + std::thread thread_publisher([&] + { + SharedMemTransport transport(my_descriptor); + ASSERT_TRUE(transport.init()); + + Semaphore sem; + MockReceiverResource receiver(transport, pub_locator); + MockMessageReceiver* msg_recv = dynamic_cast(receiver.CreateMessageReceiver()); + + int samples_sent = 0; + + LocatorList_t send_locators_list; + send_locators_list.push_back(sub_locator); + + eprosima::fastrtps::rtps::SendResourceList send_resource_list; + ASSERT_TRUE(transport.OpenOutputChannel(send_resource_list, sub_locator)); + + std::chrono::high_resolution_clock::rep total_times = 0; + std::chrono::high_resolution_clock::rep min_time = (std::numeric_limits::max)(); + std::chrono::high_resolution_clock::rep max_time = (std::numeric_limits::min)(); + + auto t0 = std::chrono::high_resolution_clock::now(); + + Locators locators_begin(send_locators_list.begin()); + Locators locators_end(send_locators_list.end()); + + EXPECT_TRUE(send_resource_list.at(0)->send(data, sizeof(data), &locators_begin, &locators_end, + (std::chrono::steady_clock::now() + std::chrono::milliseconds(100)))); + + std::function pub_callback = [&]() + { + if (++samples_sent < num_samples) + { + auto t1 = std::chrono::high_resolution_clock::now(); + + auto t = std::chrono::duration_cast(t1 - t0).count(); + if (t < min_time) + { + min_time = t; + } + if (t > max_time) + { + max_time = t; + } + total_times += t; + + t0 = std::chrono::high_resolution_clock::now(); + + Locators locators_begin(send_locators_list.begin()); + Locators locators_end(send_locators_list.end()); + + EXPECT_TRUE(send_resource_list.at(0)->send(data, sizeof(data), &locators_begin, &locators_end, + (std::chrono::steady_clock::now() + std::chrono::milliseconds(100)))); + } + else + { + sem.post(); + } + }; + + msg_recv->setCallback(pub_callback); + + sem.wait(); + + printf("LatencyTest for %d samples. Avg = %.3f(us) Min = %.3f(us) Max = %.3f(us)\n", num_samples, total_times / (num_samples * 1000.0), min_time / 1000.0, max_time / 1000.0); + }); + + thread_subscriber.join(); + thread_publisher.join(); + }*/ + +/*TEST_F(SHMTransportTests, simple_throughput) + { + const size_t sample_size = 1024; + int num_samples_per_batch = 100000; + + std::atomic samples_received(0); + + octet sample_data[sample_size]; + memset(sample_data, 0, sizeof(sample_data)); + + Locator_t sub_locator; + sub_locator.kind = LOCATOR_KIND_SHM; + sub_locator.port = 0; + + Locator_t pub_locator; + pub_locator.kind = LOCATOR_KIND_SHM; + pub_locator.port = 1; + + SharedMemTransportDescriptor my_descriptor; + + my_descriptor.port_queue_capacity = num_samples_per_batch; + my_descriptor.segment_size = sample_size * num_samples_per_batch; + + // Subscriber + + SharedMemTransport sub_transport(my_descriptor); + ASSERT_TRUE(sub_transport.init()); + + MockReceiverResource sub_receiver(sub_transport, sub_locator); + MockMessageReceiver* sub_msg_recv = dynamic_cast(sub_receiver.CreateMessageReceiver()); + + std::function sub_callback = [&]() + { + samples_received.fetch_add(1); + }; + + sub_msg_recv->setCallback(sub_callback); + + // Publisher + + SharedMemTransport pub_transport(my_descriptor); + ASSERT_TRUE(pub_transport.init()); + + LocatorList_t send_locators_list; + send_locators_list.push_back(sub_locator); + + eprosima::fastrtps::rtps::SendResourceList send_resource_list; + ASSERT_TRUE(pub_transport.OpenOutputChannel(send_resource_list, sub_locator)); + + auto t0 = std::chrono::high_resolution_clock::now(); + + for (int i=0; isend(sample_data, sizeof(sample_data), &locators_begin, &locators_end, + (std::chrono::steady_clock::now() + std::chrono::milliseconds(100)))); + } + + auto t1 = std::chrono::high_resolution_clock::now(); + + auto real_samples_received = samples_received.load(); + printf("Samples [sent,received] [%d,%d] send_time_per_sample %.3f(us)\n" + , num_samples_per_batch + , real_samples_received + , std::chrono::duration_cast(t1-t0).count() / (num_samples_per_batch*1000.0)); + }*/ + +// This test is linux only +/*TEST_F(SHMTransportTests, simple_throughput_inter) + { + const size_t sample_size = 1024; + int num_samples_per_batch = 100000; + + std::atomic samples_received(0); + + octet sample_data[sample_size]; + memset(sample_data, 0, sizeof(sample_data)); + + Locator_t sub_locator; + sub_locator.kind = LOCATOR_KIND_SHM; + sub_locator.port = 0; + + Locator_t pub_locator; + pub_locator.kind = LOCATOR_KIND_SHM; + pub_locator.port = 1; + + SharedMemTransportDescriptor my_descriptor; + + my_descriptor.port_queue_capacity = num_samples_per_batch; + my_descriptor.segment_size = sample_size * num_samples_per_batch; + + // Child + if(fork() == 0) + { + Semaphore sem_end_subscriber; + + // Subscriber + SharedMemTransport sub_transport(my_descriptor); + ASSERT_TRUE(sub_transport.init()); + + MockReceiverResource sub_receiver(sub_transport, sub_locator); + MockMessageReceiver* sub_msg_recv = dynamic_cast(sub_receiver.CreateMessageReceiver()); + + std::function sub_callback = [&]() + { + if(samples_received.fetch_add(1)+1 == num_samples_per_batch) + { + sem_end_subscriber.post(); + } + }; + + sub_msg_recv->setCallback(sub_callback); + + sem_end_subscriber.wait(); + + printf("Samples [received] [%d]\n", samples_received.load()); + } + else + { + // Give time to the subscriber to listen + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Publisher + SharedMemTransport pub_transport(my_descriptor); + ASSERT_TRUE(pub_transport.init()); + + LocatorList_t send_locators_list; + send_locators_list.push_back(sub_locator); + + SendResourceList send_resource_list; + ASSERT_TRUE(pub_transport.OpenOutputChannel(send_resource_list, sub_locator)); + + auto t0 = std::chrono::high_resolution_clock::now(); + + for (int i=0; isend(sample_data, sizeof(sample_data), &locators_begin, &locators_end, + (std::chrono::steady_clock::now() + std::chrono::milliseconds(100)))); + } + + auto t1 = std::chrono::high_resolution_clock::now(); + + printf("Samples [sent] [%d] send_time_per_sample %.3f(us)\n" + , num_samples_per_batch + , std::chrono::duration_cast(t1-t0).count() / (num_samples_per_batch*1000.0)); + } + }*/ + +/*INSTANTIATE_TEST_CASE_P( + SHMTransportTests, + SHMRingBufferMultiThread, + testing::Values( + std::make_tuple( + (std::max)((unsigned int)1, std::thread::hardware_concurrency()/2), 100000, 0), + std::make_tuple( + (std::max)((unsigned int)1, std::thread::hardware_concurrency())*2, 100,0), + std::make_tuple( + (std::max)((unsigned int)1, std::thread::hardware_concurrency()/2), 100000, std::thread::hardware_concurrency()/2), + std::make_tuple( + (std::max)((unsigned int)1, std::thread::hardware_concurrency())*2, 100,std::thread::hardware_concurrency()) + ) + );*/ + +TEST_F(SHMTransportTests, dump_file) +{ + std::string log_file = "shm_transport_dump.txt"; + std::remove(log_file.c_str()); + + { + SharedMemTransportDescriptor shm_descriptor; + + shm_descriptor.rtps_dump_file(log_file); + + SharedMemTransport transportUnderTest(shm_descriptor); + ASSERT_TRUE(transportUnderTest.init()); + + Locator_t unicastLocator; + unicastLocator.kind = LOCATOR_KIND_SHM; + unicastLocator.port = g_default_port; + + Locator_t outputChannelLocator; + outputChannelLocator.kind = LOCATOR_KIND_SHM; + outputChannelLocator.port = g_default_port + 1; + + Semaphore sem; + MockReceiverResource receiver(transportUnderTest, unicastLocator); + MockMessageReceiver *msg_recv = dynamic_cast(receiver.CreateMessageReceiver()); + + eprosima::fastrtps::rtps::SendResourceList send_resource_list; + ASSERT_TRUE(transportUnderTest.OpenOutputChannel(send_resource_list, outputChannelLocator)); + ASSERT_FALSE(send_resource_list.empty()); + ASSERT_TRUE(transportUnderTest.IsInputChannelOpen(unicastLocator)); + octet message[5] = { 'H','e','l','l','o' }; + + std::function recCallback = [&]() + { + EXPECT_EQ(memcmp(message, msg_recv->data, 5), 0); + sem.post(); + }; + msg_recv->setCallback(recCallback); + + LocatorList_t locator_list; + locator_list.push_back(unicastLocator); + + auto sendThreadFunction = [&]() + { + Locators locators_begin(locator_list.begin()); + Locators locators_end(locator_list.end()); + + EXPECT_TRUE(send_resource_list.at(0)->send(message, 5, &locators_begin, &locators_end, + (std::chrono::steady_clock::now()+ std::chrono::microseconds(1000)))); + }; + + std::unique_ptr sender_thread; + sender_thread.reset(new std::thread(sendThreadFunction)); + + sem.wait(); + sender_thread->join(); + } + + { + std::ifstream dump_file(log_file.c_str()); + std::string dump_text((std::istreambuf_iterator(dump_file)), + std::istreambuf_iterator()); + + ASSERT_EQ(dump_text.length(), 312u); + ASSERT_EQ(dump_text.c_str()[308], '6'); + ASSERT_EQ(dump_text.c_str()[309], 'f'); + ASSERT_EQ(dump_text.c_str()[310], 10); + ASSERT_EQ(dump_text.c_str()[311], 10); + } + + std::remove(log_file.c_str()); +} + +int main( + int argc, + char** argv) +{ + eprosima::fastdds::dds::Log::SetVerbosity(eprosima::fastdds::dds::Log::Info); + g_default_port = get_port(4000); + g_output_port = get_port(5000); + g_input_port = get_port(5010); + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/unittest/transport/TCPv6Tests.cpp b/test/unittest/transport/TCPv6Tests.cpp index 95298c055ba..a5060ac645d 100644 --- a/test/unittest/transport/TCPv6Tests.cpp +++ b/test/unittest/transport/TCPv6Tests.cpp @@ -149,7 +149,7 @@ TEST_F(TCPv6Tests, opening_and_closing_input_channel) NetworkFactory factory; factory.RegisterTransport(descriptor); std::vector> receivers; - factory.BuildReceiverResources(multicastFilterLocator, 0x8FFF, receivers); + factory.BuildReceiverResources(multicastFilterLocator, receivers, 0x8FFF); ReceiverResource* receiver = receivers.back().get(); // Then diff --git a/test/unittest/transport/UDPv4Tests.cpp b/test/unittest/transport/UDPv4Tests.cpp index a54e8acd7c8..d3bde30dce5 100644 --- a/test/unittest/transport/UDPv4Tests.cpp +++ b/test/unittest/transport/UDPv4Tests.cpp @@ -626,6 +626,71 @@ TEST_F(UDPv4Tests, open_a_blocked_socket) } } +TEST_F(UDPv4Tests, simple_throughput) +{ + const size_t sample_size = 1024; + int num_samples_per_batch = 100000; + + std::atomic samples_received(0); + + Semaphore sem_end_subscriber; + + octet sample_data[sample_size]; + memset(sample_data, 0, sizeof(sample_data)); + + Locator_t sub_locator; + sub_locator.kind = LOCATOR_KIND_UDPv4; + sub_locator.port = 50000; + IPLocator::setIPv4(sub_locator, 127, 0, 0, 1); + + UDPv4TransportDescriptor my_descriptor; + + // Subscriber + + UDPv4Transport sub_transport(my_descriptor); + ASSERT_TRUE(sub_transport.init()); + + MockReceiverResource sub_receiver(sub_transport, sub_locator); + MockMessageReceiver* sub_msg_recv = dynamic_cast(sub_receiver.CreateMessageReceiver()); + + std::function sub_callback = [&]() + { + samples_received.fetch_add(1); + }; + + sub_msg_recv->setCallback(sub_callback); + + // Publisher + + UDPv4Transport pub_transport(my_descriptor); + ASSERT_TRUE(pub_transport.init()); + + LocatorList_t send_locators_list; + send_locators_list.push_back(sub_locator); + + SendResourceList send_resource_list; + ASSERT_TRUE(pub_transport.OpenOutputChannel(send_resource_list, sub_locator)); + + auto t0 = std::chrono::high_resolution_clock::now(); + + for (int i=0; isend(sample_data, sizeof(sample_data), &locators_begin, &locators_end, + (std::chrono::steady_clock::now() + std::chrono::milliseconds(100)))); + } + + auto t1 = std::chrono::high_resolution_clock::now(); + + auto real_samples_received = samples_received.load(); + printf("Samples [sent,received] [%d,%d] send_time_per_sample %.3f(us)\n" + , num_samples_per_batch + , real_samples_received + , std::chrono::duration_cast(t1-t0).count() / (num_samples_per_batch*1000.0)); +} + void UDPv4Tests::HELPER_SetDescriptorDefaults() { descriptor.maxMessageSize = 5; diff --git a/test/unittest/transport/mock/SharedMemGlobalMock.hpp b/test/unittest/transport/mock/SharedMemGlobalMock.hpp new file mode 100644 index 00000000000..cb5fbe29e89 --- /dev/null +++ b/test/unittest/transport/mock/SharedMemGlobalMock.hpp @@ -0,0 +1,77 @@ +// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_MOCKSHAREDMEM_GLOBAL_H_ +#define _FASTDDS_MOCKSHAREDMEM_GLOBAL_H_ + +#include + +namespace eprosima{ +namespace fastdds{ +namespace rtps{ + +class MockPortSharedMemGlobal +{ +public: + + static void remove_port_mutex(const std::string& domain_name, uint32_t port_id) + { + auto port_segment_name = domain_name + "_port" + std::to_string(port_id); + + SharedMemSegment::named_mutex::remove(port_segment_name.c_str()); + } + + static std::unique_ptr get_port_mutex(const std::string& domain_name, uint32_t port_id) + { + auto port_segment_name = domain_name + "_port" + std::to_string(port_id); + + std::unique_ptr port_mutex = + SharedMemSegment::open_named_mutex(port_segment_name + "_mutex"); + + return std::unique_ptr(SharedMemSegment::open_named_mutex(port_segment_name + "_mutex")); + } + + static bool lock_empty_cv_mutex(SharedMemGlobal::Port& port) + { + return port.node_->empty_cv_mutex.try_lock(); + } + + /** + * Simulates a deadlocked wait_pop. + * Deadlock until is_listener_closed is true + */ + static void wait_pop_deadlock( + SharedMemGlobal::Port& port, + SharedMemGlobal::Listener& listener, + const std::atomic& is_listener_closed) + { + (void)listener; + + std::unique_lock lock(port.node_->empty_cv_mutex); + + port.node_->waiting_count++; + + port.node_->empty_cv.wait(lock, [&] { + return is_listener_closed.load(); + }); + + port.node_->waiting_count--; + } +}; + +} // namespace rtps +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_MOCKSHAREDMEM_GLOBAL_H_ \ No newline at end of file diff --git a/test/unittest/xmlparser/CMakeLists.txt b/test/unittest/xmlparser/CMakeLists.txt index 1e71a174811..5d54e4e4cea 100644 --- a/test/unittest/xmlparser/CMakeLists.txt +++ b/test/unittest/xmlparser/CMakeLists.txt @@ -55,6 +55,9 @@ if(NOT ((MSVC OR MSVC_IDE) AND EPROSIMA_INSTALLER)) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/UDP_transport_descriptors_config.xml ${CMAKE_CURRENT_BINARY_DIR}/UDP_transport_descriptors_config.xml COPYONLY) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/SHM_transport_descriptors_config.xml + ${CMAKE_CURRENT_BINARY_DIR}/SHM_transport_descriptors_config.xml + COPYONLY) set(XMLPROFILEPARSER_SOURCE XMLProfileParserTests.cpp @@ -115,6 +118,7 @@ if(NOT ((MSVC OR MSVC_IDE) AND EPROSIMA_INSTALLER)) ${PROJECT_SOURCE_DIR}/test/mock/rtps/UDPTransportDescriptor ${PROJECT_SOURCE_DIR}/test/mock/rtps/UDPv4TransportDescriptor ${PROJECT_SOURCE_DIR}/test/mock/rtps/UDPv6TransportDescriptor + ${PROJECT_SOURCE_DIR}/test/mock/rtps/SharedMemTransportDescriptor ${PROJECT_SOURCE_DIR}/include ${PROJECT_BINARY_DIR}/include) target_link_libraries(XMLProfileParserTests ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES} @@ -188,6 +192,7 @@ if(NOT ((MSVC OR MSVC_IDE) AND EPROSIMA_INSTALLER)) ${PROJECT_SOURCE_DIR}/test/mock/rtps/UDPTransportDescriptor ${PROJECT_SOURCE_DIR}/test/mock/rtps/UDPv4TransportDescriptor ${PROJECT_SOURCE_DIR}/test/mock/rtps/UDPv6TransportDescriptor + ${PROJECT_SOURCE_DIR}/test/mock/rtps/SharedMemTransportDescriptor ${PROJECT_SOURCE_DIR}/include ${PROJECT_BINARY_DIR}/include ) diff --git a/test/unittest/xmlparser/SHM_transport_descriptors_config.xml b/test/unittest/xmlparser/SHM_transport_descriptors_config.xml new file mode 100644 index 00000000000..391bfba9d8b --- /dev/null +++ b/test/unittest/xmlparser/SHM_transport_descriptors_config.xml @@ -0,0 +1,18 @@ + + + + + + Test + SHM + 4294967295 + 4294967295 + DISCARD + FAIL + 4294967295 + test_file.dump + 128000 + + + + diff --git a/test/unittest/xmlparser/XMLProfileParserTests.cpp b/test/unittest/xmlparser/XMLProfileParserTests.cpp index 72882bebfdb..37ef410137f 100644 --- a/test/unittest/xmlparser/XMLProfileParserTests.cpp +++ b/test/unittest/xmlparser/XMLProfileParserTests.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -719,6 +720,30 @@ TEST_F(XMLProfileParserTests, UDP_transport_descriptors_config) EXPECT_EQ(descriptor->m_output_udp_socket, 5101u); } +TEST_F(XMLProfileParserTests, SHM_transport_descriptors_config) +{ + ASSERT_EQ(xmlparser::XMLP_ret::XML_OK, + xmlparser::XMLProfileManager::loadXMLFile("SHM_transport_descriptors_config.xml")); + + xmlparser::sp_transport_t transport = xmlparser::XMLProfileManager::getTransportById("Test"); + + using SHMDescriptor = std::shared_ptr; + SHMDescriptor descriptor = std::dynamic_pointer_cast( + transport); + + ASSERT_NE(descriptor, nullptr); + ASSERT_EQ(descriptor->segment_size(), std::numeric_limits::max()); + ASSERT_EQ(descriptor->port_queue_capacity(), std::numeric_limits::max()); + ASSERT_EQ(descriptor->port_overflow_policy(), + eprosima::fastdds::rtps::SharedMemTransportDescriptor::OverflowPolicy::DISCARD); + ASSERT_EQ(descriptor->segment_overflow_policy(), + eprosima::fastdds::rtps::SharedMemTransportDescriptor::OverflowPolicy::FAIL); + ASSERT_EQ(descriptor->healthy_check_timeout_ms(), std::numeric_limits::max()); + ASSERT_EQ(descriptor->rtps_dump_file(), "test_file.dump"); + ASSERT_EQ(descriptor->maxMessageSize, 128000u); + ASSERT_EQ(descriptor->max_message_size(), 128000u); +} + int main( int argc, char** argv) diff --git a/thirdparty/boost/include/boost/config/abi/msvc_prefix.hpp b/thirdparty/boost/include/boost/config/abi/msvc_prefix.hpp new file mode 100644 index 00000000000..97f06cdc0c2 --- /dev/null +++ b/thirdparty/boost/include/boost/config/abi/msvc_prefix.hpp @@ -0,0 +1,22 @@ +// (C) Copyright John Maddock 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// +// Boost binaries are built with the compiler's default ABI settings, +// if the user changes their default alignment in the VS IDE then their +// code will no longer be binary compatible with the bjam built binaries +// unless this header is included to force Boost code into a consistent ABI. +// +// Note that inclusion of this header is only necessary for libraries with +// separate source, header only libraries DO NOT need this as long as all +// translation units are built with the same options. +// +#if defined(_M_X64) +# pragma pack(push,16) +#else +# pragma pack(push,8) +#endif + + diff --git a/thirdparty/boost/include/boost/config/abi/msvc_suffix.hpp b/thirdparty/boost/include/boost/config/abi/msvc_suffix.hpp new file mode 100644 index 00000000000..a64d783eb0f --- /dev/null +++ b/thirdparty/boost/include/boost/config/abi/msvc_suffix.hpp @@ -0,0 +1,8 @@ +// (C) Copyright John Maddock 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#pragma pack(pop) + + diff --git a/thirdparty/boost/include/boost/config/abi_prefix.hpp b/thirdparty/boost/include/boost/config/abi_prefix.hpp new file mode 100644 index 00000000000..3b1347492ca --- /dev/null +++ b/thirdparty/boost/include/boost/config/abi_prefix.hpp @@ -0,0 +1,25 @@ +// abi_prefix header -------------------------------------------------------// + +// (c) Copyright John Maddock 2003 + +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt). + +#ifndef BOOST_CONFIG_ABI_PREFIX_HPP +# define BOOST_CONFIG_ABI_PREFIX_HPP +#else +# error double inclusion of header boost/config/abi_prefix.hpp is an error +#endif + +#include + +// this must occur after all other includes and before any code appears: +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +#if defined( __BORLANDC__ ) +#pragma nopushoptwarn +#endif + diff --git a/thirdparty/boost/include/boost/config/abi_suffix.hpp b/thirdparty/boost/include/boost/config/abi_suffix.hpp new file mode 100644 index 00000000000..939161662ae --- /dev/null +++ b/thirdparty/boost/include/boost/config/abi_suffix.hpp @@ -0,0 +1,27 @@ +// abi_sufffix header -------------------------------------------------------// + +// (c) Copyright John Maddock 2003 + +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt). + +// This header should be #included AFTER code that was preceded by a #include +// . + +#ifndef BOOST_CONFIG_ABI_PREFIX_HPP +# error Header boost/config/abi_suffix.hpp must only be used after boost/config/abi_prefix.hpp +#else +# undef BOOST_CONFIG_ABI_PREFIX_HPP +#endif + +// the suffix header occurs after all of our code: +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#if defined( __BORLANDC__ ) +#pragma nopushoptwarn +#endif + + diff --git a/thirdparty/boost/include/boost/date_time/dst_rules.hpp b/thirdparty/boost/include/boost/date_time/dst_rules.hpp new file mode 100644 index 00000000000..73a98996d81 --- /dev/null +++ b/thirdparty/boost/include/boost/date_time/dst_rules.hpp @@ -0,0 +1,391 @@ +#ifndef DATE_TIME_DST_RULES_HPP__ +#define DATE_TIME_DST_RULES_HPP__ + +/* Copyright (c) 2002,2003, 2007 CrystalClear Software, Inc. + * Use, modification and distribution is subject to the + * Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + * Author: Jeff Garland, Bart Garst + * $Date$ + */ + +/*! @file dst_rules.hpp + Contains template class to provide static dst rule calculations +*/ + +#include "boost/date_time/date_generators.hpp" +#include "boost/date_time/period.hpp" +#include "boost/date_time/date_defs.hpp" +#include + +namespace boost { + namespace date_time { + + enum time_is_dst_result {is_not_in_dst, is_in_dst, + ambiguous, invalid_time_label}; + + + //! Dynamic class used to caluclate dst transition information + template + class dst_calculator + { + public: + typedef time_duration_type_ time_duration_type; + typedef date_type_ date_type; + + //! Check the local time offset when on dst start day + /*! On this dst transition, the time label between + * the transition boundary and the boudary + the offset + * are invalid times. If before the boundary then still + * not in dst. + *@param time_of_day Time offset in the day for the local time + *@param dst_start_offset_minutes Local day offset for start of dst + *@param dst_length_minutes Number of minutes to adjust clock forward + *@retval status of time label w.r.t. dst + */ + static time_is_dst_result + process_local_dst_start_day(const time_duration_type& time_of_day, + unsigned int dst_start_offset_minutes, + long dst_length_minutes) + { + //std::cout << "here" << std::endl; + if (time_of_day < time_duration_type(0,dst_start_offset_minutes,0)) { + return is_not_in_dst; + } + long offset = dst_start_offset_minutes + dst_length_minutes; + if (time_of_day >= time_duration_type(0,offset,0)) { + return is_in_dst; + } + return invalid_time_label; + } + + //! Check the local time offset when on the last day of dst + /*! This is the calculation for the DST end day. On that day times + * prior to the conversion time - dst_length (1 am in US) are still + * in dst. Times between the above and the switch time are + * ambiguous. Times after the start_offset are not in dst. + *@param time_of_day Time offset in the day for the local time + *@param dst_end_offset_minutes Local time of day for end of dst + *@retval status of time label w.r.t. dst + */ + static time_is_dst_result + process_local_dst_end_day(const time_duration_type& time_of_day, + unsigned int dst_end_offset_minutes, + long dst_length_minutes) + { + //in US this will be 60 so offset in day is 1,0,0 + int offset = dst_end_offset_minutes-dst_length_minutes; + if (time_of_day < time_duration_type(0,offset,0)) { + return is_in_dst; + } + if (time_of_day >= time_duration_type(0,dst_end_offset_minutes,0)) { + return is_not_in_dst; + } + return ambiguous; + } + + //! Calculates if the given local time is dst or not + /*! Determines if the time is really in DST or not. Also checks for + * invalid and ambiguous. + * @param current_day The day to check for dst + * @param time_of_day Time offset within the day to check + * @param dst_start_day Starting day of dst for the given locality + * @param dst_start_offset Time offset within day for dst boundary + * @param dst_end_day Ending day of dst for the given locality + * @param dst_end_offset Time offset within day given in dst for dst boundary + * @param dst_length lenght of dst adjusment + * @retval The time is either ambiguous, invalid, in dst, or not in dst + */ + static time_is_dst_result + local_is_dst(const date_type& current_day, + const time_duration_type& time_of_day, + const date_type& dst_start_day, + const time_duration_type& dst_start_offset, + const date_type& dst_end_day, + const time_duration_type& dst_end_offset, + const time_duration_type& dst_length_minutes) + { + unsigned int start_minutes = static_cast( + dst_start_offset.hours() * 60 + dst_start_offset.minutes()); + unsigned int end_minutes = static_cast( + dst_end_offset.hours() * 60 + dst_end_offset.minutes()); + long length_minutes = static_cast( + dst_length_minutes.hours() * 60 + dst_length_minutes.minutes()); + + return local_is_dst(current_day, time_of_day, + dst_start_day, start_minutes, + dst_end_day, end_minutes, + length_minutes); + } + + //! Calculates if the given local time is dst or not + /*! Determines if the time is really in DST or not. Also checks for + * invalid and ambiguous. + * @param current_day The day to check for dst + * @param time_of_day Time offset within the day to check + * @param dst_start_day Starting day of dst for the given locality + * @param dst_start_offset_minutes Offset within day for dst + * boundary (eg 120 for US which is 02:00:00) + * @param dst_end_day Ending day of dst for the given locality + * @param dst_end_offset_minutes Offset within day given in dst for dst + * boundary (eg 120 for US which is 02:00:00) + * @param dst_length_minutes Length of dst adjusment (eg: 60 for US) + * @retval The time is either ambiguous, invalid, in dst, or not in dst + */ + static time_is_dst_result + local_is_dst(const date_type& current_day, + const time_duration_type& time_of_day, + const date_type& dst_start_day, + unsigned int dst_start_offset_minutes, + const date_type& dst_end_day, + unsigned int dst_end_offset_minutes, + long dst_length_minutes) + { + //in northern hemisphere dst is in the middle of the year + if (dst_start_day < dst_end_day) { + if ((current_day > dst_start_day) && (current_day < dst_end_day)) { + return is_in_dst; + } + if ((current_day < dst_start_day) || (current_day > dst_end_day)) { + return is_not_in_dst; + } + } + else {//southern hemisphere dst is at begining /end of year + if ((current_day < dst_start_day) && (current_day > dst_end_day)) { + return is_not_in_dst; + } + if ((current_day > dst_start_day) || (current_day < dst_end_day)) { + return is_in_dst; + } + } + + if (current_day == dst_start_day) { + return process_local_dst_start_day(time_of_day, + dst_start_offset_minutes, + dst_length_minutes); + } + + if (current_day == dst_end_day) { + return process_local_dst_end_day(time_of_day, + dst_end_offset_minutes, + dst_length_minutes); + } + //you should never reach this statement + return invalid_time_label; + } + + }; + + + //! Compile-time configurable daylight savings time calculation engine + /* This template provides the ability to configure a daylight savings + * calculation at compile time covering all the cases. Unfortunately + * because of the number of dimensions related to daylight savings + * calculation the number of parameters is high. In addition, the + * start and end transition rules are complex types that specify + * an algorithm for calculation of the starting day and ending + * day of daylight savings time including the month and day + * specifications (eg: last sunday in October). + * + * @param date_type A type that represents dates, typically gregorian::date + * @param time_duration_type Used for the offset in the day calculations + * @param dst_traits A set of traits that define the rules of dst + * calculation. The dst_trait must include the following: + * start_rule_functor - Rule to calculate the starting date of a + * dst transition (eg: last_kday_of_month). + * start_day - static function that returns month of dst start for + * start_rule_functor + * start_month -static function that returns day or day of week for + * dst start of dst + * end_rule_functor - Rule to calculate the end of dst day. + * end_day - static fucntion that returns end day for end_rule_functor + * end_month - static function that returns end month for end_rule_functor + * dst_start_offset_minutes - number of minutes from start of day to transition to dst -- 120 (or 2:00 am) is typical for the U.S. and E.U. + * dst_start_offset_minutes - number of minutes from start of day to transition off of dst -- 180 (or 3:00 am) is typical for E.U. + * dst_length_minutes - number of minutes that dst shifts clock + */ + template + class dst_calc_engine + { + public: + typedef typename date_type::year_type year_type; + typedef typename date_type::calendar_type calendar_type; + typedef dst_calculator dstcalc; + + //! Calculates if the given local time is dst or not + /*! Determines if the time is really in DST or not. Also checks for + * invalid and ambiguous. + * @retval The time is either ambiguous, invalid, in dst, or not in dst + */ + static time_is_dst_result local_is_dst(const date_type& d, + const time_duration_type& td) + { + + year_type y = d.year(); + date_type dst_start = local_dst_start_day(y); + date_type dst_end = local_dst_end_day(y); + return dstcalc::local_is_dst(d,td, + dst_start, + dst_traits::dst_start_offset_minutes(), + dst_end, + dst_traits::dst_end_offset_minutes(), + dst_traits::dst_shift_length_minutes()); + + } + + static bool is_dst_boundary_day(date_type d) + { + year_type y = d.year(); + return ((d == local_dst_start_day(y)) || + (d == local_dst_end_day(y))); + } + + //! The time of day for the dst transition (eg: typically 01:00:00 or 02:00:00) + static time_duration_type dst_offset() + { + return time_duration_type(0,dst_traits::dst_shift_length_minutes(),0); + } + + static date_type local_dst_start_day(year_type year) + { + return dst_traits::local_dst_start_day(year); + } + + static date_type local_dst_end_day(year_type year) + { + return dst_traits::local_dst_end_day(year); + } + + + }; + + //! Depricated: Class to calculate dst boundaries for US time zones + /* Use dst_calc_engine instead. + * In 2007 US/Canada DST rules changed + * (http://en.wikipedia.org/wiki/Energy_Policy_Act_of_2005#Change_to_daylight_saving_time). + */ + template //1 hour == 60 min in US + class us_dst_rules + { + public: + typedef time_duration_type_ time_duration_type; + typedef date_type_ date_type; + typedef typename date_type::year_type year_type; + typedef typename date_type::calendar_type calendar_type; + typedef date_time::last_kday_of_month lkday; + typedef date_time::first_kday_of_month fkday; + typedef date_time::nth_kday_of_month nkday; + typedef dst_calculator dstcalc; + + //! Calculates if the given local time is dst or not + /*! Determines if the time is really in DST or not. Also checks for + * invalid and ambiguous. + * @retval The time is either ambiguous, invalid, in dst, or not in dst + */ + static time_is_dst_result local_is_dst(const date_type& d, + const time_duration_type& td) + { + + year_type y = d.year(); + date_type dst_start = local_dst_start_day(y); + date_type dst_end = local_dst_end_day(y); + return dstcalc::local_is_dst(d,td, + dst_start,dst_start_offset_minutes, + dst_end, dst_start_offset_minutes, + dst_length_minutes); + + } + + + static bool is_dst_boundary_day(date_type d) + { + year_type y = d.year(); + return ((d == local_dst_start_day(y)) || + (d == local_dst_end_day(y))); + } + + static date_type local_dst_start_day(year_type year) + { + if (year >= year_type(2007)) { + //second sunday in march + nkday ssim(nkday::second, Sunday, gregorian::Mar); + return ssim.get_date(year); + } else { + //first sunday in april + fkday fsia(Sunday, gregorian::Apr); + return fsia.get_date(year); + } + } + + static date_type local_dst_end_day(year_type year) + { + if (year >= year_type(2007)) { + //first sunday in november + fkday fsin(Sunday, gregorian::Nov); + return fsin.get_date(year); + } else { + //last sunday in october + lkday lsio(Sunday, gregorian::Oct); + return lsio.get_date(year); + } + } + + static time_duration_type dst_offset() + { + return time_duration_type(0,dst_length_minutes,0); + } + + private: + + + }; + + //! Used for local time adjustments in places that don't use dst + template + class null_dst_rules + { + public: + typedef time_duration_type_ time_duration_type; + typedef date_type_ date_type; + + + //! Calculates if the given local time is dst or not + /*! @retval Always is_not_in_dst since this is for zones without dst + */ + static time_is_dst_result local_is_dst(const date_type&, + const time_duration_type&) + { + return is_not_in_dst; + } + + //! Calculates if the given utc time is in dst + static time_is_dst_result utc_is_dst(const date_type&, + const time_duration_type&) + { + return is_not_in_dst; + } + + static bool is_dst_boundary_day(date_type /*d*/) + { + return false; + } + + static time_duration_type dst_offset() + { + return time_duration_type(0,0,0); + } + + }; + + + } } //namespace date_time + + + +#endif diff --git a/thirdparty/boost/include/boost/date_time/posix_time/date_duration_operators.hpp b/thirdparty/boost/include/boost/date_time/posix_time/date_duration_operators.hpp new file mode 100644 index 00000000000..f3c61d7a954 --- /dev/null +++ b/thirdparty/boost/include/boost/date_time/posix_time/date_duration_operators.hpp @@ -0,0 +1,114 @@ +#ifndef DATE_DURATION_OPERATORS_HPP___ +#define DATE_DURATION_OPERATORS_HPP___ + +/* Copyright (c) 2004 CrystalClear Software, Inc. + * Subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or + * http://www.boost.org/LICENSE_1_0.txt) + * Author: Jeff Garland, Bart Garst + * $Date$ + */ + +#include "boost/date_time/gregorian/greg_duration_types.hpp" +#include "boost/date_time/posix_time/ptime.hpp" + +namespace boost { +namespace posix_time { + + /*!@file date_duration_operators.hpp Operators for ptime and + * optional gregorian types. Operators use snap-to-end-of-month behavior. + * Further details on this behavior can be found in reference for + * date_time/date_duration_types.hpp and documentation for + * month and year iterators. + */ + + + /*! Adds a months object and a ptime. Result will be same + * day-of-month as ptime unless original day was the last day of month. + * see date_time::months_duration for more details */ + inline + ptime + operator+(const ptime& t, const boost::gregorian::months& m) + { + return t + m.get_offset(t.date()); + } + + /*! Adds a months object to a ptime. Result will be same + * day-of-month as ptime unless original day was the last day of month. + * see date_time::months_duration for more details */ + inline + ptime + operator+=(ptime& t, const boost::gregorian::months& m) + { + // get_neg_offset returns a negative duration, so we add + return t += m.get_offset(t.date()); + } + + /*! Subtracts a months object and a ptime. Result will be same + * day-of-month as ptime unless original day was the last day of month. + * see date_time::months_duration for more details */ + inline + ptime + operator-(const ptime& t, const boost::gregorian::months& m) + { + // get_neg_offset returns a negative duration, so we add + return t + m.get_neg_offset(t.date()); + } + + /*! Subtracts a months object from a ptime. Result will be same + * day-of-month as ptime unless original day was the last day of month. + * see date_time::months_duration for more details */ + inline + ptime + operator-=(ptime& t, const boost::gregorian::months& m) + { + return t += m.get_neg_offset(t.date()); + } + + // ptime & years + + /*! Adds a years object and a ptime. Result will be same + * month and day-of-month as ptime unless original day was the + * last day of month. see date_time::years_duration for more details */ + inline + ptime + operator+(const ptime& t, const boost::gregorian::years& y) + { + return t + y.get_offset(t.date()); + } + + /*! Adds a years object to a ptime. Result will be same + * month and day-of-month as ptime unless original day was the + * last day of month. see date_time::years_duration for more details */ + inline + ptime + operator+=(ptime& t, const boost::gregorian::years& y) + { + return t += y.get_offset(t.date()); + } + + /*! Subtracts a years object and a ptime. Result will be same + * month and day-of-month as ptime unless original day was the + * last day of month. see date_time::years_duration for more details */ + inline + ptime + operator-(const ptime& t, const boost::gregorian::years& y) + { + // get_neg_offset returns a negative duration, so we add + return t + y.get_neg_offset(t.date()); + } + + /*! Subtracts a years object from a ptime. Result will be same + * month and day-of-month as ptime unless original day was the + * last day of month. see date_time::years_duration for more details */ + inline + ptime + operator-=(ptime& t, const boost::gregorian::years& y) + { + // get_neg_offset returns a negative duration, so we add + return t += y.get_neg_offset(t.date()); + } + +}} // namespaces + +#endif // DATE_DURATION_OPERATORS_HPP___ diff --git a/thirdparty/boost/include/boost/date_time/posix_time/posix_time_types.hpp b/thirdparty/boost/include/boost/date_time/posix_time/posix_time_types.hpp new file mode 100644 index 00000000000..f2488f8bed6 --- /dev/null +++ b/thirdparty/boost/include/boost/date_time/posix_time/posix_time_types.hpp @@ -0,0 +1,55 @@ +/* Copyright (c) 2002,2003 CrystalClear Software, Inc. + * Use, modification and distribution is subject to the + * Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + * Author: Jeff Garland + */ +#ifndef POSIX_TIME_TYPES_HPP___ +#define POSIX_TIME_TYPES_HPP___ + +#include "boost/date_time/time_clock.hpp" +#include "boost/date_time/microsec_time_clock.hpp" +#include "boost/date_time/posix_time/ptime.hpp" +#if defined(BOOST_DATE_TIME_OPTIONAL_GREGORIAN_TYPES) +#include "boost/date_time/posix_time/date_duration_operators.hpp" +#endif +#include "boost/date_time/posix_time/posix_time_duration.hpp" +#include "boost/date_time/posix_time/posix_time_system.hpp" +#include "boost/date_time/posix_time/time_period.hpp" +#include "boost/date_time/time_iterator.hpp" +#include "boost/date_time/dst_rules.hpp" + +namespace boost { + +//!Defines a non-adjusted time system with nano-second resolution and stable calculation properties +namespace posix_time { + + //! Iterator over a defined time duration + /*! \ingroup time_basics + */ + typedef date_time::time_itr time_iterator; + //! A time clock that has a resolution of one second + /*! \ingroup time_basics + */ + typedef date_time::second_clock second_clock; + +#ifdef BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK + //! A time clock that has a resolution of one microsecond + /*! \ingroup time_basics + */ + typedef date_time::microsec_clock microsec_clock; +#endif + + //! Define a dst null dst rule for the posix_time system + typedef date_time::null_dst_rules no_dst; + //! Define US dst rule calculator for the posix_time system + typedef date_time::us_dst_rules us_dst; + + +} } //namespace posix_time + + + + +#endif + diff --git a/thirdparty/boost/include/boost/date_time/posix_time/time_period.hpp b/thirdparty/boost/include/boost/date_time/posix_time/time_period.hpp new file mode 100644 index 00000000000..7c6095b8965 --- /dev/null +++ b/thirdparty/boost/include/boost/date_time/posix_time/time_period.hpp @@ -0,0 +1,29 @@ +#ifndef POSIX_TIME_PERIOD_HPP___ +#define POSIX_TIME_PERIOD_HPP___ + +/* Copyright (c) 2002,2003 CrystalClear Software, Inc. + * Use, modification and distribution is subject to the + * Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + * Author: Jeff Garland + * $Date$ + */ + +#include "boost/date_time/period.hpp" +#include "boost/date_time/posix_time/posix_time_duration.hpp" +#include "boost/date_time/posix_time/ptime.hpp" + +namespace boost { +namespace posix_time { + + //! Time period type + /*! \ingroup time_basics + */ + typedef date_time::period time_period; + + +} }//namespace posix_time + + +#endif + diff --git a/thirdparty/boost/include/boost/date_time/time_iterator.hpp b/thirdparty/boost/include/boost/date_time/time_iterator.hpp new file mode 100644 index 00000000000..64439363d7e --- /dev/null +++ b/thirdparty/boost/include/boost/date_time/time_iterator.hpp @@ -0,0 +1,52 @@ +#ifndef DATE_TIME_TIME_ITERATOR_HPP___ +#define DATE_TIME_TIME_ITERATOR_HPP___ + +/* Copyright (c) 2002,2003 CrystalClear Software, Inc. + * Use, modification and distribution is subject to the + * Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + * Author: Jeff Garland, Bart Garst + * $Date$ + */ + + +namespace boost { +namespace date_time { + + + //! Simple time iterator skeleton class + template + class time_itr { + public: + typedef typename time_type::time_duration_type time_duration_type; + time_itr(time_type t, time_duration_type d) : current_(t), offset_(d) {} + time_itr& operator++() + { + current_ = current_ + offset_; + return *this; + } + time_itr& operator--() + { + current_ = current_ - offset_; + return *this; + } + time_type operator*() {return current_;} + time_type* operator->() {return ¤t_;} + bool operator< (const time_type& t) {return current_ < t;} + bool operator<= (const time_type& t) {return current_ <= t;} + bool operator!= (const time_type& t) {return current_ != t;} + bool operator== (const time_type& t) {return current_ == t;} + bool operator> (const time_type& t) {return current_ > t;} + bool operator>= (const time_type& t) {return current_ >= t;} + + private: + time_type current_; + time_duration_type offset_; + }; + + + +} }//namespace date_time + + +#endif diff --git a/thirdparty/boost/include/boost/thread/thread_time.hpp b/thirdparty/boost/include/boost/thread/thread_time.hpp new file mode 100644 index 00000000000..ffdcf850f73 --- /dev/null +++ b/thirdparty/boost/include/boost/thread/thread_time.hpp @@ -0,0 +1,55 @@ +#ifndef BOOST_THREAD_TIME_HPP +#define BOOST_THREAD_TIME_HPP +// (C) Copyright 2007 Anthony Williams +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include + +namespace boost +{ + typedef boost::posix_time::ptime system_time; + + inline system_time get_system_time() + { +#if defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) + return boost::date_time::microsec_clock::universal_time(); +#else // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) + return boost::date_time::second_clock::universal_time(); +#endif // defined(BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK) + } + + namespace detail + { + inline system_time get_system_time_sentinel() + { + return system_time(boost::posix_time::pos_infin); + } + + inline unsigned long get_milliseconds_until(system_time const& target_time) + { + if(target_time.is_pos_infinity()) + { + return ~(unsigned long)0; + } + system_time const now=get_system_time(); + if(target_time<=now) + { + return 0; + } + return static_cast((target_time-now).total_milliseconds()+1); + } + + } + +} + +#include + +#endif diff --git a/thirdparty/boost/include/boostconfig.hpp b/thirdparty/boost/include/boostconfig.hpp index 4ef29986cbf..92c547655bc 100644 --- a/thirdparty/boost/include/boostconfig.hpp +++ b/thirdparty/boost/include/boostconfig.hpp @@ -1,35 +1,120 @@ -#pragma once +// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _FASTDDS_THIRDPARTYBOOST_BOOSTCONFIG_H_ +#define _FASTDDS_THIRDPARTYBOOST_BOOSTCONFIG_H_ #define BOOST_DATE_TIME_NO_LIB +#define BOOST_INTERPROCESS_ENABLE_TIMEOUT_WHEN_LOCKING +#define BOOST_INTERPROCESS_TIMEOUT_WHEN_LOCKING_DURATION_MS 1000 + +#ifdef __APPLE__ +#define BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION +#endif #ifdef _MSC_VER #include +#include +#include +#include //#define BOOST_INTERPROCESS_BOOTSTAMP_IS_LASTBOOTUPTIME -#define BOOST_INTERPROCESS_SHARED_DIR_PATH "C:\\ProgramData\\fastrtps_interprocess" + +// TODO(Adolfo): This will fail if windows system without program data in C: drive +#define BOOST_INTERPROCESS_SHARED_DIR_PATH "C:\\ProgramData\\eprosima\\fastrtps_interprocess" #include -#if defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION) +#define BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION +/*#if defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION) #error "BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION must be disabled in boost/interprocess/detail/workarround.hpp" -#endif +#endif*/ + +// Todo(Adolfo): BlackBox.SHMTransportPubSub fail with BOOST_INTERPROCESS_USE_WINDOWS +//#define BOOST_INTERPROCESS_USE_WINDOWS +//#define BOOST_INTERPROCESS_BOOTSTAMP_IS_SESSION_MANAGER_BASED -class SharedDirCreator +#endif // _MSC_VER_ + +/** + * This singleton class performs some necesary system dependent initializations + * before start working with shared-memory + */ +class SharedMemEnvironment { public: - SharedDirCreator(); - void forzeConstruct(); - static void clean() { - system("del " BOOST_INTERPROCESS_SHARED_DIR_PATH "\\*.* /Q"); - } + + SharedMemEnvironment() +#ifdef _MSC_VER + : is_init_done_(false) +#endif + { + } + + /** + * @return the singleton instance + */ + static SharedMemEnvironment& get() + { + static SharedMemEnvironment singleton_instance; + return singleton_instance; + } + + /** + * Perform the initializacion, only the first time is called. + */ + void init() + { +#ifdef _MSC_VER + if (!is_init_done_) + { + get().create_shared_dir_if_doesnt_exist(); + + } + + is_init_done_ = true; +#endif + } + private: - bool created_; -}; -extern SharedDirCreator g_shared_dir_creator; -#define BOOST_INTERPROCESS_USE_WINDOWS -//#define BOOST_INTERPROCESS_BOOTSTAMP_IS_SESSION_MANAGER_BASED -//#define BOOST_INTERPROCESS_ENABLE_TIMEOUT_WHEN_LOCKING -//#define BOOST_INTERPROCESS_TIMEOUT_WHEN_LOCKING_DURATION_MS 1000 +#ifdef _MSC_VER -#endif // _MSC_VER_ + bool is_init_done_; + + + static void create_shared_dir_if_doesnt_exist() + { + struct stat info; + + // Cannot access shared_dir_path, we assume it doesn't exist + if (stat(BOOST_INTERPROCESS_SHARED_DIR_PATH, &info) != 0) + { + // Try to create it + if (system(("mkdir " BOOST_INTERPROCESS_SHARED_DIR_PATH)) != 0) + { + throw std::runtime_error("couldn't access nor create " BOOST_INTERPROCESS_SHARED_DIR_PATH); + } + } + } + + void clean() + { + system("del " BOOST_INTERPROCESS_SHARED_DIR_PATH "\\*.* /Q"); + } +#endif + +}; + +#endif // _FASTDDS_THIRDPARTYBOOST_BOOSTCONFIG_H_ diff --git a/thirdparty/boost/test/ThirdpartyBoostCompile_test.cpp b/thirdparty/boost/test/ThirdpartyBoostCompile_test.cpp index 9cfaa5abcc5..7c971c4b65a 100644 --- a/thirdparty/boost/test/ThirdpartyBoostCompile_test.cpp +++ b/thirdparty/boost/test/ThirdpartyBoostCompile_test.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include /** * This is a temporary class intented to check the correct compilation of the @@ -44,6 +44,7 @@ class ThirdpartyBoostCompileTest { boost::interprocess::interprocess_mutex mutex; boost::interprocess::interprocess_condition cv; + boost::get_system_time(); } catch(const std::exception&) { diff --git a/valgrind.supp b/valgrind.supp index 3fd77fb4355..a5701dede3d 100644 --- a/valgrind.supp +++ b/valgrind.supp @@ -27,3 +27,8 @@ fun:store fun:*atomic*store*memory_order } +{ + + Memcheck:Param + fun:sem_open +}