Compare commits

...

14 Commits

Author SHA1 Message Date
Alexey Rybalchenko
e484bf4578 Shm: Fix SetUsedSize() 2021-09-20 13:29:28 +02:00
Dennis Klein
b442483dc3 fix: Deprecate static string helper 2021-09-09 18:01:55 +02:00
Dennis Klein
727a709aff fix: -Winfinite-recursion 2021-09-09 18:01:55 +02:00
Alexey Rybalchenko
bce380d871 Implement shmem msg zero-copy 2021-09-07 20:53:16 +02:00
Alexey Rybalchenko
c57410b820 Extend test for empty messages 2021-09-07 20:53:16 +02:00
Alexey Rybalchenko
815b2f1d76 shm: reimplement alignment 2021-09-07 20:53:16 +02:00
Dennis Klein
4e8f247a0d fix: First round of using new non-namespaced typenames 2021-09-07 20:53:16 +02:00
Dennis Klein
0bf765e6ba build: Improve summary output 2021-09-07 20:53:16 +02:00
Dennis Klein
24fbf94946 build: Use fairmq-tidy on our own codebase when RUN_FAIRMQ_TIDY=ON 2021-09-07 20:53:16 +02:00
Dennis Klein
d392f60c09 build: Have color output depend on a common switch DISABLE_COLOR 2021-09-07 20:53:16 +02:00
Dennis Klein
dff2b4b7d1 feat(tidy): Add new FairMQTidy.cmake module 2021-09-07 20:53:16 +02:00
Dennis Klein
9cbaf7e0fd feat(tools): Move the error code to the Tools target 2021-09-07 20:53:16 +02:00
Dennis Klein
db727092c5 feat(tidy): Add new fairmq-tidy tool 2021-09-07 20:53:16 +02:00
Dennis Klein
8e6c50e7cc refactor: Prepare deprecation of non-namespaced types and headers 2021-09-07 20:53:16 +02:00
63 changed files with 3199 additions and 2239 deletions

5
.gitignore vendored
View File

@@ -1,5 +1,6 @@
build
install
.DS_Store
.vscode
/compile_commands.json
.cache

View File

@@ -39,6 +39,8 @@ fairmq_build_option(BUILD_EXAMPLES "Build FairMQ examples."
DEFAULT ON REQUIRES "BUILD_FAIRMQ")
fairmq_build_option(BUILD_SDK "Build the FairMQ controller SDK."
DEFAULT OFF REQUIRES "BUILD_DDS_PLUGIN;BUILD_SDK_COMMANDS")
fairmq_build_option(BUILD_TIDY_TOOL "Build the fairmq-tidy tool."
DEFAULT OFF)
fairmq_build_option(BUILD_DOCS "Build FairMQ documentation."
DEFAULT OFF)
fairmq_build_option(USE_EXTERNAL_GTEST "Do not use bundled GTest. Not recommended."
@@ -76,6 +78,10 @@ if(BUILD_DOCS)
doxygen_add_docs(doxygen README.md fairmq)
add_custom_target(docs ALL DEPENDS doxygen)
endif()
if(BUILD_TIDY_TOOL)
add_subdirectory(fairmq/tidy)
endif()
################################################################################
@@ -107,6 +113,9 @@ endif()
if(BUILD_SDK_COMMANDS)
list(APPEND PROJECT_PACKAGE_COMPONENTS sdk_commands)
endif()
if(BUILD_TIDY_TOOL)
list(APPEND PROJECT_PACKAGE_COMPONENTS tidy_tool)
endif()
################################################################################
@@ -126,6 +135,11 @@ if(BUILD_DOCS)
DESTINATION ${PROJECT_INSTALL_DATADIR}/docs
)
endif()
if(BUILD_TIDY_TOOL)
install(FILES cmake/FairMQTidy.cmake
DESTINATION ${PROJECT_INSTALL_CMAKEMODDIR}
)
endif()
include(FairMQPackage)
install_cmake_package()

View File

@@ -147,6 +147,9 @@ After the `find_package(FairMQ)` call the following CMake variables are defined:
3. [Introspection](docs/Configuration.md#33-introspection)
4. [Development](docs/Development.md#4-development)
1. [Testing](docs/Development.md#41-testing)
2. [Static Analysis](docs/Development.md#42-static-analysis)
1. [CMake Integration](docs/Development.md#421-cmake-integration)
2. [Extra Compiler Arguments](docs/Development.md#422-extra-compiler-arguments)
5. [Logging](docs/Logging.md#5-logging)
1. [Log severity](docs/Logging.md#51-log-severity)
2. [Log verbosity](docs/Logging.md#52-log-verbosity)

View File

@@ -15,7 +15,7 @@ include(FairMQBundlePackageHelper)
if(BUILD_FAIRMQ OR BUILD_SDK)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package2(PUBLIC Threads REQUIRED)
set(Threads_PREFIX "<unknown system prefix>")
set(Threads_PREFIX "<system>")
endif()
if(BUILD_OFI_TRANSPORT)
@@ -37,7 +37,7 @@ if(BUILD_PMIX_PLUGIN)
find_package2(PRIVATE PMIx REQUIRED VERSION 2.1.4)
endif()
if(BUILD_FAIRMQ OR BUILD_SDK)
if(BUILD_FAIRMQ OR BUILD_SDK OR BUILD_TIDY_TOOL)
find_package2(PUBLIC FairLogger REQUIRED VERSION 1.6.0)
find_package2(PUBLIC Boost REQUIRED VERSION 1.66
COMPONENTS container program_options filesystem date_time regex
@@ -59,6 +59,7 @@ if(BUILD_FAIRMQ)
endif()
find_package2(BUNDLED PicoSHA2 REQUIRED)
set(PicoSHA2_VERSION "1.0.0")
set(PicoSHA2_PREFIX "<bundled>")
endif()
if(BUILD_TESTING)
@@ -68,6 +69,7 @@ if(BUILD_TESTING)
find_package2(BUNDLED GTest REQUIRED)
if(GTest_BUNDLED)
set(GTest_VERSION "Apr 28 2021 @f5e592d8")
set(GTest_PREFIX "<bundled>")
endif()
endif()
@@ -77,6 +79,13 @@ if(BUILD_DOCS)
)
endif()
if(BUILD_TIDY_TOOL)
find_package2(PRIVATE LLVM REQUIRED)
find_package2(PRIVATE Clang REQUIRED)
set(Clang_VERSION ${LLVM_VERSION})
find_package2(PRIVATE CLI11 REQUIRED)
endif()
find_package2_implicit_dependencies() # Always call last after latest find_package2
if(PROJECT_PACKAGE_DEPENDENCIES)

View File

@@ -119,11 +119,11 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g ${_warnings} -DNDEBUG ${_sanitizers}"
unset(_warnings)
unset(_sanitizers)
if(CMAKE_GENERATOR STREQUAL "Ninja" AND
((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) OR
(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.5)))
# Force colored warnings in Ninja's output, if the compiler has -fdiagnostics-color support.
# Rationale in https://github.com/ninja-build/ninja/issues/814
if(DISABLE_COLOR)
set(CMAKE_COLOR_MAKEFILE OFF)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=never")
else()
set(CMAKE_COLOR_MAKEFILE ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always")
endif()

View File

@@ -69,6 +69,12 @@ macro(fairmq_summary_components)
set(sdk_commands_summary "${BRed} NO${CR} (default, enable with ${BMagenta}-DBUILD_SDK_COMMANDS=ON${CR})")
endif()
message(STATUS " ${BWhite}sdk_commands${CR} ${sdk_commands_summary}")
if(BUILD_TIDY_TOOL)
set(sdk_tidy_summary "${BGreen}YES${CR} (disable with ${BMagenta}-DBUILD_TIDY_TOOL=OFF${CR})")
else()
set(sdk_tidy_summary "${BRed} NO${CR} (default, enable with ${BMagenta}-DBUILD_TIDY_TOOL=ON${CR})")
endif()
message(STATUS " ${BWhite}tidy_tool${CR} ${sdk_tidy_summary}")
endmacro()
macro(fairmq_summary_static_analysis)
@@ -91,9 +97,17 @@ macro(fairmq_summary_static_analysis)
endforeach()
set(static_ana_summary "${BWhite}(${CR}${analyser_list}${BWhite})${CR} (disable with ${BMagenta}-DRUN_STATIC_ANALYSIS=OFF${CR})")
else()
set(static_ana_summary "${BRed}OFF${CR} (default, enable with ${BMagenta}-DRUN_STATIC_ANALYSIS=ON${CR})")
set(static_ana_summary "${BRed} NO${CR} (default, enable with ${BMagenta}-DRUN_STATIC_ANALYSIS=ON${CR})")
endif()
message(STATUS " ${Cyan}RUN STATIC ANALYSIS ${static_ana_summary}")
if(BUILD_TIDY_TOOL)
if(RUN_FAIRMQ_TIDY)
set(tidy_summary "${BGreen}YES${CR} (disable with ${BMagenta}-DRUN_FAIRMQ_TIDY=OFF${CR})")
else()
set(tidy_summary "${BRed} NO${CR} (default, enable with ${BMagenta}-DRUN_FAIRMQ_TIDY=ON${CR})")
endif()
message(STATUS " ${Cyan}RUN fairmq-tidy ${tidy_summary}")
endif()
endmacro()
macro(fairmq_summary_install_prefix)

103
cmake/FairMQTidy.cmake Normal file
View File

@@ -0,0 +1,103 @@
################################################################################
# Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
# #
# This software is distributed under the terms of the #
# GNU Lesser General Public Licence (LGPL) version 3, #
# copied verbatim in the file "LICENSE" #
################################################################################
include_guard(GLOBAL)
#[=======================================================================[.rst:
``fairmq_target_tidy()``
========================
Runs the FairMQ static analyzer ``fairmq-tidy`` on source files in given
target.
.. code-block:: cmake
fairmq_target_tidy(TARGET <target> [EXTRA_ARGS <args>]
[CLANG_EXECUTABLE <clang>])
Registers a custom command that depends on ``<target>`` which runs ``fairmq-tidy``
on the source files that belong to ``<target>``. Optional extra arguments
``<args>`` are passed to the ``fairmq-tidy`` command-line.
Requires ``CMAKE_EXPORT_COMPILE_COMMANDS`` to be enabled.
#]=======================================================================]
function(fairmq_target_tidy)
cmake_parse_arguments(PARSE_ARGV 0 ARG "" "TARGET;EXTRA_ARGS;CLANG_EXECUTABLE" "")
if(NOT CMAKE_EXPORT_COMPILE_COMMANDS)
message(AUTHOR_WARNING "CMAKE_EXPORT_COMPILE_COMMANDS is not enabled. Skipping.")
return()
endif()
if(NOT ARG_TARGET)
message(AUTHOR_WARNING "TARGET argument is required. Skipping.")
return()
endif()
if(NOT TARGET ${ARG_TARGET})
message(AUTHOR_WARNING "Given TARGET argument `${ARG_TARGET}` is not a target. Skipping.")
return()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(NOT fairmq_tidy_clang_resource_dir)
if(ARG_CLANG_EXECUTABLE)
set(clang_exe ${ARG_CLANG_EXECUTABLE})
else()
if(TARGET clang)
get_property(clang_exe TARGET clang PROPERTY LOCATION)
else()
set(clang_exe "clang")
endif()
endif()
execute_process(
COMMAND ${clang_exe} -print-resource-dir
OUTPUT_VARIABLE clang_resource_dir
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(fairmq_tidy_clang_resource_dir ${clang_resource_dir}
CACHE PATH "fairmq_target_tidy() internal state" FORCE)
endif()
list(APPEND ARG_EXTRA_ARGS
"--extra-arg-before=-I${fairmq_tidy_clang_resource_dir}/include")
endif()
if(ARG_EXTRA_ARGS)
set(extra 1)
else()
set(extra 0)
endif()
get_target_property(sources ${ARG_TARGET} SOURCES)
list(FILTER sources INCLUDE REGEX "\.(cpp|cxx)$")
string(REPLACE ":" "-" target_nocolon "${ARG_TARGET}")
set(src_deps)
foreach(source IN LISTS sources)
string(REPLACE "\/" "-" source_noslash "${source}")
set(src_stamp "${CMAKE_CURRENT_BINARY_DIR}/${target_nocolon}-${source_noslash}.fairmq-tidy")
add_custom_command(
OUTPUT ${src_stamp}
COMMAND $<TARGET_FILE:FairMQ::fairmq-tidy> -p=${CMAKE_BINARY_DIR}
$<${extra}:${ARG_EXTRA_ARGS}> ${source}
COMMAND ${CMAKE_COMMAND} -E touch ${src_stamp}
COMMENT "fairmq-tidy: Analyzing source file '${source}' of target '${ARG_TARGET}'"
DEPENDS ${ARG_TARGET}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND_EXPAND_LISTS VERBATIM
)
list(APPEND src_deps ${src_stamp})
endforeach()
if(src_deps)
add_custom_target("fairmq-tidy-${target_nocolon}" ALL DEPENDS ${src_deps})
endif()
endfunction()

View File

@@ -48,6 +48,9 @@
#
include(GoogleTest)
if(BUILD_TIDY_TOOL)
include(FairMQTidy)
endif()
function(add_testsuite suitename)
cmake_parse_arguments(testsuite
@@ -74,6 +77,9 @@ function(add_testsuite suitename)
if(testsuite_DEFINITIONS)
target_compile_definitions("${target}" PUBLIC ${testsuite_DEFINITIONS})
endif()
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET ${target})
endif()
# add_test(NAME "${suitename}" WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${target})
if(testsuite_TIMEOUT)
@@ -120,6 +126,9 @@ function(add_testhelper helpername)
if(testhelper_DEFINITIONS)
target_compile_definitions(${target} PUBLIC ${testhelper_DEFINITIONS})
endif()
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET ${target})
endif()
list(APPEND ALL_TEST_TARGETS ${target})
set(ALL_TEST_TARGETS ${ALL_TEST_TARGETS} PARENT_SCOPE)
@@ -154,6 +163,9 @@ function(add_testlib libname)
if(testlib_DEFINITIONS)
target_compile_definitions(${target} PUBLIC ${testlib_DEFINITIONS})
endif()
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET ${target})
endif()
list(APPEND ALL_TEST_TARGETS ${target})
set(ALL_TEST_TARGETS ${ALL_TEST_TARGETS} PARENT_SCOPE)

View File

@@ -8,6 +8,51 @@ For unit testing it is often not feasible to boot up a full-blown distributed sy
In some scenarios it is useful to not even instantiate a `FairMQDevice` at all. Please see [this example](../test/protocols/_push_pull_multipart.cxx) for single and multi threaded unit test without a device instance. If you store your transport factories and channels on the heap, pls make sure, you destroy the channels before you destroy the related transport factory for proper shutdown. Channels provide all the `Send/Receive` and `New*Message/New*Poller` APIs provided by the device too.
TODO Multiple devices in one process.
## 4.2 Static Analysis
With `-DBUILD_TIDY_TOOL=ON` you can build the `fairmq-tidy` tool that implements static checks on your source code. To use it, enable the generation of a [compilation database](https://clang.llvm.org/docs/JSONCompilationDatabase.html) in your project via `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` (generates a file `<builddir>/compile_commands.json`):
```
fairmq-tidy -p <builddir> mysourcefile.cpp
```
If you find any issue (e.g. false positives) with this tool, please tell us by opening an issue on github.
### 4.2.1 CMake Integration
When discovering a FairMQ installation in your project, explicitely state, that you want one with the `fairmq-tidy` tool included:
```
find_package(FairMQ COMPONENTS tidy_tool)
```
Now the CMake module [`FairMQTidy.cmake`](../cmake/FairMQTidy.cmake) is available for inclusion:
```
include(FairMQTidy)
```
It provides the CMake function `fairmq_target_tidy()` which attaches appropriate `fairmq-tidy` build rules to each source file contained in the passed library or executable target, e.g. if you have an executable that uses FairMQ:
```
add_executable(myexe mysource1.cpp mysource2.cpp)
target_link_libraries(myexe PRIVATE FairMQ::FairMQ)
fairmq_target_tidy(TARGET myexe)
```
### 4.2.2 Extra Compiler Arguments
On most Linux distros you are likely to use GCC to compile your projects, so the resulting `compile_commands.json` contains the command-line tuned for GCC which might be missing options needed to successfully invoke the Clang frontend which is used internally by `fairmq-tidy`. In general, you can pass extra `clang` options via the following options:
```
--extra-arg=<string> - Additional argument to append to the compiler command line
--extra-arg-before=<string> - Additional argument to prepend to the compiler command line
```
E.g. if standard headers are not found, you can hint the location like this:
```
fairmq-top -p <builddir> --extra-arg-before=-I$(clang -print-resource-dir)/include mysourcefile.cpp
```
← [Back](../README.md)

View File

@@ -27,34 +27,34 @@ The next table shows the supported address types for each transport implementati
## 2.1 Message
Devices transport data between each other in form of `FairMQMessage`s. These can be filled with arbitrary content. Message can be initialized in three different ways by calling `NewMessage()`:
Devices transport data between each other in form of `fair::mq::Message`s. These can be filled with arbitrary content. Message can be initialized in three different ways by calling `NewMessage()`:
```cpp
FairMQMessagePtr NewMessage() const;
fair::mq::MessagePtr NewMessage() const;
```
**with no parameters**: Initializes an empty message (typically used for receiving).
```cpp
FairMQMessagePtr NewMessage(const size_t size) const;
fair::mq::MessagePtr NewMessage(const size_t size) const;
```
**given message size**: Initializes message body with a given size. Fill the created contents via buffer pointer.
```cpp
using fairmq_free_fn = void(void* data, void* hint);
FairMQMessagePtr NewMessage(void* data, const size_t size, fairmq_free_fn* ffn, void* hint = nullptr) const;
fair::mq::MessagePtr NewMessage(void* data, const size_t size, fairmq_free_fn* ffn, void* hint = nullptr) const;
```
**given existing buffer and a size**: Initialize the message from an existing buffer. In case of ZeroMQ this is a zero-copy operation.
Additionally, FairMQ provides two more message factories for convenience:
```cpp
template<typename T>
FairMQMessagePtr NewSimpleMessage(const T& data) const
fair::mq::MessagePtr NewSimpleMessage(const T& data) const
```
**copy and own**: Copy the `data` argument into the returned message and take ownership (free memory after message is sent). This interface is useful for small, [trivially copyable](http://en.cppreference.com/w/cpp/concept/TriviallyCopyable) data.
```cpp
template<typename T>
FairMQMessagePtr NewStaticMessage(const T& data) const
fair::mq::MessagePtr NewStaticMessage(const T& data) const
```
**point to existing memory**: The returned message will point to the `data` argument, but not take ownership (someone else must destruct this variable). Make sure that `data` lives long enough to be successfully sent. This interface is most useful for third party managed, contiguous memory (Be aware of shallow types with internal pointer references! These will not be sent.)
@@ -65,19 +65,19 @@ The component of a program, that is reponsible for the allocation or destruction
After queuing a message for sending in FairMQ, the transport takes ownership over the message body and will free it with `free()` after it is no longer used. A callback can be passed to the message object, to be called instead of the destruction with `free()` (for initialization via buffer+size).
```cpp
static void FairMQNoCleanup(void* /*data*/, void* /*obj*/) {}
static void fair::mq::NoCleanup(void* /*data*/, void* /*obj*/) {}
template<typename T>
static void FairMQSimpleMsgCleanup(void* /*data*/, void* obj) { delete static_cast<T*>(obj); }
static void fair::mq::SimpleMsgCleanup(void* /*data*/, void* obj) { delete static_cast<T*>(obj); }
```
For convenience, two common deleter callbacks are already defined in the `FairMQTransportFactory` class to aid the user in controlling ownership of the data.
For convenience, two common deleter callbacks are already defined in the `fair::mq::TransportFactory` class to aid the user in controlling ownership of the data.
## 2.2 Channel
A channel represents a communication endpoint in FairMQ. Usage is similar to a traditional Unix network socket. A device usually contains a number of channels that can either listen for incoming connections from channels of other devices or they can connect to other listening channels. Channels are organized by a channel name and a subchannel index.
```cpp
const FairMQChannel& GetChannel(const std::string& channelName, const int index = 0) const;
const fair::mq::Channel& GetChannel(const std::string& channelName, const int index = 0) const;
```
All subchannels with a common channel name need to be of the same transport type.
@@ -87,7 +87,7 @@ All subchannels with a common channel name need to be of the same transport type
A poller allows to wait on multiple channels either to receive or send a message.
```cpp
FairMQPollerPtr NewPoller(const std::vector<const FairMQChannel*>& channels)
fair::mq::PollerPtr NewPoller(const std::vector<const fair::mq::Channel*>& channels)
```
**list channels**: This poller waits on all supplied channels. Currently, it is limited to channels of the same transport type only.

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -8,10 +8,15 @@
#include <fairmq/Device.h>
#include <fairmq/runDevice.h>
#include <memory>
using namespace std;
using namespace fair::mq;
namespace bpo = boost::program_options;
struct Receiver : fair::mq::Device
namespace {
struct Receiver : Device
{
void InitTask() override
{
@@ -21,15 +26,14 @@ struct Receiver : fair::mq::Device
void Run() override
{
FairMQChannel& dataInChannel = fChannels.at("sr").at(0);
Channel& dataInChannel = fChannels.at("sr").at(0);
while (!NewStatePending()) {
FairMQMessagePtr msg(dataInChannel.Transport()->CreateMessage());
auto msg(dataInChannel.NewMessage());
dataInChannel.Receive(msg);
// void* ptr = msg->GetData();
if (fMaxIterations > 0 && ++fNumIterations >= fMaxIterations) {
LOG(info) << "Configured maximum number of iterations reached. Leaving RUNNING state.";
LOG(info) << "Configured max number of iterations reached. Leaving RUNNING state.";
break;
}
}
@@ -40,13 +44,14 @@ struct Receiver : fair::mq::Device
uint64_t fNumIterations = 0;
};
} // namespace
void addCustomOptions(bpo::options_description& options)
{
options.add_options()
("max-iterations", bpo::value<uint64_t>()->default_value(0), "Maximum number of iterations of Run/ConditionalRun/OnData (0 - infinite)");
options.add_options()(
"max-iterations",
bpo::value<uint64_t>()->default_value(0),
"Maximum number of iterations of Run/ConditionalRun/OnData (0 - infinite)");
}
std::unique_ptr<fair::mq::Device> getDevice(fair::mq::ProgOptions& /*config*/)
{
return std::make_unique<Receiver>();
}
unique_ptr<Device> getDevice(ProgOptions& /*config*/) { return make_unique<Receiver>(); }

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -8,30 +8,34 @@
#include <fairmq/Device.h>
#include <fairmq/runDevice.h>
#include <memory>
namespace bpo = boost::program_options;
using namespace std;
using namespace fair::mq;
struct Sink : fair::mq::Device
namespace {
struct Sink : Device
{
void InitTask() override
{
// Get the fMaxIterations value from the command line options (via fConfig)
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
fChannels.at("data").at(0).Transport()->SubscribeToRegionEvents([](FairMQRegionInfo info) {
fChannels.at("data").at(0).Transport()->SubscribeToRegionEvents([](RegionInfo info) {
LOG(info) << "Region event: " << info.event << ": "
<< (info.managed ? "managed" : "unmanaged")
<< ", id: " << info.id
<< ", ptr: " << info.ptr
<< ", size: " << info.size
<< ", flags: " << info.flags;
<< (info.managed ? "managed" : "unmanaged") << ", id: " << info.id
<< ", ptr: " << info.ptr << ", size: " << info.size
<< ", flags: " << info.flags;
});
}
void Run() override
{
FairMQChannel& dataInChannel = fChannels.at("data").at(0);
Channel& dataInChannel = fChannels.at("data").at(0);
while (!NewStatePending()) {
FairMQMessagePtr msg(dataInChannel.Transport()->CreateMessage());
auto msg(dataInChannel.Transport()->CreateMessage());
dataInChannel.Receive(msg);
// void* ptr = msg->GetData();
@@ -39,11 +43,12 @@ struct Sink : fair::mq::Device
// LOG(info) << "check: " << cptr[3];
if (fMaxIterations > 0 && ++fNumIterations >= fMaxIterations) {
LOG(info) << "Configured maximum number of iterations reached. Leaving RUNNING state.";
LOG(info) << "Configured max number of iterations reached. Leaving RUNNING state.";
break;
}
}
}
void ResetTask() override
{
fChannels.at("data").at(0).Transport()->UnsubscribeFromRegionEvents();
@@ -54,14 +59,14 @@ struct Sink : fair::mq::Device
uint64_t fNumIterations = 0;
};
} // namespace
void addCustomOptions(bpo::options_description& options)
{
options.add_options()
("max-iterations", bpo::value<uint64_t>()->default_value(0), "Maximum number of iterations of Run/ConditionalRun/OnData (0 - infinite)");
options.add_options()(
"max-iterations",
bpo::value<uint64_t>()->default_value(0),
"Maximum number of iterations of Run/ConditionalRun/OnData (0 - infinite)");
}
std::unique_ptr<fair::mq::Device> getDevice(fair::mq::ProgOptions& /*config*/)
{
return std::make_unique<Sink>();
}
unique_ptr<Device> getDevice(ProgOptions& /*config*/) { return make_unique<Sink>(); }

View File

@@ -7,6 +7,11 @@
################################################################################
if(BUILD_FAIRMQ OR BUILD_SDK)
if(BUILD_TIDY_TOOL)
include(FairMQTidy)
endif()
###########
# Version #
###########
@@ -33,10 +38,12 @@ if(BUILD_FAIRMQ OR BUILD_SDK)
tools/Strings.h
tools/Unique.h
tools/Version.h
Error.h
Tools.h
)
set(TOOLS_SOURCE_FILES
Error.cxx
tools/Network.cxx
tools/Process.cxx
tools/Semaphore.cxx
@@ -65,6 +72,9 @@ if(BUILD_FAIRMQ OR BUILD_SDK)
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
OUTPUT_NAME FairMQ${target}
)
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET ${target})
endif()
install(
TARGETS ${target}
EXPORT ${PROJECT_EXPORT_SET}
@@ -120,6 +130,9 @@ if(BUILD_FAIRMQ OR BUILD_SDK)
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
OUTPUT_NAME FairMQ${target}
)
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET ${target})
endif()
install(
TARGETS ${target}
EXPORT ${PROJECT_EXPORT_SET}
@@ -159,6 +172,7 @@ if(BUILD_FAIRMQ)
MemoryResourceTools.h
MemoryResources.h
Message.h
Parts.h
Plugin.h
PluginManager.h
PluginServices.h
@@ -167,6 +181,7 @@ if(BUILD_FAIRMQ)
ProgOptionsFwd.h
Properties.h
PropertyOutput.h
Socket.h
SuboptParser.h
TransportFactory.h
Transports.h
@@ -217,24 +232,20 @@ if(BUILD_FAIRMQ)
# libFairMQ source files #
##########################
set(FAIRMQ_SOURCE_FILES
Channel.cxx
Device.cxx
DeviceRunner.cxx
FairMQChannel.cxx
FairMQDevice.cxx
FairMQLogger.cxx
FairMQMessage.cxx
FairMQPoller.cxx
FairMQSocket.cxx
FairMQTransportFactory.cxx
JSONParser.cxx
MemoryResources.cxx
Plugin.cxx
PluginManager.cxx
PluginServices.cxx
ProgOptions.cxx
JSONParser.cxx
Properties.cxx
SuboptParser.cxx
TransportFactory.cxx
plugins/config/Config.cxx
plugins/control/Control.cxx
MemoryResources.cxx
shmem/Manager.cxx
shmem/Monitor.cxx
)
@@ -327,6 +338,9 @@ if(BUILD_FAIRMQ)
VERSION ${PROJECT_VERSION}
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
)
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET ${target})
endif()
###############
@@ -334,21 +348,39 @@ if(BUILD_FAIRMQ)
###############
add_executable(fairmq-bsampler devices/runBenchmarkSampler.cxx)
target_link_libraries(fairmq-bsampler FairMQ)
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET fairmq-bsampler)
endif()
add_executable(fairmq-merger devices/runMerger.cxx)
target_link_libraries(fairmq-merger FairMQ)
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET fairmq-merger)
endif()
add_executable(fairmq-multiplier devices/runMultiplier.cxx)
target_link_libraries(fairmq-multiplier FairMQ)
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET fairmq-multiplier)
endif()
add_executable(fairmq-proxy devices/runProxy.cxx)
target_link_libraries(fairmq-proxy FairMQ)
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET fairmq-proxy)
endif()
add_executable(fairmq-sink devices/runSink.cxx)
target_link_libraries(fairmq-sink FairMQ)
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET fairmq-sink)
endif()
add_executable(fairmq-splitter devices/runSplitter.cxx)
target_link_libraries(fairmq-splitter FairMQ)
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET fairmq-splitter)
endif()
add_executable(fairmq-shmmonitor shmem/Monitor.cxx shmem/Monitor.h shmem/runMonitor.cxx)
target_compile_features(fairmq-shmmonitor PUBLIC cxx_std_17)
@@ -370,12 +402,18 @@ if(BUILD_FAIRMQ)
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>
)
target_compile_definitions(fairmq-shmmonitor PUBLIC FAIRMQ_HAS_STD_FILESYSTEM=${FAIRMQ_HAS_STD_FILESYSTEM})
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET fairmq-shmmonitor)
endif()
add_executable(fairmq-uuid-gen tools/runUuidGenerator.cxx)
target_link_libraries(fairmq-uuid-gen PUBLIC
Boost::program_options
Tools
)
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET fairmq-uuid-gen)
endif()
###########

View File

@@ -1,30 +1,27 @@
/********************************************************************************
* Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include "FairMQChannel.h"
#include <fairmq/tools/Strings.h>
#include <fairmq/Properties.h>
#include <boost/algorithm/string.hpp> // join/split
#include <cstddef> // size_t
#include <fairlogger/Logger.h>
#include <boost/algorithm/string.hpp> // join/split
#include <cstddef> // size_t
#include <fairmq/Channel.h>
#include <fairmq/Properties.h>
#include <fairmq/Tools.h>
#include <random>
#include <regex>
#include <set>
#include <random>
namespace fair::mq {
using namespace std;
using namespace fair::mq;
template<typename T>
T GetPropertyOrDefault(const fair::mq::Properties& m, const string& k, const T& ifNotFound)
T GetPropertyOrDefault(const Properties& m, const string& k, const T& ifNotFound)
{
if (m.count(k)) {
return boost::any_cast<T>(m.at(k));
@@ -32,39 +29,39 @@ T GetPropertyOrDefault(const fair::mq::Properties& m, const string& k, const T&
return ifNotFound;
}
constexpr fair::mq::Transport FairMQChannel::DefaultTransportType;
constexpr const char* FairMQChannel::DefaultTransportName;
constexpr const char* FairMQChannel::DefaultName;
constexpr const char* FairMQChannel::DefaultType;
constexpr const char* FairMQChannel::DefaultMethod;
constexpr const char* FairMQChannel::DefaultAddress;
constexpr int FairMQChannel::DefaultSndBufSize;
constexpr int FairMQChannel::DefaultRcvBufSize;
constexpr int FairMQChannel::DefaultSndKernelSize;
constexpr int FairMQChannel::DefaultRcvKernelSize;
constexpr int FairMQChannel::DefaultLinger;
constexpr int FairMQChannel::DefaultRateLogging;
constexpr int FairMQChannel::DefaultPortRangeMin;
constexpr int FairMQChannel::DefaultPortRangeMax;
constexpr bool FairMQChannel::DefaultAutoBind;
constexpr Transport Channel::DefaultTransportType;
constexpr const char* Channel::DefaultTransportName;
constexpr const char* Channel::DefaultName;
constexpr const char* Channel::DefaultType;
constexpr const char* Channel::DefaultMethod;
constexpr const char* Channel::DefaultAddress;
constexpr int Channel::DefaultSndBufSize;
constexpr int Channel::DefaultRcvBufSize;
constexpr int Channel::DefaultSndKernelSize;
constexpr int Channel::DefaultRcvKernelSize;
constexpr int Channel::DefaultLinger;
constexpr int Channel::DefaultRateLogging;
constexpr int Channel::DefaultPortRangeMin;
constexpr int Channel::DefaultPortRangeMax;
constexpr bool Channel::DefaultAutoBind;
FairMQChannel::FairMQChannel()
: FairMQChannel(DefaultName, DefaultType, DefaultMethod, DefaultAddress, nullptr)
Channel::Channel()
: Channel(DefaultName, DefaultType, DefaultMethod, DefaultAddress, nullptr)
{}
FairMQChannel::FairMQChannel(const string& name)
: FairMQChannel(name, DefaultType, DefaultMethod, DefaultAddress, nullptr)
Channel::Channel(const string& name)
: Channel(name, DefaultType, DefaultMethod, DefaultAddress, nullptr)
{}
FairMQChannel::FairMQChannel(const string& type, const string& method, const string& address)
: FairMQChannel(DefaultName, type, method, address, nullptr)
Channel::Channel(const string& type, const string& method, const string& address)
: Channel(DefaultName, type, method, address, nullptr)
{}
FairMQChannel::FairMQChannel(const string& name, const string& type, shared_ptr<FairMQTransportFactory> factory)
: FairMQChannel(name, type, DefaultMethod, DefaultAddress, factory)
Channel::Channel(const string& name, const string& type, shared_ptr<TransportFactory> factory)
: Channel(name, type, DefaultMethod, DefaultAddress, factory)
{}
FairMQChannel::FairMQChannel(string name, string type, string method, string address, shared_ptr<FairMQTransportFactory> factory)
Channel::Channel(string name, string type, string method, string address, shared_ptr<TransportFactory> factory)
: fTransportFactory(factory)
, fTransportType(factory ? factory->GetType() : DefaultTransportType)
, fSocket(factory ? factory->CreateSocket(type, name) : nullptr)
@@ -87,8 +84,8 @@ FairMQChannel::FairMQChannel(string name, string type, string method, string add
// LOG(warn) << "Constructing channel '" << fName << "'";
}
FairMQChannel::FairMQChannel(const string& name, int index, const fair::mq::Properties& properties)
: FairMQChannel(tools::ToString(name, "[", index, "]"), "unspecified", "unspecified", "unspecified", nullptr)
Channel::Channel(const string& name, int index, const Properties& properties)
: Channel(tools::ToString(name, "[", index, "]"), "unspecified", "unspecified", "unspecified", nullptr)
{
string prefix(tools::ToString("chans.", name, ".", index, "."));
@@ -107,11 +104,11 @@ FairMQChannel::FairMQChannel(const string& name, int index, const fair::mq::Prop
fAutoBind = GetPropertyOrDefault(properties, string(prefix + "autoBind"), DefaultAutoBind);
}
FairMQChannel::FairMQChannel(const FairMQChannel& chan)
: FairMQChannel(chan, chan.fName)
Channel::Channel(const Channel& chan)
: Channel(chan, chan.fName)
{}
FairMQChannel::FairMQChannel(const FairMQChannel& chan, string newName)
Channel::Channel(const Channel& chan, string newName)
: fTransportFactory(nullptr)
, fTransportType(chan.fTransportType)
, fSocket(nullptr)
@@ -132,7 +129,7 @@ FairMQChannel::FairMQChannel(const FairMQChannel& chan, string newName)
, fMultipart(chan.fMultipart)
{}
FairMQChannel& FairMQChannel::operator=(const FairMQChannel& chan)
Channel& Channel::operator=(const Channel& chan)
{
if (this == &chan) {
return *this;
@@ -160,7 +157,7 @@ FairMQChannel& FairMQChannel::operator=(const FairMQChannel& chan)
return *this;
}
bool FairMQChannel::Validate()
bool Channel::Validate()
try {
stringstream ss;
ss << "Validating channel '" << fName << "'... ";
@@ -305,11 +302,11 @@ try {
LOG(debug) << ss.str();
return true;
} catch (exception& e) {
LOG(error) << "Exception caught in FairMQChannel::ValidateChannel: " << e.what();
LOG(error) << "Exception caught in Channel::ValidateChannel: " << e.what();
throw ChannelConfigurationError(tools::ToString(e.what()));
}
void FairMQChannel::Init()
void Channel::Init()
{
fSocket = fTransportFactory->CreateSocket(fType, fName);
@@ -329,12 +326,12 @@ void FairMQChannel::Init()
}
}
bool FairMQChannel::ConnectEndpoint(const string& endpoint)
bool Channel::ConnectEndpoint(const string& endpoint)
{
return fSocket->Connect(endpoint);
}
bool FairMQChannel::BindEndpoint(string& endpoint)
bool Channel::BindEndpoint(string& endpoint)
{
// try to bind to the configured port. If it fails, try random one (if AutoBind is on).
if (fSocket->Bind(endpoint)) {
@@ -374,5 +371,6 @@ bool FairMQChannel::BindEndpoint(string& endpoint)
return false;
}
}
}
} // namespace fair::mq

View File

@@ -9,12 +9,449 @@
#ifndef FAIR_MQ_CHANNEL_H
#define FAIR_MQ_CHANNEL_H
#include <FairMQChannel.h>
#include <cstdint> // int64_t
#include <fairmq/Message.h>
#include <fairmq/Parts.h>
#include <fairmq/Properties.h>
#include <fairmq/Socket.h>
#include <fairmq/TransportFactory.h>
#include <fairmq/Transports.h>
#include <fairmq/UnmanagedRegion.h>
#include <memory> // unique_ptr, shared_ptr
#include <mutex>
#include <stdexcept>
#include <string>
#include <utility> // std::move
#include <vector>
namespace fair::mq {
using Channel = FairMQChannel;
/**
* @class Channel Channel.h <fairmq/Channel.h>
* @brief Wrapper class for Socket and related methods
*
* The class is not thread-safe.
*/
class Channel
{
friend class Device;
} // namespace fair::mq
public:
/// Default constructor
Channel();
/// Constructor
/// @param name Channel name
Channel(const std::string& name);
/// Constructor
/// @param type Socket type (push/pull/pub/sub/spub/xsub/pair/req/rep/dealer/router/)
/// @param method Socket method (bind/connect)
/// @param address Network address to bind/connect to (e.g. "tcp://127.0.0.1:5555" or "ipc://abc")
Channel(const std::string& type, const std::string& method, const std::string& address);
/// Constructor
/// @param name Channel name
/// @param type Socket type (push/pull/pub/sub/spub/xsub/pair/req/rep/dealer/router/)
/// @param factory TransportFactory
Channel(const std::string& name, const std::string& type, std::shared_ptr<TransportFactory> factory);
/// Constructor
/// @param name Channel name
/// @param type Socket type (push/pull/pub/sub/spub/xsub/pair/req/rep/dealer/router/)
/// @param method Socket method (bind/connect)
/// @param address Network address to bind/connect to (e.g. "tcp://127.0.0.1:5555" or "ipc://abc")
/// @param factory TransportFactory
Channel(std::string name, std::string type, std::string method, std::string address, std::shared_ptr<TransportFactory> factory);
Channel(const std::string& name, int index, const Properties& properties);
/// Copy Constructor
Channel(const Channel&);
/// Copy Constructor (with new name)
Channel(const Channel&, std::string name);
/// Move constructor
// Channel(Channel&&) = delete;
/// Assignment operator
Channel& operator=(const Channel&);
/// Move assignment operator
// Channel& operator=(Channel&&) = delete;
/// Destructor
virtual ~Channel() = default;
// { LOG(warn) << "Destroying channel '" << fName << "'"; }
struct ChannelConfigurationError : std::runtime_error { using std::runtime_error::runtime_error; };
Socket& GetSocket() const { assert(fSocket); return *fSocket; }
bool Bind(const std::string& address)
{
fMethod = "bind";
fAddress = address;
return fSocket->Bind(address);
}
bool Connect(const std::string& address)
{
fMethod = "connect";
fAddress = address;
return fSocket->Connect(address);
}
/// Get channel name
/// @return Returns full channel name (e.g. "data[0]")
std::string GetName() const { return fName; }
/// Get channel prefix
/// @return Returns channel prefix (e.g. "data" in "data[0]")
std::string GetPrefix() const
{
std::string prefix = fName;
prefix = prefix.erase(fName.rfind('['));
return prefix;
}
/// Get channel index
/// @return Returns channel index (e.g. 0 in "data[0]")
std::string GetIndex() const
{
std::string indexStr = fName;
indexStr.erase(indexStr.rfind(']'));
indexStr.erase(0, indexStr.rfind('[') + 1);
return indexStr;
}
/// Get socket type
/// @return Returns socket type (push/pull/pub/sub/spub/xsub/pair/req/rep/dealer/router/)
std::string GetType() const { return fType; }
/// Get socket method
/// @return Returns socket method (bind/connect)
std::string GetMethod() const { return fMethod; }
/// Get socket address (e.g. "tcp://127.0.0.1:5555" or "ipc://abc")
/// @return Returns socket address (e.g. "tcp://127.0.0.1:5555" or "ipc://abc")
std::string GetAddress() const { return fAddress; }
/// Get channel transport name ("default", "zeromq" or "shmem")
/// @return Returns channel transport name (e.g. "default", "zeromq" or "shmem")
std::string GetTransportName() const { return TransportName(fTransportType); }
/// Get channel transport type
/// @return Returns channel transport type
mq::Transport GetTransportType() const { return fTransportType; }
/// Get socket send buffer size (in number of messages)
/// @return Returns socket send buffer size (in number of messages)
int GetSndBufSize() const { return fSndBufSize; }
/// Get socket receive buffer size (in number of messages)
/// @return Returns socket receive buffer size (in number of messages)
int GetRcvBufSize() const { return fRcvBufSize; }
/// Get socket kernel transmit send buffer size (in bytes)
/// @return Returns socket kernel transmit send buffer size (in bytes)
int GetSndKernelSize() const { return fSndKernelSize; }
/// Get socket kernel transmit receive buffer size (in bytes)
/// @return Returns socket kernel transmit receive buffer size (in bytes)
int GetRcvKernelSize() const { return fRcvKernelSize; }
/// Get linger duration (in milliseconds)
/// @return Returns linger duration (in milliseconds)
int GetLinger() const { return fLinger; }
/// Get socket rate logging interval (in seconds)
/// @return Returns socket rate logging interval (in seconds)
int GetRateLogging() const { return fRateLogging; }
/// Get start of the port range for automatic binding
/// @return start of the port range
int GetPortRangeMin() const { return fPortRangeMin; }
/// Get end of the port range for automatic binding
/// @return end of the port range
int GetPortRangeMax() const { return fPortRangeMax; }
/// Set automatic binding (pick random port if bind fails)
/// @return true/false, true if automatic binding is enabled
bool GetAutoBind() const { return fAutoBind; }
/// Set channel name
/// @param name Arbitrary channel name
void UpdateName(const std::string& name) { fName = name; Invalidate(); }
/// Set socket type
/// @param type Socket type (push/pull/pub/sub/spub/xsub/pair/req/rep/dealer/router/)
void UpdateType(const std::string& type) { fType = type; Invalidate(); }
/// Set socket method
/// @param method Socket method (bind/connect)
void UpdateMethod(const std::string& method) { fMethod = method; Invalidate(); }
/// Set socket address
/// @param Socket address (e.g. "tcp://127.0.0.1:5555" or "ipc://abc")
void UpdateAddress(const std::string& address) { fAddress = address; Invalidate(); }
/// Set channel transport
/// @param transport transport string ("default", "zeromq" or "shmem")
void UpdateTransport(const std::string& transport) { fTransportType = TransportType(transport); Invalidate(); }
/// Set socket send buffer size
/// @param sndBufSize Socket send buffer size (in number of messages)
void UpdateSndBufSize(const int sndBufSize) { fSndBufSize = sndBufSize; Invalidate(); }
/// Set socket receive buffer size
/// @param rcvBufSize Socket receive buffer size (in number of messages)
void UpdateRcvBufSize(const int rcvBufSize) { fRcvBufSize = rcvBufSize; Invalidate(); }
/// Set socket kernel transmit send buffer size (in bytes)
/// @param sndKernelSize Socket send buffer size (in bytes)
void UpdateSndKernelSize(const int sndKernelSize) { fSndKernelSize = sndKernelSize; Invalidate(); }
/// Set socket kernel transmit receive buffer size (in bytes)
/// @param rcvKernelSize Socket receive buffer size (in bytes)
void UpdateRcvKernelSize(const int rcvKernelSize) { fRcvKernelSize = rcvKernelSize; Invalidate(); }
/// Set linger duration (in milliseconds)
/// @param duration linger duration (in milliseconds)
void UpdateLinger(const int duration) { fLinger = duration; Invalidate(); }
/// Set socket rate logging interval (in seconds)
/// @param rateLogging Socket rate logging interval (in seconds)
void UpdateRateLogging(const int rateLogging) { fRateLogging = rateLogging; Invalidate(); }
/// Set start of the port range for automatic binding
/// @param minPort start of the port range
void UpdatePortRangeMin(const int minPort) { fPortRangeMin = minPort; Invalidate(); }
/// Set end of the port range for automatic binding
/// @param maxPort end of the port range
void UpdatePortRangeMax(const int maxPort) { fPortRangeMax = maxPort; Invalidate(); }
/// Set automatic binding (pick random port if bind fails)
/// @param autobind true/false, true to enable automatic binding
void UpdateAutoBind(const bool autobind) { fAutoBind = autobind; Invalidate(); }
/// Checks if the configured channel settings are valid (checks the validity parameter, without running full validation (as oposed to ValidateChannel()))
/// @return true if channel settings are valid, false otherwise.
bool IsValid() const { return fValid; }
/// Validates channel configuration
/// @return true if channel settings are valid, false otherwise.
bool Validate();
void Init();
bool ConnectEndpoint(const std::string& endpoint);
bool BindEndpoint(std::string& endpoint);
/// invalidates the channel (requires validation to be used again).
void Invalidate() { fValid = false; }
/// Sends a message to the socket queue.
/// @param msg Constant reference of unique_ptr to a Message
/// @param sndTimeoutInMs send timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Send(MessagePtr& msg, int sndTimeoutInMs = -1)
{
CheckSendCompatibility(msg);
return fSocket->Send(msg, sndTimeoutInMs);
}
/// Receives a message from the socket queue.
/// @param msg Constant reference of unique_ptr to a Message
/// @param rcvTimeoutInMs receive timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Receive(MessagePtr& msg, int rcvTimeoutInMs = -1)
{
CheckReceiveCompatibility(msg);
return fSocket->Receive(msg, rcvTimeoutInMs);
}
/// Send a vector of messages
/// @param msgVec message vector reference
/// @param sndTimeoutInMs send timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Send(std::vector<MessagePtr>& msgVec, int sndTimeoutInMs = -1)
{
CheckSendCompatibility(msgVec);
return fSocket->Send(msgVec, sndTimeoutInMs);
}
/// Receive a vector of messages
/// @param msgVec message vector reference
/// @param rcvTimeoutInMs receive timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Receive(std::vector<MessagePtr>& msgVec, int rcvTimeoutInMs = -1)
{
CheckReceiveCompatibility(msgVec);
return fSocket->Receive(msgVec, rcvTimeoutInMs);
}
/// Send Parts
/// @param parts Parts reference
/// @param sndTimeoutInMs send timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Send(Parts& parts, int sndTimeoutInMs = -1)
{
return Send(parts.fParts, sndTimeoutInMs);
}
/// Receive Parts
/// @param parts Parts reference
/// @param rcvTimeoutInMs receive timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Receive(Parts& parts, int rcvTimeoutInMs = -1)
{
return Receive(parts.fParts, rcvTimeoutInMs);
}
unsigned long GetBytesTx() const { return fSocket->GetBytesTx(); }
unsigned long GetBytesRx() const { return fSocket->GetBytesRx(); }
unsigned long GetMessagesTx() const { return fSocket->GetMessagesTx(); }
unsigned long GetMessagesRx() const { return fSocket->GetMessagesRx(); }
auto Transport() -> TransportFactory* { return fTransportFactory.get(); };
template<typename... Args>
MessagePtr NewMessage(Args&&... args)
{
return Transport()->CreateMessage(std::forward<Args>(args)...);
}
template<typename T>
MessagePtr NewSimpleMessage(const T& data)
{
return Transport()->NewSimpleMessage(data);
}
template<typename T>
MessagePtr NewStaticMessage(const T& data)
{
return Transport()->NewStaticMessage(data);
}
template<typename... Args>
UnmanagedRegionPtr NewUnmanagedRegion(Args&&... args)
{
return Transport()->CreateUnmanagedRegion(std::forward<Args>(args)...);
}
static constexpr mq::Transport DefaultTransportType = mq::Transport::DEFAULT;
static constexpr const char* DefaultTransportName = "default";
static constexpr const char* DefaultName = "";
static constexpr const char* DefaultType = "unspecified";
static constexpr const char* DefaultMethod = "unspecified";
static constexpr const char* DefaultAddress = "unspecified";
static constexpr int DefaultSndBufSize = 1000;
static constexpr int DefaultRcvBufSize = 1000;
static constexpr int DefaultSndKernelSize = 0;
static constexpr int DefaultRcvKernelSize = 0;
static constexpr int DefaultLinger = 500;
static constexpr int DefaultRateLogging = 1;
static constexpr int DefaultPortRangeMin = 22000;
static constexpr int DefaultPortRangeMax = 23000;
static constexpr bool DefaultAutoBind = true;
private:
std::shared_ptr<TransportFactory> fTransportFactory;
mq::Transport fTransportType;
std::unique_ptr<Socket> fSocket;
std::string fName;
std::string fType;
std::string fMethod;
std::string fAddress;
int fSndBufSize;
int fRcvBufSize;
int fSndKernelSize;
int fRcvKernelSize;
int fLinger;
int fRateLogging;
int fPortRangeMin;
int fPortRangeMax;
bool fAutoBind;
bool fValid;
bool fMultipart;
void CheckSendCompatibility(MessagePtr& msg)
{
if (fTransportType != msg->GetType()) {
if (msg->GetSize() > 0) {
MessagePtr msgWrapper(NewMessage(
msg->GetData(),
msg->GetSize(),
[](void* /*data*/, void* _msg) { delete static_cast<Message*>(_msg); },
msg.get()
));
msg.release();
msg = move(msgWrapper);
} else {
MessagePtr newMsg(NewMessage());
msg = move(newMsg);
}
}
}
void CheckSendCompatibility(std::vector<MessagePtr>& msgVec)
{
for (auto& msg : msgVec) {
if (fTransportType != msg->GetType()) {
if (msg->GetSize() > 0) {
MessagePtr msgWrapper(NewMessage(
msg->GetData(),
msg->GetSize(),
[](void* /*data*/, void* _msg) { delete static_cast<Message*>(_msg); },
msg.get()
));
msg.release();
msg = move(msgWrapper);
} else {
MessagePtr newMsg(NewMessage());
msg = move(newMsg);
}
}
}
}
void CheckReceiveCompatibility(MessagePtr& msg)
{
if (fTransportType != msg->GetType()) {
MessagePtr newMsg(NewMessage());
msg = move(newMsg);
}
}
void CheckReceiveCompatibility(std::vector<MessagePtr>& msgVec)
{
for (auto& msg : msgVec) {
if (fTransportType != msg->GetType()) {
MessagePtr newMsg(NewMessage());
msg = move(newMsg);
}
}
}
void InitTransport(std::shared_ptr<TransportFactory> factory)
{
fTransportFactory = factory;
fTransportType = factory->GetType();
}
};
} // namespace fair::mq
// using FairMQChannel [[deprecated("Use fair::mq::Channel")]] = fair::mq::Channel;
using FairMQChannel = fair::mq::Channel;
#endif // FAIR_MQ_CHANNEL_H

View File

@@ -1,51 +1,48 @@
/********************************************************************************
* Copyright (C) 2012-2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2012-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include <FairMQDevice.h>
#include <fairmq/tools/RateLimit.h>
#include <fairmq/tools/Network.h>
#include <boost/algorithm/string.hpp> // join/split
#include <list>
#include <algorithm> // std::max
#include <boost/algorithm/string.hpp> // join/split
#include <chrono>
#include <fairmq/Device.h>
#include <fairmq/Tools.h>
#include <future>
#include <iomanip>
#include <list>
#include <mutex>
#include <thread>
#include <iomanip>
#include <future>
#include <algorithm> // std::max
namespace fair::mq {
using namespace std;
using namespace fair::mq;
constexpr const char* FairMQDevice::DefaultId;
constexpr int FairMQDevice::DefaultIOThreads;
constexpr const char* FairMQDevice::DefaultTransportName;
constexpr fair::mq::Transport FairMQDevice::DefaultTransportType;
constexpr const char* FairMQDevice::DefaultNetworkInterface;
constexpr int FairMQDevice::DefaultInitTimeout;
constexpr uint64_t FairMQDevice::DefaultMaxRunTime;
constexpr float FairMQDevice::DefaultRate;
constexpr const char* FairMQDevice::DefaultSession;
constexpr const char* Device::DefaultId;
constexpr int Device::DefaultIOThreads;
constexpr const char* Device::DefaultTransportName;
constexpr mq::Transport Device::DefaultTransportType;
constexpr const char* Device::DefaultNetworkInterface;
constexpr int Device::DefaultInitTimeout;
constexpr uint64_t Device::DefaultMaxRunTime;
constexpr float Device::DefaultRate;
constexpr const char* Device::DefaultSession;
struct StateSubscription
{
fair::mq::StateMachine& fStateMachine;
fair::mq::StateQueue& fStateQueue;
StateMachine& fStateMachine;
StateQueue& fStateQueue;
string fId;
explicit StateSubscription(string id, fair::mq::StateMachine& stateMachine, fair::mq::StateQueue& stateQueue)
explicit StateSubscription(string id, StateMachine& stateMachine, StateQueue& stateQueue)
: fStateMachine(stateMachine)
, fStateQueue(stateQueue)
, fId(std::move(id))
{
fStateMachine.SubscribeToStateChange(fId, [&](fair::mq::State state) {
fStateMachine.SubscribeToStateChange(fId, [&](State state) {
fStateQueue.Push(state);
});
}
@@ -55,23 +52,23 @@ struct StateSubscription
}
};
FairMQDevice::FairMQDevice()
: FairMQDevice(nullptr, {0, 0, 0})
Device::Device()
: Device(nullptr, {0, 0, 0})
{}
FairMQDevice::FairMQDevice(ProgOptions& config)
: FairMQDevice(&config, {0, 0, 0})
Device::Device(ProgOptions& config)
: Device(&config, {0, 0, 0})
{}
FairMQDevice::FairMQDevice(const tools::Version version)
: FairMQDevice(nullptr, version)
Device::Device(const tools::Version version)
: Device(nullptr, version)
{}
FairMQDevice::FairMQDevice(ProgOptions& config, const tools::Version version)
: FairMQDevice(&config, version)
Device::Device(ProgOptions& config, const tools::Version version)
: Device(&config, version)
{}
FairMQDevice::FairMQDevice(ProgOptions* config, const tools::Version version)
Device::Device(ProgOptions* config, const tools::Version version)
: fTransportFactory(nullptr)
, fInternalConfig(config ? nullptr : make_unique<ProgOptions>())
, fConfig(config ? config : fInternalConfig.get())
@@ -97,34 +94,34 @@ FairMQDevice::FairMQDevice(ProgOptions* config, const tools::Version version)
}
});
fStateMachine.HandleStates([&](fair::mq::State state) {
fStateMachine.HandleStates([&](State state) {
LOG(trace) << "device notified on new state: " << state;
fStateQueue.Push(state);
switch (state) {
case fair::mq::State::InitializingDevice:
case State::InitializingDevice:
InitWrapper();
break;
case fair::mq::State::Binding:
case State::Binding:
BindWrapper();
break;
case fair::mq::State::Connecting:
case State::Connecting:
ConnectWrapper();
break;
case fair::mq::State::InitializingTask:
case State::InitializingTask:
InitTaskWrapper();
break;
case fair::mq::State::Running:
case State::Running:
RunWrapper();
break;
case fair::mq::State::ResettingTask:
case State::ResettingTask:
ResetTaskWrapper();
break;
case fair::mq::State::ResettingDevice:
case State::ResettingDevice:
ResetWrapper();
break;
case fair::mq::State::Exiting:
case State::Exiting:
Exit();
break;
default:
@@ -136,7 +133,7 @@ FairMQDevice::FairMQDevice(ProgOptions* config, const tools::Version version)
fStateMachine.Start();
}
void FairMQDevice::TransitionTo(const fair::mq::State s)
void Device::TransitionTo(const State s)
{
{
lock_guard<mutex> lock(fTransitionMtx);
@@ -147,7 +144,7 @@ void FairMQDevice::TransitionTo(const fair::mq::State s)
fTransitioning = true;
}
using fair::mq::State;
using mq::State;
StateQueue sq;
StateSubscription ss(tools::ToString(fId, ".TransitionTo"), fStateMachine, sq);
@@ -203,7 +200,7 @@ void FairMQDevice::TransitionTo(const fair::mq::State s)
}
}
void FairMQDevice::InitWrapper()
void Device::InitWrapper()
{
// run initialization once CompleteInit transition is requested
fStateMachine.WaitForPendingState();
@@ -217,7 +214,7 @@ void FairMQDevice::InitWrapper()
fInitializationTimeoutInS = fConfig->GetProperty<int>("init-timeout", DefaultInitTimeout);
try {
fDefaultTransportType = fair::mq::TransportTypes.at(fConfig->GetProperty<string>("transport", DefaultTransportName));
fDefaultTransportType = TransportTypes.at(fConfig->GetProperty<string>("transport", DefaultTransportName));
} catch (const exception& e) {
LOG(error) << "exception: " << e.what();
LOG(error) << "invalid transport type provided: " << fConfig->GetProperty<string>("transport", "not provided");
@@ -231,7 +228,7 @@ void FairMQDevice::InitWrapper()
}
}
LOG(debug) << "Setting '" << fair::mq::TransportNames.at(fDefaultTransportType) << "' as default transport for the device";
LOG(debug) << "Setting '" << TransportNames.at(fDefaultTransportType) << "' as default transport for the device";
fTransportFactory = AddTransport(fDefaultTransportType);
string networkInterface = fConfig->GetProperty<string>("network-interface", DefaultNetworkInterface);
@@ -241,7 +238,7 @@ void FairMQDevice::InitWrapper()
int subChannelIndex = 0;
for (auto& subChannel : channel.second) {
// set channel transport
LOG(debug) << "Initializing transport for channel " << subChannel.fName << ": " << fair::mq::TransportNames.at(subChannel.fTransportType);
LOG(debug) << "Initializing transport for channel " << subChannel.fName << ": " << TransportNames.at(subChannel.fTransportType);
subChannel.InitTransport(AddTransport(subChannel.fTransportType));
if (subChannel.fMethod == "bind") {
@@ -278,7 +275,7 @@ void FairMQDevice::InitWrapper()
// ChangeState(Transition::Auto);
}
void FairMQDevice::BindWrapper()
void Device::BindWrapper()
{
// Bind channels. Here one run is enough, because bind settings should be available locally
// If necessary this could be handled in the same way as the connecting channels
@@ -294,7 +291,7 @@ void FairMQDevice::BindWrapper()
ChangeState(Transition::Auto);
}
void FairMQDevice::ConnectWrapper()
void Device::ConnectWrapper()
{
// go over the list of channels until all are initialized (and removed from the uninitialized list)
int numAttempts = 1;
@@ -331,7 +328,7 @@ void FairMQDevice::ConnectWrapper()
ChangeState(Transition::Auto);
}
void FairMQDevice::AttachChannels(vector<FairMQChannel*>& chans)
void Device::AttachChannels(vector<Channel*>& chans)
{
auto itr = chans.begin();
@@ -351,7 +348,7 @@ void FairMQDevice::AttachChannels(vector<FairMQChannel*>& chans)
}
}
bool FairMQDevice::AttachChannel(FairMQChannel& chan)
bool Device::AttachChannel(Channel& chan)
{
vector<string> endpoints;
string chanAddress = chan.GetAddress();
@@ -424,19 +421,19 @@ bool FairMQDevice::AttachChannel(FairMQChannel& chan)
return true;
}
void FairMQDevice::InitTaskWrapper()
void Device::InitTaskWrapper()
{
InitTask();
ChangeState(Transition::Auto);
}
void FairMQDevice::RunWrapper()
void Device::RunWrapper()
{
LOG(info) << "DEVICE: Running...";
// start the rate logger thread
future<void> rateLogger = async(launch::async, &FairMQDevice::LogSocketRates, this);
future<void> rateLogger = async(launch::async, &Device::LogSocketRates, this);
// notify transports to resume transfers
for (auto& t : fTransports) {
@@ -486,7 +483,7 @@ void FairMQDevice::RunWrapper()
rateLogger.get();
}
void FairMQDevice::HandleSingleChannelInput()
void Device::HandleSingleChannelInput()
{
bool proceed = true;
@@ -501,14 +498,14 @@ void FairMQDevice::HandleSingleChannelInput()
}
}
void FairMQDevice::HandleMultipleChannelInput()
void Device::HandleMultipleChannelInput()
{
// check if more than one transport is used
fMultitransportInputs.clear();
for (const auto& k : fInputChannelKeys) {
fair::mq::Transport t = fChannels.at(k).at(0).fTransportType;
mq::Transport t = fChannels.at(k).at(0).fTransportType;
if (fMultitransportInputs.find(t) == fMultitransportInputs.end()) {
fMultitransportInputs.insert(pair<fair::mq::Transport, vector<string>>(t, vector<string>()));
fMultitransportInputs.insert(pair<mq::Transport, vector<string>>(t, vector<string>()));
fMultitransportInputs.at(t).push_back(k);
} else {
fMultitransportInputs.at(t).push_back(k);
@@ -533,7 +530,7 @@ void FairMQDevice::HandleMultipleChannelInput()
} else { // otherwise poll directly
bool proceed = true;
FairMQPollerPtr poller(fChannels.at(fInputChannelKeys.at(0)).at(0).fTransportFactory->CreatePoller(fChannels, fInputChannelKeys));
PollerPtr poller(fChannels.at(fInputChannelKeys.at(0)).at(0).fTransportFactory->CreatePoller(fChannels, fInputChannelKeys));
while (!NewStatePending() && proceed) {
poller->Poll(200);
@@ -561,14 +558,14 @@ void FairMQDevice::HandleMultipleChannelInput()
}
}
void FairMQDevice::HandleMultipleTransportInput()
void Device::HandleMultipleTransportInput()
{
vector<thread> threads;
fMultitransportProceed = true;
for (const auto& i : fMultitransportInputs) {
threads.emplace_back(thread(&FairMQDevice::PollForTransport, this, fTransports.at(i.first).get(), i.second));
threads.emplace_back(thread(&Device::PollForTransport, this, fTransports.at(i.first).get(), i.second));
}
for (thread& t : threads) {
@@ -576,10 +573,10 @@ void FairMQDevice::HandleMultipleTransportInput()
}
}
void FairMQDevice::PollForTransport(const FairMQTransportFactory* factory, const vector<string>& channelKeys)
void Device::PollForTransport(const TransportFactory* factory, const vector<string>& channelKeys)
{
try {
FairMQPollerPtr poller(factory->CreatePoller(fChannels, channelKeys));
PollerPtr poller(factory->CreatePoller(fChannels, channelKeys));
while (!NewStatePending() && fMultitransportProceed) {
poller->Poll(500);
@@ -610,14 +607,14 @@ void FairMQDevice::PollForTransport(const FairMQTransportFactory* factory, const
}
}
} catch (exception& e) {
LOG(error) << "FairMQDevice::PollForTransport() failed: " << e.what() << ", going to ERROR state.";
throw runtime_error(tools::ToString("FairMQDevice::PollForTransport() failed: ", e.what(), ", going to ERROR state."));
LOG(error) << "fair::mq::Device::PollForTransport() failed: " << e.what() << ", going to ERROR state.";
throw runtime_error(tools::ToString("fair::mq::Device::PollForTransport() failed: ", e.what(), ", going to ERROR state."));
}
}
bool FairMQDevice::HandleMsgInput(const string& chName, const InputMsgCallback& callback, int i)
bool Device::HandleMsgInput(const string& chName, const InputMsgCallback& callback, int i)
{
unique_ptr<FairMQMessage> input(fChannels.at(chName).at(i).fTransportFactory->CreateMessage());
unique_ptr<Message> input(fChannels.at(chName).at(i).fTransportFactory->CreateMessage());
if (Receive(input, chName, i) >= 0) {
return callback(input, i);
@@ -626,9 +623,9 @@ bool FairMQDevice::HandleMsgInput(const string& chName, const InputMsgCallback&
}
}
bool FairMQDevice::HandleMultipartInput(const string& chName, const InputMultipartCallback& callback, int i)
bool Device::HandleMultipartInput(const string& chName, const InputMultipartCallback& callback, int i)
{
FairMQParts input;
Parts input;
if (Receive(input, chName, i) >= 0) {
return callback(input, i);
@@ -637,34 +634,34 @@ bool FairMQDevice::HandleMultipartInput(const string& chName, const InputMultipa
}
}
shared_ptr<FairMQTransportFactory> FairMQDevice::AddTransport(fair::mq::Transport transport)
shared_ptr<TransportFactory> Device::AddTransport(mq::Transport transport)
{
if (transport == fair::mq::Transport::DEFAULT) {
if (transport == mq::Transport::DEFAULT) {
transport = fDefaultTransportType;
}
auto i = fTransports.find(transport);
if (i == fTransports.end()) {
LOG(debug) << "Adding '" << fair::mq::TransportNames.at(transport) << "' transport";
auto tr = FairMQTransportFactory::CreateTransportFactory(fair::mq::TransportNames.at(transport), fId, fConfig);
LOG(debug) << "Adding '" << TransportNames.at(transport) << "' transport";
auto tr = TransportFactory::CreateTransportFactory(TransportNames.at(transport), fId, fConfig);
fTransports.insert({transport, tr});
return tr;
} else {
LOG(debug) << "Reusing existing '" << fair::mq::TransportNames.at(transport) << "' transport";
LOG(debug) << "Reusing existing '" << TransportNames.at(transport) << "' transport";
return i->second;
}
}
void FairMQDevice::SetConfig(ProgOptions& config)
void Device::SetConfig(ProgOptions& config)
{
fInternalConfig.reset();
fConfig = &config;
}
void FairMQDevice::LogSocketRates()
void Device::LogSocketRates()
{
vector<FairMQChannel*> filteredChannels;
vector<Channel*> filteredChannels;
vector<string> filteredChannelNames;
vector<int> logIntervals;
vector<int> intervalCounters;
@@ -760,21 +757,21 @@ void FairMQDevice::LogSocketRates()
}
}
void FairMQDevice::UnblockTransports()
void Device::UnblockTransports()
{
for (auto& transport : fTransports) {
transport.second->Interrupt();
}
}
void FairMQDevice::ResetTaskWrapper()
void Device::ResetTaskWrapper()
{
ResetTask();
ChangeState(Transition::Auto);
}
void FairMQDevice::ResetWrapper()
void Device::ResetWrapper()
{
for (auto& transport : fTransports) {
transport.second->Reset();
@@ -788,9 +785,11 @@ void FairMQDevice::ResetWrapper()
ChangeState(Transition::Auto);
}
FairMQDevice::~FairMQDevice()
Device::~Device()
{
UnsubscribeFromNewTransition("device");
fStateMachine.StopHandlingStates();
LOG(debug) << "Shutting down device " << fId;
}
} // namespace fair::mq

View File

@@ -9,13 +9,629 @@
#ifndef FAIR_MQ_DEVICE_H
#define FAIR_MQ_DEVICE_H
#include <FairMQDevice.h>
#include <algorithm> // find
#include <atomic>
#include <chrono>
#include <cstddef>
#include <fairlogger/Logger.h>
#include <fairmq/Channel.h>
#include <fairmq/Message.h>
#include <fairmq/Parts.h>
#include <fairmq/ProgOptions.h>
#include <fairmq/StateMachine.h>
#include <fairmq/StateQueue.h>
#include <fairmq/Tools.h>
#include <fairmq/TransportFactory.h>
#include <fairmq/Transports.h>
#include <fairmq/UnmanagedRegion.h>
#include <functional>
#include <memory> // unique_ptr
#include <mutex>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <utility> // pair
#include <vector>
namespace fair::mq
namespace fair::mq {
using ChannelMap = std::unordered_map<std::string, std::vector<Channel>>;
struct OngoingTransition : std::runtime_error
{
using std::runtime_error::runtime_error;
};
using Device = ::FairMQDevice;
using InputMsgCallback = std::function<bool(MessagePtr&, int)>;
} // namespace fair::mq
using InputMultipartCallback = std::function<bool(Parts&, int)>;
class Device
{
friend class Channel;
public:
Device();
Device(ProgOptions& config);
Device(const tools::Version version);
Device(ProgOptions& config, const tools::Version version);
private:
Device(ProgOptions* config, const tools::Version version);
public:
Device(const Device&) = delete;
Device operator=(const Device&) = delete;
virtual ~Device();
/// Outputs the socket transfer rates
virtual void LogSocketRates();
template<typename Serializer, typename DataType, typename... Args>
[[deprecated]] void Serialize(Message& msg, DataType&& data, Args&&... args) const
{
Serializer().Serialize(msg, std::forward<DataType>(data), std::forward<Args>(args)...);
}
template<typename Deserializer, typename DataType, typename... Args>
[[deprecated]] void Deserialize(Message& msg, DataType&& data, Args&&... args) const
{
Deserializer().Deserialize(msg, std::forward<DataType>(data), std::forward<Args>(args)...);
}
/// Shorthand method to send `msg` on `chan` at index `i`
/// @param msg message reference
/// @param chan channel name
/// @param i channel index
/// @param sndTimeoutInMs send timeout in ms, -1 will wait forever (or until interrupt (e.g. via
/// state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out,
/// TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by
/// requested state change)
int64_t Send(MessagePtr& msg,
const std::string& channel,
const int index = 0,
int sndTimeoutInMs = -1)
{
return GetChannel(channel, index).Send(msg, sndTimeoutInMs);
}
/// Shorthand method to receive `msg` on `chan` at index `i`
/// @param msg message reference
/// @param chan channel name
/// @param i channel index
/// @param rcvTimeoutInMs receive timeout in ms, -1 will wait forever (or until interrupt (e.g.
/// via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out,
/// TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by
/// requested state change)
int64_t Receive(MessagePtr& msg,
const std::string& channel,
const int index = 0,
int rcvTimeoutInMs = -1)
{
return GetChannel(channel, index).Receive(msg, rcvTimeoutInMs);
}
/// Shorthand method to send Parts on `chan` at index `i`
/// @param parts parts reference
/// @param chan channel name
/// @param i channel index
/// @param sndTimeoutInMs send timeout in ms, -1 will wait forever (or until interrupt (e.g. via
/// state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out,
/// TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by
/// requested state change)
int64_t Send(Parts& parts,
const std::string& channel,
const int index = 0,
int sndTimeoutInMs = -1)
{
return GetChannel(channel, index).Send(parts.fParts, sndTimeoutInMs);
}
/// Shorthand method to receive Parts on `chan` at index `i`
/// @param parts parts reference
/// @param chan channel name
/// @param i channel index
/// @param rcvTimeoutInMs receive timeout in ms, -1 will wait forever (or until interrupt (e.g.
/// via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out,
/// TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by
/// requested state change)
int64_t Receive(Parts& parts,
const std::string& channel,
const int index = 0,
int rcvTimeoutInMs = -1)
{
return GetChannel(channel, index).Receive(parts.fParts, rcvTimeoutInMs);
}
/// @brief Getter for default transport factory
auto Transport() const -> TransportFactory* { return fTransportFactory.get(); }
// creates message with the default device transport
template<typename... Args>
MessagePtr NewMessage(Args&&... args)
{
return Transport()->CreateMessage(std::forward<Args>(args)...);
}
// creates message with the transport of the specified channel
template<typename... Args>
MessagePtr NewMessageFor(const std::string& channel, int index, Args&&... args)
{
return GetChannel(channel, index).NewMessage(std::forward<Args>(args)...);
}
// creates a message that will not be cleaned up after transfer, with the default device
// transport
template<typename T>
MessagePtr NewStaticMessage(const T& data)
{
return Transport()->NewStaticMessage(data);
}
// creates a message that will not be cleaned up after transfer, with the transport of the
// specified channel
template<typename T>
MessagePtr NewStaticMessageFor(const std::string& channel, int index, const T& data)
{
return GetChannel(channel, index).NewStaticMessage(data);
}
// creates a message with a copy of the provided data, with the default device transport
template<typename T>
MessagePtr NewSimpleMessage(const T& data)
{
return Transport()->NewSimpleMessage(data);
}
// creates a message with a copy of the provided data, with the transport of the specified
// channel
template<typename T>
MessagePtr NewSimpleMessageFor(const std::string& channel, int index, const T& data)
{
return GetChannel(channel, index).NewSimpleMessage(data);
}
// creates unamanaged region with the default device transport
template<typename... Args>
UnmanagedRegionPtr NewUnmanagedRegion(Args&&... args)
{
return Transport()->CreateUnmanagedRegion(std::forward<Args>(args)...);
}
// creates unmanaged region with the transport of the specified channel
template<typename... Args>
UnmanagedRegionPtr NewUnmanagedRegionFor(const std::string& channel, int index, Args&&... args)
{
return GetChannel(channel, index).NewUnmanagedRegion(std::forward<Args>(args)...);
}
template<typename... Ts>
PollerPtr NewPoller(const Ts&... inputs)
{
std::vector<std::string> chans{inputs...};
// if more than one channel provided, check compatibility
if (chans.size() > 1) {
mq::Transport type = GetChannel(chans.at(0), 0).Transport()->GetType();
for (unsigned int i = 1; i < chans.size(); ++i) {
if (type != GetChannel(chans.at(i), 0).Transport()->GetType()) {
LOG(error) << "poller failed: different transports within same poller are not "
"yet supported. Going to ERROR state.";
throw std::runtime_error("poller failed: different transports within same "
"poller are not yet supported.");
}
}
}
return GetChannel(chans.at(0), 0).Transport()->CreatePoller(fChannels, chans);
}
PollerPtr NewPoller(const std::vector<Channel*>& channels)
{
// if more than one channel provided, check compatibility
if (channels.size() > 1) {
mq::Transport type = channels.at(0)->Transport()->GetType();
for (unsigned int i = 1; i < channels.size(); ++i) {
if (type != channels.at(i)->Transport()->GetType()) {
LOG(error) << "poller failed: different transports within same poller are not "
"yet supported. Going to ERROR state.";
throw std::runtime_error("poller failed: different transports within same "
"poller are not yet supported.");
}
}
}
return channels.at(0)->Transport()->CreatePoller(channels);
}
/// Adds a transport to the device if it doesn't exist
/// @param transport Transport string ("zeromq"/"shmem")
std::shared_ptr<TransportFactory> AddTransport(const mq::Transport transport);
/// Assigns config to the device
void SetConfig(ProgOptions& config);
/// Get pointer to the config
ProgOptions* GetConfig() const { return fConfig; }
// overload to easily bind member functions
template<typename T>
void OnData(const std::string& channelName,
bool (T::*memberFunction)(MessagePtr& msg, int index))
{
fDataCallbacks = true;
fMsgInputs.insert(
std::make_pair(channelName, [this, memberFunction](MessagePtr& msg, int index) {
return (static_cast<T*>(this)->*memberFunction)(msg, index);
}));
if (find(fInputChannelKeys.begin(), fInputChannelKeys.end(), channelName)
== fInputChannelKeys.end()) {
fInputChannelKeys.push_back(channelName);
}
}
void OnData(const std::string& channelName, InputMsgCallback callback)
{
fDataCallbacks = true;
fMsgInputs.insert(make_pair(channelName, callback));
if (find(fInputChannelKeys.begin(), fInputChannelKeys.end(), channelName)
== fInputChannelKeys.end()) {
fInputChannelKeys.push_back(channelName);
}
}
// overload to easily bind member functions
template<typename T>
void OnData(const std::string& channelName, bool (T::*memberFunction)(Parts& parts, int index))
{
fDataCallbacks = true;
fMultipartInputs.insert(
std::make_pair(channelName, [this, memberFunction](Parts& parts, int index) {
return (static_cast<T*>(this)->*memberFunction)(parts, index);
}));
if (find(fInputChannelKeys.begin(), fInputChannelKeys.end(), channelName)
== fInputChannelKeys.end()) {
fInputChannelKeys.push_back(channelName);
}
}
void OnData(const std::string& channelName, InputMultipartCallback callback)
{
fDataCallbacks = true;
fMultipartInputs.insert(make_pair(channelName, callback));
if (find(fInputChannelKeys.begin(), fInputChannelKeys.end(), channelName)
== fInputChannelKeys.end()) {
fInputChannelKeys.push_back(channelName);
}
}
Channel& GetChannel(const std::string& channelName, const int index = 0)
try {
return fChannels.at(channelName).at(index);
} catch (const std::out_of_range& oor) {
LOG(error)
<< "requested channel has not been configured? check channel names/configuration.";
LOG(error) << "channel: " << channelName << ", index: " << index;
LOG(error) << "out of range: " << oor.what();
throw;
}
virtual void RegisterChannelEndpoints() {}
bool RegisterChannelEndpoint(const std::string& channelName,
uint16_t minNumSubChannels = 1,
uint16_t maxNumSubChannels = 1)
{
bool ok = fChannelRegistry
.insert(std::make_pair(channelName,
std::make_pair(minNumSubChannels, maxNumSubChannels)))
.second;
if (!ok) {
LOG(warn) << "Registering channel: name already registered: \"" << channelName << "\"";
}
return ok;
}
void PrintRegisteredChannels()
{
if (fChannelRegistry.empty()) {
LOGV(info, verylow) << "no channels registered.";
} else {
for (const auto& c : fChannelRegistry) {
LOGV(info, verylow) << c.first << ":" << c.second.first << ":" << c.second.second;
}
}
}
void SetId(const std::string& id) { fId = id; }
std::string GetId() { return fId; }
const tools::Version GetVersion() const { return fVersion; }
void SetNumIoThreads(int numIoThreads) { fConfig->SetProperty("io-threads", numIoThreads); }
int GetNumIoThreads() const
{
return fConfig->GetProperty<int>("io-threads", DefaultIOThreads);
}
void SetNetworkInterface(const std::string& networkInterface)
{
fConfig->SetProperty("network-interface", networkInterface);
}
std::string GetNetworkInterface() const
{
return fConfig->GetProperty<std::string>("network-interface", DefaultNetworkInterface);
}
void SetDefaultTransport(const std::string& name) { fConfig->SetProperty("transport", name); }
std::string GetDefaultTransport() const
{
return fConfig->GetProperty<std::string>("transport", DefaultTransportName);
}
void SetInitTimeoutInS(int initTimeoutInS)
{
fConfig->SetProperty("init-timeout", initTimeoutInS);
}
int GetInitTimeoutInS() const
{
return fConfig->GetProperty<int>("init-timeout", DefaultInitTimeout);
}
/// Sets the default transport for the device
/// @param transport Transport string ("zeromq"/"shmem")
void SetTransport(const std::string& transport)
{
fConfig->SetProperty("transport", transport);
}
/// Gets the default transport name
std::string GetTransportName() const
{
return fConfig->GetProperty<std::string>("transport", DefaultTransportName);
}
void SetRawCmdLineArgs(const std::vector<std::string>& args) { fRawCmdLineArgs = args; }
std::vector<std::string> GetRawCmdLineArgs() const { return fRawCmdLineArgs; }
void RunStateMachine() { fStateMachine.ProcessWork(); };
/// Wait for the supplied amount of time or for interruption.
/// If interrupted, returns false, otherwise true.
/// @param duration wait duration
template<typename Rep, typename Period>
bool WaitFor(std::chrono::duration<Rep, Period> const& duration)
{
return !fStateMachine.WaitForPendingStateFor(
std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
}
protected:
std::shared_ptr<TransportFactory> fTransportFactory; ///< Default transport factory
std::unordered_map<mq::Transport, std::shared_ptr<TransportFactory>>
fTransports; ///< Container for transports
public:
std::unordered_map<std::string, std::vector<Channel>> fChannels; ///< Device channels
std::unique_ptr<ProgOptions> fInternalConfig; ///< Internal program options configuration
ProgOptions* fConfig; ///< Pointer to config (internal or external)
void AddChannel(const std::string& name, Channel&& channel)
{
fConfig->AddChannel(name, channel);
}
protected:
std::string fId; ///< Device ID
/// Additional user initialization (can be overloaded in child classes). Prefer to use
/// InitTask().
virtual void Init() {}
virtual void Bind() {}
virtual void Connect() {}
/// Task initialization (can be overloaded in child classes)
virtual void InitTask() {}
/// Runs the device (to be overloaded in child classes)
virtual void Run() {}
/// Called in the RUNNING state once before executing the Run()/ConditionalRun() method
virtual void PreRun() {}
/// Called during RUNNING state repeatedly until it returns false or device state changes
virtual bool ConditionalRun() { return false; }
/// Called in the RUNNING state once after executing the Run()/ConditionalRun() method
virtual void PostRun() {}
/// Resets the user task (to be overloaded in child classes)
virtual void ResetTask() {}
/// Resets the device (can be overloaded in child classes)
virtual void Reset() {}
public:
/// @brief Request a device state transition
/// @param transition state transition
///
/// The state transition may not happen immediately, but when the current state evaluates the
/// pending transition event and terminates. In other words, the device states are scheduled
/// cooperatively.
bool ChangeState(const Transition transition) { return fStateMachine.ChangeState(transition); }
/// @brief Request a device state transition
/// @param transition state transition
///
/// The state transition may not happen immediately, but when the current state evaluates the
/// pending transition event and terminates. In other words, the device states are scheduled
/// cooperatively.
bool ChangeState(const std::string& transition)
{
return fStateMachine.ChangeState(GetTransition(transition));
}
/// @brief waits for the next state (any) to occur
State WaitForNextState() { return fStateQueue.WaitForNext(); }
/// @brief waits for the specified state to occur
/// @param state state to wait for
void WaitForState(State state) { fStateQueue.WaitForState(state); }
/// @brief waits for the specified state to occur
/// @param state state to wait for
void WaitForState(const std::string& state) { WaitForState(GetState(state)); }
void TransitionTo(const State state);
/// @brief Subscribe with a callback to state changes
/// @param key id to identify your subscription
/// @param callback callback (called with the new state as the parameter)
///
/// The callback is called at the beginning of a new state.
/// The callback is called from the thread the state is running in.
void SubscribeToStateChange(const std::string& key, std::function<void(const State)> callback)
{
fStateMachine.SubscribeToStateChange(key, callback);
}
/// @brief Unsubscribe from state changes
/// @param key id (that was used when subscribing)
void UnsubscribeFromStateChange(const std::string& key)
{
fStateMachine.UnsubscribeFromStateChange(key);
}
/// @brief Subscribe with a callback to incoming state transitions
/// @param key id to identify your subscription
/// @param callback callback (called with the incoming transition as the parameter)
/// The callback is called when new transition is initiated.
/// The callback is called from the thread that initiates the transition (via ChangeState).
void SubscribeToNewTransition(const std::string& key,
std::function<void(const Transition)> callback)
{
fStateMachine.SubscribeToNewTransition(key, callback);
}
/// @brief Unsubscribe from state transitions
/// @param key id (that was used when subscribing)
void UnsubscribeFromNewTransition(const std::string& key)
{
fStateMachine.UnsubscribeFromNewTransition(key);
}
/// @brief Returns true if a new state has been requested, signaling the current handler to
/// stop.
bool NewStatePending() const { return fStateMachine.NewStatePending(); }
/// @brief Returns the current state
State GetCurrentState() const { return fStateMachine.GetCurrentState(); }
/// @brief Returns the name of the current state as a string
std::string GetCurrentStateName() const { return fStateMachine.GetCurrentStateName(); }
/// @brief Returns name of the given state as a string
/// @param state state
[[deprecated("Use fair::mq::GetStateName from <fairmq/States.h> directly")]]
static std::string GetStateName(const State state) { return fair::mq::GetStateName(state); }
/// @brief Returns name of the given transition as a string
/// @param transition transition
[[deprecated("Use fair::mq::GetTransitionName from <fairmq/States.h> directly")]]
static std::string GetTransitionName(const Transition transition)
{
return fair::mq::GetTransitionName(transition);
}
static constexpr const char* DefaultId = "";
static constexpr int DefaultIOThreads = 1;
static constexpr const char* DefaultTransportName = "zeromq";
static constexpr mq::Transport DefaultTransportType = mq::Transport::ZMQ;
static constexpr const char* DefaultNetworkInterface = "default";
static constexpr int DefaultInitTimeout = 120;
static constexpr uint64_t DefaultMaxRunTime = 0;
static constexpr float DefaultRate = 0.;
static constexpr const char* DefaultSession = "default";
private:
mq::Transport fDefaultTransportType; ///< Default transport for the device
StateMachine fStateMachine;
/// Handles the initialization
void InitWrapper();
/// Initializes binding channels
void BindWrapper();
/// Initializes connecting channels
void ConnectWrapper();
/// Handles the InitTask() method
void InitTaskWrapper();
/// Handles the Run() method
void RunWrapper();
/// Handles the ResetTask() method
void ResetTaskWrapper();
/// Handles the Reset() method
void ResetWrapper();
/// Notifies transports to cease any blocking activity
void UnblockTransports();
/// Shuts down the transports and the device
void Exit() {}
/// Attach (bind/connect) channels in the list
void AttachChannels(std::vector<Channel*>& chans);
bool AttachChannel(Channel& ch);
void HandleSingleChannelInput();
void HandleMultipleChannelInput();
void HandleMultipleTransportInput();
void PollForTransport(const TransportFactory* factory,
const std::vector<std::string>& channelKeys);
bool HandleMsgInput(const std::string& chName, const InputMsgCallback& callback, int i);
bool HandleMultipartInput(const std::string& chName,
const InputMultipartCallback& callback,
int i);
std::vector<Channel*> fUninitializedBindingChannels;
std::vector<Channel*> fUninitializedConnectingChannels;
bool fDataCallbacks;
std::unordered_map<std::string, InputMsgCallback> fMsgInputs;
std::unordered_map<std::string, InputMultipartCallback> fMultipartInputs;
std::unordered_map<mq::Transport, std::vector<std::string>> fMultitransportInputs;
std::unordered_map<std::string, std::pair<uint16_t, uint16_t>> fChannelRegistry;
std::vector<std::string> fInputChannelKeys;
std::mutex fMultitransportMutex;
std::atomic<bool> fMultitransportProceed;
const tools::Version fVersion;
float fRate; ///< Rate limiting for ConditionalRun
uint64_t fMaxRunRuntimeInS; ///< Maximum runtime for the Running state handler, after which
///< state will change to Ready (in seconds, 0 for no limit).
int fInitializationTimeoutInS;
std::vector<std::string> fRawCmdLineArgs;
StateQueue fStateQueue;
std::mutex fTransitionMtx;
bool fTransitioning;
};
} // namespace fair::mq
// using FairMQChannelMap [[deprecated("Use fair::mq::ChannelMap")]] = fair::mq::ChannelMap;
// using InputMsgCallback [[deprecated("Use fair::mq::InputMsgCallback")]] =
// fair::mq::InputMsgCallback;
// using InputMultipartCallback [[deprecated("Use fair::mq::InputMultipartCallback")]] =
// fair::mq::InputMultipartCallback;
// using FairMQDevice [[deprecated("Use fair::mq::Device")]] = fair::mq::Device;
using FairMQChannelMap = fair::mq::ChannelMap;
using InputMsgCallback = fair::mq::InputMsgCallback;
using InputMultipartCallback = fair::mq::InputMultipartCallback;
using FairMQDevice = fair::mq::Device;
#endif /* FAIR_MQ_DEVICE_H */

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2019-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -8,13 +8,9 @@
#include "Error.h"
namespace fair::mq
{
namespace fair::mq {
const char* ErrorCategory::name() const noexcept
{
return "fairmq";
}
const char* ErrorCategory::name() const noexcept { return "fairmq"; }
std::string ErrorCategory::message(int ev) const
{
@@ -40,4 +36,4 @@ const ErrorCategory errorCategory{};
std::error_code MakeErrorCode(ErrorCode e) { return {static_cast<int>(e), errorCategory}; }
} // namespace fair::mq
} // namespace fair::mq

54
fairmq/Error.h Normal file
View File

@@ -0,0 +1,54 @@
/********************************************************************************
* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#ifndef FAIR_MQ_ERROR_H
#define FAIR_MQ_ERROR_H
#include <fairmq/tools/Strings.h>
#include <stdexcept>
#include <system_error>
namespace fair::mq {
struct RuntimeError : ::std::runtime_error
{
template<typename... T>
explicit RuntimeError(T&&... t)
: ::std::runtime_error::runtime_error(tools::ToString(std::forward<T>(t)...))
{}
};
enum class ErrorCode
{
OperationInProgress = 10,
OperationTimeout,
OperationCanceled,
DeviceChangeStateFailed,
DeviceGetPropertiesFailed,
DeviceSetPropertiesFailed
};
std::error_code MakeErrorCode(ErrorCode);
struct ErrorCategory : std::error_category
{
const char* name() const noexcept override;
std::string message(int ev) const override;
};
} // namespace fair::mq
namespace std {
template<>
struct is_error_code_enum<fair::mq::ErrorCode> : true_type
{};
} // namespace std
#endif /* FAIR_MQ_ERROR_H */

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2014-2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -9,442 +9,12 @@
#ifndef FAIRMQCHANNEL_H_
#define FAIRMQCHANNEL_H_
#include <FairMQTransportFactory.h>
#include <FairMQUnmanagedRegion.h>
#include <FairMQSocket.h>
#include <fairmq/Transports.h>
#include <FairMQParts.h>
#include <fairmq/Properties.h>
#include <FairMQMessage.h>
#if 0
#ifndef FAIR_MQ_CHANNEL_H
#pragma GCC warning "Deprecated header: Use <fairmq/Channel.h> instead"
#endif
#endif
#include <string>
#include <memory> // unique_ptr, shared_ptr
#include <vector>
#include <mutex>
#include <stdexcept>
#include <utility> // std::move
#include <cstdint> // int64_t
/**
* @class FairMQChannel FairMQChannel.h <FairMQChannel.h>
* @brief Wrapper class for FairMQSocket and related methods
*
* The class is not thread-safe.
*/
class FairMQChannel
{
friend class FairMQDevice;
public:
/// Default constructor
FairMQChannel();
/// Constructor
/// @param name Channel name
FairMQChannel(const std::string& name);
/// Constructor
/// @param type Socket type (push/pull/pub/sub/spub/xsub/pair/req/rep/dealer/router/)
/// @param method Socket method (bind/connect)
/// @param address Network address to bind/connect to (e.g. "tcp://127.0.0.1:5555" or "ipc://abc")
FairMQChannel(const std::string& type, const std::string& method, const std::string& address);
/// Constructor
/// @param name Channel name
/// @param type Socket type (push/pull/pub/sub/spub/xsub/pair/req/rep/dealer/router/)
/// @param factory TransportFactory
FairMQChannel(const std::string& name, const std::string& type, std::shared_ptr<FairMQTransportFactory> factory);
/// Constructor
/// @param name Channel name
/// @param type Socket type (push/pull/pub/sub/spub/xsub/pair/req/rep/dealer/router/)
/// @param method Socket method (bind/connect)
/// @param address Network address to bind/connect to (e.g. "tcp://127.0.0.1:5555" or "ipc://abc")
/// @param factory TransportFactory
FairMQChannel(std::string name, std::string type, std::string method, std::string address, std::shared_ptr<FairMQTransportFactory> factory);
FairMQChannel(const std::string& name, int index, const fair::mq::Properties& properties);
/// Copy Constructor
FairMQChannel(const FairMQChannel&);
/// Copy Constructor (with new name)
FairMQChannel(const FairMQChannel&, std::string name);
/// Move constructor
// FairMQChannel(FairMQChannel&&) = delete;
/// Assignment operator
FairMQChannel& operator=(const FairMQChannel&);
/// Move assignment operator
// FairMQChannel& operator=(FairMQChannel&&) = delete;
/// Destructor
virtual ~FairMQChannel() = default;
// { LOG(warn) << "Destroying channel '" << fName << "'"; }
struct ChannelConfigurationError : std::runtime_error { using std::runtime_error::runtime_error; };
FairMQSocket& GetSocket() const { assert(fSocket); return *fSocket; }
bool Bind(const std::string& address)
{
fMethod = "bind";
fAddress = address;
return fSocket->Bind(address);
}
bool Connect(const std::string& address)
{
fMethod = "connect";
fAddress = address;
return fSocket->Connect(address);
}
/// Get channel name
/// @return Returns full channel name (e.g. "data[0]")
std::string GetName() const { return fName; }
/// Get channel prefix
/// @return Returns channel prefix (e.g. "data" in "data[0]")
std::string GetPrefix() const
{
std::string prefix = fName;
prefix = prefix.erase(fName.rfind('['));
return prefix;
}
/// Get channel index
/// @return Returns channel index (e.g. 0 in "data[0]")
std::string GetIndex() const
{
std::string indexStr = fName;
indexStr.erase(indexStr.rfind(']'));
indexStr.erase(0, indexStr.rfind('[') + 1);
return indexStr;
}
/// Get socket type
/// @return Returns socket type (push/pull/pub/sub/spub/xsub/pair/req/rep/dealer/router/)
std::string GetType() const { return fType; }
/// Get socket method
/// @return Returns socket method (bind/connect)
std::string GetMethod() const { return fMethod; }
/// Get socket address (e.g. "tcp://127.0.0.1:5555" or "ipc://abc")
/// @return Returns socket address (e.g. "tcp://127.0.0.1:5555" or "ipc://abc")
std::string GetAddress() const { return fAddress; }
/// Get channel transport name ("default", "zeromq" or "shmem")
/// @return Returns channel transport name (e.g. "default", "zeromq" or "shmem")
std::string GetTransportName() const { return fair::mq::TransportName(fTransportType); }
/// Get channel transport type
/// @return Returns channel transport type
fair::mq::Transport GetTransportType() const { return fTransportType; }
/// Get socket send buffer size (in number of messages)
/// @return Returns socket send buffer size (in number of messages)
int GetSndBufSize() const { return fSndBufSize; }
/// Get socket receive buffer size (in number of messages)
/// @return Returns socket receive buffer size (in number of messages)
int GetRcvBufSize() const { return fRcvBufSize; }
/// Get socket kernel transmit send buffer size (in bytes)
/// @return Returns socket kernel transmit send buffer size (in bytes)
int GetSndKernelSize() const { return fSndKernelSize; }
/// Get socket kernel transmit receive buffer size (in bytes)
/// @return Returns socket kernel transmit receive buffer size (in bytes)
int GetRcvKernelSize() const { return fRcvKernelSize; }
/// Get linger duration (in milliseconds)
/// @return Returns linger duration (in milliseconds)
int GetLinger() const { return fLinger; }
/// Get socket rate logging interval (in seconds)
/// @return Returns socket rate logging interval (in seconds)
int GetRateLogging() const { return fRateLogging; }
/// Get start of the port range for automatic binding
/// @return start of the port range
int GetPortRangeMin() const { return fPortRangeMin; }
/// Get end of the port range for automatic binding
/// @return end of the port range
int GetPortRangeMax() const { return fPortRangeMax; }
/// Set automatic binding (pick random port if bind fails)
/// @return true/false, true if automatic binding is enabled
bool GetAutoBind() const { return fAutoBind; }
/// Set channel name
/// @param name Arbitrary channel name
void UpdateName(const std::string& name) { fName = name; Invalidate(); }
/// Set socket type
/// @param type Socket type (push/pull/pub/sub/spub/xsub/pair/req/rep/dealer/router/)
void UpdateType(const std::string& type) { fType = type; Invalidate(); }
/// Set socket method
/// @param method Socket method (bind/connect)
void UpdateMethod(const std::string& method) { fMethod = method; Invalidate(); }
/// Set socket address
/// @param Socket address (e.g. "tcp://127.0.0.1:5555" or "ipc://abc")
void UpdateAddress(const std::string& address) { fAddress = address; Invalidate(); }
/// Set channel transport
/// @param transport transport string ("default", "zeromq" or "shmem")
void UpdateTransport(const std::string& transport) { fTransportType = fair::mq::TransportType(transport); Invalidate(); }
/// Set socket send buffer size
/// @param sndBufSize Socket send buffer size (in number of messages)
void UpdateSndBufSize(const int sndBufSize) { fSndBufSize = sndBufSize; Invalidate(); }
/// Set socket receive buffer size
/// @param rcvBufSize Socket receive buffer size (in number of messages)
void UpdateRcvBufSize(const int rcvBufSize) { fRcvBufSize = rcvBufSize; Invalidate(); }
/// Set socket kernel transmit send buffer size (in bytes)
/// @param sndKernelSize Socket send buffer size (in bytes)
void UpdateSndKernelSize(const int sndKernelSize) { fSndKernelSize = sndKernelSize; Invalidate(); }
/// Set socket kernel transmit receive buffer size (in bytes)
/// @param rcvKernelSize Socket receive buffer size (in bytes)
void UpdateRcvKernelSize(const int rcvKernelSize) { fRcvKernelSize = rcvKernelSize; Invalidate(); }
/// Set linger duration (in milliseconds)
/// @param duration linger duration (in milliseconds)
void UpdateLinger(const int duration) { fLinger = duration; Invalidate(); }
/// Set socket rate logging interval (in seconds)
/// @param rateLogging Socket rate logging interval (in seconds)
void UpdateRateLogging(const int rateLogging) { fRateLogging = rateLogging; Invalidate(); }
/// Set start of the port range for automatic binding
/// @param minPort start of the port range
void UpdatePortRangeMin(const int minPort) { fPortRangeMin = minPort; Invalidate(); }
/// Set end of the port range for automatic binding
/// @param maxPort end of the port range
void UpdatePortRangeMax(const int maxPort) { fPortRangeMax = maxPort; Invalidate(); }
/// Set automatic binding (pick random port if bind fails)
/// @param autobind true/false, true to enable automatic binding
void UpdateAutoBind(const bool autobind) { fAutoBind = autobind; Invalidate(); }
/// Checks if the configured channel settings are valid (checks the validity parameter, without running full validation (as oposed to ValidateChannel()))
/// @return true if channel settings are valid, false otherwise.
bool IsValid() const { return fValid; }
/// Validates channel configuration
/// @return true if channel settings are valid, false otherwise.
bool Validate();
void Init();
bool ConnectEndpoint(const std::string& endpoint);
bool BindEndpoint(std::string& endpoint);
/// invalidates the channel (requires validation to be used again).
void Invalidate() { fValid = false; }
/// Sends a message to the socket queue.
/// @param msg Constant reference of unique_ptr to a FairMQMessage
/// @param sndTimeoutInMs send timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Send(FairMQMessagePtr& msg, int sndTimeoutInMs = -1)
{
CheckSendCompatibility(msg);
return fSocket->Send(msg, sndTimeoutInMs);
}
/// Receives a message from the socket queue.
/// @param msg Constant reference of unique_ptr to a FairMQMessage
/// @param rcvTimeoutInMs receive timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Receive(FairMQMessagePtr& msg, int rcvTimeoutInMs = -1)
{
CheckReceiveCompatibility(msg);
return fSocket->Receive(msg, rcvTimeoutInMs);
}
/// Send a vector of messages
/// @param msgVec message vector reference
/// @param sndTimeoutInMs send timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Send(std::vector<FairMQMessagePtr>& msgVec, int sndTimeoutInMs = -1)
{
CheckSendCompatibility(msgVec);
return fSocket->Send(msgVec, sndTimeoutInMs);
}
/// Receive a vector of messages
/// @param msgVec message vector reference
/// @param rcvTimeoutInMs receive timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Receive(std::vector<FairMQMessagePtr>& msgVec, int rcvTimeoutInMs = -1)
{
CheckReceiveCompatibility(msgVec);
return fSocket->Receive(msgVec, rcvTimeoutInMs);
}
/// Send FairMQParts
/// @param parts FairMQParts reference
/// @param sndTimeoutInMs send timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Send(FairMQParts& parts, int sndTimeoutInMs = -1)
{
return Send(parts.fParts, sndTimeoutInMs);
}
/// Receive FairMQParts
/// @param parts FairMQParts reference
/// @param rcvTimeoutInMs receive timeout in ms. -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Receive(FairMQParts& parts, int rcvTimeoutInMs = -1)
{
return Receive(parts.fParts, rcvTimeoutInMs);
}
unsigned long GetBytesTx() const { return fSocket->GetBytesTx(); }
unsigned long GetBytesRx() const { return fSocket->GetBytesRx(); }
unsigned long GetMessagesTx() const { return fSocket->GetMessagesTx(); }
unsigned long GetMessagesRx() const { return fSocket->GetMessagesRx(); }
auto Transport() -> FairMQTransportFactory* { return fTransportFactory.get(); };
template<typename... Args>
FairMQMessagePtr NewMessage(Args&&... args)
{
return Transport()->CreateMessage(std::forward<Args>(args)...);
}
template<typename T>
FairMQMessagePtr NewSimpleMessage(const T& data)
{
return Transport()->NewSimpleMessage(data);
}
template<typename T>
FairMQMessagePtr NewStaticMessage(const T& data)
{
return Transport()->NewStaticMessage(data);
}
template<typename... Args>
FairMQUnmanagedRegionPtr NewUnmanagedRegion(Args&&... args)
{
return Transport()->CreateUnmanagedRegion(std::forward<Args>(args)...);
}
static constexpr fair::mq::Transport DefaultTransportType = fair::mq::Transport::DEFAULT;
static constexpr const char* DefaultTransportName = "default";
static constexpr const char* DefaultName = "";
static constexpr const char* DefaultType = "unspecified";
static constexpr const char* DefaultMethod = "unspecified";
static constexpr const char* DefaultAddress = "unspecified";
static constexpr int DefaultSndBufSize = 1000;
static constexpr int DefaultRcvBufSize = 1000;
static constexpr int DefaultSndKernelSize = 0;
static constexpr int DefaultRcvKernelSize = 0;
static constexpr int DefaultLinger = 500;
static constexpr int DefaultRateLogging = 1;
static constexpr int DefaultPortRangeMin = 22000;
static constexpr int DefaultPortRangeMax = 23000;
static constexpr bool DefaultAutoBind = true;
private:
std::shared_ptr<FairMQTransportFactory> fTransportFactory;
fair::mq::Transport fTransportType;
std::unique_ptr<FairMQSocket> fSocket;
std::string fName;
std::string fType;
std::string fMethod;
std::string fAddress;
int fSndBufSize;
int fRcvBufSize;
int fSndKernelSize;
int fRcvKernelSize;
int fLinger;
int fRateLogging;
int fPortRangeMin;
int fPortRangeMax;
bool fAutoBind;
bool fValid;
bool fMultipart;
void CheckSendCompatibility(FairMQMessagePtr& msg)
{
if (fTransportType != msg->GetType()) {
if (msg->GetSize() > 0) {
FairMQMessagePtr msgWrapper(NewMessage(
msg->GetData(),
msg->GetSize(),
[](void* /*data*/, void* _msg) { delete static_cast<FairMQMessage*>(_msg); },
msg.get()
));
msg.release();
msg = move(msgWrapper);
} else {
FairMQMessagePtr newMsg(NewMessage());
msg = move(newMsg);
}
}
}
void CheckSendCompatibility(std::vector<FairMQMessagePtr>& msgVec)
{
for (auto& msg : msgVec) {
if (fTransportType != msg->GetType()) {
if (msg->GetSize() > 0) {
FairMQMessagePtr msgWrapper(NewMessage(
msg->GetData(),
msg->GetSize(),
[](void* /*data*/, void* _msg) { delete static_cast<FairMQMessage*>(_msg); },
msg.get()
));
msg.release();
msg = move(msgWrapper);
} else {
FairMQMessagePtr newMsg(NewMessage());
msg = move(newMsg);
}
}
}
}
void CheckReceiveCompatibility(FairMQMessagePtr& msg)
{
if (fTransportType != msg->GetType()) {
FairMQMessagePtr newMsg(NewMessage());
msg = move(newMsg);
}
}
void CheckReceiveCompatibility(std::vector<FairMQMessagePtr>& msgVec)
{
for (auto& msg : msgVec) {
if (fTransportType != msg->GetType()) {
FairMQMessagePtr newMsg(NewMessage());
msg = move(newMsg);
}
}
}
void InitTransport(std::shared_ptr<FairMQTransportFactory> factory)
{
fTransportFactory = factory;
fTransportType = factory->GetType();
}
};
#include <fairmq/Channel.h>
#endif /* FAIRMQCHANNEL_H_ */

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2012-2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2012-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -9,545 +9,12 @@
#ifndef FAIRMQDEVICE_H_
#define FAIRMQDEVICE_H_
#include <FairMQChannel.h>
#include <FairMQLogger.h>
#include <FairMQMessage.h>
#include <FairMQParts.h>
#include <FairMQTransportFactory.h>
#include <FairMQUnmanagedRegion.h>
#include <fairmq/ProgOptions.h>
#include <fairmq/StateMachine.h>
#include <fairmq/StateQueue.h>
#include <fairmq/Transports.h>
#include <fairmq/tools/Version.h>
#if 0
#ifndef FAIR_MQ_DEVICE_H
#pragma GCC warning "Deprecated header: Use <fairmq/Device.h> instead"
#endif
#endif
#include <vector>
#include <memory> // unique_ptr
#include <algorithm> // find
#include <string>
#include <chrono>
#include <unordered_map>
#include <functional>
#include <stdexcept>
#include <mutex>
#include <atomic>
#include <cstddef>
#include <utility> // pair
using FairMQChannelMap = std::unordered_map<std::string, std::vector<FairMQChannel>>;
using InputMsgCallback = std::function<bool(FairMQMessagePtr&, int)>;
using InputMultipartCallback = std::function<bool(FairMQParts&, int)>;
namespace fair::mq
{
struct OngoingTransition : std::runtime_error { using std::runtime_error::runtime_error; };
}
class FairMQDevice
{
friend class FairMQChannel;
public:
/// Default constructor
FairMQDevice();
/// Constructor with external fair::mq::ProgOptions
FairMQDevice(fair::mq::ProgOptions& config);
/// Constructor that sets the version
FairMQDevice(const fair::mq::tools::Version version);
/// Constructor that sets the version and external fair::mq::ProgOptions
FairMQDevice(fair::mq::ProgOptions& config, const fair::mq::tools::Version version);
private:
FairMQDevice(fair::mq::ProgOptions* config, const fair::mq::tools::Version version);
public:
/// Copy constructor (disabled)
FairMQDevice(const FairMQDevice&) = delete;
/// Assignment operator (disabled)
FairMQDevice operator=(const FairMQDevice&) = delete;
/// Default destructor
virtual ~FairMQDevice();
/// Outputs the socket transfer rates
virtual void LogSocketRates();
template<typename Serializer, typename DataType, typename... Args>
void Serialize(FairMQMessage& msg, DataType&& data, Args&&... args) const
{
Serializer().Serialize(msg, std::forward<DataType>(data), std::forward<Args>(args)...);
}
template<typename Deserializer, typename DataType, typename... Args>
void Deserialize(FairMQMessage& msg, DataType&& data, Args&&... args) const
{
Deserializer().Deserialize(msg, std::forward<DataType>(data), std::forward<Args>(args)...);
}
/// Shorthand method to send `msg` on `chan` at index `i`
/// @param msg message reference
/// @param chan channel name
/// @param i channel index
/// @param sndTimeoutInMs send timeout in ms, -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Send(FairMQMessagePtr& msg, const std::string& channel, const int index = 0, int sndTimeoutInMs = -1)
{
return GetChannel(channel, index).Send(msg, sndTimeoutInMs);
}
/// Shorthand method to receive `msg` on `chan` at index `i`
/// @param msg message reference
/// @param chan channel name
/// @param i channel index
/// @param rcvTimeoutInMs receive timeout in ms, -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Receive(FairMQMessagePtr& msg, const std::string& channel, const int index = 0, int rcvTimeoutInMs = -1)
{
return GetChannel(channel, index).Receive(msg, rcvTimeoutInMs);
}
/// Shorthand method to send FairMQParts on `chan` at index `i`
/// @param parts parts reference
/// @param chan channel name
/// @param i channel index
/// @param sndTimeoutInMs send timeout in ms, -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot send)
/// @return Number of bytes that have been queued, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Send(FairMQParts& parts, const std::string& channel, const int index = 0, int sndTimeoutInMs = -1)
{
return GetChannel(channel, index).Send(parts.fParts, sndTimeoutInMs);
}
/// Shorthand method to receive FairMQParts on `chan` at index `i`
/// @param parts parts reference
/// @param chan channel name
/// @param i channel index
/// @param rcvTimeoutInMs receive timeout in ms, -1 will wait forever (or until interrupt (e.g. via state change)), 0 will not wait (return immediately if cannot receive)
/// @return Number of bytes that have been received, TransferCode::timeout if timed out, TransferCode::error if there was an error, TransferCode::interrupted if interrupted (e.g. by requested state change)
int64_t Receive(FairMQParts& parts, const std::string& channel, const int index = 0, int rcvTimeoutInMs = -1)
{
return GetChannel(channel, index).Receive(parts.fParts, rcvTimeoutInMs);
}
/// @brief Getter for default transport factory
auto Transport() const -> FairMQTransportFactory*
{
return fTransportFactory.get();
}
// creates message with the default device transport
template<typename... Args>
FairMQMessagePtr NewMessage(Args&&... args)
{
return Transport()->CreateMessage(std::forward<Args>(args)...);
}
// creates message with the transport of the specified channel
template<typename... Args>
FairMQMessagePtr NewMessageFor(const std::string& channel, int index, Args&&... args)
{
return GetChannel(channel, index).NewMessage(std::forward<Args>(args)...);
}
// creates a message that will not be cleaned up after transfer, with the default device transport
template<typename T>
FairMQMessagePtr NewStaticMessage(const T& data)
{
return Transport()->NewStaticMessage(data);
}
// creates a message that will not be cleaned up after transfer, with the transport of the specified channel
template<typename T>
FairMQMessagePtr NewStaticMessageFor(const std::string& channel, int index, const T& data)
{
return GetChannel(channel, index).NewStaticMessage(data);
}
// creates a message with a copy of the provided data, with the default device transport
template<typename T>
FairMQMessagePtr NewSimpleMessage(const T& data)
{
return Transport()->NewSimpleMessage(data);
}
// creates a message with a copy of the provided data, with the transport of the specified channel
template<typename T>
FairMQMessagePtr NewSimpleMessageFor(const std::string& channel, int index, const T& data)
{
return GetChannel(channel, index).NewSimpleMessage(data);
}
// creates unamanaged region with the default device transport
template<typename... Args>
FairMQUnmanagedRegionPtr NewUnmanagedRegion(Args&&... args)
{
return Transport()->CreateUnmanagedRegion(std::forward<Args>(args)...);
}
// creates unmanaged region with the transport of the specified channel
template<typename... Args>
FairMQUnmanagedRegionPtr NewUnmanagedRegionFor(const std::string& channel, int index, Args&&... args)
{
return GetChannel(channel, index).NewUnmanagedRegion(std::forward<Args>(args)...);
}
template<typename ...Ts>
FairMQPollerPtr NewPoller(const Ts&... inputs)
{
std::vector<std::string> chans{inputs...};
// if more than one channel provided, check compatibility
if (chans.size() > 1)
{
fair::mq::Transport type = GetChannel(chans.at(0), 0).Transport()->GetType();
for (unsigned int i = 1; i < chans.size(); ++i)
{
if (type != GetChannel(chans.at(i), 0).Transport()->GetType())
{
LOG(error) << "poller failed: different transports within same poller are not yet supported. Going to ERROR state.";
throw std::runtime_error("poller failed: different transports within same poller are not yet supported.");
}
}
}
return GetChannel(chans.at(0), 0).Transport()->CreatePoller(fChannels, chans);
}
FairMQPollerPtr NewPoller(const std::vector<FairMQChannel*>& channels)
{
// if more than one channel provided, check compatibility
if (channels.size() > 1)
{
fair::mq::Transport type = channels.at(0)->Transport()->GetType();
for (unsigned int i = 1; i < channels.size(); ++i)
{
if (type != channels.at(i)->Transport()->GetType())
{
LOG(error) << "poller failed: different transports within same poller are not yet supported. Going to ERROR state.";
throw std::runtime_error("poller failed: different transports within same poller are not yet supported.");
}
}
}
return channels.at(0)->Transport()->CreatePoller(channels);
}
/// Adds a transport to the device if it doesn't exist
/// @param transport Transport string ("zeromq"/"shmem")
std::shared_ptr<FairMQTransportFactory> AddTransport(const fair::mq::Transport transport);
/// Assigns config to the device
void SetConfig(fair::mq::ProgOptions& config);
/// Get pointer to the config
fair::mq::ProgOptions* GetConfig() const
{
return fConfig;
}
// overload to easily bind member functions
template<typename T>
void OnData(const std::string& channelName, bool (T::* memberFunction)(FairMQMessagePtr& msg, int index))
{
fDataCallbacks = true;
fMsgInputs.insert(std::make_pair(channelName, [this, memberFunction](FairMQMessagePtr& msg, int index)
{
return (static_cast<T*>(this)->*memberFunction)(msg, index);
}));
if (find(fInputChannelKeys.begin(), fInputChannelKeys.end(), channelName) == fInputChannelKeys.end())
{
fInputChannelKeys.push_back(channelName);
}
}
void OnData(const std::string& channelName, InputMsgCallback callback)
{
fDataCallbacks = true;
fMsgInputs.insert(make_pair(channelName, callback));
if (find(fInputChannelKeys.begin(), fInputChannelKeys.end(), channelName) == fInputChannelKeys.end())
{
fInputChannelKeys.push_back(channelName);
}
}
// overload to easily bind member functions
template<typename T>
void OnData(const std::string& channelName, bool (T::* memberFunction)(FairMQParts& parts, int index))
{
fDataCallbacks = true;
fMultipartInputs.insert(std::make_pair(channelName, [this, memberFunction](FairMQParts& parts, int index)
{
return (static_cast<T*>(this)->*memberFunction)(parts, index);
}));
if (find(fInputChannelKeys.begin(), fInputChannelKeys.end(), channelName) == fInputChannelKeys.end())
{
fInputChannelKeys.push_back(channelName);
}
}
void OnData(const std::string& channelName, InputMultipartCallback callback)
{
fDataCallbacks = true;
fMultipartInputs.insert(make_pair(channelName, callback));
if (find(fInputChannelKeys.begin(), fInputChannelKeys.end(), channelName) == fInputChannelKeys.end())
{
fInputChannelKeys.push_back(channelName);
}
}
FairMQChannel& GetChannel(const std::string& channelName, const int index = 0)
try {
return fChannels.at(channelName).at(index);
} catch (const std::out_of_range& oor) {
LOG(error) << "requested channel has not been configured? check channel names/configuration.";
LOG(error) << "channel: " << channelName << ", index: " << index;
LOG(error) << "out of range: " << oor.what();
throw;
}
virtual void RegisterChannelEndpoints() {}
bool RegisterChannelEndpoint(const std::string& channelName, uint16_t minNumSubChannels = 1, uint16_t maxNumSubChannels = 1)
{
bool ok = fChannelRegistry.insert(std::make_pair(channelName, std::make_pair(minNumSubChannels, maxNumSubChannels))).second;
if (!ok) {
LOG(warn) << "Registering channel: name already registered: \"" << channelName << "\"";
}
return ok;
}
void PrintRegisteredChannels()
{
if (fChannelRegistry.empty()) {
LOGV(info, verylow) << "no channels registered.";
} else {
for (const auto& c : fChannelRegistry) {
LOGV(info, verylow) << c.first << ":" << c.second.first << ":" << c.second.second;
}
}
}
void SetId(const std::string& id) { fId = id; }
std::string GetId() { return fId; }
const fair::mq::tools::Version GetVersion() const { return fVersion; }
void SetNumIoThreads(int numIoThreads) { fConfig->SetProperty("io-threads", numIoThreads);}
int GetNumIoThreads() const { return fConfig->GetProperty<int>("io-threads", DefaultIOThreads); }
void SetNetworkInterface(const std::string& networkInterface) { fConfig->SetProperty("network-interface", networkInterface); }
std::string GetNetworkInterface() const { return fConfig->GetProperty<std::string>("network-interface", DefaultNetworkInterface); }
void SetDefaultTransport(const std::string& name) { fConfig->SetProperty("transport", name); }
std::string GetDefaultTransport() const { return fConfig->GetProperty<std::string>("transport", DefaultTransportName); }
void SetInitTimeoutInS(int initTimeoutInS) { fConfig->SetProperty("init-timeout", initTimeoutInS); }
int GetInitTimeoutInS() const { return fConfig->GetProperty<int>("init-timeout", DefaultInitTimeout); }
/// Sets the default transport for the device
/// @param transport Transport string ("zeromq"/"shmem")
void SetTransport(const std::string& transport) { fConfig->SetProperty("transport", transport); }
/// Gets the default transport name
std::string GetTransportName() const { return fConfig->GetProperty<std::string>("transport", DefaultTransportName); }
void SetRawCmdLineArgs(const std::vector<std::string>& args) { fRawCmdLineArgs = args; }
std::vector<std::string> GetRawCmdLineArgs() const { return fRawCmdLineArgs; }
void RunStateMachine()
{
fStateMachine.ProcessWork();
};
/// Wait for the supplied amount of time or for interruption.
/// If interrupted, returns false, otherwise true.
/// @param duration wait duration
template<typename Rep, typename Period>
bool WaitFor(std::chrono::duration<Rep, Period> const& duration)
{
return !fStateMachine.WaitForPendingStateFor(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
}
protected:
std::shared_ptr<FairMQTransportFactory> fTransportFactory; ///< Default transport factory
std::unordered_map<fair::mq::Transport, std::shared_ptr<FairMQTransportFactory>> fTransports; ///< Container for transports
public:
std::unordered_map<std::string, std::vector<FairMQChannel>> fChannels; ///< Device channels
std::unique_ptr<fair::mq::ProgOptions> fInternalConfig; ///< Internal program options configuration
fair::mq::ProgOptions* fConfig; ///< Pointer to config (internal or external)
void AddChannel(const std::string& name, FairMQChannel&& channel)
{
fConfig->AddChannel(name, channel);
}
protected:
std::string fId; ///< Device ID
/// Additional user initialization (can be overloaded in child classes). Prefer to use InitTask().
virtual void Init() {}
virtual void Bind() {}
virtual void Connect() {}
/// Task initialization (can be overloaded in child classes)
virtual void InitTask() {}
/// Runs the device (to be overloaded in child classes)
virtual void Run() {}
/// Called in the RUNNING state once before executing the Run()/ConditionalRun() method
virtual void PreRun() {}
/// Called during RUNNING state repeatedly until it returns false or device state changes
virtual bool ConditionalRun() { return false; }
/// Called in the RUNNING state once after executing the Run()/ConditionalRun() method
virtual void PostRun() {}
/// Resets the user task (to be overloaded in child classes)
virtual void ResetTask() {}
/// Resets the device (can be overloaded in child classes)
virtual void Reset() {}
public:
/// @brief Request a device state transition
/// @param transition state transition
///
/// The state transition may not happen immediately, but when the current state evaluates the
/// pending transition event and terminates. In other words, the device states are scheduled cooperatively.
bool ChangeState(const fair::mq::Transition transition) { return fStateMachine.ChangeState(transition); }
/// @brief Request a device state transition
/// @param transition state transition
///
/// The state transition may not happen immediately, but when the current state evaluates the
/// pending transition event and terminates. In other words, the device states are scheduled cooperatively.
bool ChangeState(const std::string& transition) { return fStateMachine.ChangeState(fair::mq::GetTransition(transition)); }
/// @brief waits for the next state (any) to occur
fair::mq::State WaitForNextState() { return fStateQueue.WaitForNext(); }
/// @brief waits for the specified state to occur
/// @param state state to wait for
void WaitForState(fair::mq::State state) { fStateQueue.WaitForState(state); }
/// @brief waits for the specified state to occur
/// @param state state to wait for
void WaitForState(const std::string& state) { WaitForState(fair::mq::GetState(state)); }
void TransitionTo(const fair::mq::State state);
/// @brief Subscribe with a callback to state changes
/// @param key id to identify your subscription
/// @param callback callback (called with the new state as the parameter)
///
/// The callback is called at the beginning of a new state.
/// The callback is called from the thread the state is running in.
void SubscribeToStateChange(const std::string& key, std::function<void(const fair::mq::State)> callback) { fStateMachine.SubscribeToStateChange(key, callback); }
/// @brief Unsubscribe from state changes
/// @param key id (that was used when subscribing)
void UnsubscribeFromStateChange(const std::string& key) { fStateMachine.UnsubscribeFromStateChange(key); }
/// @brief Subscribe with a callback to incoming state transitions
/// @param key id to identify your subscription
/// @param callback callback (called with the incoming transition as the parameter)
/// The callback is called when new transition is initiated.
/// The callback is called from the thread that initiates the transition (via ChangeState).
void SubscribeToNewTransition(const std::string& key, std::function<void(const fair::mq::Transition)> callback) { fStateMachine.SubscribeToNewTransition(key, callback); }
/// @brief Unsubscribe from state transitions
/// @param key id (that was used when subscribing)
void UnsubscribeFromNewTransition(const std::string& key) { fStateMachine.UnsubscribeFromNewTransition(key); }
/// @brief Returns true if a new state has been requested, signaling the current handler to stop.
bool NewStatePending() const { return fStateMachine.NewStatePending(); }
/// @brief Returns the current state
fair::mq::State GetCurrentState() const { return fStateMachine.GetCurrentState(); }
/// @brief Returns the name of the current state as a string
std::string GetCurrentStateName() const { return fStateMachine.GetCurrentStateName(); }
/// @brief Returns name of the given state as a string
/// @param state state
static std::string GetStateName(const fair::mq::State state) { return fair::mq::GetStateName(state); }
/// @brief Returns name of the given transition as a string
/// @param transition transition
static std::string GetTransitionName(const fair::mq::Transition transition) { return fair::mq::GetTransitionName(transition); }
static constexpr const char* DefaultId = "";
static constexpr int DefaultIOThreads = 1;
static constexpr const char* DefaultTransportName = "zeromq";
static constexpr fair::mq::Transport DefaultTransportType = fair::mq::Transport::ZMQ;
static constexpr const char* DefaultNetworkInterface = "default";
static constexpr int DefaultInitTimeout = 120;
static constexpr uint64_t DefaultMaxRunTime = 0;
static constexpr float DefaultRate = 0.;
static constexpr const char* DefaultSession = "default";
private:
fair::mq::Transport fDefaultTransportType; ///< Default transport for the device
fair::mq::StateMachine fStateMachine;
/// Handles the initialization
void InitWrapper();
/// Initializes binding channels
void BindWrapper();
/// Initializes connecting channels
void ConnectWrapper();
/// Handles the InitTask() method
void InitTaskWrapper();
/// Handles the Run() method
void RunWrapper();
/// Handles the ResetTask() method
void ResetTaskWrapper();
/// Handles the Reset() method
void ResetWrapper();
/// Notifies transports to cease any blocking activity
void UnblockTransports();
/// Shuts down the transports and the device
void Exit() {}
/// Attach (bind/connect) channels in the list
void AttachChannels(std::vector<FairMQChannel*>& chans);
bool AttachChannel(FairMQChannel& ch);
void HandleSingleChannelInput();
void HandleMultipleChannelInput();
void HandleMultipleTransportInput();
void PollForTransport(const FairMQTransportFactory* factory, const std::vector<std::string>& channelKeys);
bool HandleMsgInput(const std::string& chName, const InputMsgCallback& callback, int i);
bool HandleMultipartInput(const std::string& chName, const InputMultipartCallback& callback, int i);
std::vector<FairMQChannel*> fUninitializedBindingChannels;
std::vector<FairMQChannel*> fUninitializedConnectingChannels;
bool fDataCallbacks;
std::unordered_map<std::string, InputMsgCallback> fMsgInputs;
std::unordered_map<std::string, InputMultipartCallback> fMultipartInputs;
std::unordered_map<fair::mq::Transport, std::vector<std::string>> fMultitransportInputs;
std::unordered_map<std::string, std::pair<uint16_t, uint16_t>> fChannelRegistry;
std::vector<std::string> fInputChannelKeys;
std::mutex fMultitransportMutex;
std::atomic<bool> fMultitransportProceed;
const fair::mq::tools::Version fVersion;
float fRate; ///< Rate limiting for ConditionalRun
uint64_t fMaxRunRuntimeInS; ///< Maximum runtime for the Running state handler, after which state will change to Ready (in seconds, 0 for no limit).
int fInitializationTimeoutInS;
std::vector<std::string> fRawCmdLineArgs;
fair::mq::StateQueue fStateQueue;
std::mutex fTransitionMtx;
bool fTransitioning;
};
#include <fairmq/Device.h>
#endif /* FAIRMQDEVICE_H_ */

View File

@@ -1,15 +0,0 @@
/********************************************************************************
* Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
/**
* FairMQLogger.cxx
*
* @since 2012-12-04
* @author D. Klein, A. Rybalchenko
*/
#include "FairMQLogger.h"

View File

@@ -1,13 +0,0 @@
/********************************************************************************
* Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
/**
* FairMQMessage.cxx
*
* @since 2012-12-05
* @author D. Klein, A. Rybalchenko
*/

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2014-2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -9,65 +9,12 @@
#ifndef FAIRMQMESSAGE_H_
#define FAIRMQMESSAGE_H_
#include <cstddef> // for size_t
#include <memory> // unique_ptr
#include <stdexcept>
#if 0
#ifndef FAIR_MQ_MESSAGE_H
#pragma GCC warning "Deprecated header: Use <fairmq/Message.h> instead"
#endif
#endif
#include <fairmq/Transports.h>
using fairmq_free_fn = void(void* data, void* hint);
class FairMQTransportFactory;
namespace fair::mq
{
struct Alignment
{
size_t alignment;
explicit operator size_t() const { return alignment; }
};
} // namespace fair::mq
class FairMQMessage
{
public:
FairMQMessage() = default;
FairMQMessage(FairMQTransportFactory* factory) : fTransport(factory) {}
virtual void Rebuild() = 0;
virtual void Rebuild(fair::mq::Alignment alignment) = 0;
virtual void Rebuild(const size_t size) = 0;
virtual void Rebuild(const size_t size, fair::mq::Alignment alignment) = 0;
virtual void Rebuild(void* data, const size_t size, fairmq_free_fn* ffn, void* hint = nullptr) = 0;
virtual void* GetData() const = 0;
virtual size_t GetSize() const = 0;
virtual bool SetUsedSize(const size_t size) = 0;
virtual fair::mq::Transport GetType() const = 0;
FairMQTransportFactory* GetTransport() { return fTransport; }
void SetTransport(FairMQTransportFactory* transport) { fTransport = transport; }
virtual void Copy(const FairMQMessage& msg) = 0;
virtual ~FairMQMessage() = default;
private:
FairMQTransportFactory* fTransport{nullptr};
};
using FairMQMessagePtr = std::unique_ptr<FairMQMessage>;
namespace fair::mq
{
using Message = FairMQMessage;
using MessagePtr = FairMQMessagePtr;
struct MessageError : std::runtime_error { using std::runtime_error::runtime_error; };
struct MessageBadAlloc : std::runtime_error { using std::runtime_error::runtime_error; };
} // namespace fair::mq
#include <fairmq/Message.h>
#endif /* FAIRMQMESSAGE_H_ */

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -9,90 +9,12 @@
#ifndef FAIRMQPARTS_H_
#define FAIRMQPARTS_H_
#include "FairMQTransportFactory.h"
#include "FairMQMessage.h"
#if 0
#ifndef FAIR_MQ_PARTS_H
#pragma GCC warning "Deprecated header: Use <fairmq/Parts.h> instead"
#endif
#endif
#include <vector>
#include <memory> // unique_ptr
/// FairMQParts is a lightweight convenience wrapper around a vector of unique pointers to FairMQMessage, used for sending multi-part messages
class FairMQParts
{
private:
using container = std::vector<std::unique_ptr<FairMQMessage>>;
public:
/// Default constructor
FairMQParts() = default;
/// Copy Constructor
FairMQParts(const FairMQParts&) = delete;
/// Move constructor
FairMQParts(FairMQParts&& p) = default;
/// Assignment operator
FairMQParts& operator=(const FairMQParts&) = delete;
/// Constructor from argument pack of std::unique_ptr<FairMQMessage> rvalues
template <typename... Ts>
FairMQParts(Ts&&... messages) { AddPart(std::forward<Ts>(messages)...); }
/// Default destructor
~FairMQParts() = default;
/// Adds part (FairMQMessage) to the container
/// @param msg message pointer (for example created with NewMessage() method of FairMQDevice)
void AddPart(FairMQMessage* msg)
{
fParts.push_back(std::unique_ptr<FairMQMessage>(msg));
}
/// Adds part (std::unique_ptr<FairMQMessage>&) to the container (move)
/// @param msg unique pointer to FairMQMessage
/// rvalue ref (move required when passing argument)
void AddPart(std::unique_ptr<FairMQMessage>&& msg)
{
fParts.push_back(std::move(msg));
}
/// Add variable list of parts to the container (move)
template <typename... Ts>
void AddPart(std::unique_ptr<FairMQMessage>&& first, Ts&&... remaining)
{
AddPart(std::move(first));
AddPart(std::forward<Ts>(remaining)...);
}
/// Add content of another object by move
void AddPart(FairMQParts&& other)
{
container parts = std::move(other.fParts);
for (auto& part : parts) {
fParts.push_back(std::move(part));
}
}
/// Get reference to part in the container at index (without bounds check)
/// @param index container index
FairMQMessage& operator[](const int index) { return *(fParts[index]); }
/// Get reference to unique pointer to part in the container at index (with bounds check)
/// @param index container index
std::unique_ptr<FairMQMessage>& At(const int index) { return fParts.at(index); }
// ref version
FairMQMessage& AtRef(const int index) { return *(fParts.at(index)); }
/// Get number of parts in the container
/// @return number of parts in the container
int Size() const { return fParts.size(); }
container fParts;
// forward container iterators
using iterator = container::iterator;
using const_iterator = container::const_iterator;
auto begin() -> decltype(fParts.begin()) { return fParts.begin(); }
auto end() -> decltype(fParts.end()) { return fParts.end(); }
auto cbegin() -> decltype(fParts.cbegin()) { return fParts.cbegin(); }
auto cend() -> decltype(fParts.cend()) { return fParts.cend(); }
};
#include <fairmq/Parts.h>
#endif /* FAIRMQPARTS_H_ */

View File

@@ -1,13 +0,0 @@
/********************************************************************************
* Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
/**
* FairMQPoller.cxx
*
* @since 2014-01-23
* @author A. Rybalchenko
*/

View File

@@ -9,31 +9,12 @@
#ifndef FAIRMQPOLLER_H_
#define FAIRMQPOLLER_H_
#include <memory>
#include <stdexcept>
#include <string>
#if 0
#ifndef FAIR_MQ_POLLER_H
#pragma GCC warning "Deprecated header: Use <fairmq/Poller.h> instead"
#endif
#endif
class FairMQPoller
{
public:
virtual void Poll(const int timeout) = 0;
virtual bool CheckInput(const int index) = 0;
virtual bool CheckOutput(const int index) = 0;
virtual bool CheckInput(const std::string& channelKey, const int index) = 0;
virtual bool CheckOutput(const std::string& channelKey, const int index) = 0;
virtual ~FairMQPoller() = default;
};
using FairMQPollerPtr = std::unique_ptr<FairMQPoller>;
namespace fair::mq
{
using Poller = FairMQPoller;
using PollerPtr = FairMQPollerPtr;
struct PollerError : std::runtime_error { using std::runtime_error::runtime_error; };
} // namespace fair::mq
#include <fairmq/Poller.h>
#endif /* FAIRMQPOLLER_H_ */

View File

@@ -1,9 +0,0 @@
/********************************************************************************
* Copyright (C) 2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include <FairMQSocket.h>

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2014-2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -9,88 +9,12 @@
#ifndef FAIRMQSOCKET_H_
#define FAIRMQSOCKET_H_
#include "FairMQMessage.h"
#if 0
#ifndef FAIR_MQ_SOCKET_H
#pragma GCC warning "Deprecated header: Use <fairmq/Socket.h> instead"
#endif
#endif
#include <memory>
#include <ostream>
#include <stdexcept>
#include <string>
#include <vector>
class FairMQTransportFactory;
namespace fair::mq
{
enum class TransferCode : int
{
success = 0,
error = -1,
timeout = -2,
interrupted = -3
};
} // namespace fair::mq
class FairMQSocket
{
public:
FairMQSocket() = default;
FairMQSocket(FairMQTransportFactory* fac) : fTransport(fac) {}
virtual std::string GetId() const = 0;
virtual bool Bind(const std::string& address) = 0;
virtual bool Connect(const std::string& address) = 0;
virtual int64_t Send(FairMQMessagePtr& msg, int timeout = -1) = 0;
virtual int64_t Receive(FairMQMessagePtr& msg, int timeout = -1) = 0;
virtual int64_t Send(std::vector<std::unique_ptr<FairMQMessage>>& msgVec, int timeout = -1) = 0;
virtual int64_t Receive(std::vector<std::unique_ptr<FairMQMessage>>& msgVec, int timeout = -1) = 0;
virtual void Close() = 0;
virtual void SetOption(const std::string& option, const void* value, size_t valueSize) = 0;
virtual void GetOption(const std::string& option, void* value, size_t* valueSize) = 0;
/// If the backend supports it, fills the unsigned integer @a events with the ZMQ_EVENTS value
/// DISCLAIMER: this API is experimental and unsupported and might be dropped / refactored in
/// the future.
virtual int Events(uint32_t* events) = 0;
virtual void SetLinger(const int value) = 0;
virtual int GetLinger() const = 0;
virtual void SetSndBufSize(const int value) = 0;
virtual int GetSndBufSize() const = 0;
virtual void SetRcvBufSize(const int value) = 0;
virtual int GetRcvBufSize() const = 0;
virtual void SetSndKernelSize(const int value) = 0;
virtual int GetSndKernelSize() const = 0;
virtual void SetRcvKernelSize(const int value) = 0;
virtual int GetRcvKernelSize() const = 0;
virtual unsigned long GetBytesTx() const = 0;
virtual unsigned long GetBytesRx() const = 0;
virtual unsigned long GetMessagesTx() const = 0;
virtual unsigned long GetMessagesRx() const = 0;
FairMQTransportFactory* GetTransport() { return fTransport; }
void SetTransport(FairMQTransportFactory* transport) { fTransport = transport; }
virtual ~FairMQSocket() = default;
private:
FairMQTransportFactory* fTransport{nullptr};
};
using FairMQSocketPtr = std::unique_ptr<FairMQSocket>;
namespace fair::mq
{
using Socket = FairMQSocket;
using SocketPtr = FairMQSocketPtr;
struct SocketError : std::runtime_error { using std::runtime_error::runtime_error; };
} // namespace fair::mq
#include <fairmq/Socket.h>
#endif /* FAIRMQSOCKET_H_ */

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2014-2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -9,174 +9,12 @@
#ifndef FAIRMQTRANSPORTFACTORY_H_
#define FAIRMQTRANSPORTFACTORY_H_
#include <FairMQMessage.h>
#include <FairMQPoller.h>
#include <FairMQSocket.h>
#include <FairMQUnmanagedRegion.h>
#include <fairmq/MemoryResources.h>
#include <fairmq/Transports.h>
#if 0
#ifndef FAIR_MQ_TRANSPORTFACTORY_H
#pragma GCC warning "Deprecated header: Use <fairmq/TransportFactory.h> instead"
#endif
#endif
#include <string>
#include <memory> // shared_ptr
#include <vector>
#include <unordered_map>
#include <stdexcept>
#include <cstddef> // size_t
class FairMQChannel;
namespace fair::mq { class ProgOptions; }
class FairMQTransportFactory
{
private:
/// Topology wide unique id
const std::string fkId;
/// The polymorphic memory resource associated with the transport
fair::mq::ChannelResource fMemoryResource{this};
public:
/// ctor
/// @param id Topology wide unique id, usually the device id.
FairMQTransportFactory(std::string id);
auto GetId() const -> const std::string { return fkId; };
/// Get a pointer to the associated polymorphic memory resource
fair::mq::ChannelResource* GetMemoryResource() { return &fMemoryResource; }
operator fair::mq::ChannelResource*() { return &fMemoryResource; }
/// @brief Create empty FairMQMessage (for receiving)
/// @return pointer to FairMQMessage
virtual FairMQMessagePtr CreateMessage() = 0;
/// @brief Create empty FairMQMessage (for receiving), align received buffer to specified alignment
/// @param alignment alignment to align received buffer to
/// @return pointer to FairMQMessage
virtual FairMQMessagePtr CreateMessage(fair::mq::Alignment alignment) = 0;
/// @brief Create new FairMQMessage of specified size
/// @param size message size
/// @return pointer to FairMQMessage
virtual FairMQMessagePtr CreateMessage(const size_t size) = 0;
/// @brief Create new FairMQMessage of specified size and alignment
/// @param size message size
/// @param alignment message alignment
/// @return pointer to FairMQMessage
virtual FairMQMessagePtr CreateMessage(const size_t size, fair::mq::Alignment alignment) = 0;
/// @brief Create new FairMQMessage with user provided buffer and size
/// @param data pointer to user provided buffer
/// @param size size of the user provided buffer
/// @param ffn callback, called when the message is transfered (and can be deleted)
/// @param obj optional helper pointer that can be used in the callback
/// @return pointer to FairMQMessage
virtual FairMQMessagePtr CreateMessage(void* data, const size_t size, fairmq_free_fn* ffn, void* hint = nullptr) = 0;
/// @brief create a message with the buffer located within the corresponding unmanaged region
/// @param unmanagedRegion the unmanaged region that this message buffer belongs to
/// @param data message buffer (must be within the region - checked at runtime by the transport)
/// @param size size of the message
/// @param hint optional parameter, returned to the user in the FairMQRegionCallback
virtual FairMQMessagePtr CreateMessage(FairMQUnmanagedRegionPtr& unmanagedRegion, void* data, const size_t size, void* hint = 0) = 0;
/// @brief Create a socket
virtual FairMQSocketPtr CreateSocket(const std::string& type, const std::string& name) = 0;
/// @brief Create a poller for a single channel (all subchannels)
virtual FairMQPollerPtr CreatePoller(const std::vector<FairMQChannel>& channels) const = 0;
/// @brief Create a poller for specific channels
virtual FairMQPollerPtr CreatePoller(const std::vector<FairMQChannel*>& channels) const = 0;
/// @brief Create a poller for specific channels (all subchannels)
virtual FairMQPollerPtr CreatePoller(const std::unordered_map<std::string, std::vector<FairMQChannel>>& channelsMap, const std::vector<std::string>& channelList) const = 0;
/// @brief Create new UnmanagedRegion
/// @param size size of the region
/// @param callback callback to be called when a message belonging to this region is no longer needed by the transport
/// @param path optional parameter to pass to the underlying transport
/// @param flags optional parameter to pass to the underlying transport
/// @return pointer to UnmanagedRegion
virtual FairMQUnmanagedRegionPtr CreateUnmanagedRegion(const size_t size, FairMQRegionCallback callback = nullptr, const std::string& path = "", int flags = 0, fair::mq::RegionConfig cfg = fair::mq::RegionConfig()) = 0;
virtual FairMQUnmanagedRegionPtr CreateUnmanagedRegion(const size_t size, FairMQRegionBulkCallback callback = nullptr, const std::string& path = "", int flags = 0, fair::mq::RegionConfig cfg = fair::mq::RegionConfig()) = 0;
/// @brief Create new UnmanagedRegion
/// @param size size of the region
/// @param userFlags flags to be stored with the region, have no effect on the transport, but can be retrieved from the region by the user
/// @param callback callback to be called when a message belonging to this region is no longer needed by the transport
/// @param path optional parameter to pass to the underlying transport
/// @param flags optional parameter to pass to the underlying transport
/// @return pointer to UnmanagedRegion
virtual FairMQUnmanagedRegionPtr CreateUnmanagedRegion(const size_t size, const int64_t userFlags, FairMQRegionCallback callback = nullptr, const std::string& path = "", int flags = 0, fair::mq::RegionConfig cfg = fair::mq::RegionConfig()) = 0;
virtual FairMQUnmanagedRegionPtr CreateUnmanagedRegion(const size_t size, const int64_t userFlags, FairMQRegionBulkCallback callback = nullptr, const std::string& path = "", int flags = 0, fair::mq::RegionConfig cfg = fair::mq::RegionConfig()) = 0;
/// @brief Subscribe to region events (creation, destruction, ...)
/// @param callback the callback that is called when a region event occurs
virtual void SubscribeToRegionEvents(FairMQRegionEventCallback callback) = 0;
/// @brief Check if there is an active subscription to region events
/// @return true/false
virtual bool SubscribedToRegionEvents() = 0;
/// @brief Unsubscribe from region events
virtual void UnsubscribeFromRegionEvents() = 0;
virtual std::vector<FairMQRegionInfo> GetRegionInfo() = 0;
/// Get transport type
virtual fair::mq::Transport GetType() const = 0;
virtual void Interrupt() = 0;
virtual void Resume() = 0;
virtual void Reset() = 0;
virtual ~FairMQTransportFactory() = default;
static auto CreateTransportFactory(const std::string& type, const std::string& id = "", const fair::mq::ProgOptions* config = nullptr) -> std::shared_ptr<FairMQTransportFactory>;
static void FairMQNoCleanup(void* /*data*/, void* /*obj*/)
{
}
template<typename T>
static void FairMQSimpleMsgCleanup(void* /*data*/, void* obj)
{
delete static_cast<T*>(obj);
}
template<typename T>
FairMQMessagePtr NewSimpleMessage(const T& data)
{
// todo: is_trivially_copyable not available on gcc < 5, workaround?
// static_assert(std::is_trivially_copyable<T>::value, "The argument type for NewSimpleMessage has to be trivially copyable!");
T* dataCopy = new T(data);
return CreateMessage(dataCopy, sizeof(T), FairMQSimpleMsgCleanup<T>, dataCopy);
}
template<std::size_t N>
FairMQMessagePtr NewSimpleMessage(const char(&data)[N])
{
std::string* msgStr = new std::string(data);
return CreateMessage(const_cast<char*>(msgStr->c_str()), msgStr->length(), FairMQSimpleMsgCleanup<std::string>, msgStr);
}
FairMQMessagePtr NewSimpleMessage(const std::string& str)
{
std::string* msgStr = new std::string(str);
return CreateMessage(const_cast<char*>(msgStr->c_str()), msgStr->length(), FairMQSimpleMsgCleanup<std::string>, msgStr);
}
template<typename T>
FairMQMessagePtr NewStaticMessage(const T& data)
{
return CreateMessage(data, sizeof(T), FairMQNoCleanup, nullptr);
}
FairMQMessagePtr NewStaticMessage(const std::string& str)
{
return CreateMessage(const_cast<char*>(str.c_str()), str.length(), FairMQNoCleanup, nullptr);
}
};
namespace fair::mq
{
using TransportFactory = FairMQTransportFactory;
struct TransportFactoryError : std::runtime_error { using std::runtime_error::runtime_error; };
} // namespace fair::mq
#include <fairmq/TransportFactory.h>
#endif /* FAIRMQTRANSPORTFACTORY_H_ */

View File

@@ -9,120 +9,12 @@
#ifndef FAIRMQUNMANAGEDREGION_H_
#define FAIRMQUNMANAGEDREGION_H_
#include <cstddef> // size_t
#include <cstdint> // uint32_t
#include <fairmq/Transports.h>
#include <functional> // std::function
#include <memory> // std::unique_ptr
#include <ostream> // std::ostream
#include <vector>
#if 0
#ifndef FAIR_MQ_UNMANAGEDREGION_H
#pragma GCC warning "Deprecated header: Use <fairmq/UnmanagedRegion.h> instead"
#endif
#endif
class FairMQTransportFactory;
enum class FairMQRegionEvent : int
{
created,
destroyed,
local_only
};
struct FairMQRegionInfo
{
FairMQRegionInfo() = default;
FairMQRegionInfo(bool _managed, uint64_t _id, void* _ptr, size_t _size, int64_t _flags, FairMQRegionEvent _event)
: managed(_managed)
, id(_id)
, ptr(_ptr)
, size(_size)
, flags(_flags)
, event(_event)
{}
bool managed = true; // managed/unmanaged
uint64_t id = 0; // id of the region
void* ptr = nullptr; // pointer to the start of the region
size_t size = 0; // region size
int64_t flags = 0; // custom flags set by the creator
FairMQRegionEvent event = FairMQRegionEvent::created;
};
struct FairMQRegionBlock {
void* ptr;
size_t size;
void* hint;
FairMQRegionBlock(void* p, size_t s, void* h)
: ptr(p), size(s), hint(h)
{}
};
using FairMQRegionCallback = std::function<void(void*, size_t, void*)>;
using FairMQRegionBulkCallback = std::function<void(const std::vector<FairMQRegionBlock>&)>;
using FairMQRegionEventCallback = std::function<void(FairMQRegionInfo)>;
class FairMQUnmanagedRegion
{
public:
FairMQUnmanagedRegion() = default;
FairMQUnmanagedRegion(FairMQTransportFactory* factory) : fTransport(factory) {}
virtual void* GetData() const = 0;
virtual size_t GetSize() const = 0;
virtual uint16_t GetId() const = 0;
virtual void SetLinger(uint32_t linger) = 0;
virtual uint32_t GetLinger() const = 0;
virtual fair::mq::Transport GetType() const = 0;
FairMQTransportFactory* GetTransport() { return fTransport; }
void SetTransport(FairMQTransportFactory* transport) { fTransport = transport; }
virtual ~FairMQUnmanagedRegion() = default;
private:
FairMQTransportFactory* fTransport{nullptr};
};
using FairMQUnmanagedRegionPtr = std::unique_ptr<FairMQUnmanagedRegion>;
inline std::ostream& operator<<(std::ostream& os, const FairMQRegionEvent& event)
{
switch (event) {
case FairMQRegionEvent::created:
return os << "created";
case FairMQRegionEvent::destroyed:
return os << "destroyed";
case FairMQRegionEvent::local_only:
return os << "local_only";
default:
return os << "unrecognized event";
}
}
namespace fair::mq
{
struct RegionConfig
{
RegionConfig() = default;
RegionConfig(bool l, bool z)
: lock(l), zero(z)
{}
bool lock = false;
bool zero = false;
};
using RegionCallback = FairMQRegionCallback;
using RegionBulkCallback = FairMQRegionBulkCallback;
using RegionEventCallback = FairMQRegionEventCallback;
using RegionEvent = FairMQRegionEvent;
using RegionInfo = FairMQRegionInfo;
using RegionBlock = FairMQRegionBlock;
using UnmanagedRegion = FairMQUnmanagedRegion;
using UnmanagedRegionPtr = FairMQUnmanagedRegionPtr;
} // namespace fair::mq
#include <fairmq/UnmanagedRegion.h>
#endif /* FAIRMQUNMANAGEDREGION_H_ */

View File

@@ -11,21 +11,35 @@
#include <fairmq/ProgOptionsFwd.h>
class FairMQChannel;
class FairMQDevice;
class FairMQMemoryResource;
class FairMQMessage;
class FairMQParts;
class FairMQPoller;
class FairMQRegionBlock;
class FairMQRegionConfig;
class FairMQRegionInfo;
class FairMQSocket;
class FairMQTransportFactory;
class FairMQUnmanagedRegion;
namespace fair::mq {
class FairMQMemoryResource;
}
class Channel;
class Device;
class MemoryResource;
class Message;
class Parts;
class Poller;
class RegionBlock;
class RegionConfig;
class RegionInfo;
class Socket;
class TransportFactory;
class UnmanagedRegion;
using FairMQMemoryResource = MemoryResource;
} // namespace fair::mq
using FairMQChannel = fair::mq::Channel;
using FairMQDevice = fair::mq::Device;
using FairMQMessage = fair::mq::Message;
using FairMQParts = fair::mq::Parts;
using FairMQPoller = fair::mq::Poller;
using FairMQRegionBlock = fair::mq::RegionBlock;
using FairMQRegionConfig = fair::mq::RegionConfig;
using FairMQRegionInfo = fair::mq::RegionInfo;
using FairMQSocket = fair::mq::Socket;
using FairMQTransportFactory = fair::mq::TransportFactory;
using FairMQUnmanagedRegion = fair::mq::UnmanagedRegion;
#endif // FAIR_MQ_FWDDECLS_H

View File

@@ -13,18 +13,19 @@
*/
#include "JSONParser.h"
#include "FairMQChannel.h"
#include <fairmq/PropertyOutput.h>
#include <fairmq/tools/Strings.h>
#include <fairlogger/Logger.h>
#include <boost/any.hpp>
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/property_tree/json_parser.hpp>
#undef BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/property_tree/ptree.hpp>
#include <boost/any.hpp>
#include <fairlogger/Logger.h>
#include <fairmq/Channel.h>
#include <fairmq/JSONParser.h>
#include <fairmq/PropertyOutput.h>
#include <fairmq/tools/Strings.h>
#include <iomanip>
using namespace std;

View File

@@ -1,10 +1,10 @@
/********************************************************************************
* Copyright (C) 2018 CERN and copyright holders of ALICE O2 *
* Copyright (C) 2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2018 CERN and copyright holders of ALICE O2 *
* Copyright (C) 2018-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
/// @brief Memory allocators and interfaces related to managing memory via the
@@ -15,27 +15,24 @@
#ifndef FAIR_MQ_MEMORY_RESOURCES_H
#define FAIR_MQ_MEMORY_RESOURCES_H
#include <FairMQMessage.h>
class FairMQTransportFactory;
#include <boost/container/container_fwd.hpp>
#include <boost/container/flat_map.hpp>
#include <boost/container/pmr/memory_resource.hpp>
#include <cstring>
#include <fairmq/Message.h>
#include <stdexcept>
#include <utility>
namespace fair::mq
{
namespace fair::mq {
class TransportFactory;
using byte = unsigned char;
namespace pmr = boost::container::pmr;
/// All FairMQ related memory resources need to inherit from this interface
/// class for the
/// getMessage() api.
class FairMQMemoryResource : public pmr::memory_resource
class MemoryResource : public pmr::memory_resource
{
public:
/// return the message containing data associated with the pointer (to start
@@ -43,9 +40,9 @@ class FairMQMemoryResource : public pmr::memory_resource
/// buffer), e.g. pointer returned by std::vector::data() return nullptr if
/// returning
/// a message does not make sense!
virtual FairMQMessagePtr getMessage(void *p) = 0;
virtual void *setMessage(FairMQMessagePtr) = 0;
virtual FairMQTransportFactory *getTransportFactory() noexcept = 0;
virtual MessagePtr getMessage(void* p) = 0;
virtual void* setMessage(MessagePtr) = 0;
virtual TransportFactory* getTransportFactory() noexcept = 0;
virtual size_t getNumberOfMessages() const noexcept = 0;
};
@@ -54,41 +51,42 @@ class FairMQMemoryResource : public pmr::memory_resource
/// delegated to FairMQ so standard (e.g. STL) containers can construct their
/// stuff in
/// memory regions appropriate for the data channel configuration.
class ChannelResource : public FairMQMemoryResource
class ChannelResource : public MemoryResource
{
protected:
FairMQTransportFactory* factory{nullptr};
TransportFactory* factory{nullptr};
// TODO: for now a map to keep track of allocations, something else would
// probably be
// faster, but for now this does not need to be fast.
boost::container::flat_map<void*, FairMQMessagePtr> messageMap;
boost::container::flat_map<void*, MessagePtr> messageMap;
public:
ChannelResource() = delete;
ChannelResource(FairMQTransportFactory* _factory)
ChannelResource(TransportFactory* _factory)
: factory(_factory)
{
if (!_factory) {
throw std::runtime_error("Tried to construct from a nullptr FairMQTransportFactory");
throw std::runtime_error(
"Tried to construct from a nullptr fair::mq::TransportFactory");
}
};
FairMQMessagePtr getMessage(void* p) override
MessagePtr getMessage(void* p) override
{
auto mes = std::move(messageMap[p]);
messageMap.erase(p);
return mes;
}
void* setMessage(FairMQMessagePtr message) override
void* setMessage(MessagePtr message) override
{
void* addr = message->GetData();
messageMap[addr] = std::move(message);
return addr;
}
FairMQTransportFactory* getTransportFactory() noexcept override { return factory; }
TransportFactory* getTransportFactory() noexcept override { return factory; }
size_t getNumberOfMessages() const noexcept override { return messageMap.size(); }
@@ -99,12 +97,16 @@ class ChannelResource : public FairMQMemoryResource
messageMap.erase(p);
};
bool do_is_equal(const pmr::memory_resource &other) const noexcept override
bool do_is_equal(const pmr::memory_resource& other) const noexcept override
{
return this == &other;
};
};
} // namespace fair::mq
// using FairMQMemoryResource [[deprecated("Use fair::mq::MemoryResource")]] =
// MemoryResource;
using FairMQMemoryResource = MemoryResource;
} // namespace fair::mq
#endif /* FAIR_MQ_MEMORY_RESOURCES_H */

View File

@@ -9,6 +9,75 @@
#ifndef FAIR_MQ_MESSAGE_H
#define FAIR_MQ_MESSAGE_H
#include <FairMQMessage.h>
#include <cstddef> // for size_t
#include <fairmq/Transports.h>
#include <memory> // unique_ptr
#include <stdexcept>
namespace fair::mq {
using FreeFn = void(void* data, void* hint);
class TransportFactory;
struct Alignment
{
size_t alignment;
explicit operator size_t() const { return alignment; }
};
struct Message
{
Message() = default;
Message(TransportFactory* factory)
: fTransport(factory)
{}
virtual void Rebuild() = 0;
virtual void Rebuild(Alignment alignment) = 0;
virtual void Rebuild(const size_t size) = 0;
virtual void Rebuild(const size_t size, Alignment alignment) = 0;
virtual void Rebuild(void* data, const size_t size, FreeFn* ffn, void* hint = nullptr) = 0;
virtual void* GetData() const = 0;
virtual size_t GetSize() const = 0;
virtual bool SetUsedSize(const size_t size) = 0;
virtual Transport GetType() const = 0;
TransportFactory* GetTransport() { return fTransport; }
void SetTransport(TransportFactory* transport) { fTransport = transport; }
/// Copy the message buffer from another message
/// Transport may choose not to physically copy the buffer, but to share across the messages.
/// Modifying the buffer after a call to Copy() is undefined behaviour.
/// @param msg message to copy the buffer from.
virtual void Copy(const Message& msg) = 0;
virtual ~Message() = default;
private:
TransportFactory* fTransport{nullptr};
};
using MessagePtr = std::unique_ptr<Message>;
struct MessageError : std::runtime_error
{
using std::runtime_error::runtime_error;
};
struct MessageBadAlloc : std::runtime_error
{
using std::runtime_error::runtime_error;
};
} // namespace fair::mq
// using fairmq_free_fn [[deprecated("Use fair::mq::FreeFn")]] = fair::mq::FreeFn;
// using FairMQMessage [[deprecated("Use fair::mq::Message")]] = fair::mq::Message;
// using FairMQMessagePtr [[deprecated("Use fair::mq::MessagePtr")]] = fair::mq::MessagePtr;
using fairmq_free_fn = fair::mq::FreeFn;
using FairMQMessage = fair::mq::Message;
using FairMQMessagePtr = fair::mq::MessagePtr;
#endif // FAIR_MQ_MESSAGE_H

94
fairmq/Parts.h Normal file
View File

@@ -0,0 +1,94 @@
/********************************************************************************
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#ifndef FAIR_MQ_PARTS_H
#define FAIR_MQ_PARTS_H
#include <fairmq/Message.h>
#include <memory> // unique_ptr
#include <vector>
namespace fair::mq {
/// fair::mq::Parts is a lightweight convenience wrapper around a vector of unique pointers to
/// Message, used for sending multi-part messages
class Parts
{
private:
using container = std::vector<MessagePtr>;
public:
Parts() = default;
Parts(const Parts&) = delete;
Parts(Parts&& p) = default;
Parts& operator=(const Parts&) = delete;
template<typename... Ts>
Parts(Ts&&... messages)
{
AddPart(std::forward<Ts>(messages)...);
}
~Parts() = default;
/// Adds part (Message) to the container
/// @param msg message pointer (for example created with NewMessage() method of Device)
void AddPart(Message* msg) { fParts.push_back(MessagePtr(msg)); }
/// Adds part to the container (move)
/// @param msg unique pointer to Message
/// rvalue ref (move required when passing argument)
void AddPart(MessagePtr&& msg) { fParts.push_back(std::move(msg)); }
/// Add variable list of parts to the container (move)
template<typename... Ts>
void AddPart(MessagePtr&& first, Ts&&... remaining)
{
AddPart(std::move(first));
AddPart(std::forward<Ts>(remaining)...);
}
/// Add content of another object by move
void AddPart(Parts&& other)
{
container parts = std::move(other.fParts);
for (auto& part : parts) {
fParts.push_back(std::move(part));
}
}
/// Get reference to part in the container at index (without bounds check)
/// @param index container index
Message& operator[](const int index) { return *(fParts[index]); }
/// Get reference to unique pointer to part in the container at index (with bounds check)
/// @param index container index
MessagePtr& At(const int index) { return fParts.at(index); }
// ref version
Message& AtRef(const int index) { return *(fParts.at(index)); }
/// Get number of parts in the container
/// @return number of parts in the container
int Size() const { return fParts.size(); }
container fParts;
// forward container iterators
using iterator = container::iterator;
using const_iterator = container::const_iterator;
auto begin() -> decltype(fParts.begin()) { return fParts.begin(); }
auto end() -> decltype(fParts.end()) { return fParts.end(); }
auto cbegin() -> decltype(fParts.cbegin()) { return fParts.cbegin(); }
auto cend() -> decltype(fParts.cend()) { return fParts.cend(); }
};
} // namespace fair::mq
// using FairMQParts [[deprecated("Use fair::mq::Parts")]] = fair::mq::Parts;
using FairMQParts = fair::mq::Parts;
#endif /* FAIR_MQ_PARTS_H */

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2017-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -9,8 +9,8 @@
#ifndef FAIR_MQ_PLUGINSERVICES_H
#define FAIR_MQ_PLUGINSERVICES_H
#include <fairmq/Device.h>
#include <fairmq/States.h>
#include <FairMQDevice.h>
#include <fairmq/ProgOptions.h>
#include <fairmq/Properties.h>
@@ -40,7 +40,7 @@ class PluginServices
{
public:
PluginServices() = delete;
PluginServices(ProgOptions& config, FairMQDevice& device)
PluginServices(ProgOptions& config, Device& device)
: fConfig(config)
, fDevice(device)
{}
@@ -117,7 +117,7 @@ class PluginServices
/// The state transition may not happen immediately, but when the current state evaluates the
/// pending transition event and terminates. In other words, the device states are scheduled cooperatively.
/// If the device control role has not been taken yet, calling this function will take over control implicitely.
auto ChangeDeviceState(const std::string& controller, const DeviceStateTransition next) -> bool;
auto ChangeDeviceState(const std::string& controller, DeviceStateTransition next) -> bool;
/// @brief Subscribe with a callback to device state changes
/// @param subscriber id
@@ -269,7 +269,7 @@ class PluginServices
private:
fair::mq::ProgOptions& fConfig;
FairMQDevice& fDevice;
Device& fDevice;
boost::optional<std::string> fDeviceController;
mutable std::mutex fDeviceControllerMutex;
std::condition_variable fReleaseDeviceControlCondition;

View File

@@ -9,6 +9,35 @@
#ifndef FAIR_MQ_POLLER_H
#define FAIR_MQ_POLLER_H
#include <FairMQPoller.h>
#include <memory>
#include <stdexcept>
#include <string>
namespace fair::mq {
struct Poller
{
virtual void Poll(const int timeout) = 0;
virtual bool CheckInput(const int index) = 0;
virtual bool CheckOutput(const int index) = 0;
virtual bool CheckInput(const std::string& channelKey, const int index) = 0;
virtual bool CheckOutput(const std::string& channelKey, const int index) = 0;
virtual ~Poller() = default;
};
using PollerPtr = std::unique_ptr<Poller>;
struct PollerError : std::runtime_error
{
using std::runtime_error::runtime_error;
};
} // namespace fair::mq
// using FairMQPoller [[deprecated("Use fair::mq::Poller")]] = fair::mq::Poller;
// using FairMQPollerPtr [[deprecated("Use fair::mq::PollerPtr")]] = fair::mq::PollerPtr;
using FairMQPoller = fair::mq::Poller;
using FairMQPollerPtr = fair::mq::PollerPtr;
#endif // FAIR_MQ_POLLER_H

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2014-2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -9,22 +9,20 @@
#ifndef FAIR_MQ_PROGOPTIONS_H
#define FAIR_MQ_PROGOPTIONS_H
#include "FairMQChannel.h"
#include "FairMQLogger.h"
#include <boost/program_options.hpp>
#include <fairlogger/Logger.h>
#include <fairmq/Channel.h>
#include <fairmq/EventManager.h>
#include <fairmq/ProgOptionsFwd.h>
#include <fairmq/Properties.h>
#include <fairmq/tools/Strings.h>
#include <boost/program_options.hpp>
#include <functional>
#include <map>
#include <mutex>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <vector>
#include <stdexcept>
namespace fair::mq
{
@@ -38,10 +36,10 @@ class ProgOptions
virtual ~ProgOptions() = default;
void ParseAll(const std::vector<std::string>& cmdArgs, bool allowUnregistered);
void ParseAll(const int argc, char const* const* argv, bool allowUnregistered = true);
void ParseAll(int argc, char const* const* argv, bool allowUnregistered = true);
void Notify();
void AddToCmdLineOptions(const boost::program_options::options_description optDesc, bool visible = true);
void AddToCmdLineOptions(boost::program_options::options_description optDesc, bool visible = true);
boost::program_options::options_description& GetCmdLineOptions();
/// @brief Checks a property with the given key exist in the configuration
@@ -174,7 +172,7 @@ class ProgOptions
/// @brief Takes the provided channel and creates properties based on it
/// @param name channel name
/// @param channel FairMQChannel reference
void AddChannel(const std::string& name, const FairMQChannel& channel);
void AddChannel(const std::string& name, const Channel& channel);
/// @brief Subscribe to property updates of type T
/// @param subscriber

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2019-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -9,11 +9,11 @@
#ifndef FAIR_MQ_PROGOPTIONSFWD_H
#define FAIR_MQ_PROGOPTIONSFWD_H
namespace fair::mq
{
namespace fair::mq {
class ProgOptions;
}
// using FairMQProgOptions [[deprecated("Use fair::mq::ProgOptions")]] = fair::mq::ProgOptions;
using FairMQProgOptions = fair::mq::ProgOptions;
#endif /* FAIR_MQ_PROGOPTIONSFWD_H */

View File

@@ -9,6 +9,87 @@
#ifndef FAIR_MQ_SOCKET_H
#define FAIR_MQ_SOCKET_H
#include <FairMQSocket.h>
#include <fairmq/Message.h>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
namespace fair::mq {
class TransportFactory;
enum class TransferCode : int
{
success = 0,
error = -1,
timeout = -2,
interrupted = -3
};
struct Socket
{
Socket() = default;
Socket(TransportFactory* fac)
: fTransport(fac)
{}
virtual std::string GetId() const = 0;
virtual bool Bind(const std::string& address) = 0;
virtual bool Connect(const std::string& address) = 0;
virtual int64_t Send(MessagePtr& msg, int timeout = -1) = 0;
virtual int64_t Receive(MessagePtr& msg, int timeout = -1) = 0;
virtual int64_t Send(std::vector<std::unique_ptr<Message>>& msgVec, int timeout = -1) = 0;
virtual int64_t Receive(std::vector<std::unique_ptr<Message>>& msgVec, int timeout = -1) = 0;
virtual void Close() = 0;
virtual void SetOption(const std::string& option, const void* value, size_t valueSize) = 0;
virtual void GetOption(const std::string& option, void* value, size_t* valueSize) = 0;
/// If the backend supports it, fills the unsigned integer @a events with the ZMQ_EVENTS value
/// DISCLAIMER: this API is experimental and unsupported and might be dropped / refactored in
/// the future.
virtual int Events(uint32_t* events) = 0;
virtual void SetLinger(const int value) = 0;
virtual int GetLinger() const = 0;
virtual void SetSndBufSize(const int value) = 0;
virtual int GetSndBufSize() const = 0;
virtual void SetRcvBufSize(const int value) = 0;
virtual int GetRcvBufSize() const = 0;
virtual void SetSndKernelSize(const int value) = 0;
virtual int GetSndKernelSize() const = 0;
virtual void SetRcvKernelSize(const int value) = 0;
virtual int GetRcvKernelSize() const = 0;
virtual unsigned long GetBytesTx() const = 0;
virtual unsigned long GetBytesRx() const = 0;
virtual unsigned long GetMessagesTx() const = 0;
virtual unsigned long GetMessagesRx() const = 0;
TransportFactory* GetTransport() { return fTransport; }
void SetTransport(TransportFactory* transport) { fTransport = transport; }
virtual ~Socket() = default;
private:
TransportFactory* fTransport{nullptr};
};
using SocketPtr = std::unique_ptr<Socket>;
struct SocketError : std::runtime_error
{
using std::runtime_error::runtime_error;
};
} // namespace fair::mq
// using FairMQSocket [[deprecated("Use fair::mq::Socket")]] = fair::mq::Socket;
// using FairMQSocketPtr [[deprecated("Use fair::mq::SocketPtr")]] = fair::mq::SocketPtr;
using FairMQSocket = fair::mq::Socket;
using FairMQSocketPtr = fair::mq::SocketPtr;
#endif // FAIR_MQ_SOCKET_H

View File

@@ -1,53 +1,47 @@
/********************************************************************************
* Copyright (C) 2017-2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2017-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include <FairMQTransportFactory.h>
#include <fairmq/TransportFactory.h>
#include <fairmq/shmem/TransportFactory.h>
#include <fairmq/zeromq/TransportFactory.h>
#ifdef BUILD_OFI_TRANSPORT
#include <fairmq/ofi/TransportFactory.h>
#endif
#include <FairMQLogger.h>
#include <fairmq/tools/Unique.h>
#include <fairmq/tools/Strings.h>
#include <fairlogger/Logger.h>
#include <fairmq/Tools.h>
#include <memory>
#include <string>
#include <utility> // move
#include <utility> // move
using namespace std;
FairMQTransportFactory::FairMQTransportFactory(string id)
: fkId(std::move(id))
{}
namespace fair::mq {
auto FairMQTransportFactory::CreateTransportFactory(const string& type,
const string& id,
const fair::mq::ProgOptions* config)
-> shared_ptr<FairMQTransportFactory>
auto TransportFactory::CreateTransportFactory(const string& type,
const string& id,
const ProgOptions* config)
-> shared_ptr<TransportFactory>
{
auto finalId = id;
// Generate uuid if empty
if (finalId.empty()) {
finalId = fair::mq::tools::Uuid();
finalId = tools::Uuid();
}
if (type == "zeromq") {
return make_shared<fair::mq::zmq::TransportFactory>(finalId, config);
return make_shared<zmq::TransportFactory>(finalId, config);
} else if (type == "shmem") {
return make_shared<fair::mq::shmem::TransportFactory>(finalId, config);
return make_shared<shmem::TransportFactory>(finalId, config);
}
#ifdef BUILD_OFI_TRANSPORT
else if (type == "ofi") {
return make_shared<fair::mq::ofi::TransportFactory>(finalId, config);
return make_shared<ofi::TransportFactory>(finalId, config);
}
#endif /* BUILD_OFI_TRANSPORT */
else {
@@ -60,6 +54,8 @@ auto FairMQTransportFactory::CreateTransportFactory(const string& type,
<< ", and \"ofi\""
#endif /* BUILD_OFI_TRANSPORT */
<< ". Exiting.";
throw fair::mq::TransportFactoryError(fair::mq::tools::ToString("Unavailable transport requested: ", type));
throw TransportFactoryError(tools::ToString("Unavailable transport requested: ", type));
}
}
} // namespace fair::mq

View File

@@ -9,6 +9,220 @@
#ifndef FAIR_MQ_TRANSPORTFACTORY_H
#define FAIR_MQ_TRANSPORTFACTORY_H
#include <FairMQTransportFactory.h>
#include <cstddef> // size_t
#include <fairmq/MemoryResources.h>
#include <fairmq/Message.h>
#include <fairmq/Poller.h>
#include <fairmq/Socket.h>
#include <fairmq/Transports.h>
#include <fairmq/UnmanagedRegion.h>
#include <memory> // shared_ptr
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <vector>
namespace fair::mq {
class Channel;
class ProgOptions;
class TransportFactory
{
private:
/// Topology wide unique id
const std::string fkId;
/// The polymorphic memory resource associated with the transport
ChannelResource fMemoryResource{this};
public:
/// ctor
/// @param id Topology wide unique id, usually the device id.
TransportFactory(std::string id)
: fkId(std::move(id))
{}
auto GetId() const -> const std::string { return fkId; };
/// Get a pointer to the associated polymorphic memory resource
ChannelResource* GetMemoryResource() { return &fMemoryResource; }
operator ChannelResource*() { return &fMemoryResource; }
/// @brief Create empty Message (for receiving)
/// @return pointer to Message
virtual MessagePtr CreateMessage() = 0;
/// @brief Create empty Message (for receiving), align received buffer to specified alignment
/// @param alignment alignment to align received buffer to
/// @return pointer to Message
virtual MessagePtr CreateMessage(Alignment alignment) = 0;
/// @brief Create new Message of specified size
/// @param size message size
/// @return pointer to Message
virtual MessagePtr CreateMessage(size_t size) = 0;
/// @brief Create new Message of specified size and alignment
/// @param size message size
/// @param alignment message alignment
/// @return pointer to Message
virtual MessagePtr CreateMessage(size_t size, Alignment alignment) = 0;
/// @brief Create new Message with user provided buffer and size
/// @param data pointer to user provided buffer
/// @param size size of the user provided buffer
/// @param ffn callback, called when the message is transfered (and can be deleted)
/// @param obj optional helper pointer that can be used in the callback
/// @return pointer to Message
virtual MessagePtr CreateMessage(void* data,
size_t size,
FreeFn* ffn,
void* hint = nullptr) = 0;
/// @brief create a message with the buffer located within the corresponding unmanaged region
/// @param unmanagedRegion the unmanaged region that this message buffer belongs to
/// @param data message buffer (must be within the region - checked at runtime by the transport)
/// @param size size of the message
/// @param hint optional parameter, returned to the user in the RegionCallback
virtual MessagePtr CreateMessage(UnmanagedRegionPtr& unmanagedRegion,
void* data,
size_t size,
void* hint = nullptr) = 0;
/// @brief Create a socket
virtual SocketPtr CreateSocket(const std::string& type, const std::string& name) = 0;
/// @brief Create a poller for a single channel (all subchannels)
virtual PollerPtr CreatePoller(const std::vector<Channel>& channels) const = 0;
/// @brief Create a poller for specific channels
virtual PollerPtr CreatePoller(const std::vector<Channel*>& channels) const = 0;
/// @brief Create a poller for specific channels (all subchannels)
virtual PollerPtr CreatePoller(
const std::unordered_map<std::string, std::vector<Channel>>& channelsMap,
const std::vector<std::string>& channelList) const = 0;
/// @brief Create new UnmanagedRegion
/// @param size size of the region
/// @param callback callback to be called when a message belonging to this region is no longer
/// needed by the transport
/// @param path optional parameter to pass to the underlying transport
/// @param flags optional parameter to pass to the underlying transport
/// @return pointer to UnmanagedRegion
virtual UnmanagedRegionPtr CreateUnmanagedRegion(const size_t size,
RegionCallback callback = nullptr,
const std::string& path = "",
int flags = 0,
RegionConfig cfg = RegionConfig()) = 0;
virtual UnmanagedRegionPtr CreateUnmanagedRegion(const size_t size,
RegionBulkCallback callback = nullptr,
const std::string& path = "",
int flags = 0,
RegionConfig cfg = RegionConfig()) = 0;
/// @brief Create new UnmanagedRegion
/// @param size size of the region
/// @param userFlags flags to be stored with the region, have no effect on the transport, but
/// can be retrieved from the region by the user
/// @param callback callback to be called when a message belonging to this region is no longer
/// needed by the transport
/// @param path optional parameter to pass to the underlying transport
/// @param flags optional parameter to pass to the underlying transport
/// @return pointer to UnmanagedRegion
virtual UnmanagedRegionPtr CreateUnmanagedRegion(const size_t size,
const int64_t userFlags,
RegionCallback callback = nullptr,
const std::string& path = "",
int flags = 0,
RegionConfig cfg = RegionConfig()) = 0;
virtual UnmanagedRegionPtr CreateUnmanagedRegion(const size_t size,
const int64_t userFlags,
RegionBulkCallback callback = nullptr,
const std::string& path = "",
int flags = 0,
RegionConfig cfg = RegionConfig()) = 0;
/// @brief Subscribe to region events (creation, destruction, ...)
/// @param callback the callback that is called when a region event occurs
virtual void SubscribeToRegionEvents(RegionEventCallback callback) = 0;
/// @brief Check if there is an active subscription to region events
/// @return true/false
virtual bool SubscribedToRegionEvents() = 0;
/// @brief Unsubscribe from region events
virtual void UnsubscribeFromRegionEvents() = 0;
virtual std::vector<RegionInfo> GetRegionInfo() = 0;
/// Get transport type
virtual Transport GetType() const = 0;
virtual void Interrupt() = 0;
virtual void Resume() = 0;
virtual void Reset() = 0;
virtual ~TransportFactory() = default;
static auto CreateTransportFactory(const std::string& type,
const std::string& id = "",
const ProgOptions* config = nullptr)
-> std::shared_ptr<TransportFactory>;
static void NoCleanup(void* /*data*/, void* /*obj*/) {}
template<typename T>
static void SimpleMsgCleanup(void* /*data*/, void* obj)
{
delete static_cast<T*>(obj);
}
template<typename T>
MessagePtr NewSimpleMessage(const T& data)
{
// todo: is_trivially_copyable not available on gcc < 5, workaround?
// static_assert(std::is_trivially_copyable<T>::value, "The argument type for
// NewSimpleMessage has to be trivially copyable!");
T* dataCopy = new T(data);
return CreateMessage(dataCopy, sizeof(T), SimpleMsgCleanup<T>, dataCopy);
}
template<std::size_t N>
MessagePtr NewSimpleMessage(const char (&data)[N])
{
std::string* msgStr = new std::string(data);
return CreateMessage(const_cast<char*>(msgStr->c_str()),
msgStr->length(),
SimpleMsgCleanup<std::string>,
msgStr);
}
MessagePtr NewSimpleMessage(const std::string& str)
{
std::string* msgStr = new std::string(str);
return CreateMessage(const_cast<char*>(msgStr->c_str()),
msgStr->length(),
SimpleMsgCleanup<std::string>,
msgStr);
}
template<typename T>
MessagePtr NewStaticMessage(const T& data)
{
return CreateMessage(data, sizeof(T), NoCleanup, nullptr);
}
MessagePtr NewStaticMessage(const std::string& str)
{
return CreateMessage(const_cast<char*>(str.c_str()), str.length(), NoCleanup, nullptr);
}
};
struct TransportFactoryError : std::runtime_error
{
using std::runtime_error::runtime_error;
};
} // namespace fair::mq
// using FairMQTransportFactory [[deprecated("Use fair::mq::TransportFactory")]] =
// fair::mq::TransportFactory;
// using FairMQTransportFactoryError [[deprecated("Use fair::mq::TransportFactoryError")]] =
// fair::mq::TransportFactoryError;
using FairMQTransportFactory = fair::mq::TransportFactory;
using FairMQTransportFactoryError = fair::mq::TransportFactoryError;
#endif // FAIR_MQ_TRANSPORTFACTORY_H

View File

@@ -9,6 +9,139 @@
#ifndef FAIR_MQ_UNMANAGEDREGION_H
#define FAIR_MQ_UNMANAGEDREGION_H
#include <FairMQUnmanagedRegion.h>
#include <cstddef> // size_t
#include <cstdint> // uint32_t
#include <fairmq/Transports.h>
#include <functional> // std::function
#include <memory> // std::unique_ptr
#include <ostream> // std::ostream
#include <vector>
namespace fair::mq {
class TransportFactory;
enum class RegionEvent : int
{
created,
destroyed,
local_only
};
struct RegionInfo
{
RegionInfo() = default;
RegionInfo(bool _managed,
uint64_t _id,
void* _ptr,
size_t _size,
int64_t _flags,
RegionEvent _event)
: managed(_managed)
, id(_id)
, ptr(_ptr)
, size(_size)
, flags(_flags)
, event(_event)
{}
bool managed = true; // managed/unmanaged
uint64_t id = 0; // id of the region
void* ptr = nullptr; // pointer to the start of the region
size_t size = 0; // region size
int64_t flags = 0; // custom flags set by the creator
RegionEvent event = RegionEvent::created;
};
struct RegionBlock
{
void* ptr;
size_t size;
void* hint;
RegionBlock(void* p, size_t s, void* h)
: ptr(p)
, size(s)
, hint(h)
{}
};
using RegionCallback = std::function<void(void*, size_t, void*)>;
using RegionBulkCallback = std::function<void(const std::vector<RegionBlock>&)>;
using RegionEventCallback = std::function<void(RegionInfo)>;
struct UnmanagedRegion
{
UnmanagedRegion() = default;
UnmanagedRegion(TransportFactory* factory)
: fTransport(factory)
{}
virtual void* GetData() const = 0;
virtual size_t GetSize() const = 0;
virtual uint16_t GetId() const = 0;
virtual void SetLinger(uint32_t linger) = 0;
virtual uint32_t GetLinger() const = 0;
virtual Transport GetType() const = 0;
TransportFactory* GetTransport() { return fTransport; }
void SetTransport(TransportFactory* transport) { fTransport = transport; }
virtual ~UnmanagedRegion() = default;
private:
TransportFactory* fTransport{nullptr};
};
using UnmanagedRegionPtr = std::unique_ptr<UnmanagedRegion>;
inline std::ostream& operator<<(std::ostream& os, const RegionEvent& event)
{
switch (event) {
case RegionEvent::created:
return os << "created";
case RegionEvent::destroyed:
return os << "destroyed";
case RegionEvent::local_only:
return os << "local_only";
default:
return os << "unrecognized event";
}
}
struct RegionConfig
{
RegionConfig() = default;
RegionConfig(bool l, bool z)
: lock(l)
, zero(z)
{}
bool lock = false;
bool zero = false;
};
} // namespace fair::mq
// using FairMQRegionEvent [[deprecated("Use fair::mq::RegionBlock")]] = fair::mq::RegionEvent;
// using FairMQRegionInfo [[deprecated("Use fair::mq::RegionInfo")]] = fair::mq::RegionInfo;
// using FairMQRegionBlock [[deprecated("Use fair::mq::RegionBlock")]] = fair::mq::RegionBlock;
// using FairMQRegionCallback [[deprecated("Use fair::mq::RegionCallback")]] = fair::mq::RegionCallback;
// using FairMQRegionBulkCallback [[deprecated("Use fair::mq::RegionBulkCallback")]] = fair::mq::RegionBulkCallback;
// using FairMQRegionEventCallback [[deprecated("Use fair::mq::RegionEventCallback")]] = fair::mq::RegionEventCallback;
// using FairMQUnmanagedRegion [[deprecated("Use fair::mq::UnmanagedRegion")]] = fair::mq::UnmanagedRegion;
// using FairMQUnmanagedRegionPtr [[deprecated("Use fair::mq::UnmanagedRegionPtr")]] = fair::mq::UnmanagedRegionPtr;
// using FairMQRegionConfig [[deprecated("Use fair::mq::RegionConfig")]] = fair::mq::RegionConfig;
using FairMQRegionEvent = fair::mq::RegionEvent;
using FairMQRegionInfo = fair::mq::RegionInfo;
using FairMQRegionBlock = fair::mq::RegionBlock;
using FairMQRegionCallback = fair::mq::RegionCallback;
using FairMQRegionBulkCallback = fair::mq::RegionBulkCallback;
using FairMQRegionEventCallback = fair::mq::RegionEventCallback;
using FairMQUnmanagedRegion = fair::mq::UnmanagedRegion;
using FairMQUnmanagedRegionPtr = fair::mq::UnmanagedRegionPtr;
using FairMQRegionConfig = fair::mq::RegionConfig;
#endif // FAIR_MQ_UNMANAGEDREGION_H

View File

@@ -6,6 +6,10 @@
# copied verbatim in the file "LICENSE" #
################################################################################
if(BUILD_TIDY_TOOL)
include(FairMQTidy)
endif()
#################
# libFairMQ_SDK #
#################
@@ -36,7 +40,6 @@ set(SDK_SOURCE_FILES
DDSEnvironment.cxx
DDSSession.cxx
DDSTopology.cxx
Error.cxx
Topology.cxx
)
@@ -74,6 +77,9 @@ set_target_properties(${target} PROPERTIES
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
OUTPUT_NAME FairMQ_${target}
)
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
fairmq_target_tidy(TARGET ${target})
endif()
###############
# executables #

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2019-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -9,53 +9,12 @@
#ifndef FAIR_MQ_SDK_ERROR_H
#define FAIR_MQ_SDK_ERROR_H
#include <fairmq/tools/Strings.h>
#include <stdexcept>
#include <system_error>
#include <fairmq/Error.h>
namespace fair::mq
{
namespace fair::mq::sdk {
namespace sdk
{
using RuntimeError = fair::mq::RuntimeError;
struct RuntimeError : ::std::runtime_error
{
template<typename... T>
explicit RuntimeError(T&&... t)
: ::std::runtime_error::runtime_error(tools::ToString(std::forward<T>(t)...))
{}
};
} /* namespace sdk */
enum class ErrorCode
{
OperationInProgress = 10,
OperationTimeout,
OperationCanceled,
DeviceChangeStateFailed,
DeviceGetPropertiesFailed,
DeviceSetPropertiesFailed
};
std::error_code MakeErrorCode(ErrorCode);
struct ErrorCategory : std::error_category
{
const char* name() const noexcept override;
std::string message(int ev) const override;
};
} // namespace fair::mq
namespace std
{
template<>
struct is_error_code_enum<fair::mq::ErrorCode> : true_type
{};
} // namespace std
} /* namespace fair::mq::sdk */
#endif /* FAIR_MQ_SDK_ERROR_H */

View File

@@ -146,9 +146,10 @@ struct MetaHeader
{
size_t fSize;
size_t fHint;
uint16_t fRegionId;
uint16_t fSegmentId;
boost::interprocess::managed_shared_memory::handle_t fHandle;
mutable boost::interprocess::managed_shared_memory::handle_t fShared;
uint16_t fRegionId;
mutable uint16_t fSegmentId;
};
#ifdef FAIRMQ_DEBUG_MODE
@@ -271,22 +272,22 @@ struct SegmentHandleFromAddress : public boost::static_visitor<boost::interproce
const void* ptr;
};
struct SegmentAddressFromHandle : public boost::static_visitor<void*>
struct SegmentAddressFromHandle : public boost::static_visitor<char*>
{
SegmentAddressFromHandle(const boost::interprocess::managed_shared_memory::handle_t _handle) : handle(_handle) {}
template<typename S>
void* operator()(S& s) const { return s.get_address_from_handle(handle); }
char* operator()(S& s) const { return reinterpret_cast<char*>(s.get_address_from_handle(handle)); }
const boost::interprocess::managed_shared_memory::handle_t handle;
};
struct SegmentAllocate : public boost::static_visitor<void*>
struct SegmentAllocate : public boost::static_visitor<char*>
{
SegmentAllocate(const size_t _size) : size(_size) {}
template<typename S>
void* operator()(S& s) const { return s.allocate(size); }
char* operator()(S& s) const { return reinterpret_cast<char*>(s.allocate(size)); }
const size_t size;
};
@@ -322,12 +323,12 @@ struct SegmentBufferShrink : public boost::static_visitor<char*>
struct SegmentDeallocate : public boost::static_visitor<>
{
SegmentDeallocate(void* _ptr) : ptr(_ptr) {}
SegmentDeallocate(char* _ptr) : ptr(_ptr) {}
template<typename S>
void operator()(S& s) const { return s.deallocate(ptr); }
void* ptr;
char* ptr;
};
} // namespace fair::mq::shmem

View File

@@ -33,8 +33,11 @@
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/variant.hpp>
#include <algorithm> // max
#include <condition_variable>
#include <cstddef> // max_align_t
#include <cstdlib> // getenv
#include <cstring> // memcpy
#include <memory> // make_unique
#include <mutex>
#include <set>
@@ -49,12 +52,84 @@
#include <unistd.h> // getuid
#include <sys/types.h> // getuid
#include <sys/mman.h> // mlock
namespace fair::mq::shmem
{
// ShmHeader stores user buffer alignment and the reference count in the following structure:
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// The alignment of Hdr depends on the alignment of std::atomic and is stored in the first entry
struct ShmHeader
{
struct Hdr
{
uint16_t userOffset;
std::atomic<uint16_t> refCount;
};
static Hdr* HdrPtr(char* ptr)
{
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// ^
return reinterpret_cast<Hdr*>(ptr + sizeof(uint16_t) + *(reinterpret_cast<uint16_t*>(ptr)));
}
static uint16_t HdrPartSize() // [HdrOffset(uint16_t)][Hdr alignment][Hdr]
{
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// <--------------------------------------->
return sizeof(uint16_t) + alignof(Hdr) + sizeof(Hdr);
}
static std::atomic<uint16_t>& RefCountPtr(char* ptr)
{
// get the ref count ptr from the Hdr
return HdrPtr(ptr)->refCount;
}
static uint16_t UserOffset(char* ptr)
{
return HdrPartSize() + HdrPtr(ptr)->userOffset;
}
static char* UserPtr(char* ptr)
{
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// ^
return ptr + HdrPartSize() + HdrPtr(ptr)->userOffset;
}
static uint16_t RefCount(char* ptr) { return RefCountPtr(ptr).load(); }
static uint16_t IncrementRefCount(char* ptr) { return RefCountPtr(ptr).fetch_add(1); }
static uint16_t DecrementRefCount(char* ptr) { return RefCountPtr(ptr).fetch_sub(1); }
static size_t FullSize(size_t size, size_t alignment)
{
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// <--------------------------------------------------------------------------->
return HdrPartSize() + alignment + size;
}
static void Construct(char* ptr, size_t alignment)
{
// place the Hdr in the aligned location, fill it and store its offset to HdrOffset
// the address alignment should be at least 2
assert(reinterpret_cast<uintptr_t>(ptr) % 2 == 0);
// offset to the beginning of the Hdr. store it in the beginning
uint16_t hdrOffset = alignof(Hdr) - ((reinterpret_cast<uintptr_t>(ptr) + sizeof(uint16_t)) % alignof(Hdr));
memcpy(ptr, &hdrOffset, sizeof(hdrOffset));
// offset to the beginning of the user buffer, store in Hdr together with the ref count
uint16_t userOffset = alignment - ((reinterpret_cast<uintptr_t>(ptr) + HdrPartSize()) % alignment);
new(ptr + sizeof(uint16_t) + hdrOffset) Hdr{ userOffset, std::atomic<uint16_t>(1) };
}
static void Destruct(char* ptr) { RefCountPtr(ptr).~atomic(); }
};
class Manager
{
public:
@@ -613,39 +688,35 @@ class Manager
{
return boost::apply_visitor(SegmentHandleFromAddress(ptr), fSegments.at(segmentId));
}
void* GetAddressFromHandle(const boost::interprocess::managed_shared_memory::handle_t handle, uint16_t segmentId) const
char* GetAddressFromHandle(const boost::interprocess::managed_shared_memory::handle_t handle, uint16_t segmentId) const
{
return boost::apply_visitor(SegmentAddressFromHandle(handle), fSegments.at(segmentId));
}
char* Allocate(const size_t size, size_t alignment = 0)
char* Allocate(size_t size, size_t alignment = 0)
{
alignment = std::max(alignment, alignof(std::max_align_t));
char* ptr = nullptr;
// tools::RateLimiter rateLimiter(20);
size_t fullSize = ShmHeader::FullSize(size, alignment);
while (ptr == nullptr) {
try {
// boost::interprocess::managed_shared_memory::size_type actualSize = size;
// char* hint = 0; // unused for boost::interprocess::allocate_new
// ptr = fSegments.at(fSegmentId).allocation_command<char>(boost::interprocess::allocate_new, size, actualSize, hint);
size_t segmentSize = boost::apply_visitor(SegmentSize(), fSegments.at(fSegmentId));
if (size > segmentSize) {
throw MessageBadAlloc(tools::ToString("Requested message size (", size, ") exceeds segment size (", segmentSize, ")"));
}
if (alignment == 0) {
ptr = reinterpret_cast<char*>(boost::apply_visitor(SegmentAllocate{size}, fSegments.at(fSegmentId)));
} else {
ptr = reinterpret_cast<char*>(boost::apply_visitor(SegmentAllocateAligned{size, alignment}, fSegments.at(fSegmentId)));
if (fullSize > segmentSize) {
throw MessageBadAlloc(tools::ToString("Requested message size (", fullSize, ") exceeds segment size (", segmentSize, ")"));
}
ptr = boost::apply_visitor(SegmentAllocate{fullSize}, fSegments.at(fSegmentId));
ShmHeader::Construct(ptr, alignment);
} catch (boost::interprocess::bad_alloc& ba) {
// LOG(warn) << "Shared memory full...";
if (ThrowingOnBadAlloc()) {
throw MessageBadAlloc(tools::ToString("shmem: could not create a message of size ", size, ", alignment: ", (alignment != 0) ? std::to_string(alignment) : "default", ", free memory: ", boost::apply_visitor(SegmentFreeMemory(), fSegments.at(fSegmentId))));
}
// rateLimiter.maybe_sleep();
std::this_thread::sleep_for(std::chrono::milliseconds(50));
if (Interrupted()) {
return ptr;
throw MessageBadAlloc(tools::ToString("shmem: could not create a message of size ", size, ", alignment: ", (alignment != 0) ? std::to_string(alignment) : "default", ", free memory: ", boost::apply_visitor(SegmentFreeMemory(), fSegments.at(fSegmentId))));
} else {
continue;
}
@@ -657,7 +728,7 @@ class Manager
(*fMsgDebug).emplace(fSegmentId, fShmVoidAlloc);
}
(*fMsgDebug).at(fSegmentId).emplace(
static_cast<size_t>(GetHandleFromAddress(ptr, fSegmentId)),
static_cast<size_t>(GetHandleFromAddress(ShmHeader::UserPtr(ptr), fSegmentId)),
MsgDebug(getpid(), size, std::chrono::system_clock::now().time_since_epoch().count())
);
#endif
@@ -668,7 +739,9 @@ class Manager
void Deallocate(boost::interprocess::managed_shared_memory::handle_t handle, uint16_t segmentId)
{
boost::apply_visitor(SegmentDeallocate(GetAddressFromHandle(handle, segmentId)), fSegments.at(segmentId));
char* ptr = GetAddressFromHandle(handle, segmentId);
ShmHeader::Destruct(ptr);
boost::apply_visitor(SegmentDeallocate(ptr), fSegments.at(segmentId));
#ifdef FAIRMQ_DEBUG_MODE
boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock(fShmMtx);
DecrementShmMsgCounter(segmentId);

View File

@@ -38,7 +38,7 @@ class Message final : public fair::mq::Message
: fair::mq::Message(factory)
, fManager(manager)
, fQueued(false)
, fMeta{0, 0, 0, fManager.GetSegmentId(), -1}
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId()}
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
{
@@ -49,7 +49,7 @@ class Message final : public fair::mq::Message
: fair::mq::Message(factory)
, fManager(manager)
, fQueued(false)
, fMeta{0, 0, 0, fManager.GetSegmentId(), -1}
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId()}
, fAlignment(alignment.alignment)
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
@@ -61,7 +61,7 @@ class Message final : public fair::mq::Message
: fair::mq::Message(factory)
, fManager(manager)
, fQueued(false)
, fMeta{0, 0, 0, fManager.GetSegmentId(), -1}
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId()}
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
{
@@ -73,7 +73,7 @@ class Message final : public fair::mq::Message
: fair::mq::Message(factory)
, fManager(manager)
, fQueued(false)
, fMeta{0, 0, 0, fManager.GetSegmentId(), -1}
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId()}
, fAlignment(alignment.alignment)
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
@@ -86,7 +86,7 @@ class Message final : public fair::mq::Message
: fair::mq::Message(factory)
, fManager(manager)
, fQueued(false)
, fMeta{0, 0, 0, fManager.GetSegmentId(), -1}
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId()}
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
{
@@ -105,7 +105,7 @@ class Message final : public fair::mq::Message
: fair::mq::Message(factory)
, fManager(manager)
, fQueued(false)
, fMeta{size, reinterpret_cast<size_t>(hint), static_cast<UnmanagedRegion*>(region.get())->fRegionId, fManager.GetSegmentId(), -1}
, fMeta{size, reinterpret_cast<size_t>(hint), -1, -1, static_cast<UnmanagedRegion*>(region.get())->fRegionId, fManager.GetSegmentId()}
, fRegionPtr(nullptr)
, fLocalPtr(static_cast<char*>(data))
{
@@ -187,7 +187,7 @@ class Message final : public fair::mq::Message
if (fMeta.fRegionId == 0) {
if (fMeta.fSize > 0) {
fManager.GetSegment(fMeta.fSegmentId);
fLocalPtr = reinterpret_cast<char*>(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId));
fLocalPtr = ShmHeader::UserPtr(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId));
} else {
fLocalPtr = nullptr;
}
@@ -202,7 +202,7 @@ class Message final : public fair::mq::Message
}
}
return fLocalPtr;
return static_cast<void*>(fLocalPtr);
}
size_t GetSize() const override { return fMeta.fSize; }
@@ -217,7 +217,10 @@ class Message final : public fair::mq::Message
} else if (newSize <= fMeta.fSize) {
try {
try {
fLocalPtr = fManager.ShrinkInPlace(newSize, fLocalPtr, fMeta.fSegmentId);
char* oldPtr = fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId);
uint16_t userOffset = ShmHeader::UserOffset(oldPtr);
char* ptr = fManager.ShrinkInPlace(userOffset + newSize, oldPtr, fMeta.fSegmentId);
fLocalPtr = ShmHeader::UserPtr(ptr);
fMeta.fSize = newSize;
return true;
} catch (boost::interprocess::bad_alloc& e) {
@@ -225,16 +228,12 @@ class Message final : public fair::mq::Message
// unused size >= 1000000 bytes: reallocate fully
// unused size < 1000000 bytes: simply reset the size and keep the rest of the buffer until message destruction
if (fMeta.fSize - newSize >= 1000000) {
char* newPtr = fManager.Allocate(newSize, fAlignment);
if (newPtr) {
std::memcpy(newPtr, fLocalPtr, newSize);
fManager.Deallocate(fMeta.fHandle, fMeta.fSegmentId);
fLocalPtr = newPtr;
fMeta.fHandle = fManager.GetHandleFromAddress(fLocalPtr, fMeta.fSegmentId);
} else {
LOG(debug) << "could not set used size: " << e.what();
return false;
}
char* ptr = fManager.Allocate(newSize, fAlignment);
char* userPtr = ShmHeader::UserPtr(ptr);
std::memcpy(userPtr, fLocalPtr, newSize);
fManager.Deallocate(fMeta.fHandle, fMeta.fSegmentId);
fLocalPtr = userPtr;
fMeta.fHandle = fManager.GetHandleFromAddress(ptr, fMeta.fSegmentId);
}
fMeta.fSize = newSize;
return true;
@@ -251,33 +250,65 @@ class Message final : public fair::mq::Message
Transport GetType() const override { return fair::mq::Transport::SHM; }
void Copy(const fair::mq::Message& msg) override
uint16_t GetRefCount() const
{
if (fMeta.fHandle < 0) {
boost::interprocess::managed_shared_memory::handle_t otherHandle = static_cast<const Message&>(msg).fMeta.fHandle;
if (otherHandle) {
if (InitializeChunk(msg.GetSize())) {
std::memcpy(GetData(), msg.GetData(), msg.GetSize());
}
return 1;
}
if (fMeta.fRegionId == 0) { // managed segment
fManager.GetSegment(fMeta.fSegmentId);
return ShmHeader::RefCount(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId));
} else { // unmanaged region
if (fMeta.fShared < 0) { // UR msg is not yet shared
return 1;
} else {
LOG(error) << "copy fail: source message not initialized!";
fManager.GetSegment(fMeta.fSegmentId);
return ShmHeader::RefCount(fManager.GetAddressFromHandle(fMeta.fShared, fMeta.fSegmentId));
}
} else {
LOG(error) << "copy fail: target message already initialized!";
}
}
~Message() override
void Copy(const fair::mq::Message& other) override
{
try {
const Message& otherMsg = static_cast<const Message&>(other);
if (otherMsg.fMeta.fHandle < 0) {
// if the other message is not initialized, close this one too and return
CloseMessage();
} catch(SharedMemoryError& sme) {
LOG(error) << "error closing message: " << sme.what();
} catch(boost::interprocess::lock_exception& le) {
LOG(error) << "error closing message: " << le.what();
return;
}
if (fMeta.fHandle >= 0) {
// if this msg is already initialized, close it first
CloseMessage();
}
if (otherMsg.fMeta.fRegionId == 0) { // managed segment
fMeta = otherMsg.fMeta;
fManager.GetSegment(fMeta.fSegmentId);
ShmHeader::IncrementRefCount(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId));
} else { // unmanaged region
if (otherMsg.fMeta.fShared < 0) { // if UR msg is not yet shared
// TODO: minimize the size to 0 and don't create extra space for user buffer alignment
char* ptr = fManager.Allocate(2, 0);
// point the fShared in the unmanaged region message to the refCount holder
otherMsg.fMeta.fShared = fManager.GetHandleFromAddress(ptr, fMeta.fSegmentId);
// the message needs to be able to locate in which segment the refCount is stored
otherMsg.fMeta.fSegmentId = fMeta.fSegmentId;
// point this message to the same content as the unmanaged region message
fMeta = otherMsg.fMeta;
// increment the refCount
ShmHeader::IncrementRefCount(ptr);
} else { // if the UR msg is already shared
fMeta = otherMsg.fMeta;
fManager.GetSegment(fMeta.fSegmentId);
ShmHeader::IncrementRefCount(fManager.GetAddressFromHandle(fMeta.fShared, fMeta.fSegmentId));
}
}
}
~Message() override { CloseMessage(); }
private:
Manager& fManager;
bool fQueued;
@@ -288,43 +319,70 @@ class Message final : public fair::mq::Message
char* InitializeChunk(const size_t size, size_t alignment = 0)
{
fLocalPtr = fManager.Allocate(size, alignment);
if (fLocalPtr) {
fMeta.fHandle = fManager.GetHandleFromAddress(fLocalPtr, fMeta.fSegmentId);
fMeta.fSize = size;
if (size == 0) {
fMeta.fSize = 0;
return fLocalPtr;
}
char* ptr = fManager.Allocate(size, alignment);
fMeta.fHandle = fManager.GetHandleFromAddress(ptr, fMeta.fSegmentId);
fMeta.fSize = size;
fLocalPtr = ShmHeader::UserPtr(ptr);
return fLocalPtr;
}
void Deallocate()
{
if (fMeta.fHandle >= 0 && !fQueued) {
if (fMeta.fRegionId == 0) {
if (fMeta.fRegionId == 0) { // managed segment
fManager.GetSegment(fMeta.fSegmentId);
fManager.Deallocate(fMeta.fHandle, fMeta.fSegmentId);
fMeta.fHandle = -1;
} else {
if (!fRegionPtr) {
fRegionPtr = fManager.GetRegion(fMeta.fRegionId);
uint16_t refCount = ShmHeader::DecrementRefCount(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId));
if (refCount == 1) {
fManager.Deallocate(fMeta.fHandle, fMeta.fSegmentId);
}
if (fRegionPtr) {
fRegionPtr->ReleaseBlock({fMeta.fHandle, fMeta.fSize, fMeta.fHint});
} else { // unmanaged region
if (fMeta.fShared >= 0) {
// make sure segment is initialized in this transport
fManager.GetSegment(fMeta.fSegmentId);
// release unmanaged region block if ref count is one
uint16_t refCount = ShmHeader::DecrementRefCount(fManager.GetAddressFromHandle(fMeta.fShared, fMeta.fSegmentId));
if (refCount == 1) {
fManager.Deallocate(fMeta.fShared, fMeta.fSegmentId);
ReleaseUnmanagedRegionBlock();
}
} else {
LOG(warn) << "region ack queue for id " << fMeta.fRegionId << " no longer exist. Not sending ack";
ReleaseUnmanagedRegionBlock();
}
}
}
fMeta.fHandle = -1;
fLocalPtr = nullptr;
fMeta.fSize = 0;
}
void ReleaseUnmanagedRegionBlock()
{
if (!fRegionPtr) {
fRegionPtr = fManager.GetRegion(fMeta.fRegionId);
}
if (fRegionPtr) {
fRegionPtr->ReleaseBlock({fMeta.fHandle, fMeta.fSize, fMeta.fHint});
} else {
LOG(warn) << "region ack queue for id " << fMeta.fRegionId << " no longer exist. Not sending ack";
}
}
void CloseMessage()
{
Deallocate();
fAlignment = 0;
fManager.DecrementMsgCounter();
try {
Deallocate();
fAlignment = 0;
fManager.DecrementMsgCounter();
} catch(SharedMemoryError& sme) {
LOG(error) << "error closing message: " << sme.what();
} catch(boost::interprocess::lock_exception& le) {
LOG(error) << "error closing message: " << le.what();
}
}
};

View File

@@ -1,25 +1,22 @@
/********************************************************************************
* Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#ifndef FAIR_MQ_SHMEM_POLLER_H_
#define FAIR_MQ_SHMEM_POLLER_H_
#include "Socket.h"
#include <fairlogger/Logger.h>
#include <fairmq/Channel.h>
#include <fairmq/Poller.h>
#include <fairmq/shmem/Socket.h>
#include <fairmq/tools/Strings.h>
#include <FairMQChannel.h>
#include <FairMQLogger.h>
#include <FairMQPoller.h>
#include <zmq.h>
#include <unordered_map>
#include <vector>
class FairMQChannel;
#include <zmq.h>
namespace fair::mq::shmem
{
@@ -27,7 +24,7 @@ namespace fair::mq::shmem
class Poller final : public fair::mq::Poller
{
public:
Poller(const std::vector<FairMQChannel>& channels)
Poller(const std::vector<Channel>& channels)
: fItems()
, fNumItems(0)
{
@@ -47,7 +44,7 @@ class Poller final : public fair::mq::Poller
}
}
Poller(const std::vector<FairMQChannel*>& channels)
Poller(const std::vector<Channel*>& channels)
: fItems()
, fNumItems(0)
{
@@ -67,7 +64,7 @@ class Poller final : public fair::mq::Poller
}
}
Poller(const std::unordered_map<std::string, std::vector<FairMQChannel>>& channelsMap, const std::vector<std::string>& channelList)
Poller(const std::unordered_map<std::string, std::vector<Channel>>& channelsMap, const std::vector<std::string>& channelList)
: fItems()
, fNumItems(0)
{

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -22,7 +22,9 @@
#include <atomic>
#include <memory> // make_unique
class FairMQTransportFactory;
namespace fair::mq {
class TransportFactory;
}
namespace fair::mq::shmem
{

View File

@@ -0,0 +1,25 @@
################################################################################
# Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
# #
# This software is distributed under the terms of the #
# GNU Lesser General Public Licence (LGPL) version 3, #
# copied verbatim in the file "LICENSE" #
################################################################################
set(target fairmq-tidy)
add_executable(${target}
ModernizeNonNamespacedTypes.h
Tool.h
runTool.cpp
)
add_executable("${PROJECT_NAME}::${target}" ALIAS ${target})
target_compile_features(${target} PRIVATE cxx_std_17)
target_link_libraries(${target} PRIVATE clang-cpp LLVM)
target_include_directories(${target} PRIVATE
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>
)
install(TARGETS ${target}
EXPORT ${PROJECT_EXPORT_SET}
RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR}
)

View File

@@ -0,0 +1,81 @@
/********************************************************************************
* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#ifndef FAIR_MQ_TIDY_MODERNIZENONNAMESPACEDTYPES
#define FAIR_MQ_TIDY_MODERNIZENONNAMESPACEDTYPES
#include <clang/AST/AST.h>
#include <clang/ASTMatchers/ASTMatchFinder.h>
#include <clang/ASTMatchers/ASTMatchers.h>
#include <clang/Basic/Diagnostic.h>
#include <llvm/Support/Casting.h>
#include <sstream>
namespace fair::mq::tidy {
struct ModernizeNonNamespacedTypes
{
ModernizeNonNamespacedTypes() = delete;
ModernizeNonNamespacedTypes(clang::ast_matchers::MatchFinder& finder)
{
using namespace clang::ast_matchers;
// https://clang.llvm.org/docs/LibASTMatchersReference.html
finder.addMatcher(
typeLoc(loc(qualType(hasDeclaration(namedDecl(matchesName("FairMQ.*")).bind("decl")))))
.bind("loc"),
&fCallback);
}
struct Callback : clang::ast_matchers::MatchFinder::MatchCallback
{
auto run(clang::ast_matchers::MatchFinder::MatchResult const& m) -> void final
{
using namespace clang;
auto const type_loc(m.Nodes.getNodeAs<TypeLoc>("loc"));
auto const named_decl(m.Nodes.getNodeAs<NamedDecl>("decl"));
if (auto const type_alias_decl = m.Nodes.getNodeAs<TypeAliasDecl>("decl")) {
auto const underlying_type(type_alias_decl->getUnderlyingType());
// auto ldecl_ctx(type_loc->getType()getLexicalDeclContext());
// std::stringstream s;
// while (ldecl_ctx) {
// s << "." << ldecl_ctx->getDeclKindName();
// if (ldecl_ctx->isNamespace()) {
// s << dyn_cast<NamespaceDecl>(ldecl_ctx)->getNameAsString();
// }
// ldecl_ctx = ldecl_ctx->getLexicalParent();
// }
if (underlying_type.getAsString().rfind("fair::mq::", 0) == 0) {
auto& diag_engine(m.Context->getDiagnostics());
auto builder(
diag_engine.Report(type_loc->getBeginLoc(),
diag_engine.getCustomDiagID(
DiagnosticsEngine::Warning,
"Modernize non-namespaced type %0 with %1. [%2]")));
builder << named_decl;
builder << underlying_type;
builder << "fairmq-modernize-nonnamespaced-types";
builder.AddFixItHint(FixItHint::CreateReplacement(
type_loc->getSourceRange(), underlying_type.getAsString()));
}
}
}
};
private:
Callback fCallback;
};
} // namespace fair::mq::tidy
#endif /* FAIR_MQ_TIDY_MODERNIZENONNAMESPACEDTYPES */

86
fairmq/tidy/Tool.h Normal file
View File

@@ -0,0 +1,86 @@
/********************************************************************************
* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#ifndef FAIR_MQ_TIDY_TOOL
#define FAIR_MQ_TIDY_TOOL
#include <algorithm>
#include <clang/ASTMatchers/ASTMatchFinder.h>
#include <clang/Basic/FileManager.h>
#include <clang/Tooling/CompilationDatabase.h>
#include <clang/Tooling/Tooling.h>
#include <fairmq/tidy/ModernizeNonNamespacedTypes.h>
#include <string>
namespace fair::mq::tidy {
// Getting up to speed, read this:
// https://clang.llvm.org/docs/LibTooling.html
// https://clang.llvm.org/docs/IntroductionToTheClangAST.html
// Watch https://www.youtube.com/watch?v=VqCkCDFLSsc !!!
//
// optional, but helpful:
// https://static.linaro.org/connect/yvr18/presentations/yvr18-223.pdf
// https://steveire.wordpress.com/2018/11/20/composing-ast-matchers-in-clang-tidy/
// https://www.goldsborough.me/c++/clang/llvm/tools/2017/02/24/00-00-06-emitting_diagnostics_and_fixithints_in_clang_tools/
// https://steveire.com/CodeDive2018Presentation.pdf
//
// reference (no built-in search, however google will find things):
// https://clang.llvm.org/doxygen/
//
// Note: Implementing a standalone tool will impose double PP and parsing cost if one also
// runs clang-tidy. At the moment, one cannot extend clang-tidy with new checks without
// recompiling clang-tidy. In principle llvm-project/clang-extra-tools/clang-tidy
// has install rules, but Fedora is not packaging them which is likely due to their
// unstable state (lots of comments aka todo, fixme, refactor etc). Also, it seems
// focus has shifted towards implementing the
// https://microsoft.github.io/language-server-protocol/
// via https://clangd.llvm.org/ which includes clang-tidy, but also does not have
// an extension/plugin interface for third-party checks yet AFAICS.
struct Tool
{
static auto run(clang::tooling::CompilationDatabase const &compilations,
clang::ArrayRef<std::string> sources) -> int
{
using namespace clang;
// compose all checks in a single match finder
ast_matchers::MatchFinder finder;
ModernizeNonNamespacedTypes check1(finder);
// configure the clang tool
tooling::ClangTool tool(compilations, sources);
tool.appendArgumentsAdjuster(
[](tooling::CommandLineArguments const &_args, StringRef /*file*/) {
tooling::CommandLineArguments args(_args);
auto const no_std_arg_present =
std::find_if(cbegin(args),
cend(args),
[](std::string const &arg) {
return arg.find("-std=") != std::string::npos;
})
== std::cend(args);
if (no_std_arg_present) {
args.emplace(std::cbegin(args) + 1, "-std=c++17");
}
args.emplace_back("-Wno-everything");
return args;
});
// run checks on given files
return tool.run(clang::tooling::newFrontendActionFactory(&finder).get());
}
};
} // namespace fair::mq::tidy
#endif /* FAIR_MQ_TIDY_TOOL */

29
fairmq/tidy/runTool.cpp Normal file
View File

@@ -0,0 +1,29 @@
/********************************************************************************
* Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include <clang/Tooling/CommonOptionsParser.h>
#include <clang/Tooling/Tooling.h>
#include <fairmq/tidy/Tool.h>
#include <llvm/ADT/STLExtras.h>
#include <llvm/Support/CommandLine.h>
static llvm::cl::OptionCategory ToolCategory("fairmq-tidy options");
static llvm::cl::extrahelp CommonHelp(clang::tooling::CommonOptionsParser::HelpMessage);
int main(int argc, const char** argv)
{
// TODO Replace command line parser with CLI11
auto parser(clang::tooling::CommonOptionsParser::create(
argc, argv, ToolCategory, llvm::cl::NumOccurrencesFlag::ZeroOrMore, ""));
if (!parser) {
llvm::errs() << parser.takeError();
return EXIT_FAILURE;
}
return fair::mq::tidy::Tool::run(parser->getCompilations(), parser->getSourcePathList());
}

View File

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
@@ -9,16 +9,14 @@
#ifndef FAIR_MQ_ZMQ_POLLER_H
#define FAIR_MQ_ZMQ_POLLER_H
#include <fairmq/zeromq/Socket.h>
#include <fairlogger/Logger.h>
#include <fairmq/Channel.h>
#include <fairmq/Poller.h>
#include <fairmq/tools/Strings.h>
#include <FairMQChannel.h>
#include <FairMQLogger.h>
#include <FairMQPoller.h>
#include <zmq.h>
#include <fairmq/zeromq/Socket.h>
#include <unordered_map>
#include <vector>
#include <zmq.h>
namespace fair::mq::zmq
{
@@ -26,7 +24,7 @@ namespace fair::mq::zmq
class Poller final : public fair::mq::Poller
{
public:
Poller(const std::vector<FairMQChannel>& channels)
Poller(const std::vector<Channel>& channels)
: fItems()
, fNumItems(0)
{
@@ -46,7 +44,7 @@ class Poller final : public fair::mq::Poller
}
}
Poller(const std::vector<FairMQChannel*>& channels)
Poller(const std::vector<Channel*>& channels)
: fItems()
, fNumItems(0)
{
@@ -66,14 +64,14 @@ class Poller final : public fair::mq::Poller
}
}
Poller(const std::unordered_map<std::string, std::vector<FairMQChannel>>& channelsMap, const std::vector<std::string>& channelList)
Poller(const std::unordered_map<std::string, std::vector<Channel>>& channelsMap, const std::vector<std::string>& channelList)
: fItems()
, fNumItems(0)
{
try {
int offset = 0;
// calculate offsets and the total size of the poll item set
for (std::string channel : channelList) {
for (std::string const & channel : channelList) {
fOffsetMap[channel] = offset;
offset += channelsMap.at(channel).size();
fNumItems += channelsMap.at(channel).size();

View File

@@ -89,7 +89,7 @@ add_testsuite(Message
${CMAKE_CURRENT_BINARY_DIR}/runner.cxx
message/_message.cxx
LINKS FairMQ
LINKS FairMQ PicoSHA2
INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/message
${CMAKE_CURRENT_BINARY_DIR}

View File

@@ -1,15 +1,13 @@
/********************************************************************************
* Copyright (C) 2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2018-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include <FairMQChannel.h>
#include <fairmq/Channel.h>
#include <gtest/gtest.h>
#include <string>
namespace
@@ -20,15 +18,15 @@ using namespace fair::mq;
TEST(Channel, Validation)
{
FairMQChannel channel;
ASSERT_THROW(channel.Validate(), FairMQChannel::ChannelConfigurationError);
Channel channel;
ASSERT_THROW(channel.Validate(), Channel::ChannelConfigurationError);
channel.UpdateType("pair");
ASSERT_EQ(channel.Validate(), false);
ASSERT_EQ(channel.IsValid(), false);
channel.UpdateAddress("bla");
ASSERT_THROW(channel.Validate(), FairMQChannel::ChannelConfigurationError);
ASSERT_THROW(channel.Validate(), Channel::ChannelConfigurationError);
channel.UpdateMethod("connect");
ASSERT_EQ(channel.Validate(), false);
@@ -55,31 +53,31 @@ TEST(Channel, Validation)
ASSERT_EQ(channel.IsValid(), true);
channel.UpdateSndBufSize(-1);
ASSERT_THROW(channel.Validate(), FairMQChannel::ChannelConfigurationError);
ASSERT_THROW(channel.Validate(), Channel::ChannelConfigurationError);
channel.UpdateSndBufSize(1000);
ASSERT_NO_THROW(channel.Validate());
channel.UpdateRcvBufSize(-1);
ASSERT_THROW(channel.Validate(), FairMQChannel::ChannelConfigurationError);
ASSERT_THROW(channel.Validate(), Channel::ChannelConfigurationError);
channel.UpdateRcvBufSize(1000);
ASSERT_NO_THROW(channel.Validate());
channel.UpdateSndKernelSize(-1);
ASSERT_THROW(channel.Validate(), FairMQChannel::ChannelConfigurationError);
ASSERT_THROW(channel.Validate(), Channel::ChannelConfigurationError);
channel.UpdateSndKernelSize(1000);
ASSERT_NO_THROW(channel.Validate());
channel.UpdateRcvKernelSize(-1);
ASSERT_THROW(channel.Validate(), FairMQChannel::ChannelConfigurationError);
ASSERT_THROW(channel.Validate(), Channel::ChannelConfigurationError);
channel.UpdateRcvKernelSize(1000);
ASSERT_NO_THROW(channel.Validate());
channel.UpdateRateLogging(-1);
ASSERT_THROW(channel.Validate(), FairMQChannel::ChannelConfigurationError);
ASSERT_THROW(channel.Validate(), Channel::ChannelConfigurationError);
channel.UpdateRateLogging(1);
ASSERT_NO_THROW(channel.Validate());
FairMQChannel channel2 = channel;
Channel channel2 = channel;
ASSERT_NO_THROW(channel2.Validate());
ASSERT_EQ(channel2.Validate(), true);
ASSERT_EQ(channel2.IsValid(), true);

View File

@@ -73,9 +73,10 @@ int TestData::ndeallocations = 0;
TEST(MemoryResources, transportAllocatorMap)
{
size_t session{tools::UuidHash()};
// size_t session{tools::UuidHash()};
ProgOptions config;
config.SetProperty<string>("session", to_string(session));
// config.SetProperty<string>("session", to_string(session));
config.SetProperty<string>("session", "default");
FactoryType factoryZMQ = FairMQTransportFactory::CreateTransportFactory("zeromq", fair::mq::tools::Uuid(), &config);
FactoryType factorySHM = FairMQTransportFactory::CreateTransportFactory("shmem", fair::mq::tools::Uuid(), &config);

View File

@@ -1,225 +1,406 @@
/********************************************************************************
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2017-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include <FairMQChannel.h>
#include <FairMQLogger.h>
#include <FairMQTransportFactory.h>
#include <fairlogger/Logger.h>
#include <fairmq/Channel.h>
#include <fairmq/ProgOptions.h>
#include <fairmq/tools/Unique.h>
#include <fairmq/tools/Semaphore.h>
#include <fairmq/tools/Strings.h>
#include <fairmq/tools/Unique.h>
#include <fairmq/TransportFactory.h>
#include <fairmq/shmem/Message.h>
#include <gtest/gtest.h>
#include <string>
#include <array>
#include <cassert>
#include <cstdint>
#include <memory>
#include <string_view>
#include <string>
#include <utility>
namespace
{
using namespace std;
using namespace fair::mq;
void RunPushPullWithMsgResize(const string& transport, const string& _address)
auto AsStringView(Message const& msg) -> string_view
{
size_t session{fair::mq::tools::UuidHash()};
std::string address(fair::mq::tools::ToString(_address, "_", transport));
return {static_cast<char const*>(msg.GetData()), msg.GetSize()};
}
fair::mq::ProgOptions config;
config.SetProperty<string>("session", to_string(session));
auto RunPushPullWithMsgResize(string const & transport, string const & _address) -> void
{
ProgOptions config;
config.SetProperty<string>("session", tools::Uuid());
auto factory(TransportFactory::CreateTransportFactory(transport, tools::Uuid(), &config));
auto factory = FairMQTransportFactory::CreateTransportFactory(transport, fair::mq::tools::Uuid(), &config);
FairMQChannel push{"Push", "push", factory};
Channel push{"Push", "push", factory};
Channel pull{"Pull", "pull", factory};
auto const address(tools::ToString(_address, "_", transport));
push.Bind(address);
FairMQChannel pull{"Pull", "pull", factory};
pull.Connect(address);
{
FairMQMessagePtr outMsg(push.NewMessage(6));
ASSERT_EQ(outMsg->GetSize(), 6);
memcpy(outMsg->GetData(), "ABCDEF", 6);
ASSERT_EQ(outMsg->SetUsedSize(5), true);
ASSERT_EQ(outMsg->SetUsedSize(5), true);
ASSERT_EQ(outMsg->SetUsedSize(7), false);
size_t const size{6};
auto outMsg(push.NewMessage(size));
ASSERT_EQ(outMsg->GetSize(), size);
memcpy(outMsg->GetData(), "ABCDEF", size);
ASSERT_TRUE(outMsg->SetUsedSize(5));
ASSERT_TRUE(outMsg->SetUsedSize(5));
ASSERT_FALSE(outMsg->SetUsedSize(7));
ASSERT_EQ(outMsg->GetSize(), 5);
// check if the data is still intact
ASSERT_EQ(static_cast<char*>(outMsg->GetData())[0], 'A');
ASSERT_EQ(static_cast<char*>(outMsg->GetData())[1], 'B');
ASSERT_EQ(static_cast<char*>(outMsg->GetData())[2], 'C');
ASSERT_EQ(static_cast<char*>(outMsg->GetData())[3], 'D');
ASSERT_EQ(static_cast<char*>(outMsg->GetData())[4], 'E');
ASSERT_EQ(outMsg->SetUsedSize(2), true);
ASSERT_EQ(AsStringView(*outMsg)[0], 'A');
ASSERT_EQ(AsStringView(*outMsg)[1], 'B');
ASSERT_EQ(AsStringView(*outMsg)[2], 'C');
ASSERT_EQ(AsStringView(*outMsg)[3], 'D');
ASSERT_EQ(AsStringView(*outMsg)[4], 'E');
ASSERT_TRUE(outMsg->SetUsedSize(2));
ASSERT_EQ(outMsg->GetSize(), 2);
ASSERT_EQ(static_cast<char*>(outMsg->GetData())[0], 'A');
ASSERT_EQ(static_cast<char*>(outMsg->GetData())[1], 'B');
FairMQMessagePtr msgCopy(push.NewMessage());
ASSERT_EQ(AsStringView(*outMsg)[0], 'A');
ASSERT_EQ(AsStringView(*outMsg)[1], 'B');
auto msgCopy(push.NewMessage());
msgCopy->Copy(*outMsg);
ASSERT_EQ(msgCopy->GetSize(), 2);
ASSERT_EQ(push.Send(outMsg), 2);
FairMQMessagePtr inMsg(pull.NewMessage());
auto inMsg(pull.NewMessage());
ASSERT_EQ(pull.Receive(inMsg), 2);
ASSERT_EQ(inMsg->GetSize(), 2);
ASSERT_EQ(static_cast<char*>(inMsg->GetData())[0], 'A');
ASSERT_EQ(static_cast<char*>(inMsg->GetData())[1], 'B');
ASSERT_EQ(AsStringView(*inMsg)[0], 'A');
ASSERT_EQ(AsStringView(*inMsg)[1], 'B');
}
{
FairMQMessagePtr outMsg(push.NewMessage(1000));
ASSERT_EQ(outMsg->SetUsedSize(0), true);
size_t const size{1000};
auto outMsg(push.NewMessage(size));
ASSERT_TRUE(outMsg->SetUsedSize(0));
ASSERT_EQ(outMsg->GetSize(), 0);
FairMQMessagePtr msgCopy(push.NewMessage());
auto msgCopy(push.NewMessage());
msgCopy->Copy(*outMsg);
ASSERT_EQ(msgCopy->GetSize(), 0);
ASSERT_EQ(push.Send(outMsg), 0);
FairMQMessagePtr inMsg(pull.NewMessage());
auto inMsg(pull.NewMessage());
ASSERT_EQ(pull.Receive(inMsg), 0);
ASSERT_EQ(inMsg->GetSize(), 0);
}
}
void RunMsgRebuild(const string& transport)
auto RunMsgRebuild(const string& transport) -> void
{
size_t session{fair::mq::tools::UuidHash()};
ProgOptions config;
config.SetProperty<string>("session", tools::Uuid());
auto factory(TransportFactory::CreateTransportFactory(transport, tools::Uuid(), &config));
fair::mq::ProgOptions config;
config.SetProperty<string>("session", to_string(session));
auto factory = FairMQTransportFactory::CreateTransportFactory(transport, fair::mq::tools::Uuid(), &config);
FairMQMessagePtr msg(factory->CreateMessage());
size_t const msgSize{100};
string const expectedStr{"asdf"};
auto msg(factory->CreateMessage());
EXPECT_EQ(msg->GetSize(), 0);
msg->Rebuild(100);
EXPECT_EQ(msg->GetSize(), 100);
string* str = new string("asdf");
msg->Rebuild(const_cast<char*>(str->c_str()),
str->length(),
[](void* /*data*/, void* obj) { delete static_cast<string*>(obj); },
str);
EXPECT_NE(msg->GetSize(), 100);
EXPECT_EQ(msg->GetSize(), string("asdf").length());
EXPECT_EQ(string(static_cast<char*>(msg->GetData()), msg->GetSize()), string("asdf"));
msg->Rebuild(msgSize);
EXPECT_EQ(msg->GetSize(), msgSize);
auto str(make_unique<string>(expectedStr));
void* data(str->data());
auto const size(str->length());
msg->Rebuild(
data,
size,
[](void* /*data*/, void* obj) { delete static_cast<string*>(obj); }, // NOLINT
str.release());
EXPECT_NE(msg->GetSize(), msgSize);
EXPECT_EQ(msg->GetSize(), expectedStr.length());
EXPECT_EQ(AsStringView(*msg), expectedStr);
}
void Alignment(const string& transport, const string& _address)
auto CheckMsgAlignment(Message const& msg, fair::mq::Alignment alignment) -> bool
{
size_t session{fair::mq::tools::UuidHash()};
std::string address(fair::mq::tools::ToString(_address, "_", transport));
assert(static_cast<size_t>(alignment) > 0); // NOLINT
return (reinterpret_cast<uintptr_t>(msg.GetData()) % static_cast<size_t>(alignment)) == 0; // NOLINT
}
fair::mq::ProgOptions config;
config.SetProperty<string>("session", to_string(session));
auto RunPushPullWithAlignment(string const& transport, string const& _address) -> void
{
ProgOptions config;
config.SetProperty<string>("session", tools::Uuid());
auto factory(TransportFactory::CreateTransportFactory(transport, tools::Uuid(), &config));
auto factory = FairMQTransportFactory::CreateTransportFactory(transport, fair::mq::tools::Uuid(), &config);
FairMQChannel push{"Push", "push", factory};
Channel push{"Push", "push", factory};
Channel pull{"Pull", "pull", factory};
auto const address(tools::ToString(_address, "_", transport));
push.Bind(address);
FairMQChannel pull{"Pull", "pull", factory};
pull.Connect(address);
size_t alignment = 64;
{
Alignment const align{64};
for (size_t const size : {100, 32}) {
auto outMsg(push.NewMessage(size, align));
auto inMsg(pull.NewMessage(align));
FairMQMessagePtr outMsg1(push.NewMessage(100, fair::mq::Alignment{alignment}));
ASSERT_EQ(reinterpret_cast<uintptr_t>(outMsg1->GetData()) % alignment, 0);
ASSERT_EQ(push.Send(outMsg1), 100);
ASSERT_TRUE(CheckMsgAlignment(*outMsg, align));
ASSERT_EQ(push.Send(outMsg), size);
ASSERT_EQ(pull.Receive(inMsg), size);
ASSERT_TRUE(CheckMsgAlignment(*inMsg, align));
}
}
FairMQMessagePtr inMsg1(pull.NewMessage(fair::mq::Alignment{alignment}));
ASSERT_EQ(pull.Receive(inMsg1), 100);
ASSERT_EQ(reinterpret_cast<uintptr_t>(inMsg1->GetData()) % alignment, 0);
{
Alignment const align{0};
size_t const size{100};
auto outMsg(push.NewMessage(size, align));
auto inMsg(pull.NewMessage(align));
FairMQMessagePtr outMsg2(push.NewMessage(32, fair::mq::Alignment{alignment}));
ASSERT_EQ(reinterpret_cast<uintptr_t>(outMsg2->GetData()) % alignment, 0);
ASSERT_EQ(push.Send(outMsg2), 32);
ASSERT_EQ(push.Send(outMsg), size);
ASSERT_EQ(pull.Receive(inMsg), size);
}
FairMQMessagePtr inMsg2(pull.NewMessage(fair::mq::Alignment{alignment}));
ASSERT_EQ(pull.Receive(inMsg2), 32);
ASSERT_EQ(reinterpret_cast<uintptr_t>(inMsg2->GetData()) % alignment, 0);
size_t const size25{25};
size_t const size50{50};
Alignment const align64{64};
FairMQMessagePtr outMsg3(push.NewMessage(100, fair::mq::Alignment{0}));
ASSERT_EQ(push.Send(outMsg3), 100);
auto msg1(push.NewMessage(size25));
msg1->Rebuild(size50, align64);
ASSERT_TRUE(CheckMsgAlignment(*msg1, align64));
FairMQMessagePtr inMsg3(pull.NewMessage(fair::mq::Alignment{0}));
ASSERT_EQ(pull.Receive(inMsg3), 100);
Alignment const align32{32};
auto msg2(push.NewMessage(size25, align64));
msg2->Rebuild(size50, align32);
ASSERT_TRUE(CheckMsgAlignment(*msg2, align32));
FairMQMessagePtr msg1(push.NewMessage(25));
msg1->Rebuild(50, fair::mq::Alignment{alignment});
ASSERT_EQ(reinterpret_cast<uintptr_t>(msg1->GetData()) % alignment, 0);
size_t alignment2 = 32;
FairMQMessagePtr msg2(push.NewMessage(25, fair::mq::Alignment{alignment}));
msg2->Rebuild(50, fair::mq::Alignment{alignment2});
ASSERT_EQ(reinterpret_cast<uintptr_t>(msg2->GetData()) % alignment2, 0);
auto msgCopy(push.NewMessage());
msgCopy->Copy(*msg2);
ASSERT_TRUE(CheckMsgAlignment(*msgCopy, align32));
}
void EmptyMessage(const string& transport, const string& _address)
auto EmptyMessage(string const& transport, string const& _address) -> void
{
size_t session{fair::mq::tools::UuidHash()};
std::string address(fair::mq::tools::ToString(_address, "_", transport));
ProgOptions config;
config.SetProperty<string>("session", tools::Uuid());
auto factory(TransportFactory::CreateTransportFactory(transport, tools::Uuid(), &config));
fair::mq::ProgOptions config;
config.SetProperty<string>("session", to_string(session));
auto factory = FairMQTransportFactory::CreateTransportFactory(transport, fair::mq::tools::Uuid(), &config);
FairMQChannel push{"Push", "push", factory};
Channel push{"Push", "push", factory};
Channel pull{"Pull", "pull", factory};
auto const address(tools::ToString(_address, "_", transport));
push.Bind(address);
FairMQChannel pull{"Pull", "pull", factory};
pull.Connect(address);
FairMQMessagePtr outMsg(push.NewMessage());
ASSERT_EQ(outMsg->GetData(), nullptr);
ASSERT_EQ(push.Send(outMsg), 0);
{
auto outMsg(push.NewMessage());
ASSERT_EQ(outMsg->GetData(), nullptr);
ASSERT_EQ(push.Send(outMsg), 0);
FairMQMessagePtr inMsg(pull.NewMessage());
ASSERT_EQ(pull.Receive(inMsg), 0);
ASSERT_EQ(inMsg->GetData(), nullptr);
auto inMsg(pull.NewMessage());
ASSERT_EQ(pull.Receive(inMsg), 0);
ASSERT_EQ(inMsg->GetData(), nullptr);
}
{
auto outMsg(push.NewMessage(0));
ASSERT_EQ(outMsg->GetSize(), 0);
size_t const size100{100};
outMsg->Rebuild(size100);
ASSERT_EQ(outMsg->GetSize(), size100);
outMsg->Rebuild(0);
ASSERT_EQ(outMsg->GetSize(), 0);
auto msgCopy(push.NewMessage());
msgCopy->Copy(*outMsg);
ASSERT_EQ(msgCopy->GetSize(), 0);
ASSERT_EQ(push.Send(outMsg), 0);
ASSERT_EQ(push.Send(msgCopy), 0);
auto inMsg(pull.NewMessage());
auto inMsg2(pull.NewMessage());
ASSERT_EQ(pull.Receive(inMsg), 0);
ASSERT_EQ(pull.Receive(inMsg2), 0);
ASSERT_EQ(inMsg->GetSize(), 0);
ASSERT_EQ(inMsg2->GetSize(), 0);
}
}
TEST(Resize, zeromq)
// The "zero copy" property of the Copy() method is an implementation detail and is not guaranteed.
// Currently it holds true for the shmem (across devices) and for zeromq (within same device) transports.
auto ZeroCopy() -> void
{
ProgOptions config;
config.SetProperty<string>("session", tools::Uuid());
auto factory(TransportFactory::CreateTransportFactory("shmem", tools::Uuid(), &config));
unique_ptr<string> str(make_unique<string>("asdf"));
const size_t size = 2;
MessagePtr original(factory->CreateMessage(size));
memcpy(original->GetData(), "AB", size);
{
MessagePtr copy(factory->CreateMessage());
copy->Copy(*original);
EXPECT_EQ(original->GetSize(), copy->GetSize());
EXPECT_EQ(original->GetData(), copy->GetData());
EXPECT_EQ(static_cast<const shmem::Message&>(*original).GetRefCount(), 2);
EXPECT_EQ(static_cast<const shmem::Message&>(*copy).GetRefCount(), 2);
// buffer must be still intact
ASSERT_EQ(AsStringView(*original)[0], 'A');
ASSERT_EQ(AsStringView(*original)[1], 'B');
ASSERT_EQ(AsStringView(*copy)[0], 'A');
ASSERT_EQ(AsStringView(*copy)[1], 'B');
}
EXPECT_EQ(static_cast<const shmem::Message&>(*original).GetRefCount(), 1);
}
// The "zero copy" property of the Copy() method is an implementation detail and is not guaranteed.
// Currently it holds true for the shmem (across devices) and for zeromq (within same device) transports.
auto ZeroCopyFromUnmanaged(string const& address) -> void
{
ProgOptions config1;
ProgOptions config2;
string session(tools::Uuid());
config1.SetProperty<string>("session", session);
config2.SetProperty<string>("session", session);
// ref counts should be accessible accross different segments
config2.SetProperty<uint16_t>("shm-segment-id", 2);
auto factory1(TransportFactory::CreateTransportFactory("shmem", tools::Uuid(), &config1));
auto factory2(TransportFactory::CreateTransportFactory("shmem", tools::Uuid(), &config2));
const size_t msgSize{100};
const size_t regionSize{1000000};
tools::Semaphore blocker;
auto region = factory1->CreateUnmanagedRegion(regionSize, [&blocker](void*, size_t, void*) {
blocker.Signal();
});
{
FairMQChannel push("Push", "push", factory1);
FairMQChannel pull("Pull", "pull", factory2);
push.Bind(address);
pull.Connect(address);
const size_t offset = 100;
auto msg1(push.NewMessage(region, static_cast<char*>(region->GetData()), msgSize, nullptr));
auto msg2(push.NewMessage(region, static_cast<char*>(region->GetData()) + offset, msgSize, nullptr));
const size_t contentSize = 2;
memcpy(msg1->GetData(), "AB", contentSize);
memcpy(msg2->GetData(), "CD", contentSize);
EXPECT_EQ(static_cast<const shmem::Message&>(*msg1).GetRefCount(), 1);
{
auto copyFromOriginal(push.NewMessage());
copyFromOriginal->Copy(*msg1);
EXPECT_EQ(static_cast<const shmem::Message&>(*msg1).GetRefCount(), 2);
EXPECT_EQ(static_cast<const shmem::Message&>(*msg1).GetRefCount(), static_cast<const shmem::Message&>(*copyFromOriginal).GetRefCount());
{
auto copyFromCopy(push.NewMessage());
copyFromCopy->Copy(*copyFromOriginal);
EXPECT_EQ(static_cast<const shmem::Message&>(*msg1).GetRefCount(), 3);
EXPECT_EQ(static_cast<const shmem::Message&>(*msg1).GetRefCount(), static_cast<const shmem::Message&>(*copyFromCopy).GetRefCount());
EXPECT_EQ(msg1->GetSize(), copyFromOriginal->GetSize());
EXPECT_EQ(msg1->GetData(), copyFromOriginal->GetData());
EXPECT_EQ(msg1->GetSize(), copyFromCopy->GetSize());
EXPECT_EQ(msg1->GetData(), copyFromCopy->GetData());
EXPECT_EQ(copyFromOriginal->GetSize(), copyFromCopy->GetSize());
EXPECT_EQ(copyFromOriginal->GetData(), copyFromCopy->GetData());
// messing with the ref count should not have affected the user buffer
ASSERT_EQ(AsStringView(*msg1)[0], 'A');
ASSERT_EQ(AsStringView(*msg1)[1], 'B');
push.Send(copyFromCopy);
push.Send(msg2);
auto incomingCopiedMsg(pull.NewMessage());
auto incomingOriginalMsg(pull.NewMessage());
pull.Receive(incomingCopiedMsg);
pull.Receive(incomingOriginalMsg);
EXPECT_EQ(static_cast<const shmem::Message&>(*incomingCopiedMsg).GetRefCount(), 3);
EXPECT_EQ(static_cast<const shmem::Message&>(*incomingOriginalMsg).GetRefCount(), 1);
ASSERT_EQ(AsStringView(*incomingCopiedMsg)[0], 'A');
ASSERT_EQ(AsStringView(*incomingCopiedMsg)[1], 'B');
{
// copying on a different segment should work
auto copyFromIncoming(pull.NewMessage());
copyFromIncoming->Copy(*incomingOriginalMsg);
EXPECT_EQ(static_cast<const shmem::Message&>(*copyFromIncoming).GetRefCount(), 2);
ASSERT_EQ(AsStringView(*incomingOriginalMsg)[0], 'C');
ASSERT_EQ(AsStringView(*incomingOriginalMsg)[1], 'D');
}
EXPECT_EQ(static_cast<const shmem::Message&>(*incomingOriginalMsg).GetRefCount(), 1);
}
EXPECT_EQ(static_cast<const shmem::Message&>(*msg1).GetRefCount(), 2);
}
EXPECT_EQ(static_cast<const shmem::Message&>(*msg1).GetRefCount(), 1);
}
blocker.Wait();
blocker.Wait();
}
TEST(Resize, zeromq) // NOLINT
{
RunPushPullWithMsgResize("zeromq", "ipc://test_message_resize");
}
TEST(Resize, shmem)
TEST(Resize, shmem) // NOLINT
{
RunPushPullWithMsgResize("shmem", "ipc://test_message_resize");
}
TEST(Rebuild, zeromq)
TEST(Rebuild, zeromq) // NOLINT
{
RunMsgRebuild("zeromq");
}
TEST(Rebuild, shmem)
TEST(Rebuild, shmem) // NOLINT
{
RunMsgRebuild("shmem");
}
TEST(Alignment, shmem)
TEST(Alignment, shmem) // NOLINT
{
Alignment("shmem", "ipc://test_message_alignment");
RunPushPullWithAlignment("shmem", "ipc://test_message_alignment");
}
TEST(Alignment, zeromq)
TEST(Alignment, zeromq) // NOLINT
{
Alignment("zeromq", "ipc://test_message_alignment");
RunPushPullWithAlignment("zeromq", "ipc://test_message_alignment");
}
TEST(EmptyMessage, zeromq)
TEST(EmptyMessage, zeromq) // NOLINT
{
EmptyMessage("zeromq", "ipc://test_empty_message");
}
TEST(EmptyMessage, shmem)
TEST(EmptyMessage, shmem) // NOLINT
{
EmptyMessage("shmem", "ipc://test_empty_message");
}
TEST(ZeroCopy, shmem) // NOLINT
{
ZeroCopy();
}
TEST(ZeroCopyFromUnmanaged, shmem) // NOLINT
{
ZeroCopyFromUnmanaged("ipc://test_zerocopy_unmanaged");
}
} // namespace

View File

@@ -1,20 +1,19 @@
/********************************************************************************
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2017-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include <gtest/gtest.h>
#include <FairMQChannel.h>
#include <FairMQParts.h>
#include <FairMQLogger.h>
#include <FairMQTransportFactory.h>
#include <fairmq/tools/Unique.h>
#include <fairmq/ProgOptions.h>
#include <algorithm>
#include <fairlogger/Logger.h>
#include <fairmq/Channel.h>
#include <fairmq/Parts.h>
#include <fairmq/ProgOptions.h>
#include <fairmq/TransportFactory.h>
#include <fairmq/tools/Unique.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include <string>
@@ -24,23 +23,22 @@ namespace
{
using namespace std;
using namespace fair::mq;
auto RunSingleThreadedMultipart(string transport, string address1, string address2) -> void {
size_t session{fair::mq::tools::UuidHash()};
fair::mq::ProgOptions config;
config.SetProperty<string>("session", std::to_string(session));
config.SetProperty<string>("session", tools::Uuid());
auto factory = FairMQTransportFactory::CreateTransportFactory(transport, fair::mq::tools::Uuid(), &config);
auto factory = TransportFactory::CreateTransportFactory(transport, tools::Uuid(), &config);
FairMQChannel push1("Push1", "push", factory);
Channel push1("Push1", "push", factory);
ASSERT_TRUE(push1.Bind(address1));
FairMQChannel pull1("Pull1", "pull", factory);
Channel pull1("Pull1", "pull", factory);
ASSERT_TRUE(pull1.Connect(address1));
FairMQChannel push2("Push2", "push", factory);
Channel push2("Push2", "push", factory);
ASSERT_TRUE(push2.Bind(address2));
FairMQChannel pull2("Pull2", "pull", factory);
Channel pull2("Pull2", "pull", factory);
ASSERT_TRUE(pull2.Connect(address2));
// TODO validate that fTransportFactory is not nullptr
@@ -51,32 +49,32 @@ auto RunSingleThreadedMultipart(string transport, string address1, string addres
ASSERT_TRUE(pull2.Validate());
{
FairMQParts multiplePartsOut;
Parts multiplePartsOut;
multiplePartsOut.AddPart(push1.NewSimpleMessage("1"));
multiplePartsOut.AddPart(push1.NewSimpleMessage("2"));
multiplePartsOut.AddPart(push1.NewSimpleMessage("3"));
ASSERT_GE(push1.Send(multiplePartsOut), 0);
FairMQParts singlePartOut;
Parts singlePartOut;
singlePartOut.AddPart(push1.NewSimpleMessage("4"));
ASSERT_GE(push1.Send(singlePartOut), 0);
}
FairMQParts multipleParts;
Parts multipleParts;
ASSERT_GE(pull1.Receive(multipleParts), 0);
stringstream multiple;
for_each(multipleParts.cbegin(), multipleParts.cend(), [&multiple, &factory](const FairMQMessagePtr& part) {
for_each(multipleParts.cbegin(), multipleParts.cend(), [&multiple, &factory](const MessagePtr& part) {
multiple << string{static_cast<char*>(part->GetData()), part->GetSize()};
ASSERT_EQ(part->GetTransport(), factory.get());
});
ASSERT_EQ(multiple.str(), "123");
FairMQParts singlePart;
Parts singlePart;
ASSERT_GE(pull1.Receive(singlePart), 0);
stringstream single;
for_each(singlePart.cbegin(), singlePart.cend(), [&single](const FairMQMessagePtr& part) {
for_each(singlePart.cbegin(), singlePart.cend(), [&single](const MessagePtr& part) {
single << string{static_cast<char*>(part->GetData()), part->GetSize()};
});
ASSERT_EQ(single.str(), "4");
@@ -85,18 +83,18 @@ auto RunSingleThreadedMultipart(string transport, string address1, string addres
ASSERT_GE(push2.Send(multipleParts), 0);
{
FairMQParts singlePartIn;
Parts singlePartIn;
ASSERT_GE(pull2.Receive(singlePartIn), 0);
stringstream singleIn;
for_each(singlePartIn.cbegin(), singlePartIn.cend(), [&singleIn](const FairMQMessagePtr& part) {
for_each(singlePartIn.cbegin(), singlePartIn.cend(), [&singleIn](const MessagePtr& part) {
singleIn << string{static_cast<char*>(part->GetData()), part->GetSize()};
});
ASSERT_EQ(singleIn.str(), "4");
FairMQParts multiplePartsIn;
Parts multiplePartsIn;
ASSERT_GE(pull2.Receive(multiplePartsIn), 0);
stringstream multipleIn;
for_each(multiplePartsIn.cbegin(), multiplePartsIn.cend(), [&multipleIn, &factory](const FairMQMessagePtr& part) {
for_each(multiplePartsIn.cbegin(), multiplePartsIn.cend(), [&multipleIn, &factory](const MessagePtr& part) {
multipleIn << string{static_cast<char*>(part->GetData()), part->GetSize()};
ASSERT_EQ(part->GetTransport(), factory.get());
});
@@ -106,24 +104,22 @@ auto RunSingleThreadedMultipart(string transport, string address1, string addres
auto RunMultiThreadedMultipart(string transport, string address1) -> void
{
size_t session{fair::mq::tools::UuidHash()};
fair::mq::ProgOptions config;
config.SetProperty<string>("session", std::to_string(session));
ProgOptions config;
config.SetProperty<string>("session", tools::Uuid());
config.SetProperty<int>("io-threads", 1);
config.SetProperty<size_t>("shm-segment-size", 20000000);
config.SetProperty<size_t>("shm-segment-size", 20000000); // NOLINT
auto factory = FairMQTransportFactory::CreateTransportFactory(transport, fair::mq::tools::Uuid(), &config);
auto factory = TransportFactory::CreateTransportFactory(transport, tools::Uuid(), &config);
FairMQChannel push1("Push1", "push", factory);
Channel push1("Push1", "push", factory);
ASSERT_TRUE(push1.Bind(address1));
FairMQChannel pull1("Pull1", "pull", factory);
Channel pull1("Pull1", "pull", factory);
ASSERT_TRUE(pull1.Connect(address1));
auto pusher = thread{[&push1](){
ASSERT_TRUE(push1.Validate());
FairMQParts sent;
Parts sent;
sent.AddPart(push1.NewSimpleMessage("1"));
sent.AddPart(push1.NewSimpleMessage("2"));
sent.AddPart(push1.NewSimpleMessage("3"));
@@ -134,11 +130,11 @@ auto RunMultiThreadedMultipart(string transport, string address1) -> void
auto puller = thread{[&pull1](){
ASSERT_TRUE(pull1.Validate());
FairMQParts received;
Parts received;
ASSERT_GE(pull1.Receive(received), 0);
stringstream out;
for_each(received.cbegin(), received.cend(), [&out](const FairMQMessagePtr& part) {
for_each(received.cbegin(), received.cend(), [&out](const MessagePtr& part) {
out << string{static_cast<char*>(part->GetData()), part->GetSize()};
});
ASSERT_EQ(out.str(), "123");
@@ -148,42 +144,42 @@ auto RunMultiThreadedMultipart(string transport, string address1) -> void
puller.join();
}
TEST(PushPull, Multipart_ST_inproc_zeromq)
TEST(PushPull, Multipart_ST_inproc_zeromq) // NOLINT
{
RunSingleThreadedMultipart("zeromq", "inproc://test1", "inproc://test2");
}
TEST(PushPull, Multipart_ST_inproc_shmem)
TEST(PushPull, Multipart_ST_inproc_shmem) // NOLINT
{
RunSingleThreadedMultipart("shmem", "inproc://test1", "inproc://test2");
}
TEST(PushPull, Multipart_ST_ipc_zeromq)
TEST(PushPull, Multipart_ST_ipc_zeromq) // NOLINT
{
RunSingleThreadedMultipart("zeromq", "ipc://test_Multipart_ST_ipc_zeromq_1", "ipc://test_Multipart_ST_ipc_zeromq_2");
}
TEST(PushPull, Multipart_ST_ipc_shmem)
TEST(PushPull, Multipart_ST_ipc_shmem) // NOLINT
{
RunSingleThreadedMultipart("shmem", "ipc://test_Multipart_ST_ipc_shmem_1", "ipc://test_Multipart_ST_ipc_shmem_2");
}
TEST(PushPull, Multipart_MT_inproc_zeromq)
TEST(PushPull, Multipart_MT_inproc_zeromq) // NOLINT
{
RunMultiThreadedMultipart("zeromq", "inproc://test_1");
}
TEST(PushPull, Multipart_MT_inproc_shmem)
TEST(PushPull, Multipart_MT_inproc_shmem) // NOLINT
{
RunMultiThreadedMultipart("shmem", "inproc://test_1");
}
TEST(PushPull, Multipart_MT_ipc_zeromq)
TEST(PushPull, Multipart_MT_ipc_zeromq) // NOLINT
{
RunMultiThreadedMultipart("zeromq", "ipc://test_Multipart_MT_ipc_zeromq_1");
}
TEST(PushPull, Multipart_MT_ipc_shmem)
TEST(PushPull, Multipart_MT_ipc_shmem) // NOLINT
{
RunMultiThreadedMultipart("shmem", "ipc://test_Multipart_MT_ipc_shmem_1");
}

View File

@@ -199,7 +199,6 @@ void RegionCallbacks(const string& transport, const string& _address)
});
ptr2 = region2->GetData();
{
FairMQMessagePtr msg1out(push.NewMessage(region1, ptr1, size1, intPtr1.get()));
FairMQMessagePtr msg2out(push.NewMessage(region2, ptr2, size2, intPtr2.get()));

View File

@@ -1,20 +1,20 @@
/********************************************************************************
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2017-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include <gtest/gtest.h>
#include <FairMQChannel.h>
#include <FairMQParts.h>
#include <FairMQLogger.h>
#include <FairMQTransportFactory.h>
#include <fairmq/tools/Unique.h>
#include <fairmq/ProgOptions.h>
#include <algorithm>
#include <array>
#include <fairlogger/Logger.h>
#include <fairmq/Channel.h>
#include <fairmq/Parts.h>
#include <fairmq/ProgOptions.h>
#include <fairmq/TransportFactory.h>
#include <fairmq/tools/Unique.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include <string>
@@ -24,25 +24,25 @@ namespace
{
using namespace std;
using namespace fair::mq;
void CheckOldOptionInterface(FairMQChannel& channel, const string& option)
void CheckOldOptionInterface(Channel& channel, const string& option)
{
int value = 500;
int const expectedValue{500};
int value = expectedValue;
channel.GetSocket().SetOption(option, &value, sizeof(value));
value = 0;
size_t valueSize = sizeof(value);
channel.GetSocket().GetOption(option, &value, &valueSize);
ASSERT_EQ(value, 500);
ASSERT_EQ(value, expectedValue);
}
void RunOptionsTest(const string& transport)
{
size_t session{fair::mq::tools::UuidHash()};
fair::mq::ProgOptions config;
config.SetProperty<string>("session", to_string(session));
auto factory = FairMQTransportFactory::CreateTransportFactory(transport, fair::mq::tools::Uuid(), &config);
FairMQChannel channel("Push", "push", factory);
config.SetProperty<string>("session", tools::Uuid());
auto factory = TransportFactory::CreateTransportFactory(transport, tools::Uuid(), &config);
Channel channel("Push", "push", factory);
CheckOldOptionInterface(channel, "linger");
CheckOldOptionInterface(channel, "snd-hwm");
@@ -50,38 +50,37 @@ void RunOptionsTest(const string& transport)
CheckOldOptionInterface(channel, "snd-size");
CheckOldOptionInterface(channel, "rcv-size");
channel.GetSocket().SetLinger(300);
ASSERT_EQ(channel.GetSocket().GetLinger(), 300);
size_t const linger{300};
channel.GetSocket().SetLinger(linger);
ASSERT_EQ(channel.GetSocket().GetLinger(), linger);
channel.GetSocket().SetSndBufSize(500);
ASSERT_EQ(channel.GetSocket().GetSndBufSize(), 500);
size_t const bufSize{500};
channel.GetSocket().SetSndBufSize(bufSize);
ASSERT_EQ(channel.GetSocket().GetSndBufSize(), bufSize);
channel.GetSocket().SetRcvBufSize(bufSize);
ASSERT_EQ(channel.GetSocket().GetRcvBufSize(), bufSize);
channel.GetSocket().SetRcvBufSize(500);
ASSERT_EQ(channel.GetSocket().GetRcvBufSize(), 500);
channel.GetSocket().SetSndKernelSize(8000);
ASSERT_EQ(channel.GetSocket().GetSndKernelSize(), 8000);
channel.GetSocket().SetRcvKernelSize(8000);
ASSERT_EQ(channel.GetSocket().GetRcvKernelSize(), 8000);
size_t const kernelSize{8000};
channel.GetSocket().SetSndKernelSize(kernelSize);
ASSERT_EQ(channel.GetSocket().GetSndKernelSize(), kernelSize);
channel.GetSocket().SetRcvKernelSize(kernelSize);
ASSERT_EQ(channel.GetSocket().GetRcvKernelSize(), kernelSize);
}
void ZeroingAndMlock(const string& transport)
{
size_t session{fair::mq::tools::UuidHash()};
fair::mq::ProgOptions config;
config.SetProperty<string>("session", to_string(session));
config.SetProperty<size_t>("shm-segment-size", 16384);
ProgOptions config;
config.SetProperty<string>("session", tools::Uuid());
config.SetProperty<size_t>("shm-segment-size", 16384); // NOLINT
config.SetProperty<bool>("shm-zero-segment", true);
config.SetProperty<bool>("shm-mlock-segment", true);
auto factory = FairMQTransportFactory::CreateTransportFactory(transport, fair::mq::tools::Uuid(), &config);
auto factory = TransportFactory::CreateTransportFactory(transport, tools::Uuid(), &config);
FairMQMessagePtr outMsg(factory->CreateMessage(10000));
char test[10000];
memset(test, 0, sizeof(test));
ASSERT_EQ(memcmp(test, outMsg->GetData(), outMsg->GetSize()), 0);
constexpr size_t size{10000};
auto outMsg(factory->CreateMessage(size));
array<char, size> test{0};
ASSERT_EQ(memcmp(test.data(), outMsg->GetData(), outMsg->GetSize()), 0);
}
void ZeroingAndMlockOnCreation(const string& transport)
@@ -90,29 +89,29 @@ void ZeroingAndMlockOnCreation(const string& transport)
fair::mq::ProgOptions config;
config.SetProperty<string>("session", to_string(session));
config.SetProperty<size_t>("shm-segment-size", 16384);
config.SetProperty<size_t>("shm-segment-size", 16384); // NOLINT
config.SetProperty<bool>("shm-mlock-segment-on-creation", true);
config.SetProperty<bool>("shm-zero-segment-on-creation", true);
auto factory = FairMQTransportFactory::CreateTransportFactory(transport, fair::mq::tools::Uuid(), &config);
FairMQMessagePtr outMsg(factory->CreateMessage(10000));
char test[10000];
memset(test, 0, sizeof(test));
ASSERT_EQ(memcmp(test, outMsg->GetData(), outMsg->GetSize()), 0);
constexpr size_t size{10000};
auto outMsg(factory->CreateMessage(size));
array<char, size> test{0};
ASSERT_EQ(memcmp(test.data(), outMsg->GetData(), outMsg->GetSize()), 0);
}
TEST(Options, zeromq)
TEST(Options, zeromq) // NOLINT
{
RunOptionsTest("zeromq");
}
TEST(Options, shmem)
TEST(Options, shmem) // NOLINT
{
RunOptionsTest("shmem");
}
TEST(ZeroingAndMlock, shmem)
TEST(ZeroingAndMlock, shmem) // NOLINT
{
ZeroingAndMlock("shmem");
}