mirror of
https://github.com/FairRootGroup/FairMQ.git
synced 2025-10-15 17:41:45 +00:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8867e15d8c | ||
|
6b2c3c4486 | ||
|
adf743750f | ||
|
f3385173b2 | ||
|
f798f18b95 | ||
|
d1dd9d7824 | ||
|
df742272ed | ||
|
58b78995b4 | ||
|
efd6523112 | ||
|
6fc2839d02 | ||
|
c585b0d486 | ||
|
933ed8de6e | ||
|
08d64ad463 | ||
|
56a7b6e9cb | ||
|
8d575a23a5 | ||
|
18f50871ef | ||
|
c81b03ff29 | ||
|
a9ffa2a8af |
16
.github/workflows/fair-software.yml
vendored
16
.github/workflows/fair-software.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: fair-software
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
name: "fair-software"
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'FairRootGroup/FairMQ'
|
||||
steps:
|
||||
- uses: fair-software/howfairis-github-action@0.2.1
|
||||
name: Measure compliance with fair-software.eu recommendations
|
||||
env:
|
||||
PYCHARM_HOSTED: "Trick colorama into displaying colored output"
|
||||
with:
|
||||
MY_REPO_URL: "https://github.com/${{ github.repository }}"
|
87
.zenodo.json
87
.zenodo.json
@@ -1,87 +0,0 @@
|
||||
{
|
||||
"creators": [
|
||||
{
|
||||
"name": "Al-Turany, Mohammad"
|
||||
},
|
||||
{
|
||||
"orcid": "0000-0003-3787-1910",
|
||||
"name": "Klein, Dennis"
|
||||
},
|
||||
{
|
||||
"name": "Kollegger, Thorsten"
|
||||
},
|
||||
{
|
||||
"orcid": "0000-0002-6249-155X",
|
||||
"name": "Rybalchenko, Alexey"
|
||||
},
|
||||
{
|
||||
"name": "Winckler, Nicolas"
|
||||
}
|
||||
],
|
||||
"contributors": [
|
||||
{
|
||||
"type": "Other",
|
||||
"name": "Aphecetche, Laurent"
|
||||
},
|
||||
{
|
||||
"type": "Other",
|
||||
"name": "Binet, Sebastien"
|
||||
},
|
||||
{
|
||||
"type": "Other",
|
||||
"name": "Eulisse, Giulio"
|
||||
},
|
||||
{
|
||||
"type": "Other",
|
||||
"name": "Karabowicz, Radoslaw"
|
||||
},
|
||||
{
|
||||
"type": "Other",
|
||||
"name": "Kretz, Matthias"
|
||||
},
|
||||
{
|
||||
"type": "Other",
|
||||
"name": "Krzewicki, Mikolaj"
|
||||
},
|
||||
{
|
||||
"type": "Other",
|
||||
"name": "Lebedev, Andrey"
|
||||
},
|
||||
{
|
||||
"type": "Other",
|
||||
"name": "Mrnjavac, Teo"
|
||||
},
|
||||
{
|
||||
"type": "Other",
|
||||
"name": "Neskovic, Gvozden"
|
||||
},
|
||||
{
|
||||
"type": "Other",
|
||||
"name": "Richter, Matthias"
|
||||
},
|
||||
{
|
||||
"type": "Other",
|
||||
"orcid": "0000-0002-5321-8404",
|
||||
"name": "Tacke, Christian"
|
||||
},
|
||||
{
|
||||
"type": "Other",
|
||||
"name": "Uhlig, Florian"
|
||||
},
|
||||
{
|
||||
"type": "Other",
|
||||
"name": "Wenzel, Sandro"
|
||||
}
|
||||
],
|
||||
"description": "<p>C++ Message Queuing Library and Framework</p>",
|
||||
"related_identifiers": [
|
||||
{
|
||||
"identifier": "https://github.com/FairRootGroup/FairMQ/",
|
||||
"relation": "isSupplementTo",
|
||||
"resource_type": "software",
|
||||
"scheme": "url"
|
||||
}
|
||||
],
|
||||
"title": "FairMQ",
|
||||
"license": "LGPL-3.0-only"
|
||||
}
|
6
AUTHORS
6
AUTHORS
@@ -1,5 +1,5 @@
|
||||
Al-Turany, Mohammad [https://orcid.org/0000-0002-8071-4497]
|
||||
Klein, Dennis [https://orcid.org/0000-0003-3787-1910]
|
||||
Al-Turany, Mohammad
|
||||
Klein, Dennis
|
||||
Kollegger, Thorsten
|
||||
Rybalchenko, Alexey [https://orcid.org/0000-0002-6249-155X]
|
||||
Rybalchenko, Alexey
|
||||
Winckler, Nicolas
|
||||
|
@@ -1,5 +1,5 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2018-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
|
||||
# Copyright (C) 2018-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
|
||||
# #
|
||||
# This software is distributed under the terms of the #
|
||||
# GNU Lesser General Public Licence (LGPL) version 3, #
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
# Project ######################################################################
|
||||
cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
|
||||
cmake_policy(VERSION 3.15...3.26)
|
||||
cmake_policy(VERSION 3.15...3.22)
|
||||
|
||||
list(PREPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||
include(GitHelper)
|
||||
@@ -27,6 +27,10 @@ fairmq_build_option(BUILD_FAIRMQ "Build FairMQ library and devices."
|
||||
DEFAULT ON)
|
||||
fairmq_build_option(BUILD_TESTING "Build tests."
|
||||
DEFAULT OFF REQUIRES "BUILD_FAIRMQ")
|
||||
fairmq_build_option(BUILD_OFI_TRANSPORT "Build experimental OFI transport."
|
||||
DEFAULT OFF REQUIRES "BUILD_FAIRMQ")
|
||||
fairmq_build_option(BUILD_PMIX_PLUGIN "Build PMIx plugin."
|
||||
DEFAULT OFF REQUIRES "BUILD_FAIRMQ")
|
||||
fairmq_build_option(BUILD_EXAMPLES "Build FairMQ examples."
|
||||
DEFAULT ON REQUIRES "BUILD_FAIRMQ")
|
||||
fairmq_build_option(BUILD_TIDY_TOOL "Build the fairmq-tidy tool."
|
||||
@@ -82,6 +86,12 @@ endif()
|
||||
if(BUILD_TESTING)
|
||||
list(APPEND PROJECT_PACKAGE_COMPONENTS tests)
|
||||
endif()
|
||||
if(BUILD_PMIX_PLUGIN)
|
||||
list(APPEND PROJECT_PACKAGE_COMPONENTS pmix_plugin)
|
||||
endif()
|
||||
if(BUILD_OFI_TRANSPORT)
|
||||
list(APPEND PROJECT_PACKAGE_COMPONENTS ofi_transport)
|
||||
endif()
|
||||
if(BUILD_EXAMPLES)
|
||||
list(APPEND PROJECT_PACKAGE_COMPONENTS examples)
|
||||
endif()
|
||||
@@ -127,6 +137,5 @@ fairmq_summary_components()
|
||||
fairmq_summary_static_analysis()
|
||||
fairmq_summary_install_prefix()
|
||||
fairmq_summary_debug_mode()
|
||||
fairmq_summary_compile_definitions()
|
||||
message(STATUS " ")
|
||||
################################################################################
|
||||
|
@@ -3,11 +3,11 @@ Binet, Sebastien
|
||||
Eulisse, Giulio
|
||||
Karabowicz, Radoslaw
|
||||
Kretz, Matthias <kretz@kde.org>
|
||||
Krzewicki, Mikolaj
|
||||
Krzewicki, Mikolaj
|
||||
Lebedev, Andrey
|
||||
Mrnjavac, Teo <teo.m@cern.ch>
|
||||
Neskovic, Gvozden
|
||||
Richter, Matthias
|
||||
Tacke, Christian [https://orcid.org/0000-0002-5321-8404]
|
||||
Tacke, Christian
|
||||
Uhlig, Florian
|
||||
Wenzel, Sandro
|
||||
|
@@ -1,5 +1,5 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2021-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
|
||||
# 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, #
|
||||
@@ -42,6 +42,12 @@ endif()
|
||||
ctest_start(Continuous)
|
||||
|
||||
list(APPEND options "-DDISABLE_COLOR=ON" "-DBUILD_EXAMPLES=ON" "-DBUILD_TESTING=ON")
|
||||
if(HAS_PMIX)
|
||||
list(APPEND options "-DBUILD_PMIX_PLUGIN=ON")
|
||||
endif()
|
||||
if(HAS_ASIO AND HAS_ASIOFI)
|
||||
list(APPEND options "-DBUILD_OFI_TRANSPORT=ON")
|
||||
endif()
|
||||
if(RUN_STATIC_ANALYSIS)
|
||||
list(APPEND options "-DRUN_STATIC_ANALYSIS=ON")
|
||||
endif()
|
||||
@@ -81,7 +87,7 @@ ctest_submit()
|
||||
|
||||
if(NOT RUN_STATIC_ANALYSIS)
|
||||
ctest_test(BUILD "${CTEST_BINARY_DIRECTORY}"
|
||||
PARALLEL_LEVEL ${NCPUS}
|
||||
PARALLEL_LEVEL 1
|
||||
SCHEDULE_RANDOM ON
|
||||
RETURN_VALUE _ctest_test_ret_val)
|
||||
|
||||
|
42
Jenkinsfile
vendored
42
Jenkinsfile
vendored
@@ -4,21 +4,20 @@ def jobMatrix(String type, List specs) {
|
||||
def nodes = [:]
|
||||
for (spec in specs) {
|
||||
def job = ""
|
||||
def selector = "slurm"
|
||||
def selector = ""
|
||||
def os = ""
|
||||
def ver = ""
|
||||
|
||||
if (type == 'build') {
|
||||
job = "${spec.os}-${spec.ver}-${spec.arch}-${spec.compiler}"
|
||||
if (spec.os =~ /^macos/) {
|
||||
selector = "${spec.os}-${spec.ver}-${spec.arch}"
|
||||
}
|
||||
selector = "${spec.os}-${spec.ver}-${spec.arch}"
|
||||
os = spec.os
|
||||
ver = spec.ver
|
||||
} else { // == 'check'
|
||||
job = "${spec.name}"
|
||||
selector = 'fedora-35-x86_64'
|
||||
os = 'fedora'
|
||||
ver = '36'
|
||||
ver = '35'
|
||||
}
|
||||
|
||||
def label = "${job}"
|
||||
@@ -37,14 +36,14 @@ def jobMatrix(String type, List specs) {
|
||||
sh "echo \"export LABEL=\\\"\${JOB_BASE_NAME} ${label}\\\"\" >> ${jobscript}"
|
||||
if (selector =~ /^macos/) {
|
||||
sh """\
|
||||
echo \"export DDS_ROOT=\\\"\\\$(brew --prefix dds)\\\"\" >> ${jobscript}
|
||||
echo \"export PATH=\\\"\\\$(brew --prefix dds)/bin:\\\$PATH\\\"\" >> ${jobscript}
|
||||
echo \"${ctestcmd}\" >> ${jobscript}
|
||||
"""
|
||||
sh "cat ${jobscript}"
|
||||
sh "bash ${jobscript}"
|
||||
} else { // selector == "slurm"
|
||||
def imageurl = "oras://ghcr.io/fairrootgroup/fairmq-dev/${os}-${ver}-sif:latest"
|
||||
def execopts = "--net --ipc --uts --pid -B/shared"
|
||||
def containercmd = "singularity exec ${execopts} ${imageurl} bash -l -c \\\"${ctestcmd} ${extra}\\\""
|
||||
} else {
|
||||
def containercmd = "singularity exec --net --ipc --uts --pid -B/shared ${env.SINGULARITY_CONTAINER_ROOT}/fairmq/${os}.${ver}.sif bash -l -c \\\"${ctestcmd} ${extra}\\\""
|
||||
sh """\
|
||||
echo \"echo \\\"*** Job started at .......: \\\$(date -R)\\\"\" >> ${jobscript}
|
||||
echo \"echo \\\"*** Job ID ...............: \\\${SLURM_JOB_ID}\\\"\" >> ${jobscript}
|
||||
@@ -68,6 +67,12 @@ def jobMatrix(String type, List specs) {
|
||||
deleteDir()
|
||||
githubNotify(context: "${label}", description: 'Success', status: 'SUCCESS')
|
||||
} catch (e) {
|
||||
def tarball = "${type}_${job}_dds_logs.tar.gz"
|
||||
if (fileExists("build/test/.DDS")) {
|
||||
sh "tar czvf ${tarball} -C \${WORKSPACE}/build/test .DDS/"
|
||||
archiveArtifacts tarball
|
||||
}
|
||||
|
||||
deleteDir()
|
||||
githubNotify(context: "${label}", description: 'Error', status: 'ERROR')
|
||||
throw e
|
||||
@@ -84,20 +89,19 @@ pipeline{
|
||||
stage("CI") {
|
||||
steps{
|
||||
script {
|
||||
def all = '-DHAS_ASIO=ON -DHAS_ASIOFI=ON -DHAS_PMIX=ON'
|
||||
|
||||
def builds = jobMatrix('build', [
|
||||
[os: 'ubuntu', ver: '20.04', arch: 'x86_64', compiler: 'gcc-9'],
|
||||
[os: 'ubuntu', ver: '22.04', arch: 'x86_64', compiler: 'gcc-11'],
|
||||
[os: 'fedora', ver: '33', arch: 'x86_64', compiler: 'gcc-10'],
|
||||
[os: 'fedora', ver: '34', arch: 'x86_64', compiler: 'gcc-11'],
|
||||
[os: 'fedora', ver: '35', arch: 'x86_64', compiler: 'gcc-11'],
|
||||
[os: 'fedora', ver: '36', arch: 'x86_64', compiler: 'gcc-12'],
|
||||
[os: 'fedora', ver: '37', arch: 'x86_64', compiler: 'gcc-12'],
|
||||
[os: 'fedora', ver: '38', arch: 'x86_64', compiler: 'gcc-13'],
|
||||
[os: 'ubuntu', ver: '20.04', arch: 'x86_64', compiler: 'gcc-9', extra: all],
|
||||
[os: 'fedora', ver: '32', arch: 'x86_64', compiler: 'gcc-10', extra: all],
|
||||
[os: 'fedora', ver: '33', arch: 'x86_64', compiler: 'gcc-10', extra: all],
|
||||
[os: 'fedora', ver: '34', arch: 'x86_64', compiler: 'gcc-11', extra: all],
|
||||
[os: 'fedora', ver: '35', arch: 'x86_64', compiler: 'gcc-11', extra: all],
|
||||
[os: 'macos', ver: '12', arch: 'x86_64', compiler: 'apple-clang-13', extra: '-DHAS_ASIO=ON'],
|
||||
[os: 'macos', ver: '12', arch: 'arm64', compiler: 'apple-clang-13', extra: '-DHAS_ASIO=ON'],
|
||||
[os: 'macos', ver: '12', arch: 'arm64', compiler: 'apple-clang-13', extra: '-DHAS_ASIO=ON'],
|
||||
])
|
||||
|
||||
def all_debug = "-DCMAKE_BUILD_TYPE=Debug"
|
||||
def all_debug = "${all} -DCMAKE_BUILD_TYPE=Debug"
|
||||
|
||||
def checks = jobMatrix('check', [
|
||||
[name: 'static-analyzers', extra: "${all_debug} -DRUN_STATIC_ANALYSIS=ON"],
|
||||
|
25
README.md
25
README.md
@@ -1,10 +1,5 @@
|
||||
<!-- {#mainpage} -->
|
||||
# FairMQ
|
||||
|
||||
[](COPYRIGHT)
|
||||
[](https://doi.org/10.5281/zenodo.1689985)
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/6915)
|
||||
[](https://github.com/FairRootGroup/FairMQ/actions/workflows/fair-software.yml)
|
||||
# FairMQ [](COPYRIGHT)
|
||||
|
||||
C++ Message Queuing Library and Framework
|
||||
|
||||
@@ -23,8 +18,7 @@ FairMQ is designed to help implementing large-scale data processing workflows ne
|
||||
The core of FairMQ provides an abstract asynchronous message passing API with scalability protocols
|
||||
inspired by [ZeroMQ](https://github.com/zeromq/libzmq) (e.g. PUSH/PULL, PUB/SUB).
|
||||
FairMQ provides multiple implementations for its API (so-called "transports",
|
||||
e.g. `zeromq` and `shmem` (latest release of the `ofi` transport in v1.4.56, removed since v1.5+)) to cover
|
||||
a variety of use cases
|
||||
e.g. `zeromq`, `shmem` and `ofi` (in development)) to cover a variety of use cases
|
||||
(e.g. inter-thread, inter-process, inter-node communication) and machines (e.g. Ethernet, Infiniband).
|
||||
In addition to this core functionality FairMQ provides a framework for creating "devices" - actors which
|
||||
are communicating through message passing. FairMQ does not only allow the user to use different transport
|
||||
@@ -74,14 +68,25 @@ If FairMQ is not installed in system directories, you can hint the installation:
|
||||
list(PREPEND CMAKE_PREFIX_PATH /path/to/fairmq_install)
|
||||
```
|
||||
|
||||
Optionally, you can require certain FairMQ package components and a minimum version:
|
||||
|
||||
```cmake
|
||||
find_package(FairMQ 1.4.50 COMPONENTS ofi_transport)
|
||||
```
|
||||
|
||||
When building FairMQ, CMake will print a summary table of all available package components.
|
||||
|
||||
## Dependencies
|
||||
|
||||
* [asio](https://github.com/chriskohlhoff/asio)
|
||||
* [asiofi](https://github.com/FairRootGroup/asiofi)
|
||||
* [Boost](https://www.boost.org/)
|
||||
* [CMake](https://cmake.org/)
|
||||
* [Doxygen](http://www.doxygen.org/)
|
||||
* [FairCMakeModules](https://github.com/FairRootGroup/FairCMakeModules) (optionally bundled)
|
||||
* [FairLogger](https://github.com/FairRootGroup/FairLogger)
|
||||
* [GTest](https://github.com/google/googletest) (optionally bundled)
|
||||
* [PMIx](https://pmix.org/)
|
||||
* [ZeroMQ](http://zeromq.org/)
|
||||
|
||||
Which dependencies are required depends on which components are built.
|
||||
@@ -95,6 +100,8 @@ On command line:
|
||||
* `-DDISABLE_COLOR=ON` disables coloured console output.
|
||||
* `-DBUILD_TESTING=OFF` disables building of tests.
|
||||
* `-DBUILD_EXAMPLES=OFF` disables building of examples.
|
||||
* `-DBUILD_OFI_TRANSPORT=ON` enables building of the experimental OFI transport.
|
||||
* `-DBUILD_PMIX_PLUGIN=ON` enables building of the PMIx plugin.
|
||||
* `-DBUILD_DOCS=ON` enables building of API docs.
|
||||
* You can hint non-system installations for dependent packages, see the #installation-from-source section above
|
||||
|
||||
@@ -152,4 +159,4 @@ After the `find_package(FairMQ)` call the following CMake variables are defined:
|
||||
1. [Usage](docs/Plugins.md#71-usage)
|
||||
2. [Development](docs/Plugins.md#72-development)
|
||||
3. [Provided Plugins](docs/Plugins.md#73-provided-plugins)
|
||||
1. [PMIx](docs/Plugins.md#731-pmix)
|
||||
2. [PMIx](docs/Plugins.md#731-pmix)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2018-2023 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, #
|
||||
@@ -18,6 +18,15 @@ if(BUILD_FAIRMQ)
|
||||
set(Threads_PREFIX "<system>")
|
||||
endif()
|
||||
|
||||
if(BUILD_OFI_TRANSPORT)
|
||||
find_package2(PRIVATE asiofi REQUIRED VERSION 0.5)
|
||||
find_package2(PRIVATE OFI REQUIRED)
|
||||
endif()
|
||||
|
||||
if(BUILD_PMIX_PLUGIN)
|
||||
find_package2(PRIVATE PMIx REQUIRED VERSION 2.1.4)
|
||||
endif()
|
||||
|
||||
if(BUILD_FAIRMQ OR BUILD_TIDY_TOOL)
|
||||
find_package2(PUBLIC FairLogger REQUIRED VERSION 1.6.0)
|
||||
find_package2(PUBLIC Boost REQUIRED VERSION 1.66
|
||||
@@ -25,6 +34,14 @@ if(BUILD_FAIRMQ OR BUILD_TIDY_TOOL)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(BUILD_OFI_TRANSPORT)
|
||||
set(__old ${CMAKE_FIND_PACKAGE_PREFER_CONFIG})
|
||||
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
|
||||
find_package2(PUBLIC asio REQUIRED VERSION 1.18)
|
||||
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ${__old})
|
||||
unset(__old)
|
||||
endif()
|
||||
|
||||
if(BUILD_FAIRMQ)
|
||||
find_package2(PRIVATE ZeroMQ REQUIRED VERSION 4.1.4)
|
||||
if(NOT PicoSHA2_BUNDLED)
|
||||
@@ -70,6 +87,10 @@ if(PROJECT_PACKAGE_DEPENDENCIES)
|
||||
if(NOT FairLogger_PREFIX AND FairLogger_ROOT)
|
||||
set(FairLogger_PREFIX ${FairLogger_ROOT})
|
||||
endif()
|
||||
elseif(${dep} STREQUAL asiofi)
|
||||
if(NOT asiofi_PREFIX AND asiofi_ROOT)
|
||||
set(asiofi_PREFIX ${asiofi_ROOT})
|
||||
endif()
|
||||
elseif(${dep} STREQUAL Boost)
|
||||
if(TARGET Boost::headers)
|
||||
get_target_property(boost_include Boost::headers INTERFACE_INCLUDE_DIRECTORIES)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2018-2023 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, #
|
||||
@@ -34,6 +34,8 @@ endfunction()
|
||||
|
||||
# Configure/Install CMake package
|
||||
macro(install_cmake_package)
|
||||
list(SORT PROJECT_PACKAGE_DEPENDENCIES)
|
||||
list(SORT PROJECT_INTERFACE_PACKAGE_DEPENDENCIES)
|
||||
include(CMakePackageConfigHelpers)
|
||||
set(PACKAGE_INSTALL_DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}-${PROJECT_VERSION}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2018-2023 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, #
|
||||
@@ -114,8 +114,8 @@ endif()
|
||||
set(CMAKE_CONFIGURATION_TYPES "Debug" "Release" "RelWithDebInfo")
|
||||
set(_warnings "-Wshadow -Wall -Wextra -Wpedantic")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-Og -g ${_warnings} ${_sanitizers}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g ${_warnings} -DNDEBUG ${_sanitizers}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g ${_warnings} -DNDEBUG ${_sanitizers}")
|
||||
unset(_warnings)
|
||||
unset(_sanitizers)
|
||||
|
||||
@@ -168,22 +168,9 @@ if(CMAKE_GENERATOR STREQUAL Ninja AND ENABLE_CCACHE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED FAIRMQ_HAS_STD_FILESYSTEM)
|
||||
if( ( CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
|
||||
AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9)
|
||||
OR ( CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
|
||||
AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9))
|
||||
set(FAIRMQ_HAS_STD_FILESYSTEM 0)
|
||||
else()
|
||||
set(FAIRMQ_HAS_STD_FILESYSTEM 1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED FAIRMQ_HAS_STD_PMR)
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# Clang (to be more precise: libc++) currently does not implement <memory_resource>
|
||||
set(FAIRMQ_HAS_STD_PMR 0)
|
||||
else()
|
||||
set(FAIRMQ_HAS_STD_PMR 1)
|
||||
endif()
|
||||
if( CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
|
||||
AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9)
|
||||
set(FAIRMQ_HAS_STD_FILESYSTEM 0)
|
||||
else()
|
||||
set(FAIRMQ_HAS_STD_FILESYSTEM 1)
|
||||
endif()
|
||||
|
@@ -1,5 +1,5 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2018-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
|
||||
# Copyright (C) 2018-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
|
||||
# #
|
||||
# This software is distributed under the terms of the #
|
||||
# GNU Lesser General Public Licence (LGPL) version 3, #
|
||||
@@ -27,6 +27,18 @@ macro(fairmq_summary_components)
|
||||
set(tests_summary "${BRed} NO${CR} (default, enable with ${BMagenta}-DBUILD_TESTING=ON${CR})")
|
||||
endif()
|
||||
message(STATUS " ${BWhite}tests${CR} ${tests_summary}")
|
||||
if(BUILD_OFI_TRANSPORT)
|
||||
set(ofi_summary "${BGreen}YES${CR} EXPERIMENTAL (disable with ${BMagenta}-DBUILD_OFI_TRANSPORT=OFF${CR})")
|
||||
else()
|
||||
set(ofi_summary "${BRed} NO${CR} EXPERIMENTAL (default, enable with ${BMagenta}-DBUILD_OFI_TRANSPORT=ON${CR})")
|
||||
endif()
|
||||
message(STATUS " ${BWhite}ofi_transport${CR} ${ofi_summary}")
|
||||
if(BUILD_PMIX_PLUGIN)
|
||||
set(pmix_summary "${BGreen}YES${CR} EXPERIMENTAL (disable with ${BMagenta}-DBUILD_PMIX_PLUGIN=OFF${CR})")
|
||||
else()
|
||||
set(pmix_summary "${BRed} NO${CR} EXPERIMENTAL (default, enable with ${BMagenta}-DBUILD_PMIX_PLUGIN=ON${CR})")
|
||||
endif()
|
||||
message(STATUS " ${BWhite}pmix_plugin${CR} ${pmix_summary}")
|
||||
if(BUILD_EXAMPLES)
|
||||
set(examples_summary "${BGreen}YES${CR} (default, disable with ${BMagenta}-DBUILD_EXAMPLES=OFF${CR})")
|
||||
else()
|
||||
@@ -93,10 +105,3 @@ macro(fairmq_summary_debug_mode)
|
||||
message(STATUS " ${Cyan}DEBUG MODE${CR} ${BRed}${FAIRMQ_DEBUG_MODE}${CR} (enable with ${BMagenta}-DFAIRMQ_DEBUG_MODE=ON${CR})")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(fairmq_summary_compile_definitions)
|
||||
message(STATUS " ")
|
||||
message(STATUS " ${Cyan}COMPILE DEFINITION VALUE${CR}")
|
||||
message(STATUS " ${BWhite}FAIRMQ_HAS_STD_FILESYSTEM${CR} ${FAIRMQ_HAS_STD_FILESYSTEM} (overridable with ${BMagenta}-DFAIRMQ_HAS_STD_FILESYSTEM=0|1${CR})")
|
||||
message(STATUS " ${BWhite}FAIRMQ_HAS_STD_PMR${CR} ${FAIRMQ_HAS_STD_PMR} (overridable with ${BMagenta}-DFAIRMQ_HAS_STD_PMR=0|1${CR})")
|
||||
endmacro()
|
||||
|
74
cmake/FindPMIx.cmake
Normal file
74
cmake/FindPMIx.cmake
Normal file
@@ -0,0 +1,74 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2019 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" #
|
||||
################################################################################
|
||||
|
||||
|
||||
# The "lib/${CMAKE_LIBRARY_ARCHITECTURE}/pmix" part in all
|
||||
# the PATH_SUFFIXES is here to be able to find Debian's
|
||||
# libpmix-dev package. It installs everything below
|
||||
# /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/pmix
|
||||
|
||||
|
||||
find_path(PMIx_INCLUDE_DIR
|
||||
NAMES pmix.h
|
||||
HINTS ${PMIX_ROOT} $ENV{PMIX_ROOT}
|
||||
PATH_SUFFIXES include lib/${CMAKE_LIBRARY_ARCHITECTURE}/pmix/include
|
||||
)
|
||||
|
||||
find_path(PMIx_LIBRARY_DIR
|
||||
NAMES libpmix.dylib libpmix.so
|
||||
HINTS ${PMIX_ROOT} $ENV{PMIX_ROOT}
|
||||
PATH_SUFFIXES lib lib64 lib/${CMAKE_LIBRARY_ARCHITECTURE}/pmix/lib
|
||||
)
|
||||
|
||||
find_library(PMIx_LIBRARY_SHARED
|
||||
NAMES libpmix.dylib libpmix.so
|
||||
HINTS ${PMIX_ROOT} $ENV{PMIX_ROOT}
|
||||
PATH_SUFFIXES lib lib64 lib/${CMAKE_LIBRARY_ARCHITECTURE}/pmix/lib
|
||||
)
|
||||
|
||||
find_file(PMIx_VERSION_FILE
|
||||
NAMES pmix_version.h
|
||||
HINTS ${PMIX_ROOT} $ENV{PMIX_ROOT}
|
||||
PATH_SUFFIXES include lib/${CMAKE_LIBRARY_ARCHITECTURE}/pmix/include
|
||||
)
|
||||
|
||||
file(READ "${PMIx_VERSION_FILE}" __version_raw)
|
||||
string(REGEX MATCH "#define PMIX_VERSION_MAJOR ([0-9]?)L?"
|
||||
__version_major_raw "${__version_raw}"
|
||||
)
|
||||
set(PMIx_VERSION_MAJOR "${CMAKE_MATCH_1}")
|
||||
|
||||
string(REGEX MATCH "#define PMIX_VERSION_MINOR ([0-9]?)L?"
|
||||
__version_minor_raw "${__version_raw}"
|
||||
)
|
||||
set(PMIx_VERSION_MINOR "${CMAKE_MATCH_1}")
|
||||
|
||||
string(REGEX MATCH "#define PMIX_VERSION_RELEASE ([0-9]?)L?"
|
||||
__version_patch_raw "${__version_raw}"
|
||||
)
|
||||
set(PMIx_VERSION_PATCH "${CMAKE_MATCH_1}")
|
||||
|
||||
set(PMIx_VERSION "${PMIx_VERSION_MAJOR}.${PMIx_VERSION_MINOR}.${PMIx_VERSION_PATCH}")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(PMIx
|
||||
REQUIRED_VARS
|
||||
PMIx_INCLUDE_DIR
|
||||
PMIx_LIBRARY_DIR
|
||||
PMIx_LIBRARY_SHARED
|
||||
|
||||
VERSION_VAR PMIx_VERSION
|
||||
)
|
||||
|
||||
if(NOT TARGET PMIx::libpmix AND PMIx_FOUND)
|
||||
add_library(PMIx::libpmix SHARED IMPORTED)
|
||||
set_target_properties(PMIx::libpmix PROPERTIES
|
||||
IMPORTED_LOCATION ${PMIx_LIBRARY_SHARED}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${PMIx_INCLUDE_DIR}
|
||||
)
|
||||
endif()
|
@@ -1,5 +1,5 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2017-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
|
||||
# Copyright (C) 2017-2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
|
||||
# #
|
||||
# This software is distributed under the terms of the #
|
||||
# GNU Lesser General Public Licence (LGPL) version 3, #
|
||||
@@ -20,7 +20,6 @@
|
||||
# [DEPENDS dep1 [dep2 ...]]
|
||||
# [LINKS linklib1 [linklib2 ...]
|
||||
# [INCLUDES dir1 [dir2 ...]
|
||||
# [ENVIRONMENT var1=op:val1[;var2=op:val2 ...]]
|
||||
# [TIMEOUT seconds]
|
||||
# [RUN_SERIAL ON/OFF])
|
||||
#
|
||||
@@ -57,7 +56,7 @@ function(add_testsuite suitename)
|
||||
cmake_parse_arguments(testsuite
|
||||
""
|
||||
"TIMEOUT;RUN_SERIAL"
|
||||
"SOURCES;LINKS;DEPENDS;INCLUDES;DEFINITIONS;ENVIRONMENT"
|
||||
"SOURCES;LINKS;DEPENDS;INCLUDES;DEFINITIONS"
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
@@ -78,9 +77,6 @@ function(add_testsuite suitename)
|
||||
if(testsuite_DEFINITIONS)
|
||||
target_compile_definitions("${target}" PUBLIC ${testsuite_DEFINITIONS})
|
||||
endif()
|
||||
if(testsuite_ENVIRONMENT AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.22)
|
||||
set(env "ENVIRONMENT_MODIFICATION" ${testsuite_ENVIRONMENT})
|
||||
endif()
|
||||
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
|
||||
fairmq_target_tidy(TARGET ${target})
|
||||
endif()
|
||||
@@ -101,7 +97,6 @@ function(add_testsuite suitename)
|
||||
TEST_PREFIX ${suitename}.
|
||||
PROPERTIES RUN_SERIAL ${testsuite_RUN_SERIAL}
|
||||
TIMEOUT ${testsuite_TIMEOUT}
|
||||
${env}
|
||||
)
|
||||
|
||||
list(APPEND ALL_TEST_TARGETS ${target})
|
||||
|
@@ -17,7 +17,6 @@
|
||||
},
|
||||
{
|
||||
"@type": "Person",
|
||||
"@id": "https://orcid.org/0000-0003-3787-1910",
|
||||
"givenName": "Dennis",
|
||||
"familyName": "Klein"
|
||||
},
|
||||
@@ -28,7 +27,6 @@
|
||||
},
|
||||
{
|
||||
"@type": "Person",
|
||||
"@id": "https://orcid.org/0000-0002-6249-155X",
|
||||
"givenName": "Alexey",
|
||||
"familyName": "Rybalchenko"
|
||||
},
|
||||
@@ -93,7 +91,6 @@
|
||||
},
|
||||
{
|
||||
"@type": "Person",
|
||||
"@id": "https://orcid.org/0000-0002-5321-8404",
|
||||
"givenName": "Christian",
|
||||
"familyName": "Tacke"
|
||||
},
|
||||
|
83
codemeta_update.py
Executable file
83
codemeta_update.py
Executable file
@@ -0,0 +1,83 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
class CodeMetaManipulator(object):
|
||||
def load(self, filename='codemeta.json'):
|
||||
with open(filename, 'rb') as fp:
|
||||
self.data = json.load(fp, object_pairs_hook=OrderedDict)
|
||||
|
||||
@staticmethod
|
||||
def _dict_entry_cmp(dict1, dict2, field):
|
||||
if (field in dict1) and (field in dict2):
|
||||
return dict1[field] == dict2[field]
|
||||
else:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def find_person_entry(cls, person_list, matchdict):
|
||||
for entry in person_list:
|
||||
if cls._dict_entry_cmp(entry, matchdict, 'email'):
|
||||
return entry
|
||||
if cls._dict_entry_cmp(entry, matchdict, 'familyName') \
|
||||
and cls._dict_entry_cmp(entry, matchdict, 'givenName'):
|
||||
return entry
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def update_person_entry(entry, matchdict):
|
||||
if entry is None:
|
||||
entry = OrderedDict()
|
||||
entry['@type'] = 'Person'
|
||||
for field in ('givenName', 'familyName', 'email'):
|
||||
val = matchdict.get(field, None)
|
||||
if val is not None:
|
||||
entry[field] = val
|
||||
return entry
|
||||
|
||||
def handle_person_list_file(self, filename, cm_field_name):
|
||||
fp = open(filename, 'r', encoding='utf8')
|
||||
findregex = re.compile(r'^(?P<familyName>[-\w\s]*[-\w]),\s*'
|
||||
r'(?P<givenName>[-\w\s]*[-\w])\s*'
|
||||
r'(?:<(?P<email>\S+@\S+)>)?$')
|
||||
person_list = self.data.setdefault(cm_field_name, [])
|
||||
for line in fp:
|
||||
line = line.strip()
|
||||
m = findregex.match(line)
|
||||
if m is None:
|
||||
raise RuntimeError("Could not analyze line %r" % line)
|
||||
found_entry = self.find_person_entry(person_list, m.groupdict())
|
||||
entry = self.update_person_entry(found_entry, m.groupdict())
|
||||
if found_entry is None:
|
||||
person_list.append(entry)
|
||||
|
||||
def save(self, filename='codemeta.json'):
|
||||
with open('codemeta.json', 'w', encoding='utf8') as fp:
|
||||
json.dump(self.data, fp, indent=2)
|
||||
fp.write('\n')
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Update codemeta.json')
|
||||
parser.add_argument('--set-version', dest='newversion')
|
||||
args = parser.parse_args()
|
||||
|
||||
cm = CodeMetaManipulator()
|
||||
cm.load()
|
||||
if args.newversion is not None:
|
||||
cm.data['softwareVersion'] = args.newversion
|
||||
cm.handle_person_list_file('AUTHORS', 'author')
|
||||
cm.handle_person_list_file('CONTRIBUTORS', 'contributor')
|
||||
cm.save()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@@ -28,6 +28,7 @@ Here is an overview of the device/channel options and when they are applied:
|
||||
| `init-timeout` | at the end of `fair::mq::State::InitializingDevice` |
|
||||
| `shm-segment-size` | at the end of `fair::mq::State::InitializingDevice` |
|
||||
| `shm-monitor` | at the end of `fair::mq::State::InitializingDevice` |
|
||||
| `ofi-size-hint` | at the end of `fair::mq::State::InitializingDevice` |
|
||||
| `rate` | at the end of `fair::mq::State::InitializingDevice` |
|
||||
| `session` | at the end of `fair::mq::State::InitializingDevice` |
|
||||
| `chan.*` | at the end of `fair::mq::State::InitializingDevice` (channel addresses can be also applied during `fair::mq::State::Binding`/`fair::mq::State::Connecting`) |
|
||||
|
@@ -56,6 +56,8 @@ A more complete example which may serve as a start including example CMake code
|
||||
|
||||
### 7.3.1 PMIx
|
||||
|
||||
The [PMIx](https://pmix.org/) plugin enables launching a FairMQ topology with any PMIx capable launcher, e.g. the [Open Run-Time Environment (ORTE) of OpenMPI](https://www.open-mpi.org/doc/v4.0/man1/mpirun.1.php) or the [Slurm workload manager](https://slurm.schedmd.com/srun.html). This experimental plugin has been last released in v1.4.56 and is removed in v1.5+. For now there are no plans to pick up development of it again.
|
||||
The [PMIx](https://pmix.org/) plugin enables launching a FairMQ topology with any PMIx capable launcher, e.g. the [Open Run-Time Environment (ORTE) of OpenMPI](https://www.open-mpi.org/doc/v4.0/man1/mpirun.1.php) or the [Slurm workload manager](https://slurm.schedmd.com/srun.html). This plugin is not (yet) very mature and serves as a proof of concept at the moment.
|
||||
|
||||
TODO example usage
|
||||
|
||||
← [Back](../README.md)
|
||||
|
@@ -1,9 +1,51 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2014-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
|
||||
# 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" #
|
||||
################################################################################
|
||||
|
||||
add_example(NAME 1-1 DEVICE sampler sink)
|
||||
add_executable(fairmq-ex-1-1-sampler sampler.cxx)
|
||||
target_link_libraries(fairmq-ex-1-1-sampler PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-1-1-sink sink.cxx)
|
||||
target_link_libraries(fairmq-ex-1-1-sink PRIVATE FairMQ)
|
||||
|
||||
add_custom_target(Example11 DEPENDS fairmq-ex-1-1-sampler fairmq-ex-1-1-sink)
|
||||
|
||||
set(EX_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-1-1.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-1-1.sh)
|
||||
|
||||
# test
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-1-1.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-1-1.sh)
|
||||
|
||||
add_test(NAME Example.1-1.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-1-1.sh zeromq)
|
||||
set_tests_properties(Example.1-1.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received: ")
|
||||
|
||||
add_test(NAME Example.1-1.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-1-1.sh shmem)
|
||||
set_tests_properties(Example.1-1.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received: ")
|
||||
|
||||
# install
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
fairmq-ex-1-1-sampler
|
||||
fairmq-ex-1-1-sink
|
||||
|
||||
LIBRARY DESTINATION ${PROJECT_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
# configure run script with different executable paths for build and for install directories
|
||||
set(EX_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-1-1.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-1-1.sh_install)
|
||||
|
||||
install(
|
||||
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-1-1.sh_install
|
||||
DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
RENAME fairmq-start-ex-1-1.sh
|
||||
)
|
||||
|
@@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
|
||||
|
||||
transport="zeromq"
|
||||
@@ -15,7 +13,7 @@ chan="data"
|
||||
chanAddr="/tmp/fmq_$session""_""$chan""_""$transport"
|
||||
|
||||
# setup a trap to kill everything if the test fails/timeouts
|
||||
trap 'set +e; kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; wait $SAMPLER_PID; wait $SINK_PID; rm $chanAddr; exit 0' TERM
|
||||
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; wait $SAMPLER_PID; wait $SINK_PID; rm $chanAddr' TERM
|
||||
|
||||
SAMPLER="fairmq-ex-1-1-sampler"
|
||||
SAMPLER+=" --id sampler1"
|
||||
@@ -24,7 +22,6 @@ SAMPLER+=" --transport $transport"
|
||||
SAMPLER+=" --verbosity veryhigh"
|
||||
SAMPLER+=" --session $session"
|
||||
SAMPLER+=" --shm-segment-size 100000000"
|
||||
SAMPLER+=" --shm-monitor true"
|
||||
SAMPLER+=" --control static --color false"
|
||||
SAMPLER+=" --max-iterations 1"
|
||||
SAMPLER+=" --channel-config name=$chan,type=push,method=bind,address=ipc://$chanAddr,rateLogging=0"
|
||||
@@ -37,7 +34,6 @@ SINK+=" --transport $transport"
|
||||
SINK+=" --verbosity veryhigh"
|
||||
SINK+=" --session $session"
|
||||
SINK+=" --shm-segment-size 100000000"
|
||||
SINK+=" --shm-monitor true"
|
||||
SINK+=" --control static --color false"
|
||||
SINK+=" --max-iterations 1"
|
||||
SINK+=" --channel-config name=$chan,type=pull,method=connect,address=ipc://$chanAddr,rateLogging=0"
|
||||
@@ -48,6 +44,4 @@ SINK_PID=$!
|
||||
wait $SAMPLER_PID
|
||||
wait $SINK_PID
|
||||
|
||||
set +e
|
||||
rm $chanAddr
|
||||
exit 0
|
||||
|
@@ -1,9 +1,64 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2014-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
|
||||
# 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" #
|
||||
################################################################################
|
||||
|
||||
add_example(NAME 1-n-1 DEVICE sampler processor sink CONFIG)
|
||||
add_executable(fairmq-ex-1-n-1-sampler sampler.cxx)
|
||||
target_link_libraries(fairmq-ex-1-n-1-sampler PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-1-n-1-processor processor.cxx)
|
||||
target_link_libraries(fairmq-ex-1-n-1-processor PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-1-n-1-sink sink.cxx)
|
||||
target_link_libraries(fairmq-ex-1-n-1-sink PRIVATE FairMQ)
|
||||
|
||||
add_custom_target(Example1N1 DEPENDS fairmq-ex-1-n-1-sampler fairmq-ex-1-n-1-processor fairmq-ex-1-n-1-sink)
|
||||
|
||||
set(EX_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(EX_CONF_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-1-n-1.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-1-n-1.sh)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ex-1-n-1.json ${CMAKE_CURRENT_BINARY_DIR}/ex-1-n-1.json)
|
||||
|
||||
# test
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-1-n-1.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-1-n-1.sh)
|
||||
|
||||
add_test(NAME Example.1-n-1.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-1-n-1.sh zeromq)
|
||||
set_tests_properties(Example.1-n-1.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received: ")
|
||||
|
||||
add_test(NAME Example.1-n-1.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-1-n-1.sh shmem)
|
||||
set_tests_properties(Example.1-n-1.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received: ")
|
||||
|
||||
# install
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
fairmq-ex-1-n-1-sampler
|
||||
fairmq-ex-1-n-1-processor
|
||||
fairmq-ex-1-n-1-sink
|
||||
|
||||
LIBRARY DESTINATION ${PROJECT_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
# configure run script with different executable paths for build and for install directories
|
||||
set(EX_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR})
|
||||
set(EX_CONF_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_DATADIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-1-n-1.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-1-n-1.sh_install)
|
||||
|
||||
install(
|
||||
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-1-n-1.sh_install
|
||||
DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
RENAME fairmq-start-ex-1-n-1.sh
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/ex-1-n-1.json
|
||||
DESTINATION ${PROJECT_INSTALL_DATADIR}
|
||||
)
|
||||
|
@@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
|
||||
|
||||
transport="zeromq"
|
||||
@@ -17,7 +15,7 @@ chan1Addr="/tmp/fmq_$session""_""$chan1""_""$transport"
|
||||
chan2Addr="/tmp/fmq_$session""_""$chan2""_""$transport"
|
||||
|
||||
# setup a trap to kill everything if the test fails/timeouts
|
||||
trap 'set +e; kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; kill -TERM $PROCESSOR1_PID; kill -TERM $PROCESSOR2_PID; wait $SAMPLER_PID; wait $SINK_PID; wait $PROCESSOR1_PID; wait $PROCESSOR2_PID; rm $chan1Addr; rm $chan2Addr; exit 0' TERM
|
||||
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; kill -TERM $PROCESSOR1_PID; kill -TERM $PROCESSOR2_PID; wait $SAMPLER_PID; wait $SINK_PID; wait $PROCESSOR1_PID; wait $PROCESSOR2_PID; rm $chan1Addr; rm $chan2Addr' TERM
|
||||
|
||||
SAMPLER="fairmq-ex-1-n-1-sampler"
|
||||
SAMPLER+=" --id sampler1"
|
||||
@@ -26,7 +24,6 @@ SAMPLER+=" --verbosity veryhigh"
|
||||
SAMPLER+=" --session $session"
|
||||
SAMPLER+=" --severity debug"
|
||||
SAMPLER+=" --shm-segment-size 100000000"
|
||||
SAMPLER+=" --shm-monitor true"
|
||||
SAMPLER+=" --control static --color false"
|
||||
SAMPLER+=" --max-iterations 2"
|
||||
SAMPLER+=" --channel-config name=$chan1,type=push,method=bind,address=ipc://$chan1Addr,rateLogging=0"
|
||||
@@ -40,7 +37,6 @@ PROCESSOR1+=" --verbosity veryhigh"
|
||||
PROCESSOR1+=" --session $session"
|
||||
PROCESSOR1+=" --severity debug"
|
||||
PROCESSOR1+=" --shm-segment-size 100000000"
|
||||
PROCESSOR1+=" --shm-monitor true"
|
||||
PROCESSOR1+=" --control static --color false"
|
||||
PROCESSOR1+=" --channel-config name=$chan1,type=pull,method=connect,address=ipc://$chan1Addr,rateLogging=0"
|
||||
PROCESSOR1+=" name=$chan2,type=push,method=connect,address=ipc://$chan2Addr,rateLogging=0"
|
||||
@@ -54,7 +50,6 @@ PROCESSOR2+=" --verbosity veryhigh"
|
||||
PROCESSOR2+=" --session $session"
|
||||
PROCESSOR2+=" --severity debug"
|
||||
PROCESSOR2+=" --shm-segment-size 100000000"
|
||||
PROCESSOR2+=" --shm-monitor true"
|
||||
PROCESSOR2+=" --control static --color false"
|
||||
PROCESSOR2+=" --channel-config name=$chan1,type=pull,method=connect,address=ipc://$chan1Addr,rateLogging=0"
|
||||
PROCESSOR2+=" name=$chan2,type=push,method=connect,address=ipc://$chan2Addr,rateLogging=0"
|
||||
@@ -68,7 +63,6 @@ SINK+=" --verbosity veryhigh"
|
||||
SINK+=" --session $session"
|
||||
SINK+=" --severity debug"
|
||||
SINK+=" --shm-segment-size 100000000"
|
||||
SINK+=" --shm-monitor true"
|
||||
SINK+=" --control static --color false"
|
||||
SINK+=" --max-iterations 2"
|
||||
SINK+=" --channel-config name=$chan2,type=pull,method=bind,address=ipc://$chan2Addr,rateLogging=0"
|
||||
@@ -87,6 +81,4 @@ kill -SIGINT $PROCESSOR2_PID
|
||||
wait $PROCESSOR1_PID
|
||||
wait $PROCESSOR2_PID
|
||||
|
||||
set +e
|
||||
rm $chan1Addr; rm $chan2Addr
|
||||
exit 0
|
||||
|
@@ -1,145 +1,15 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2018-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
|
||||
# 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" #
|
||||
################################################################################
|
||||
|
||||
set(exe_prefix "fairmq-ex")
|
||||
set(script_prefix "fairmq-start-ex")
|
||||
set(test_script_prefix "test-ex")
|
||||
set(testsuite "Example")
|
||||
set(transports "zeromq" "shmem")
|
||||
|
||||
function(add_example)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 ARG
|
||||
"CONFIG;NO_TRANSPORT;NO_TEST"
|
||||
"NAME"
|
||||
"DEVICE;VARIANT;TRANSPORT;SCRIPT"
|
||||
)
|
||||
|
||||
if(ARG_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected unparsed arguments: ${ARG_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
|
||||
if(ARG_NAME)
|
||||
set(name ${ARG_NAME})
|
||||
else()
|
||||
message(FATAL_ERROR "NAME arg is required")
|
||||
endif()
|
||||
|
||||
if(ENABLE_SANITIZER_LEAK AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.22)
|
||||
get_filename_component(lsan_supps "${CMAKE_SOURCE_DIR}/test/leak_sanitizer_suppressions.txt" ABSOLUTE)
|
||||
set(lsan_options "LSAN_OPTIONS=set:suppressions=${lsan_supps}")
|
||||
endif()
|
||||
|
||||
if(ARG_DEVICE)
|
||||
set(exe_targets)
|
||||
foreach(device IN LISTS ARG_DEVICE)
|
||||
set(exe "${exe_prefix}-${name}-${device}")
|
||||
list(APPEND exe_targets ${exe})
|
||||
add_executable(${exe} "${device}.cxx")
|
||||
target_link_libraries(${exe} PRIVATE FairMQ)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(ARG_TRANSPORT)
|
||||
set(transports ${ARG_TRANSPORT})
|
||||
endif()
|
||||
|
||||
if(ARG_SCRIPT)
|
||||
set(scripts ${ARG_SCRIPT})
|
||||
else()
|
||||
set(scripts ${ARG_NAME})
|
||||
endif()
|
||||
|
||||
set(EX_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
if(ARG_CONFIG)
|
||||
set(EX_CONF_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq)
|
||||
foreach(script IN LISTS scripts)
|
||||
set(script_file "${script_prefix}-${script}.sh")
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${script_file}.in" "${CMAKE_CURRENT_BINARY_DIR}/${script_file}")
|
||||
endforeach()
|
||||
|
||||
if(ARG_CONFIG)
|
||||
set(config "ex-${name}.json")
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${config}" "${CMAKE_CURRENT_BINARY_DIR}/${config}")
|
||||
endif()
|
||||
|
||||
# test
|
||||
if(NOT ARG_NO_TEST)
|
||||
set(test_script "${test_script_prefix}-${name}.sh")
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${test_script}.in" "${CMAKE_CURRENT_BINARY_DIR}/${test_script}")
|
||||
|
||||
if(ARG_NO_TRANSPORT)
|
||||
set(test "${testsuite}.${name}.${transport}")
|
||||
add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${test_script} ${transport})
|
||||
set_tests_properties(${test} PROPERTIES TIMEOUT "30")
|
||||
if(lsan_options)
|
||||
set_tests_properties(${test} PROPERTIES ENVIRONMENT_MODIFICATION ${lsan_options})
|
||||
endif()
|
||||
else()
|
||||
foreach(transport IN LISTS transports)
|
||||
if(ARG_VARIANT)
|
||||
foreach(variant IN LISTS ARG_VARIANT)
|
||||
set(test "${testsuite}.${name}.${variant}.${transport}")
|
||||
add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${test_script} ${transport} ${variant})
|
||||
set_tests_properties(${test} PROPERTIES TIMEOUT "30")
|
||||
if(lsan_options)
|
||||
set_tests_properties(${test} PROPERTIES ENVIRONMENT_MODIFICATION ${lsan_options})
|
||||
endif()
|
||||
endforeach()
|
||||
else()
|
||||
set(test "${testsuite}.${name}.${transport}")
|
||||
add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${test_script} ${transport})
|
||||
set_tests_properties(${test} PROPERTIES TIMEOUT "30")
|
||||
if(lsan_options)
|
||||
set_tests_properties(${test} PROPERTIES ENVIRONMENT_MODIFICATION ${lsan_options})
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# install
|
||||
install(
|
||||
TARGETS ${exe_targets}
|
||||
LIBRARY DESTINATION ${PROJECT_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
# configure run script with different executable paths for build and for install directories
|
||||
set(EX_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR})
|
||||
if(ARG_CONFIG)
|
||||
set(EX_CONF_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_DATADIR})
|
||||
endif()
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR}/fairmq)
|
||||
foreach(script IN LISTS scripts)
|
||||
set(script_file "${script_prefix}-${script}.sh")
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${script_file}.in" "${CMAKE_CURRENT_BINARY_DIR}/${script_file}_install")
|
||||
install(
|
||||
PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/${script_file}_install"
|
||||
DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
RENAME ${script_file}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
if(ARG_CONFIG)
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/${config}
|
||||
DESTINATION ${PROJECT_INSTALL_DATADIR}
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
add_subdirectory(1-1)
|
||||
add_subdirectory(1-n-1)
|
||||
add_subdirectory(builtin-devices)
|
||||
add_subdirectory(copypush)
|
||||
add_subdirectory(custom-controller)
|
||||
add_subdirectory(dds)
|
||||
add_subdirectory(multipart)
|
||||
add_subdirectory(multiple-channels)
|
||||
|
@@ -1,11 +1,40 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2014-2023 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" #
|
||||
################################################################################
|
||||
################################################################################
|
||||
# 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" #
|
||||
################################################################################
|
||||
|
||||
add_example(NAME builtin-devices
|
||||
VARIANT single_msg multipart
|
||||
set(EX_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-builtin-devices.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-builtin-devices.sh)
|
||||
|
||||
# test
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-builtin-devices.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-builtin-devices.sh)
|
||||
|
||||
add_test(NAME Example.BuiltinDevices.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-builtin-devices.sh zeromq)
|
||||
set_tests_properties(Example.BuiltinDevices.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Configured maximum number of iterations reached")
|
||||
|
||||
add_test(NAME Example.BuiltinDevices.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-builtin-devices.sh shmem)
|
||||
set_tests_properties(Example.BuiltinDevices.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Configured maximum number of iterations reached")
|
||||
|
||||
add_test(NAME Example.BuiltinDevices.multipart.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-builtin-devices.sh zeromq true 2)
|
||||
set_tests_properties(Example.BuiltinDevices.multipart.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Configured maximum number of iterations reached")
|
||||
|
||||
add_test(NAME Example.BuiltinDevices.multipart.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-builtin-devices.sh shmem true 2)
|
||||
set_tests_properties(Example.BuiltinDevices.multipart.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Configured maximum number of iterations reached")
|
||||
|
||||
# install
|
||||
|
||||
# configure run script with different executable paths for build and for install directories
|
||||
set(EX_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-builtin-devices.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-builtin-devices.sh_install)
|
||||
|
||||
install(
|
||||
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-builtin-devices.sh_install
|
||||
DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
RENAME fairmq-start-ex-builtin-devices.sh
|
||||
)
|
||||
|
@@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
|
||||
|
||||
transport="zeromq"
|
||||
@@ -12,9 +10,12 @@ if [[ $1 =~ ^[a-z]+$ ]]; then
|
||||
transport=$1
|
||||
fi
|
||||
|
||||
if [[ $2 =~ ^multipart$ ]]; then
|
||||
multipart="true"
|
||||
numParts=2
|
||||
if [[ $2 =~ ^[a-z]+$ ]]; then
|
||||
multipart=$2
|
||||
fi
|
||||
|
||||
if [[ $3 =~ ^[0-9]+$ ]]; then
|
||||
numParts=$3
|
||||
fi
|
||||
|
||||
session="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
|
||||
@@ -32,7 +33,7 @@ chan4Addr="/tmp/fmq_$session""_""$chan4""_""$transport"
|
||||
chan5Addr="/tmp/fmq_$session""_""$chan5""_""$transport"
|
||||
|
||||
# setup a trap to kill everything if the test fails/timeouts
|
||||
trap 'set +e; kill -TERM $SAMPLER_PID; kill -TERM $SPLITTER_PID; kill -TERM $PROXY1_PID; kill -TERM $PROXY2_PID; kill -TERM $MERGER_PID; kill -TERM $MULTIPLIER_PID; kill -TERM $SINK_PID; rm $chan1Addr; rm $chan2Addr1; rm $chan2Addr2; rm $chan3Addr1; rm $chan3Addr2; rm $chan4Addr; rm $chan5Addr; exit 0' TERM
|
||||
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SPLITTER_PID; kill -TERM $PROXY1_PID; kill -TERM $PROXY2_PID; kill -TERM $MERGER_PID; kill -TERM $MULTIPLIER_PID; kill -TERM $SINK_PID; rm $chan1Addr; rm $chan2Addr1; rm $chan2Addr2; rm $chan3Addr1; rm $chan3Addr2; rm $chan4Addr; rm $chan5Addr' TERM
|
||||
|
||||
SAMPLER="fairmq-bsampler"
|
||||
SAMPLER+=" --id bsampler1"
|
||||
@@ -42,7 +43,6 @@ SAMPLER+=" --color false"
|
||||
SAMPLER+=" --control static"
|
||||
SAMPLER+=" --verbosity veryhigh"
|
||||
SAMPLER+=" --shm-segment-size 100000000"
|
||||
SAMPLER+=" --shm-monitor true"
|
||||
SAMPLER+=" --severity debug"
|
||||
SAMPLER+=" --msg-size 100000"
|
||||
SAMPLER+=" --multipart $multipart"
|
||||
@@ -62,7 +62,6 @@ SPLITTER+=" --color false"
|
||||
SPLITTER+=" --control static"
|
||||
SPLITTER+=" --verbosity veryhigh"
|
||||
SPLITTER+=" --shm-segment-size 100000000"
|
||||
SPLITTER+=" --shm-monitor true"
|
||||
SPLITTER+=" --multipart $multipart"
|
||||
SPLITTER+=" --in-channel $chan1"
|
||||
SPLITTER+=" --out-channel $chan2"
|
||||
@@ -79,7 +78,6 @@ PROXY1+=" --color false"
|
||||
PROXY1+=" --control static"
|
||||
PROXY1+=" --verbosity veryhigh"
|
||||
PROXY1+=" --shm-segment-size 100000000"
|
||||
PROXY1+=" --shm-monitor true"
|
||||
PROXY1+=" --multipart $multipart"
|
||||
PROXY1+=" --in-channel $chan2"
|
||||
PROXY1+=" --out-channel $chan3"
|
||||
@@ -96,7 +94,6 @@ PROXY2+=" --color false"
|
||||
PROXY2+=" --control static"
|
||||
PROXY2+=" --verbosity veryhigh"
|
||||
PROXY2+=" --shm-segment-size 100000000"
|
||||
PROXY2+=" --shm-monitor true"
|
||||
PROXY2+=" --multipart $multipart"
|
||||
PROXY2+=" --in-channel $chan2"
|
||||
PROXY2+=" --out-channel $chan3"
|
||||
@@ -113,7 +110,6 @@ MERGER+=" --color false"
|
||||
MERGER+=" --control static"
|
||||
MERGER+=" --verbosity veryhigh"
|
||||
MERGER+=" --shm-segment-size 100000000"
|
||||
MERGER+=" --shm-monitor true"
|
||||
MERGER+=" --multipart $multipart"
|
||||
MERGER+=" --in-channel $chan3"
|
||||
MERGER+=" --out-channel $chan4"
|
||||
@@ -130,7 +126,6 @@ MULTIPLIER+=" --color false"
|
||||
MULTIPLIER+=" --control static"
|
||||
MULTIPLIER+=" --verbosity veryhigh"
|
||||
MULTIPLIER+=" --shm-segment-size 100000000"
|
||||
MULTIPLIER+=" --shm-monitor true"
|
||||
MULTIPLIER+=" --multipart $multipart"
|
||||
MULTIPLIER+=" --in-channel $chan4"
|
||||
MULTIPLIER+=" --out-channel $chan5"
|
||||
@@ -148,7 +143,6 @@ SINK+=" --control static"
|
||||
SINK+=" --verbosity veryhigh"
|
||||
SINK+=" --severity debug"
|
||||
SINK+=" --multipart $multipart"
|
||||
SINK+=" --shm-monitor true"
|
||||
SINK+=" --max-iterations 2"
|
||||
SINK+=" --in-channel $chan5"
|
||||
SINK+=" --channel-config name=$chan5,type=pull,method=bind,sndBufSize=50,rcvBufSize=50,address=ipc://$chan5Addr"
|
||||
@@ -171,6 +165,4 @@ wait $PROXY2_PID
|
||||
wait $MERGER_PID
|
||||
wait $MULTIPLIER_PID
|
||||
|
||||
set +e
|
||||
rm $chan1Addr; rm $chan2Addr1; rm $chan2Addr2; rm $chan3Addr1; rm $chan3Addr2; rm $chan4Addr; rm $chan5Addr
|
||||
exit 0
|
||||
|
@@ -1,11 +1,52 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2014-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
|
||||
# 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" #
|
||||
################################################################################
|
||||
|
||||
add_example(NAME copypush
|
||||
DEVICE sampler sink
|
||||
|
||||
add_executable(fairmq-ex-copypush-sampler sampler.cxx)
|
||||
target_link_libraries(fairmq-ex-copypush-sampler PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-copypush-sink sink.cxx)
|
||||
target_link_libraries(fairmq-ex-copypush-sink PRIVATE FairMQ)
|
||||
|
||||
add_custom_target(ExampleCopyPush DEPENDS fairmq-ex-copypush-sampler fairmq-ex-copypush-sink)
|
||||
|
||||
set(EX_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-copypush.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-copypush.sh)
|
||||
|
||||
# test
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-copypush.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-copypush.sh)
|
||||
|
||||
add_test(NAME Example.CopyPush.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-copypush.sh zeromq)
|
||||
set_tests_properties(Example.CopyPush.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received message: ")
|
||||
|
||||
add_test(NAME Example.CopyPush.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-copypush.sh shmem)
|
||||
set_tests_properties(Example.CopyPush.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received message: ")
|
||||
|
||||
# install
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
fairmq-ex-copypush-sampler
|
||||
fairmq-ex-copypush-sink
|
||||
|
||||
LIBRARY DESTINATION ${PROJECT_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
# configure run script with different executable paths for build and for install directories
|
||||
set(EX_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-copypush.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-copypush.sh_install)
|
||||
|
||||
install(
|
||||
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-copypush.sh_install
|
||||
DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
RENAME fairmq-start-ex-copypush.sh
|
||||
)
|
||||
|
@@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
|
||||
|
||||
transport="zeromq"
|
||||
@@ -16,7 +14,7 @@ chanAddr1="/tmp/fmq_$session""_""$chan""_1""_""$transport"
|
||||
chanAddr2="/tmp/fmq_$session""_""$chan""_2""_""$transport"
|
||||
|
||||
# setup a trap to kill everything if the test fails/timeouts
|
||||
trap 'set +e; kill -TERM $SAMPLER_PID; kill -TERM $SINK1_PID; kill -TERM $SINK2_PID; wait $SAMPLER_PID; wait $SINK1_PID; wait $SINK2_PID; rm $chanAddr1; rm $chanAddr2; exit 0' TERM
|
||||
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK1_PID; kill -TERM $SINK2_PID; wait $SAMPLER_PID; wait $SINK1_PID; wait $SINK2_PID; rm $chanAddr1; rm $chanAddr2' TERM
|
||||
|
||||
SAMPLER="fairmq-ex-copypush-sampler"
|
||||
SAMPLER+=" --id sampler1"
|
||||
@@ -24,7 +22,6 @@ SAMPLER+=" --transport $transport"
|
||||
SAMPLER+=" --verbosity veryhigh"
|
||||
SAMPLER+=" --severity debug"
|
||||
SAMPLER+=" --shm-segment-size 100000000"
|
||||
SAMPLER+=" --shm-monitor true"
|
||||
SAMPLER+=" --session $session"
|
||||
SAMPLER+=" --control static --color false"
|
||||
SAMPLER+=" --max-iterations 1"
|
||||
@@ -38,7 +35,6 @@ SINK1+=" --transport $transport"
|
||||
SINK1+=" --verbosity veryhigh"
|
||||
SINK1+=" --severity debug"
|
||||
SINK1+=" --shm-segment-size 100000000"
|
||||
SINK1+=" --shm-monitor true"
|
||||
SINK1+=" --session $session"
|
||||
SINK1+=" --control static --color false"
|
||||
SINK1+=" --max-iterations 1"
|
||||
@@ -52,7 +48,6 @@ SINK2+=" --transport $transport"
|
||||
SINK2+=" --verbosity veryhigh"
|
||||
SINK2+=" --severity debug"
|
||||
SINK2+=" --shm-segment-size 100000000"
|
||||
SINK2+=" --shm-monitor true"
|
||||
SINK2+=" --session $session"
|
||||
SINK2+=" --control static --color false"
|
||||
SINK2+=" --max-iterations 1"
|
||||
@@ -65,6 +60,4 @@ wait $SAMPLER_PID
|
||||
wait $SINK1_PID
|
||||
wait $SINK2_PID
|
||||
|
||||
set +e
|
||||
rm $chanAddr1; rm $chanAddr2
|
||||
exit 0
|
||||
|
@@ -1,20 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2022-2023 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(name "custom-controller")
|
||||
set(exe "${exe_prefix}-${name}")
|
||||
add_executable(${exe} main.cxx)
|
||||
target_link_libraries(${exe} PRIVATE FairMQ)
|
||||
set_target_properties(${exe} PROPERTIES ENABLE_EXPORTS ON)
|
||||
|
||||
set(test "${testsuite}.${name}")
|
||||
add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${exe})
|
||||
set_tests_properties(${test} PROPERTIES TIMEOUT 30)
|
||||
if(lsan_options)
|
||||
set_tests_properties(${test} PROPERTIES ENVIRONMENT_MODIFICATION ${lsan_options})
|
||||
endif()
|
@@ -1,97 +0,0 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2022 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_EXAMPLE_CUSTOM_CONTROLLER_PLUGIN
|
||||
#define FAIR_MQ_EXAMPLE_CUSTOM_CONTROLLER_PLUGIN
|
||||
|
||||
#include <fairmq/Plugin.h>
|
||||
#include <utility> // for std::forward
|
||||
|
||||
namespace example {
|
||||
|
||||
struct MyController : fair::mq::Plugin // NOLINT
|
||||
{
|
||||
template<typename... Args>
|
||||
MyController(Args&&... args)
|
||||
: Plugin(std::forward<Args>(args)...)
|
||||
{
|
||||
TakeDeviceControl();
|
||||
|
||||
SubscribeToDeviceStateChange([this](auto state) {
|
||||
auto shutdown = GetProperty<bool>("please-shut-me-down", false);
|
||||
try {
|
||||
switch (state) {
|
||||
case DeviceState::Idle: {
|
||||
ChangeDeviceState(shutdown ? DeviceStateTransition::End
|
||||
: DeviceStateTransition::InitDevice);
|
||||
break;
|
||||
}
|
||||
case DeviceState::InitializingDevice: {
|
||||
ChangeDeviceState(DeviceStateTransition::CompleteInit);
|
||||
break;
|
||||
}
|
||||
case DeviceState::Initialized: {
|
||||
ChangeDeviceState(DeviceStateTransition::Bind);
|
||||
break;
|
||||
}
|
||||
case DeviceState::Bound: {
|
||||
ChangeDeviceState(DeviceStateTransition::Connect);
|
||||
break;
|
||||
}
|
||||
case DeviceState::DeviceReady: {
|
||||
ChangeDeviceState(shutdown ? DeviceStateTransition::ResetDevice
|
||||
: DeviceStateTransition::InitTask);
|
||||
break;
|
||||
}
|
||||
case DeviceState::Ready: {
|
||||
ChangeDeviceState(shutdown ? DeviceStateTransition::ResetTask
|
||||
: DeviceStateTransition::Run);
|
||||
break;
|
||||
}
|
||||
case DeviceState::Running: {
|
||||
ChangeDeviceState(DeviceStateTransition::Stop);
|
||||
break;
|
||||
}
|
||||
case DeviceState::Exiting: {
|
||||
ReleaseDeviceControl();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (fair::mq::PluginServices::DeviceControlError const&) {
|
||||
// this means we do not have device control
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
~MyController() override { ReleaseDeviceControl(); }
|
||||
};
|
||||
|
||||
// auto MyControllerProgramOptions() -> fair::mq::Plugin::ProgOptions
|
||||
// {
|
||||
// auto plugin_options = boost::program_options::options_description{"MyController Plugin"};
|
||||
// plugin_options.add_options()
|
||||
// ("custom-dummy-option", boost::program_options::value<std::string>(), "Cool custom option.")
|
||||
// ("custom-dummy-option2", boost::program_options::value<std::string>(), "Another one.");
|
||||
// return plugin_options;
|
||||
// }
|
||||
|
||||
} // namespace example
|
||||
|
||||
REGISTER_FAIRMQ_PLUGIN(example::MyController, // Class name
|
||||
mycontroller, // Plugin name (string, lower case chars only)
|
||||
(fair::mq::Plugin::Version{0, 42, 0}), // Version
|
||||
"Mr. Dummy <dummy@test.net>", // Maintainer
|
||||
"https://git.test.net/mycontroller.git", // Homepage
|
||||
// example::MyControllerProgramOptions // Free
|
||||
// function which declares custom
|
||||
// program options for the plugin
|
||||
fair::mq::Plugin::NoProgramOptions)
|
||||
|
||||
#endif /* FAIR_MQ_EXAMPLE_CUSTOM_CONTROLLER_PLUGIN */
|
@@ -1,21 +0,0 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2022 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_EXAMPLE_CUSTOM_CONTROLLER_DEVICE
|
||||
#define FAIR_MQ_EXAMPLE_CUSTOM_CONTROLLER_DEVICE
|
||||
|
||||
#include <fairmq/Device.h>
|
||||
|
||||
namespace example {
|
||||
|
||||
using MyDevice = fair::mq::Device;
|
||||
|
||||
} // namespace example
|
||||
|
||||
#endif /* FAIR_MQ_EXAMPLE_CUSTOM_CONTROLLER_DEVICE */
|
||||
|
@@ -1,46 +0,0 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2022 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 "MyController.h"
|
||||
#include "MyDevice.h"
|
||||
|
||||
#include <chrono> // for std::chrono_literals
|
||||
#include <fairlogger/Logger.h>
|
||||
#include <fairmq/DeviceRunner.h>
|
||||
#include <memory> // for std::make_unique
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
using namespace fair::mq;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
DeviceRunner runner(argc, argv);
|
||||
|
||||
runner.AddHook<hooks::LoadPlugins>([](DeviceRunner& r) {
|
||||
r.fPluginManager.LoadPlugin("s:mycontroller");
|
||||
// 's:' stands for static because the plugin is compiled into the executable
|
||||
// 'mycontroller' is the plugin name passed as second arg to REGISTER_FAIRMQ_PLUGIN
|
||||
});
|
||||
|
||||
fair::Logger::SetConsoleSeverity(fair::Severity::debug);
|
||||
|
||||
runner.AddHook<hooks::InstantiateDevice>([](DeviceRunner& r) {
|
||||
r.fConfig.SetProperty<int>("catch-signals", 0);
|
||||
r.fConfig.SetProperty<bool>("please-shut-me-down", false);
|
||||
r.fDevice = std::make_unique<example::MyDevice>(r.fConfig);
|
||||
|
||||
r.fDevice->SubscribeToStateChange("example", [&r](auto state) {
|
||||
if (state == State::Running) {
|
||||
r.fDevice->WaitFor(3s);
|
||||
r.fConfig.SetProperty<bool>("please-shut-me-down", true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return runner.RunWithExceptionHandlers();
|
||||
}
|
@@ -1,11 +1,56 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2014-2023 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" #
|
||||
################################################################################
|
||||
################################################################################
|
||||
# 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" #
|
||||
################################################################################
|
||||
|
||||
add_example(NAME multipart
|
||||
DEVICE sampler sink
|
||||
add_executable(fairmq-ex-multipart-sampler sampler.cxx)
|
||||
target_link_libraries(fairmq-ex-multipart-sampler PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-multipart-sink sink.cxx)
|
||||
target_link_libraries(fairmq-ex-multipart-sink PRIVATE FairMQ)
|
||||
|
||||
add_custom_target(ExampleMultipart DEPENDS fairmq-ex-multipart-sampler fairmq-ex-multipart-sink)
|
||||
|
||||
set(EX_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-multipart.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-multipart.sh)
|
||||
|
||||
# test
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-multipart.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multipart.sh)
|
||||
|
||||
add_test(NAME Example.Multipart.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multipart.sh zeromq)
|
||||
set_tests_properties(Example.Multipart.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received message with 7 parts")
|
||||
|
||||
add_test(NAME Example.Multipart.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multipart.sh shmem)
|
||||
set_tests_properties(Example.Multipart.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received message with 7 parts")
|
||||
|
||||
if(BUILD_OFI_TRANSPORT)
|
||||
add_test(NAME Example.Multipart.ofi COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multipart.sh ofi)
|
||||
set_tests_properties(Example.Multipart.ofi PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received message with 7 parts")
|
||||
endif()
|
||||
|
||||
# install
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
fairmq-ex-multipart-sampler
|
||||
fairmq-ex-multipart-sink
|
||||
|
||||
LIBRARY DESTINATION ${PROJECT_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
# configure run script with different executable paths for build and for install directories
|
||||
set(EX_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-multipart.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-multipart.sh_install)
|
||||
|
||||
install(
|
||||
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-multipart.sh_install
|
||||
DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
RENAME fairmq-start-ex-multipart.sh
|
||||
)
|
||||
|
@@ -24,10 +24,6 @@ struct Sink : fair::mq::Device
|
||||
{
|
||||
LOG(info) << "Received message with " << parts.Size() << " parts";
|
||||
|
||||
if (parts.Size() != 7) {
|
||||
throw std::runtime_error("Number of received parts != 7");
|
||||
}
|
||||
|
||||
example_multipart::Header header;
|
||||
header.stopFlag = (static_cast<example_multipart::Header*>(parts.At(0)->GetData()))->stopFlag;
|
||||
|
||||
|
@@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
|
||||
|
||||
transport="zeromq"
|
||||
@@ -14,10 +12,14 @@ session="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
|
||||
chan="data"
|
||||
chanAddr=""
|
||||
chanIpcFile="/tmp/fmq_$session""_""$chan""_""$transport"
|
||||
chanAddr="ipc://""$chanIpcFile"
|
||||
if [ $transport = "ofi" ]; then
|
||||
chanAddr="tcp://127.0.0.1:5656"
|
||||
else
|
||||
chanAddr="ipc://""$chanIpcFile"
|
||||
fi
|
||||
|
||||
# setup a trap to kill everything if the test fails/timeouts
|
||||
trap 'set +e; kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; wait $SAMPLER_PID; wait $SINK_PID; rm $chanIpcFile; exit 0' TERM
|
||||
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; wait $SAMPLER_PID; wait $SINK_PID; rm $chanIpcFile' TERM
|
||||
|
||||
SAMPLER="fairmq-ex-multipart-sampler"
|
||||
SAMPLER+=" --id sampler1"
|
||||
@@ -25,7 +27,6 @@ SAMPLER+=" --transport $transport"
|
||||
SAMPLER+=" --verbosity veryhigh"
|
||||
SAMPLER+=" --session $session"
|
||||
SAMPLER+=" --shm-segment-size 100000000"
|
||||
SAMPLER+=" --shm-monitor true"
|
||||
SAMPLER+=" --max-iterations 1"
|
||||
SAMPLER+=" --control static --color false"
|
||||
SAMPLER+=" --channel-config name=$chan,type=pair,method=connect,rateLogging=0,address=$chanAddr,linger=1000"
|
||||
@@ -38,7 +39,6 @@ SINK+=" --transport $transport"
|
||||
SINK+=" --verbosity veryhigh"
|
||||
SINK+=" --session $session"
|
||||
SINK+=" --shm-segment-size 100000000"
|
||||
SINK+=" --shm-monitor true"
|
||||
SINK+=" --control static --color false"
|
||||
SINK+=" --channel-config name=$chan,type=pair,method=bind,rateLogging=0,address=$chanAddr"
|
||||
@CMAKE_CURRENT_BINARY_DIR@/$SINK &
|
||||
@@ -47,6 +47,4 @@ SINK_PID=$!
|
||||
wait $SAMPLER_PID
|
||||
wait $SINK_PID
|
||||
|
||||
set +e
|
||||
rm $chanIpcFile
|
||||
exit 0
|
||||
|
@@ -1,12 +1,52 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2014-2023 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" #
|
||||
################################################################################
|
||||
################################################################################
|
||||
# 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" #
|
||||
################################################################################
|
||||
|
||||
add_example(NAME multiple-channels
|
||||
DEVICE sampler broadcaster sink
|
||||
TRANSPORT zeromq
|
||||
add_executable(fairmq-ex-multiple-channels-sampler sampler.cxx)
|
||||
target_link_libraries(fairmq-ex-multiple-channels-sampler PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-multiple-channels-broadcaster broadcaster.cxx)
|
||||
target_link_libraries(fairmq-ex-multiple-channels-broadcaster PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-multiple-channels-sink sink.cxx)
|
||||
target_link_libraries(fairmq-ex-multiple-channels-sink PRIVATE FairMQ)
|
||||
|
||||
add_custom_target(ExampleMultipleChannels DEPENDS fairmq-ex-multiple-channels-sampler fairmq-ex-multiple-channels-broadcaster fairmq-ex-multiple-channels-sink)
|
||||
|
||||
set(EX_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-multiple-channels.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-multiple-channels.sh)
|
||||
|
||||
# test
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-multiple-channels.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multiple-channels.sh)
|
||||
|
||||
add_test(NAME Example.MultipleChannels.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multiple-channels.sh zeromq)
|
||||
set_tests_properties(Example.MultipleChannels.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received messages from both sources.")
|
||||
|
||||
# install
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
fairmq-ex-multiple-channels-sampler
|
||||
fairmq-ex-multiple-channels-broadcaster
|
||||
fairmq-ex-multiple-channels-sink
|
||||
|
||||
LIBRARY DESTINATION ${PROJECT_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
# configure run script with different executable paths for build and for install directories
|
||||
set(EX_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-multiple-channels.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-multiple-channels.sh_install)
|
||||
|
||||
install(
|
||||
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-multiple-channels.sh_install
|
||||
DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
RENAME fairmq-start-ex-multiple-channels.sh
|
||||
)
|
||||
|
@@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
|
||||
|
||||
transport="zeromq"
|
||||
@@ -17,7 +15,7 @@ dataChanAddr="/tmp/fmq_$session""_""$dataChan""_""$transport"
|
||||
broadcastChanAddr="/tmp/fmq_$session""_""$broadcastChan""_""$transport"
|
||||
|
||||
# setup a trap to kill everything if the test fails/timeouts
|
||||
trap 'set +e; kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; kill -TERM $BROADCASTER_PID; wait $SAMPLER_PID; wait $SINK_PID; wait $BROADCASTER_PID; rm $dataChanAddr; rm $broadcastChanAddr; exit 0' TERM
|
||||
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; kill -TERM $BROADCASTER_PID; wait $SAMPLER_PID; wait $SINK_PID; wait $BROADCASTER_PID; rm $dataChanAddr; rm $broadcastChanAddr' TERM
|
||||
|
||||
SINK="fairmq-ex-multiple-channels-sink"
|
||||
SINK+=" --id sink1"
|
||||
@@ -25,7 +23,6 @@ SINK+=" --session $session"
|
||||
SINK+=" --transport $transport"
|
||||
SINK+=" --verbosity veryhigh --severity debug"
|
||||
SINK+=" --shm-segment-size 100000000"
|
||||
SINK+=" --shm-monitor true"
|
||||
SINK+=" --max-iterations 1"
|
||||
SINK+=" --control static --color false"
|
||||
SINK+=" --channel-config name=$dataChan,type=pull,method=connect,rateLogging=0,address=ipc://$dataChanAddr"
|
||||
@@ -41,7 +38,6 @@ SAMPLER+=" --session $session"
|
||||
SAMPLER+=" --transport $transport"
|
||||
SAMPLER+=" --verbosity veryhigh --severity debug"
|
||||
SAMPLER+=" --shm-segment-size 100000000"
|
||||
SAMPLER+=" --shm-monitor true"
|
||||
SAMPLER+=" --max-iterations 1"
|
||||
SAMPLER+=" --control static --color false"
|
||||
SAMPLER+=" --channel-config name=$dataChan,type=push,method=bind,rateLogging=0,address=ipc://$dataChanAddr"
|
||||
@@ -55,7 +51,6 @@ BROADCASTER+=" --session $session"
|
||||
BROADCASTER+=" --transport $transport"
|
||||
BROADCASTER+=" --verbosity veryhigh --severity debug"
|
||||
BROADCASTER+=" --shm-segment-size 100000000"
|
||||
BROADCASTER+=" --shm-monitor true"
|
||||
BROADCASTER+=" --control static --color false"
|
||||
BROADCASTER+=" --channel-config name=$broadcastChan,type=pub,method=bind,rateLogging=0,address=ipc://$broadcastChanAddr"
|
||||
@CMAKE_CURRENT_BINARY_DIR@/$BROADCASTER &
|
||||
@@ -70,6 +65,4 @@ kill -SIGINT $BROADCASTER_PID
|
||||
# wait for broadcaster to finish
|
||||
wait $BROADCASTER_PID
|
||||
|
||||
set +e
|
||||
rm $dataChanAddr; rm $broadcastChanAddr
|
||||
exit 0
|
||||
|
@@ -1,12 +1,51 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2014-2023 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" #
|
||||
################################################################################
|
||||
################################################################################
|
||||
# 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" #
|
||||
################################################################################
|
||||
|
||||
add_example(NAME multiple-transports
|
||||
DEVICE sampler1 sampler2 sink
|
||||
NO_TRANSPORT
|
||||
add_executable(fairmq-ex-multiple-transports-sampler1 sampler1.cxx)
|
||||
target_link_libraries(fairmq-ex-multiple-transports-sampler1 PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-multiple-transports-sampler2 sampler2.cxx)
|
||||
target_link_libraries(fairmq-ex-multiple-transports-sampler2 PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-multiple-transports-sink sink.cxx)
|
||||
target_link_libraries(fairmq-ex-multiple-transports-sink PRIVATE FairMQ)
|
||||
|
||||
add_custom_target(ExampleMultipleTransports DEPENDS fairmq-ex-multiple-transports-sampler1 fairmq-ex-multiple-transports-sampler2 fairmq-ex-multiple-transports-sink)
|
||||
|
||||
set(EX_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-multiple-transports.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-multiple-transports.sh)
|
||||
|
||||
# test
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-multiple-transports.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multiple-transports.sh)
|
||||
add_test(NAME Example.MultipleTransports COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-multiple-transports.sh)
|
||||
set_tests_properties(Example.MultipleTransports PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received messages from both sources.")
|
||||
|
||||
# install
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
fairmq-ex-multiple-transports-sampler1
|
||||
fairmq-ex-multiple-transports-sampler2
|
||||
fairmq-ex-multiple-transports-sink
|
||||
|
||||
LIBRARY DESTINATION ${PROJECT_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
# configure run script with different executable paths for install directories
|
||||
set(EX_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-multiple-transports.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-multiple-transports.sh_install)
|
||||
|
||||
install(
|
||||
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-multiple-transports.sh_install
|
||||
DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
RENAME fairmq-start-ex-multiple-transports.sh
|
||||
)
|
||||
|
@@ -9,8 +9,6 @@
|
||||
#include <fairmq/Device.h>
|
||||
#include <fairmq/runDevice.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
struct Sink : fair::mq::Device
|
||||
@@ -34,7 +32,7 @@ struct Sink : fair::mq::Device
|
||||
// Creates a message using the transport of channel ack
|
||||
fair::mq::MessagePtr ack(NewMessageFor("ack", 0));
|
||||
if (Send(ack, "ack") < 0) {
|
||||
throw std::runtime_error("could not send an ack");
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if want to be called again (otherwise go to IDLE state)
|
||||
|
@@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
|
||||
|
||||
session="$(@CMAKE_BINARY_DIR@/fairmq/fairmq-uuid-gen -h)"
|
||||
@@ -12,13 +10,12 @@ chan1Addr="/tmp/fmq_$session""_""$chan1"
|
||||
chan2Addr="/tmp/fmq_$session""_""$chan2"
|
||||
ackChanAddr="/tmp/fmq_$session""_""$ackChan"
|
||||
|
||||
trap 'set +e; kill -TERM $SAMPLER1_PID; kill -TERM $SAMPLER2_PID; kill -TERM $SINK_PID; wait $SAMPLER1_PID; wait $SAMPLER2_PID; wait $SINK_PID; @CMAKE_BINARY_DIR@/fairmq/fairmq-shmmonitor --cleanup --session $SESSION; rm $chan1Addr; rm $chan2Addr; rm $ackChanAddr; exit 0' TERM
|
||||
trap 'kill -TERM $SAMPLER1_PID; kill -TERM $SAMPLER2_PID; kill -TERM $SINK_PID; wait $SAMPLER1_PID; wait $SAMPLER2_PID; wait $SINK_PID; @CMAKE_BINARY_DIR@/fairmq/fairmq-shmmonitor --cleanup --session $SESSION; rm $chan1Addr; rm $chan2Addr; rm $ackChanAddr' TERM
|
||||
|
||||
SINK="fairmq-ex-multiple-transports-sink"
|
||||
SINK+=" --id sink1"
|
||||
SINK+=" --verbosity veryhigh --severity debug"
|
||||
SINK+=" --shm-segment-size 100000000"
|
||||
SINK+=" --shm-monitor true"
|
||||
SINK+=" --session $session"
|
||||
SINK+=" --max-iterations 1"
|
||||
SINK+=" --control static --color false"
|
||||
@@ -34,7 +31,6 @@ SAMPLER1+=" --id sampler1"
|
||||
SAMPLER1+=" --session $session"
|
||||
SAMPLER1+=" --verbosity veryhigh --severity debug"
|
||||
SAMPLER1+=" --shm-segment-size 100000000"
|
||||
SAMPLER1+=" --shm-monitor true"
|
||||
SAMPLER1+=" --max-iterations 1"
|
||||
SAMPLER1+=" --control static --color false"
|
||||
SAMPLER1+=" --transport shmem"
|
||||
@@ -48,7 +44,6 @@ SAMPLER2+=" --id sampler2"
|
||||
SAMPLER2+=" --session $session"
|
||||
SAMPLER2+=" --verbosity veryhigh --severity debug"
|
||||
SAMPLER2+=" --shm-segment-size 100000000"
|
||||
SAMPLER2+=" --shm-monitor true"
|
||||
SAMPLER2+=" --max-iterations 1"
|
||||
SAMPLER2+=" --control static --color false"
|
||||
SAMPLER2+=" --transport zeromq"
|
||||
@@ -60,6 +55,4 @@ wait $SAMPLER1_PID
|
||||
wait $SAMPLER2_PID
|
||||
wait $SINK_PID
|
||||
|
||||
set +e
|
||||
rm $chan1Addr; rm $chan2Addr; rm $ackChanAddr
|
||||
exit 0
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2020-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* Copyright (C) 2020-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
|
@@ -1,13 +1,59 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2014-2023 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" #
|
||||
################################################################################
|
||||
################################################################################
|
||||
# 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" #
|
||||
################################################################################
|
||||
|
||||
add_example(NAME readout
|
||||
DEVICE readout builder processor sender receiver
|
||||
SCRIPT readout readout-processing
|
||||
NO_TEST
|
||||
add_executable(fairmq-ex-readout-readout readout.cxx)
|
||||
target_link_libraries(fairmq-ex-readout-readout PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-readout-builder builder.cxx)
|
||||
target_link_libraries(fairmq-ex-readout-builder PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-readout-processor processor.cxx)
|
||||
target_link_libraries(fairmq-ex-readout-processor PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-readout-sender sender.cxx)
|
||||
target_link_libraries(fairmq-ex-readout-sender PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-readout-receiver receiver.cxx)
|
||||
target_link_libraries(fairmq-ex-readout-receiver PRIVATE FairMQ)
|
||||
|
||||
set(EX_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-readout.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-readout.sh)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-readout-processing.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-readout-processing.sh)
|
||||
|
||||
# install
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
fairmq-ex-readout-readout
|
||||
fairmq-ex-readout-builder
|
||||
fairmq-ex-readout-processor
|
||||
fairmq-ex-readout-sender
|
||||
fairmq-ex-readout-receiver
|
||||
|
||||
LIBRARY DESTINATION ${PROJECT_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
# configure run script with different executable paths for build and for install directories
|
||||
set(EX_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-readout.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-readout.sh_install)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-readout-processing.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-readout-processing.sh_install)
|
||||
|
||||
install(
|
||||
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-readout.sh_install
|
||||
DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
RENAME fairmq-start-ex-readout.sh
|
||||
)
|
||||
|
||||
install(
|
||||
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-readout-processing.sh_install
|
||||
DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
RENAME fairmq-start-ex-readout-processing.sh
|
||||
)
|
||||
|
@@ -7,19 +7,19 @@ This examples shows two possible topologies (out of many) for a node connected t
|
||||
```
|
||||
|------------------------------- Readout Node ---------------------------| |- Processing Node -|
|
||||
| Readout --> Builder --> Sender | --> | Receiver |
|
||||
| [# shared memory segment (unused in this topology) ##################] | zmq | |
|
||||
| [# shared memory segment (unused in this topology) ##################] | ofi | |
|
||||
| [# shmem unmanaged region (readout writes here, others read) ########] | | |
|
||||
|------------------------------------------------------------------------| |-------------------|
|
||||
```
|
||||
|
||||
The devices one the Readout Node communicate via shared memory transport. Readout device writes into shared memory unmanaged region. The data is then forwarded through Builder to Sender process, which sends it out via zeromq transport.
|
||||
The devices one the Readout Node communicate via shared memory transport. Readout device writes into shared memory unmanaged region. The data is then forwarded through Builder to Sender process, which sends it out via OFI transport.
|
||||
|
||||
## Setup with generating new data on the Readout node
|
||||
|
||||
```
|
||||
|------------------------------- Readout Node ---------------------------| |- Processing Node -|
|
||||
| Readout --> Builder --> Processor --> Sender | --> | Receiver |
|
||||
| [# shared memory segment (used between Proccessor and Sender) #######] | zmq | |
|
||||
| [# shared memory segment (used between Proccessor and Sender) #######] | ofi | |
|
||||
| [# shmem unmanaged region (readout writes here, builder & proc read) ] | | |
|
||||
|------------------------------------------------------------------------| |-------------------|
|
||||
```
|
||||
|
@@ -31,10 +31,12 @@ SENDER="fairmq-ex-readout-sender"
|
||||
SENDER+=" --id sender1"
|
||||
SENDER+=" --input-name ps"
|
||||
SENDER+=" --channel-config name=ps,type=pair,method=bind,address=tcp://localhost:7779,transport=shmem"
|
||||
#SENDER+=" name=sr,type=pair,method=connect,address=tcp://localhost:7780,transport=ofi"
|
||||
SENDER+=" name=sr,type=pair,method=connect,address=tcp://localhost:7780,transport=zeromq"
|
||||
xterm -geometry 80x23+1000+0 -hold -e @EX_BIN_DIR@/$SENDER &
|
||||
|
||||
RECEIVER="fairmq-ex-readout-receiver"
|
||||
RECEIVER+=" --id receiver1"
|
||||
#RECEIVER+=" --channel-config name=sr,type=pair,method=bind,address=tcp://localhost:7780,transport=ofi"
|
||||
RECEIVER+=" --channel-config name=sr,type=pair,method=bind,address=tcp://localhost:7780,transport=zeromq"
|
||||
xterm -geometry 80x23+1500+0 -hold -e @EX_BIN_DIR@/$RECEIVER &
|
||||
|
@@ -25,10 +25,12 @@ SENDER="fairmq-ex-readout-sender"
|
||||
SENDER+=" --id sender1"
|
||||
SENDER+=" --input-name bs"
|
||||
SENDER+=" --channel-config name=bs,type=pair,method=bind,address=tcp://localhost:7778,transport=shmem"
|
||||
# SENDER+=" name=sr,type=pair,method=connect,address=tcp://localhost:7779,transport=ofi"
|
||||
SENDER+=" name=sr,type=pair,method=connect,address=tcp://localhost:7779,transport=zeromq"
|
||||
xterm -geometry 80x23+1000+0 -hold -e @EX_BIN_DIR@/$SENDER &
|
||||
|
||||
RECEIVER="fairmq-ex-readout-receiver"
|
||||
RECEIVER+=" --id receiver1"
|
||||
# RECEIVER+=" --channel-config name=sr,type=pair,method=bind,address=tcp://localhost:7779,transport=ofi"
|
||||
RECEIVER+=" --channel-config name=sr,type=pair,method=bind,address=tcp://localhost:7779,transport=zeromq"
|
||||
xterm -geometry 80x23+1500+0 -hold -e @EX_BIN_DIR@/$RECEIVER &
|
||||
|
@@ -1,11 +1,54 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2014-2023 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" #
|
||||
################################################################################
|
||||
################################################################################
|
||||
# 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" #
|
||||
################################################################################
|
||||
|
||||
add_example(NAME region
|
||||
DEVICE sampler sink keep-alive
|
||||
add_executable(fairmq-ex-region-sampler sampler.cxx)
|
||||
target_link_libraries(fairmq-ex-region-sampler PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-region-sink sink.cxx)
|
||||
target_link_libraries(fairmq-ex-region-sink PRIVATE FairMQ)
|
||||
|
||||
add_executable(fairmq-ex-region-keep-alive keep-alive.cxx)
|
||||
target_link_libraries(fairmq-ex-region-keep-alive PRIVATE FairMQ)
|
||||
|
||||
add_custom_target(ExampleRegion DEPENDS fairmq-ex-region-sampler fairmq-ex-region-sink fairmq-ex-region-keep-alive)
|
||||
|
||||
set(EX_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-region.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-region.sh)
|
||||
|
||||
# test
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-region.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-region.sh)
|
||||
|
||||
add_test(NAME Example.Region.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-region.sh zeromq)
|
||||
set_tests_properties(Example.Region.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received [0-9*] acks")
|
||||
|
||||
add_test(NAME Example.Region.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-region.sh shmem)
|
||||
set_tests_properties(Example.Region.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received [0-9*] acks")
|
||||
|
||||
# install
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
fairmq-ex-region-sampler
|
||||
fairmq-ex-region-sink
|
||||
|
||||
LIBRARY DESTINATION ${PROJECT_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
# configure run script with different executable paths for build and for install directories
|
||||
set(EX_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-region.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-region.sh_install)
|
||||
|
||||
install(
|
||||
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-region.sh_install
|
||||
DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
RENAME fairmq-start-ex-region.sh
|
||||
)
|
||||
|
@@ -19,10 +19,6 @@ SAMPLER+=" --severity debug"
|
||||
SAMPLER+=" --msg-size $msgSize"
|
||||
# SAMPLER+=" --rate 10"
|
||||
SAMPLER+=" --transport $transport"
|
||||
# SAMPLER+=" --external-region true"
|
||||
# SAMPLER+=" --shm-no-cleaup true"
|
||||
SAMPLER+=" --shm-monitor true"
|
||||
# SAMPLER+=" --shmid 1"
|
||||
SAMPLER+=" --channel-config name=data,type=push,method=bind,address=tcp://127.0.0.1:7777,sndKernelSize=212992"
|
||||
xterm -geometry 120x60+0+0 -hold -e @EX_BIN_DIR@/$SAMPLER &
|
||||
|
||||
@@ -30,8 +26,5 @@ SINK="fairmq-ex-region-sink"
|
||||
SINK+=" --id sink1"
|
||||
SINK+=" --severity debug"
|
||||
SINK+=" --transport $transport"
|
||||
# SINK+=" --shm-no-cleaup true"
|
||||
SINK+=" --shm-monitor true"
|
||||
# SINK+=" --shmid 1"
|
||||
SINK+=" --channel-config name=data,type=pull,method=connect,address=tcp://127.0.0.1:7777,rcvKernelSize=212992"
|
||||
xterm -geometry 120x60+750+0 -hold -e @EX_BIN_DIR@/$SINK &
|
||||
|
@@ -6,9 +6,10 @@
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
#include <fairmq/shmem/Common.h>
|
||||
#include <fairmq/shmem/Monitor.h>
|
||||
#include <fairmq/shmem/Segment.h>
|
||||
#include <fairmq/shmem/UnmanagedRegion.h>
|
||||
#include <fairmq/shmem/Segment.h>
|
||||
#include <fairmq/shmem/Monitor.h>
|
||||
|
||||
#include <fairmq/tools/Unique.h>
|
||||
|
||||
#include <fairlogger/Logger.h>
|
||||
@@ -16,129 +17,75 @@
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <csignal>
|
||||
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
using namespace std;
|
||||
using namespace boost::program_options;
|
||||
|
||||
namespace {
|
||||
volatile sig_atomic_t gStopping = 0;
|
||||
volatile sig_atomic_t gResetContent = 0;
|
||||
} // namespace
|
||||
namespace
|
||||
{
|
||||
volatile sig_atomic_t gStopping = 0;
|
||||
}
|
||||
|
||||
void signalHandler(int /* signal */) { gStopping = 1; }
|
||||
|
||||
void resetContentHandler(int /* signal */) { gResetContent = 1; }
|
||||
void signalHandler(int /* signal */)
|
||||
{
|
||||
gStopping = 1;
|
||||
}
|
||||
|
||||
struct ShmManager
|
||||
{
|
||||
ShmManager(uint64_t _shmId, const vector<string>& _segments, const vector<string>& _regions, bool zero = true)
|
||||
ShmManager(uint64_t _shmId, const vector<string>& _segments, const vector<string>& _regions)
|
||||
: shmId(fair::mq::shmem::makeShmIdStr(_shmId))
|
||||
{
|
||||
LOG(info) << "Starting ShmManager for shmId: " << shmId;
|
||||
LOG(info) << "Performing full reset...";
|
||||
FullReset();
|
||||
LOG(info) << "Done.";
|
||||
LOG(info) << "Adding managed segments...";
|
||||
AddSegments(_segments, zero);
|
||||
LOG(info) << "Done.";
|
||||
LOG(info) << "Adding unmanaged regions...";
|
||||
AddRegions(_regions, zero);
|
||||
LOG(info) << "Done.";
|
||||
LOG(info) << "Shared memory is ready for use.";
|
||||
}
|
||||
|
||||
void AddSegments(const vector<string>& _segments, bool zero)
|
||||
{
|
||||
for (const auto& s : _segments) {
|
||||
vector<string> conf;
|
||||
boost::algorithm::split(conf, s, boost::algorithm::is_any_of(","));
|
||||
if (conf.size() != 3) {
|
||||
LOG(error) << "incorrect format for --segments. Expecting pairs of <id>,<size><numaid>.";
|
||||
vector<string> segmentConf;
|
||||
boost::algorithm::split(segmentConf, s, boost::algorithm::is_any_of(","));
|
||||
if (segmentConf.size() != 2) {
|
||||
LOG(error) << "incorrect format for --segments. Expecting pairs of <id>,<size>.";
|
||||
fair::mq::shmem::Monitor::Cleanup(fair::mq::shmem::ShmId{shmId});
|
||||
throw runtime_error("incorrect format for --segments. Expecting pairs of <id>,<size>,<numaid>.");
|
||||
throw runtime_error("incorrect format for --segments. Expecting pairs of <id>,<size>.");
|
||||
}
|
||||
uint16_t id = stoi(conf.at(0));
|
||||
uint64_t size = stoull(conf.at(1));
|
||||
segmentCfgs.emplace_back(fair::mq::shmem::SegmentConfig{id, size, "rbtree_best_fit"});
|
||||
|
||||
uint16_t id = stoi(segmentConf.at(0));
|
||||
uint64_t size = stoull(segmentConf.at(1));
|
||||
auto ret = segments.emplace(id, fair::mq::shmem::Segment(shmId, id, size, fair::mq::shmem::rbTreeBestFit));
|
||||
fair::mq::shmem::Segment& segment = ret.first->second;
|
||||
LOG(info) << "Created segment " << id << " of size " << segment.GetSize()
|
||||
<< ", starting at " << segment.GetData() << ". Locking...";
|
||||
LOG(info) << "Created segment " << id << " of size " << segment.GetSize() << ", starting at " << segment.GetData() << ". Locking...";
|
||||
segment.Lock();
|
||||
LOG(info) << "Done.";
|
||||
if (zero) {
|
||||
LOG(info) << "Zeroing...";
|
||||
segment.Zero();
|
||||
LOG(info) << "Done.";
|
||||
}
|
||||
LOG(info) << "Zeroing...";
|
||||
segment.Zero();
|
||||
LOG(info) << "Done.";
|
||||
}
|
||||
}
|
||||
|
||||
void AddRegions(const vector<string>& _regions, bool zero)
|
||||
{
|
||||
for (const auto& r : _regions) {
|
||||
vector<string> conf;
|
||||
boost::algorithm::split(conf, r, boost::algorithm::is_any_of(","));
|
||||
if (conf.size() != 3) {
|
||||
LOG(error) << "incorrect format for --regions. Expecting pairs of <id>,<size>,<numaid>.";
|
||||
vector<string> regionConf;
|
||||
boost::algorithm::split(regionConf, r, boost::algorithm::is_any_of(","));
|
||||
if (regionConf.size() != 2) {
|
||||
LOG(error) << "incorrect format for --regions. Expecting pairs of <id>,<size>.";
|
||||
fair::mq::shmem::Monitor::Cleanup(fair::mq::shmem::ShmId{shmId});
|
||||
throw runtime_error("incorrect format for --regions. Expecting pairs of <id>,<size>,<numaid>.");
|
||||
throw runtime_error("incorrect format for --regions. Expecting pairs of <id>,<size>.");
|
||||
}
|
||||
uint16_t id = stoi(conf.at(0));
|
||||
uint64_t size = stoull(conf.at(1));
|
||||
fair::mq::RegionConfig cfg;
|
||||
cfg.id = id;
|
||||
cfg.size = size;
|
||||
regionCfgs.push_back(cfg);
|
||||
|
||||
uint16_t id = stoi(regionConf.at(0));
|
||||
uint64_t size = stoull(regionConf.at(1));
|
||||
auto ret = regions.emplace(id, make_unique<fair::mq::shmem::UnmanagedRegion>(shmId, id, size));
|
||||
fair::mq::shmem::UnmanagedRegion& region = *(ret.first->second);
|
||||
LOG(info) << "Created unamanged region " << id << " of size " << region.GetSize()
|
||||
<< ", starting at " << region.GetData() << ". Locking...";
|
||||
LOG(info) << "Created unamanged region " << id << " of size " << region.GetSize() << ", starting at " << region.GetData() << ". Locking...";
|
||||
region.Lock();
|
||||
LOG(info) << "Done.";
|
||||
if (zero) {
|
||||
LOG(info) << "Zeroing...";
|
||||
region.Zero();
|
||||
LOG(info) << "Done.";
|
||||
}
|
||||
LOG(info) << "Zeroing...";
|
||||
region.Zero();
|
||||
LOG(info) << "Done.";
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckPresence()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(localMtx);
|
||||
for (const auto& sc : segmentCfgs) {
|
||||
if (!(fair::mq::shmem::Monitor::SegmentIsPresent(fair::mq::shmem::ShmId{shmId}, sc.id))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const auto& rc : regionCfgs) {
|
||||
if (!(fair::mq::shmem::Monitor::RegionIsPresent(fair::mq::shmem::ShmId{shmId}, rc.id.value()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ResetContent()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(localMtx);
|
||||
fair::mq::shmem::Monitor::ResetContent(fair::mq::shmem::ShmId{shmId}, segmentCfgs, regionCfgs);
|
||||
}
|
||||
|
||||
void FullReset()
|
||||
{
|
||||
segments.clear();
|
||||
regions.clear();
|
||||
fair::mq::shmem::Monitor::Cleanup(fair::mq::shmem::ShmId{shmId});
|
||||
fair::mq::shmem::Monitor::ResetContent(fair::mq::shmem::ShmId{shmId});
|
||||
}
|
||||
|
||||
~ShmManager()
|
||||
@@ -148,11 +95,8 @@ struct ShmManager
|
||||
}
|
||||
|
||||
std::string shmId;
|
||||
std::mutex localMtx;
|
||||
map<uint16_t, fair::mq::shmem::Segment> segments;
|
||||
map<uint16_t, unique_ptr<fair::mq::shmem::UnmanagedRegion>> regions;
|
||||
std::vector<fair::mq::shmem::SegmentConfig> segmentCfgs;
|
||||
std::vector<fair::mq::RegionConfig> regionCfgs;
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
@@ -161,11 +105,8 @@ int main(int argc, char** argv)
|
||||
|
||||
signal(SIGINT, signalHandler);
|
||||
signal(SIGTERM, signalHandler);
|
||||
signal(SIGUSR1, resetContentHandler);
|
||||
|
||||
try {
|
||||
bool nozero = false;
|
||||
bool checkPresence = true;
|
||||
uint64_t shmId = 0;
|
||||
vector<string> segments;
|
||||
vector<string> regions;
|
||||
@@ -173,10 +114,8 @@ int main(int argc, char** argv)
|
||||
options_description desc("Options");
|
||||
desc.add_options()
|
||||
("shmid", value<uint64_t>(&shmId)->required(), "Shm id")
|
||||
("segments", value<vector<string>>(&segments)->multitoken()->composing(), "Segments, as <id>,<size>,<numaid> <id>,<size>,<numaid> <id>,<size>,<numaid> ... (numaid: -2 disabled, -1 interleave, >=0 node)")
|
||||
("regions", value<vector<string>>(®ions)->multitoken()->composing(), "Regions, as <id>,<size> <id>,<size>,<numaid> <id>,<size>,<numaid> ...")
|
||||
("nozero", value<bool>(&nozero)->default_value(false)->implicit_value(true), "Do not zero segments after initialization")
|
||||
("check-presence", value<bool>(&checkPresence)->default_value(true)->implicit_value(true), "Check periodically if configured segments/regions are still present, and cleanup and leave if they are not")
|
||||
("segments", value<vector<string>>(&segments)->multitoken()->composing(), "Segments, as <id>,<size> <id>,<size> <id>,<size> ...")
|
||||
("regions", value<vector<string>>(®ions)->multitoken()->composing(), "Regions, as <id>,<size> <id>,<size> <id>,<size> ...")
|
||||
("help,h", "Print help");
|
||||
|
||||
variables_map vm;
|
||||
@@ -189,35 +128,15 @@ int main(int argc, char** argv)
|
||||
|
||||
notify(vm);
|
||||
|
||||
ShmManager shmManager(shmId, segments, regions, !nozero);
|
||||
ShmManager shmManager(shmId, segments, regions);
|
||||
|
||||
std::thread resetContentThread([&shmManager]() {
|
||||
while (!gStopping) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
if (gResetContent == 1) {
|
||||
LOG(info) << "Resetting content for shmId " << shmManager.shmId;
|
||||
shmManager.ResetContent();
|
||||
gResetContent = 0;
|
||||
LOG(info) << "Done resetting content for shmId " << shmManager.shmId;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (checkPresence) {
|
||||
while (!gStopping) {
|
||||
if (shmManager.CheckPresence() == false) {
|
||||
LOG(error) << "Failed to find segments, exiting.";
|
||||
gStopping = true;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
while (!gStopping) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
|
||||
resetContentThread.join();
|
||||
|
||||
LOG(info) << "stopping.";
|
||||
} catch (exception& e) {
|
||||
LOG(error) << "Exception reached the top of main: " << e.what() << ", exiting";
|
||||
LOG(error) << "Unhandled Exception reached the top of main: " << e.what() << ", application will now exit";
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
@@ -19,7 +19,6 @@ struct Sampler : fair::mq::Device
|
||||
{
|
||||
void InitTask() override
|
||||
{
|
||||
fExternalRegion = fConfig->GetProperty<bool>("external-region");
|
||||
fMsgSize = fConfig->GetProperty<int>("msg-size");
|
||||
fLinger = fConfig->GetProperty<uint32_t>("region-linger");
|
||||
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
|
||||
@@ -35,75 +34,61 @@ struct Sampler : fair::mq::Device
|
||||
|
||||
fair::mq::RegionConfig regionCfg;
|
||||
regionCfg.linger = fLinger; // delay in ms before region destruction to collect outstanding events
|
||||
// options for testing with an externally-created -region
|
||||
if (fExternalRegion) {
|
||||
regionCfg.id = 1;
|
||||
regionCfg.removeOnDestruction = false;
|
||||
}
|
||||
regionCfg.lock = !fExternalRegion; // mlock region after creation
|
||||
regionCfg.zero = !fExternalRegion; // zero region content after creation
|
||||
fRegion = fair::mq::UnmanagedRegionPtr(NewUnmanagedRegionFor(
|
||||
"data", // region is created using the transport of this channel...
|
||||
0, // ... and this sub-channel
|
||||
10000000, // region size
|
||||
[this](const std::vector<fair::mq::RegionBlock>& blocks) { // callback to be called when message buffers no longer needed by transport
|
||||
std::lock_guard<std::mutex> lock(fMtx);
|
||||
fNumUnackedMsgs -= blocks.size();
|
||||
if (fMaxIterations > 0) {
|
||||
LOG(info) << "Received " << blocks.size() << " acks";
|
||||
}
|
||||
},
|
||||
regionCfg
|
||||
));
|
||||
regionCfg.lock = true; // mlock region after creation
|
||||
regionCfg.zero = true; // zero region content after creation
|
||||
fRegion = fair::mq::UnmanagedRegionPtr(NewUnmanagedRegionFor("data", // region is created using the transport of this channel...
|
||||
0, // ... and this sub-channel
|
||||
10000000, // region size
|
||||
[this](const std::vector<fair::mq::RegionBlock>& blocks) { // callback to be called when message buffers no longer needed by transport
|
||||
std::lock_guard<std::mutex> lock(fMtx);
|
||||
fNumUnackedMsgs -= blocks.size();
|
||||
if (fMaxIterations > 0) {
|
||||
LOG(info) << "Received " << blocks.size() << " acks";
|
||||
}
|
||||
}, regionCfg));
|
||||
}
|
||||
|
||||
void Run() override
|
||||
bool ConditionalRun() override
|
||||
{
|
||||
while (!NewStatePending()) {
|
||||
fair::mq::MessagePtr msg(NewMessageFor("data", // channel
|
||||
0, // sub-channel
|
||||
fRegion, // region
|
||||
fRegion->GetData(), // ptr within region
|
||||
fMsgSize, // offset from ptr
|
||||
nullptr // hint
|
||||
));
|
||||
fair::mq::MessagePtr msg(NewMessageFor("data", // channel
|
||||
0, // sub-channel
|
||||
fRegion, // region
|
||||
fRegion->GetData(), // ptr within region
|
||||
fMsgSize, // offset from ptr
|
||||
nullptr // hint
|
||||
));
|
||||
|
||||
std::lock_guard<std::mutex> lock(fMtx);
|
||||
++fNumUnackedMsgs;
|
||||
if (Send(msg, "data", 0) > 0) {
|
||||
if (fMaxIterations > 0 && ++fNumIterations >= fMaxIterations) {
|
||||
LOG(info) << "Configured maximum number of iterations reached. Stopping sending.";
|
||||
break;
|
||||
}
|
||||
// static_cast<char*>(fRegion->GetData())[3] = 97;
|
||||
// LOG(info) << "check: " << static_cast<char*>(fRegion->GetData())[3];
|
||||
// std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
std::lock_guard<std::mutex> lock(fMtx);
|
||||
++fNumUnackedMsgs;
|
||||
if (Send(msg, "data", 0) > 0) {
|
||||
if (fMaxIterations > 0 && ++fNumIterations >= fMaxIterations) {
|
||||
LOG(info) << "Configured maximum number of iterations reached. Leaving RUNNING state.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// wait for all acks to arrive
|
||||
while (!NewStatePending()) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fMtx);
|
||||
if (fNumUnackedMsgs == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(25));
|
||||
}
|
||||
|
||||
if (fNumUnackedMsgs != 0) {
|
||||
LOG(info) << "Done, still not acknowledged: " << fNumUnackedMsgs;
|
||||
} else {
|
||||
LOG(info) << "All acknowledgements received.";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ResetTask() override
|
||||
{
|
||||
fRegion.reset();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fMtx);
|
||||
if (fNumUnackedMsgs != 0) {
|
||||
LOG(info) << "Done, still not acknowledged: " << fNumUnackedMsgs;
|
||||
} else {
|
||||
LOG(info) << "All acknowledgements received.";
|
||||
}
|
||||
}
|
||||
GetChannel("data", 0).Transport()->UnsubscribeFromRegionEvents();
|
||||
}
|
||||
|
||||
private:
|
||||
int fExternalRegion = false;
|
||||
int fMsgSize = 10000;
|
||||
uint32_t fLinger = 100;
|
||||
uint64_t fMaxIterations = 0;
|
||||
@@ -118,8 +103,7 @@ void addCustomOptions(bpo::options_description& options)
|
||||
options.add_options()
|
||||
("msg-size", bpo::value<int>()->default_value(1000), "Message size in bytes")
|
||||
("region-linger", bpo::value<uint32_t>()->default_value(100), "Linger period for regions")
|
||||
("max-iterations", bpo::value<uint64_t>()->default_value(0), "Maximum number of iterations of Run/ConditionalRun/OnData (0 - infinite)")
|
||||
("external-region", bpo::value<bool>()->default_value(false), "Use region created by another process");
|
||||
("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*/)
|
||||
|
@@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
|
||||
|
||||
transport="zeromq"
|
||||
@@ -16,7 +14,7 @@ chan="data"
|
||||
chanAddr="/tmp/fmq_$session""_""$chan""_""$transport"
|
||||
|
||||
# setup a trap to kill everything if the test fails/timeouts
|
||||
trap 'set +e; kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; wait $SAMPLER_PID; wait $SINK_PID; @CMAKE_BINARY_DIR@/fairmq/fairmq-shmmonitor --cleanup --session $SESSION; rm $chanAddr; exit 0' TERM
|
||||
trap 'kill -TERM $SAMPLER_PID; kill -TERM $SINK_PID; wait $SAMPLER_PID; wait $SINK_PID; @CMAKE_BINARY_DIR@/fairmq/fairmq-shmmonitor --cleanup --session $SESSION; rm $chanAddr' TERM
|
||||
|
||||
SAMPLER="fairmq-ex-region-sampler"
|
||||
SAMPLER+=" --id sampler1"
|
||||
@@ -24,7 +22,6 @@ SAMPLER+=" --transport $transport"
|
||||
SAMPLER+=" --severity debug"
|
||||
SAMPLER+=" --session $session"
|
||||
SAMPLER+=" --shm-segment-size 100000000"
|
||||
SAMPLER+=" --shm-monitor true"
|
||||
SAMPLER+=" --verbosity veryhigh"
|
||||
SAMPLER+=" --control static --color false"
|
||||
SAMPLER+=" --max-iterations 1"
|
||||
@@ -40,7 +37,6 @@ SINK+=" --transport $transport"
|
||||
SINK+=" --severity debug"
|
||||
SINK+=" --session $session"
|
||||
SINK+=" --shm-segment-size 100000000"
|
||||
SINK+=" --shm-monitor true"
|
||||
SINK+=" --verbosity veryhigh"
|
||||
SINK+=" --control static --color false"
|
||||
SINK+=" --max-iterations 1"
|
||||
@@ -52,6 +48,4 @@ SINK_PID=$!
|
||||
wait $SAMPLER_PID
|
||||
wait $SINK_PID
|
||||
|
||||
set +e
|
||||
rm $chanAddr
|
||||
exit 0
|
||||
|
@@ -1,11 +1,52 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2014-2023 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" #
|
||||
################################################################################
|
||||
################################################################################
|
||||
# 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" #
|
||||
################################################################################
|
||||
|
||||
add_example(NAME req-rep
|
||||
DEVICE client server
|
||||
add_executable(fairmq-ex-req-rep-client client.cxx)
|
||||
target_link_libraries(fairmq-ex-req-rep-client PRIVATE FairMQ)
|
||||
|
||||
|
||||
add_executable(fairmq-ex-req-rep-server server.cxx)
|
||||
target_link_libraries(fairmq-ex-req-rep-server PRIVATE FairMQ)
|
||||
|
||||
add_custom_target(ExampleReqRep DEPENDS fairmq-ex-req-rep-client fairmq-ex-req-rep-server)
|
||||
|
||||
set(EX_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-req-rep.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-req-rep.sh)
|
||||
|
||||
# test
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test-ex-req-rep.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test-ex-req-rep.sh)
|
||||
|
||||
add_test(NAME Example.ReqRep.zeromq COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-req-rep.sh zeromq)
|
||||
set_tests_properties(Example.ReqRep.zeromq PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received reply from server: ")
|
||||
|
||||
add_test(NAME Example.ReqRep.shmem COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test-ex-req-rep.sh shmem)
|
||||
set_tests_properties(Example.ReqRep.shmem PROPERTIES TIMEOUT "30" PASS_REGULAR_EXPRESSION "Received reply from server: ")
|
||||
|
||||
# install
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
fairmq-ex-req-rep-client
|
||||
fairmq-ex-req-rep-server
|
||||
|
||||
LIBRARY DESTINATION ${PROJECT_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
# configure run script with different executable paths for build and for install directories
|
||||
set(EX_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR})
|
||||
set(FAIRMQ_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR}/fairmq)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/fairmq-start-ex-req-rep.sh.in ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-req-rep.sh_install)
|
||||
|
||||
install(
|
||||
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fairmq-start-ex-req-rep.sh_install
|
||||
DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
RENAME fairmq-start-ex-req-rep.sh
|
||||
)
|
||||
|
@@ -1,7 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
|
||||
|
||||
transport="zeromq"
|
||||
@@ -15,7 +13,7 @@ chan="data"
|
||||
chanAddr="/tmp/fmq_$session""_""$chan""_""$transport"
|
||||
|
||||
# setup a trap to kill everything if the test fails/timeouts
|
||||
trap 'set +e; kill -TERM $CLIENT_PID; kill -TERM $SERVER_PID; wait $CLIENT_PID; wait $SERVER_PID; rm $chanAddr; exit 0' TERM
|
||||
trap 'kill -TERM $CLIENT_PID; kill -TERM $SERVER_PID; wait $CLIENT_PID; wait $SERVER_PID; rm $chanAddr' TERM
|
||||
|
||||
CLIENT="fairmq-ex-req-rep-client"
|
||||
CLIENT+=" --id client"
|
||||
@@ -23,7 +21,6 @@ CLIENT+=" --transport $transport"
|
||||
CLIENT+=" --verbosity veryhigh"
|
||||
CLIENT+=" --session $session"
|
||||
CLIENT+=" --shm-segment-size 100000000"
|
||||
CLIENT+=" --shm-monitor true"
|
||||
CLIENT+=" --control static --color false"
|
||||
CLIENT+=" --max-iterations 1"
|
||||
CLIENT+=" --channel-config name=$chan,type=req,method=connect,rateLogging=0,address=ipc://$chanAddr"
|
||||
@@ -36,7 +33,6 @@ SERVER+=" --transport $transport"
|
||||
SERVER+=" --verbosity veryhigh"
|
||||
SERVER+=" --session $session"
|
||||
SERVER+=" --shm-segment-size 100000000"
|
||||
SERVER+=" --shm-monitor true"
|
||||
SERVER+=" --control static --color false"
|
||||
SERVER+=" --max-iterations 1"
|
||||
SERVER+=" --channel-config name=$chan,type=rep,method=bind,rateLogging=0,address=ipc://$chanAddr"
|
||||
@@ -47,6 +43,4 @@ SERVER_PID=$!
|
||||
wait $CLIENT_PID
|
||||
wait $SERVER_PID
|
||||
|
||||
set +e
|
||||
rm $chanAddr
|
||||
exit 0
|
||||
|
@@ -71,7 +71,6 @@ if(BUILD_FAIRMQ)
|
||||
shmem/Monitor.h
|
||||
shmem/Segment.h
|
||||
shmem/UnmanagedRegion.h
|
||||
tools/Compiler.h
|
||||
tools/CppSTL.h
|
||||
tools/Exceptions.h
|
||||
tools/IO.h
|
||||
@@ -110,6 +109,16 @@ if(BUILD_FAIRMQ)
|
||||
zeromq/TransportFactory.h
|
||||
)
|
||||
|
||||
if(BUILD_OFI_TRANSPORT)
|
||||
set(FAIRMQ_PRIVATE_HEADER_FILES ${FAIRMQ_PRIVATE_HEADER_FILES}
|
||||
ofi/Context.h
|
||||
ofi/ControlMessages.h
|
||||
ofi/Message.h
|
||||
ofi/Socket.h
|
||||
ofi/TransportFactory.h
|
||||
)
|
||||
endif()
|
||||
|
||||
##########################
|
||||
# libFairMQ source files #
|
||||
##########################
|
||||
@@ -117,6 +126,7 @@ if(BUILD_FAIRMQ)
|
||||
Channel.cxx
|
||||
Device.cxx
|
||||
DeviceRunner.cxx
|
||||
Error.cxx
|
||||
JSONParser.cxx
|
||||
MemoryResources.cxx
|
||||
Plugin.cxx
|
||||
@@ -139,6 +149,14 @@ if(BUILD_FAIRMQ)
|
||||
tools/Unique.cxx
|
||||
)
|
||||
|
||||
if(BUILD_OFI_TRANSPORT)
|
||||
set(FAIRMQ_SOURCE_FILES ${FAIRMQ_SOURCE_FILES}
|
||||
ofi/Context.cxx
|
||||
ofi/Message.cxx
|
||||
ofi/Socket.cxx
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
###################
|
||||
# configure files #
|
||||
@@ -162,17 +180,14 @@ if(BUILD_FAIRMQ)
|
||||
############################
|
||||
# preprocessor definitions #
|
||||
############################
|
||||
target_compile_definitions(${target} PUBLIC
|
||||
BOOST_ERROR_CODE_HEADER_ONLY
|
||||
BOOST_ASIO_HAS_HAS_STD_CHRONO
|
||||
)
|
||||
target_compile_definitions(${target} PUBLIC BOOST_ERROR_CODE_HEADER_ONLY)
|
||||
if(FAIRMQ_DEBUG_MODE)
|
||||
target_compile_definitions(${target} PUBLIC FAIRMQ_DEBUG_MODE)
|
||||
endif()
|
||||
target_compile_definitions(${target} PUBLIC
|
||||
FAIRMQ_HAS_STD_FILESYSTEM=${FAIRMQ_HAS_STD_FILESYSTEM}
|
||||
FAIRMQ_HAS_STD_PMR=${FAIRMQ_HAS_STD_PMR}
|
||||
)
|
||||
if(BUILD_OFI_TRANSPORT)
|
||||
target_compile_definitions(${target} PRIVATE BUILD_OFI_TRANSPORT)
|
||||
endif()
|
||||
target_compile_definitions(${target} PUBLIC FAIRMQ_HAS_STD_FILESYSTEM=${FAIRMQ_HAS_STD_FILESYSTEM})
|
||||
|
||||
|
||||
#######################
|
||||
@@ -190,6 +205,13 @@ if(BUILD_FAIRMQ)
|
||||
##################
|
||||
# link libraries #
|
||||
##################
|
||||
if(BUILD_OFI_TRANSPORT)
|
||||
set(OFI_DEPS
|
||||
asio::asio
|
||||
asiofi::asiofi
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${target}
|
||||
INTERFACE # only consumers link against interface dependencies
|
||||
Boost::container
|
||||
@@ -200,13 +222,14 @@ if(BUILD_FAIRMQ)
|
||||
$<$<PLATFORM_ID:Linux>:rt>
|
||||
Boost::boost
|
||||
Boost::program_options
|
||||
Boost::filesystem # still needed for Boost.DLL
|
||||
Boost::filesystem
|
||||
Boost::regex
|
||||
FairLogger::FairLogger
|
||||
|
||||
PRIVATE # only libFairMQ links against private dependencies
|
||||
libzmq
|
||||
PicoSHA2
|
||||
${OFI_DEPS}
|
||||
)
|
||||
set_target_properties(${target} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
@@ -320,3 +343,10 @@ if(BUILD_FAIRMQ)
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
####################
|
||||
# external plugins #
|
||||
####################
|
||||
if(BUILD_PMIX_PLUGIN)
|
||||
add_subdirectory(plugins/PMIx)
|
||||
endif()
|
||||
|
@@ -19,7 +19,6 @@
|
||||
|
||||
#include <cstdint> // int64_t
|
||||
#include <memory> // unique_ptr, shared_ptr
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility> // std::move
|
||||
@@ -381,14 +380,6 @@ class Channel
|
||||
static constexpr int DefaultPortRangeMax = 23000;
|
||||
static constexpr bool DefaultAutoBind = true;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Channel& ch)
|
||||
{
|
||||
return os << "name: " << ch.fName
|
||||
<< ", type: " << ch.fType
|
||||
<< ", method: " << ch.fMethod
|
||||
<< ", address: " << ch.fAddress;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<TransportFactory> fTransportFactory;
|
||||
mq::Transport fTransportType;
|
||||
|
@@ -1,24 +1,19 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2012-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* Copyright (C) 2012-2022 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" *
|
||||
********************************************************************************/
|
||||
|
||||
// FairMQ
|
||||
#include <algorithm> // std::max, std::any_of
|
||||
#include <boost/algorithm/string.hpp> // join/split
|
||||
#include <chrono>
|
||||
#include <fairmq/Device.h>
|
||||
#include <fairmq/Tools.h>
|
||||
|
||||
// boost
|
||||
#include <boost/algorithm/string.hpp> // join/split
|
||||
|
||||
// std
|
||||
#include <algorithm> // std::max, std::any_of
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <list>
|
||||
#include <memory> // std::make_unique
|
||||
#include <memory> // std::make_unique
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
@@ -77,9 +72,6 @@ Device::Device(ProgOptions& config, tools::Version version)
|
||||
: Device(&config, version)
|
||||
{}
|
||||
|
||||
/// TODO: Remove this once Device::fChannels is no longer public
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
Device::Device(ProgOptions* config, tools::Version version)
|
||||
: fTransportFactory(nullptr)
|
||||
, fInternalConfig(config ? nullptr : make_unique<ProgOptions>())
|
||||
@@ -91,15 +83,18 @@ Device::Device(ProgOptions* config, tools::Version version)
|
||||
, fVersion(version)
|
||||
, fRate(DefaultRate)
|
||||
, fInitializationTimeoutInS(DefaultInitTimeout)
|
||||
, fTransitioning(false)
|
||||
{
|
||||
SubscribeToNewTransition("device", [&](Transition transition) {
|
||||
LOG(trace) << "device notified on new transition: " << transition;
|
||||
InterruptTransports();
|
||||
});
|
||||
|
||||
fStateMachine.PrepareState([&](State state) {
|
||||
LOG(trace) << "Resuming transports for " << state << " state";
|
||||
ResumeTransports();
|
||||
switch (transition) {
|
||||
case Transition::Stop:
|
||||
UnblockTransports();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
fStateMachine.HandleStates([&](State state) {
|
||||
@@ -140,7 +135,73 @@ Device::Device(ProgOptions* config, tools::Version version)
|
||||
|
||||
fStateMachine.Start();
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
void Device::TransitionTo(State s)
|
||||
{
|
||||
{
|
||||
lock_guard<mutex> lock(fTransitionMtx);
|
||||
if (fTransitioning) {
|
||||
LOG(debug) << "Attempting a transition with TransitionTo() while another one is already in progress";
|
||||
throw OngoingTransition("Attempting a transition with TransitionTo() while another one is already in progress");
|
||||
}
|
||||
fTransitioning = true;
|
||||
}
|
||||
|
||||
using mq::State;
|
||||
|
||||
StateQueue sq;
|
||||
StateSubscription ss(tools::ToString(fId, ".TransitionTo"), fStateMachine, sq);
|
||||
|
||||
State currentState = GetCurrentState();
|
||||
|
||||
while (s != currentState) {
|
||||
switch (currentState) {
|
||||
case State::Idle:
|
||||
if (s == State::Exiting) { ChangeState(Transition::End); }
|
||||
else { ChangeState(Transition::InitDevice); }
|
||||
break;
|
||||
case State::InitializingDevice:
|
||||
ChangeState(Transition::CompleteInit);
|
||||
break;
|
||||
case State::Initialized:
|
||||
if (s == State::Exiting || s == State::Idle) { ChangeState(Transition::ResetDevice); }
|
||||
else { ChangeState(Transition::Bind); }
|
||||
break;
|
||||
case State::Bound:
|
||||
if (s == State::DeviceReady || s == State::Ready || s == State::Running) { ChangeState(Transition::Connect); }
|
||||
else { ChangeState(Transition::ResetDevice); }
|
||||
break;
|
||||
case State::DeviceReady:
|
||||
if (s == State::Running || s == State::Ready) { ChangeState(Transition::InitTask); }
|
||||
else { ChangeState(Transition::ResetDevice); }
|
||||
break;
|
||||
case State::Ready:
|
||||
if (s == State::Running) { ChangeState(Transition::Run); }
|
||||
else { ChangeState(Transition::ResetTask); }
|
||||
break;
|
||||
case State::Running:
|
||||
ChangeState(Transition::Stop);
|
||||
break;
|
||||
case State::Binding:
|
||||
case State::Connecting:
|
||||
case State::InitializingTask:
|
||||
case State::ResettingDevice:
|
||||
case State::ResettingTask:
|
||||
LOG(debug) << "TransitionTo ignoring state: " << currentState << " (expected, automatic transition).";
|
||||
break;
|
||||
default:
|
||||
LOG(debug) << "TransitionTo ignoring state: " << currentState;
|
||||
break;
|
||||
}
|
||||
|
||||
currentState = sq.WaitForNext();
|
||||
}
|
||||
|
||||
{
|
||||
lock_guard<mutex> lock(fTransitionMtx);
|
||||
fTransitioning = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Device::InitWrapper()
|
||||
{
|
||||
@@ -165,7 +226,7 @@ void Device::InitWrapper()
|
||||
unordered_map<string, int> infos = fConfig->GetChannelInfo();
|
||||
for (const auto& info : infos) {
|
||||
for (int i = 0; i < info.second; ++i) {
|
||||
GetChannels()[info.first].emplace_back(info.first, i, fConfig->GetPropertiesStartingWith(tools::ToString("chans.", info.first, ".", i, ".")));
|
||||
fChannels[info.first].emplace_back(info.first, i, fConfig->GetPropertiesStartingWith(tools::ToString("chans.", info.first, ".", i, ".")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +236,7 @@ void Device::InitWrapper()
|
||||
string networkInterface = fConfig->GetProperty<string>("network-interface", DefaultNetworkInterface);
|
||||
|
||||
// Fill the uninitialized channel containers
|
||||
for (auto& channel : GetChannels()) {
|
||||
for (auto& channel : fChannels) {
|
||||
int subChannelIndex = 0;
|
||||
for (auto& subChannel : channel.second) {
|
||||
// set channel transport
|
||||
@@ -213,7 +274,7 @@ void Device::InitWrapper()
|
||||
}
|
||||
}
|
||||
|
||||
// ChangeStateOrThrow(Transition::Auto);
|
||||
// ChangeState(Transition::Auto);
|
||||
}
|
||||
|
||||
void Device::BindWrapper()
|
||||
@@ -230,7 +291,7 @@ void Device::BindWrapper()
|
||||
Bind();
|
||||
|
||||
if (!NewStatePending()) {
|
||||
ChangeStateOrThrow(Transition::Auto);
|
||||
ChangeState(Transition::Auto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +304,7 @@ void Device::ConnectWrapper()
|
||||
// first attempt
|
||||
AttachChannels(fUninitializedConnectingChannels);
|
||||
// if not all channels could be connected, update their address values from config and retry
|
||||
while (!fUninitializedConnectingChannels.empty() && !NewStatePending()) {
|
||||
while (!fUninitializedConnectingChannels.empty()) {
|
||||
this_thread::sleep_for(chrono::milliseconds(sleepTimeInMS));
|
||||
|
||||
for (auto& chan : fUninitializedConnectingChannels) {
|
||||
@@ -256,24 +317,20 @@ void Device::ConnectWrapper()
|
||||
|
||||
if (numAttempts++ > maxAttempts) {
|
||||
LOG(error) << "could not connect all channels after " << fInitializationTimeoutInS << " attempts";
|
||||
LOG(error) << "following channels are still invalid:";
|
||||
for (auto& chan : fUninitializedConnectingChannels) {
|
||||
LOG(error) << "channel: " << *chan;
|
||||
}
|
||||
throw runtime_error(tools::ToString("could not connect all channels after ", fInitializationTimeoutInS, " attempts"));
|
||||
}
|
||||
|
||||
AttachChannels(fUninitializedConnectingChannels);
|
||||
}
|
||||
|
||||
if (GetChannels().empty()) {
|
||||
if (fChannels.empty()) {
|
||||
LOG(warn) << "No channels created after finishing initialization";
|
||||
}
|
||||
|
||||
Connect();
|
||||
|
||||
if (!NewStatePending()) {
|
||||
ChangeStateOrThrow(Transition::Auto);
|
||||
ChangeState(Transition::Auto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +432,7 @@ void Device::InitTaskWrapper()
|
||||
InitTask();
|
||||
|
||||
if (!NewStatePending()) {
|
||||
ChangeStateOrThrow(Transition::Auto);
|
||||
ChangeState(Transition::Auto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,7 +442,7 @@ void Device::RunWrapper()
|
||||
|
||||
unique_ptr<thread> rateLogger;
|
||||
// Check if rate logging thread is needed
|
||||
const bool rateLogging = any_of(GetChannels().cbegin(), GetChannels().cend(), [](auto ch) {
|
||||
const bool rateLogging = any_of(fChannels.cbegin(), fChannels.cend(), [](auto ch) {
|
||||
return any_of(ch.second.cbegin(), ch.second.cend(), [](auto sub) { return sub.fRateLogging > 0; });
|
||||
});
|
||||
|
||||
@@ -396,9 +453,15 @@ void Device::RunWrapper()
|
||||
if (rateLogging && rateLogger->joinable()) { rateLogger->join(); }
|
||||
});
|
||||
|
||||
|
||||
// notify transports to resume transfers
|
||||
for (auto& t : fTransports) {
|
||||
t.second->Resume();
|
||||
}
|
||||
|
||||
// change to Error state in case of an exception, to release LogSocketRates
|
||||
tools::CallOnDestruction cod([&](){
|
||||
ChangeStateOrThrow(Transition::ErrorFound);
|
||||
ChangeState(Transition::ErrorFound);
|
||||
});
|
||||
|
||||
PreRun();
|
||||
@@ -406,7 +469,7 @@ void Device::RunWrapper()
|
||||
// process either data callbacks or ConditionalRun/Run
|
||||
if (fDataCallbacks) {
|
||||
// if only one input channel, do lightweight handling without additional polling.
|
||||
if (fInputChannelKeys.size() == 1 && GetChannels().at(fInputChannelKeys.at(0)).size() == 1) {
|
||||
if (fInputChannelKeys.size() == 1 && fChannels.at(fInputChannelKeys.at(0)).size() == 1) {
|
||||
HandleSingleChannelInput();
|
||||
} else {// otherwise do full handling with polling
|
||||
HandleMultipleChannelInput();
|
||||
@@ -425,7 +488,8 @@ void Device::RunWrapper()
|
||||
|
||||
// if Run() exited and the state is still RUNNING, transition to READY.
|
||||
if (!NewStatePending()) {
|
||||
ChangeStateOrThrow(Transition::Stop);
|
||||
UnblockTransports();
|
||||
ChangeState(Transition::Stop);
|
||||
}
|
||||
|
||||
PostRun();
|
||||
@@ -453,7 +517,7 @@ void Device::HandleMultipleChannelInput()
|
||||
// check if more than one transport is used
|
||||
fMultitransportInputs.clear();
|
||||
for (const auto& k : fInputChannelKeys) {
|
||||
mq::Transport t = GetChannel(k, 0).fTransportType;
|
||||
mq::Transport t = fChannels.at(k).at(0).fTransportType;
|
||||
if (fMultitransportInputs.find(t) == fMultitransportInputs.end()) {
|
||||
fMultitransportInputs.insert(pair<mq::Transport, vector<string>>(t, vector<string>()));
|
||||
fMultitransportInputs.at(t).push_back(k);
|
||||
@@ -463,13 +527,13 @@ void Device::HandleMultipleChannelInput()
|
||||
}
|
||||
|
||||
for (const auto& mi : fMsgInputs) {
|
||||
for (auto& i : GetChannels().at(mi.first)) {
|
||||
for (auto& i : fChannels.at(mi.first)) {
|
||||
i.fMultipart = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& mi : fMultipartInputs) {
|
||||
for (auto& i : GetChannels().at(mi.first)) {
|
||||
for (auto& i : fChannels.at(mi.first)) {
|
||||
i.fMultipart = true;
|
||||
}
|
||||
}
|
||||
@@ -480,16 +544,16 @@ void Device::HandleMultipleChannelInput()
|
||||
} else { // otherwise poll directly
|
||||
bool proceed = true;
|
||||
|
||||
PollerPtr poller(GetChannel(fInputChannelKeys.at(0), 0).fTransportFactory->CreatePoller(GetChannels(), fInputChannelKeys));
|
||||
PollerPtr poller(fChannels.at(fInputChannelKeys.at(0)).at(0).fTransportFactory->CreatePoller(fChannels, fInputChannelKeys));
|
||||
|
||||
while (!NewStatePending() && proceed) {
|
||||
poller->Poll(200);
|
||||
|
||||
// check which inputs are ready and call their data handlers if they are.
|
||||
for (const auto& ch : fInputChannelKeys) {
|
||||
for (unsigned int i = 0; i < GetChannels().at(ch).size(); ++i) {
|
||||
for (unsigned int i = 0; i < fChannels.at(ch).size(); ++i) {
|
||||
if (poller->CheckInput(ch, i)) {
|
||||
if (GetChannel(ch, i).fMultipart) {
|
||||
if (fChannels.at(ch).at(i).fMultipart) {
|
||||
proceed = HandleMultipartInput(ch, fMultipartInputs.at(ch), i);
|
||||
} else {
|
||||
proceed = HandleMsgInput(ch, fMsgInputs.at(ch), i);
|
||||
@@ -526,13 +590,13 @@ void Device::HandleMultipleTransportInput()
|
||||
void Device::PollForTransport(const TransportFactory* factory, const vector<string>& channelKeys)
|
||||
{
|
||||
try {
|
||||
PollerPtr poller(factory->CreatePoller(GetChannels(), channelKeys));
|
||||
PollerPtr poller(factory->CreatePoller(fChannels, channelKeys));
|
||||
|
||||
while (!NewStatePending() && fMultitransportProceed) {
|
||||
poller->Poll(500);
|
||||
|
||||
for (const auto& ch : channelKeys) {
|
||||
for (unsigned int i = 0; i < GetChannels().at(ch).size(); ++i) {
|
||||
for (unsigned int i = 0; i < fChannels.at(ch).size(); ++i) {
|
||||
if (poller->CheckInput(ch, i)) {
|
||||
lock_guard<mutex> lock(fMultitransportMutex);
|
||||
|
||||
@@ -540,7 +604,7 @@ void Device::PollForTransport(const TransportFactory* factory, const vector<stri
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetChannel(ch, i).fMultipart) {
|
||||
if (fChannels.at(ch).at(i).fMultipart) {
|
||||
fMultitransportProceed = HandleMultipartInput(ch, fMultipartInputs.at(ch), i);
|
||||
} else {
|
||||
fMultitransportProceed = HandleMsgInput(ch, fMsgInputs.at(ch), i);
|
||||
@@ -564,7 +628,7 @@ void Device::PollForTransport(const TransportFactory* factory, const vector<stri
|
||||
|
||||
bool Device::HandleMsgInput(const string& chName, const InputMsgCallback& callback, int i)
|
||||
{
|
||||
unique_ptr<Message> input(GetChannel(chName, i).fTransportFactory->CreateMessage());
|
||||
unique_ptr<Message> input(fChannels.at(chName).at(i).fTransportFactory->CreateMessage());
|
||||
|
||||
if (Receive(input, chName, i) >= 0) {
|
||||
return callback(input, i);
|
||||
@@ -586,8 +650,6 @@ bool Device::HandleMultipartInput(const string& chName, const InputMultipartCall
|
||||
|
||||
shared_ptr<TransportFactory> Device::AddTransport(mq::Transport transport)
|
||||
{
|
||||
lock_guard<mutex> lock(fTransportMtx);
|
||||
|
||||
if (transport == mq::Transport::DEFAULT) {
|
||||
transport = fDefaultTransportType;
|
||||
}
|
||||
@@ -621,7 +683,7 @@ void Device::LogSocketRates()
|
||||
size_t chanNameLen = 0;
|
||||
|
||||
// iterate over the channels map
|
||||
for (auto& channel : GetChannels()) {
|
||||
for (auto& channel : fChannels) {
|
||||
// iterate over the channels vector
|
||||
for (auto& subChannel : channel.second) {
|
||||
if (subChannel.fRateLogging > 0) {
|
||||
@@ -705,19 +767,10 @@ void Device::LogSocketRates()
|
||||
}
|
||||
}
|
||||
|
||||
void Device::InterruptTransports()
|
||||
void Device::UnblockTransports()
|
||||
{
|
||||
lock_guard<mutex> lock(fTransportMtx);
|
||||
for (auto& [transportType, transport] : fTransports) {
|
||||
transport->Interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
void Device::ResumeTransports()
|
||||
{
|
||||
lock_guard<mutex> lock(fTransportMtx);
|
||||
for (auto& [transportType, transport] : fTransports) {
|
||||
transport->Resume();
|
||||
for (auto& transport : fTransports) {
|
||||
transport.second->Interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,38 +779,31 @@ void Device::ResetTaskWrapper()
|
||||
ResetTask();
|
||||
|
||||
if (!NewStatePending()) {
|
||||
ChangeStateOrThrow(Transition::Auto);
|
||||
ChangeState(Transition::Auto);
|
||||
}
|
||||
}
|
||||
|
||||
void Device::ResetWrapper()
|
||||
{
|
||||
{
|
||||
lock_guard<mutex> lock(fTransportMtx);
|
||||
for (auto& [transportType, transport] : fTransports) {
|
||||
transport->Reset();
|
||||
}
|
||||
fTransports.clear();
|
||||
for (auto& transport : fTransports) {
|
||||
transport.second->Reset();
|
||||
}
|
||||
|
||||
Reset();
|
||||
|
||||
GetChannels().clear();
|
||||
fChannels.clear();
|
||||
fTransports.clear();
|
||||
fTransportFactory.reset();
|
||||
if (!NewStatePending()) {
|
||||
ChangeStateOrThrow(Transition::Auto);
|
||||
ChangeState(Transition::Auto);
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: Remove this once Device::fChannels is no longer public
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
Device::~Device()
|
||||
{
|
||||
UnsubscribeFromNewTransition("device");
|
||||
fStateMachine.StopHandlingStates();
|
||||
LOG(debug) << "Shutting down device " << fId;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
} // namespace fair::mq
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2021-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* Copyright (C) 2021-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
@@ -9,9 +9,12 @@
|
||||
#ifndef FAIR_MQ_DEVICE_H
|
||||
#define FAIR_MQ_DEVICE_H
|
||||
|
||||
// FairMQ
|
||||
#include <algorithm> // find
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <fairlogger/Logger.h>
|
||||
#include <fairmq/Channel.h>
|
||||
#include <fairmq/Error.h>
|
||||
#include <fairmq/Message.h>
|
||||
#include <fairmq/Parts.h>
|
||||
#include <fairmq/ProgOptions.h>
|
||||
@@ -21,15 +24,6 @@
|
||||
#include <fairmq/TransportFactory.h>
|
||||
#include <fairmq/Transports.h>
|
||||
#include <fairmq/UnmanagedRegion.h>
|
||||
|
||||
// logger
|
||||
#include <fairlogger/Logger.h>
|
||||
|
||||
// std
|
||||
#include <algorithm> // find
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory> // unique_ptr
|
||||
#include <mutex>
|
||||
@@ -234,7 +228,7 @@ class Device
|
||||
}
|
||||
}
|
||||
|
||||
return GetChannel(chans.at(0), 0).Transport()->CreatePoller(GetChannels(), chans);
|
||||
return GetChannel(chans.at(0), 0).Transport()->CreatePoller(fChannels, chans);
|
||||
}
|
||||
|
||||
PollerPtr NewPoller(const std::vector<Channel*>& channels)
|
||||
@@ -322,7 +316,7 @@ class Device
|
||||
|
||||
Channel& GetChannel(const std::string& channelName, const int index = 0)
|
||||
try {
|
||||
return GetChannels().at(channelName).at(index);
|
||||
return fChannels.at(channelName).at(index);
|
||||
} catch (const std::out_of_range& oor) {
|
||||
LOG(error) << "GetChannel(): '" << channelName << "[" << index << "]' does not exist.";
|
||||
throw;
|
||||
@@ -330,7 +324,7 @@ class Device
|
||||
|
||||
size_t GetNumSubChannels(const std::string& channelName)
|
||||
try {
|
||||
return GetChannels().at(channelName).size();
|
||||
return fChannels.at(channelName).size();
|
||||
} catch (const std::out_of_range& oor) {
|
||||
LOG(error) << "GetNumSubChannels(): '" << channelName << "' does not exist.";
|
||||
throw;
|
||||
@@ -341,7 +335,7 @@ class Device
|
||||
/// @param index sub-channel
|
||||
unsigned long GetNumberOfConnectedPeers(const std::string& channelName, int index = 0)
|
||||
{
|
||||
return GetChannel(channelName, index).GetNumberOfConnectedPeers();
|
||||
return fChannels.at(channelName).at(index).GetNumberOfConnectedPeers();
|
||||
}
|
||||
|
||||
virtual void RegisterChannelEndpoints() {}
|
||||
@@ -439,23 +433,7 @@ class Device
|
||||
fTransports; ///< Container for transports
|
||||
|
||||
public:
|
||||
[[deprecated("Use GetChannels() instead.")]]
|
||||
std::unordered_map<std::string, std::vector<Channel>> fChannels; ///< Device channels
|
||||
std::unordered_map<std::string, std::vector<Channel>>& GetChannels()
|
||||
{
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
return fChannels;
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
std::unordered_map<std::string, std::vector<Channel>> const& GetChannels() const
|
||||
{
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
return fChannels;
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
std::unique_ptr<ProgOptions> fInternalConfig; ///< Internal program options configuration
|
||||
ProgOptions* fConfig; ///< Pointer to config (internal or external)
|
||||
|
||||
@@ -499,49 +477,21 @@ class Device
|
||||
public:
|
||||
/// @brief Request a device state transition
|
||||
/// @param transition state transition
|
||||
/// @return whether the transition was successfully scheduled
|
||||
///
|
||||
/// 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.
|
||||
[[nodiscard]] bool ChangeState(const Transition transition)
|
||||
{
|
||||
return fStateMachine.ChangeState(transition);
|
||||
}
|
||||
bool ChangeState(const Transition transition) { return fStateMachine.ChangeState(transition); }
|
||||
/// @brief Request a device state transition
|
||||
/// @param transition state transition
|
||||
/// @return whether the transition was successfully scheduled
|
||||
///
|
||||
/// 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.
|
||||
[[nodiscard]] bool ChangeState(const std::string& transition)
|
||||
bool ChangeState(const std::string& transition)
|
||||
{
|
||||
return fStateMachine.ChangeState(GetTransition(transition));
|
||||
}
|
||||
/// @brief Request a device state transition
|
||||
/// @param transition state transition
|
||||
/// @throws when the transition could not have been scheduled
|
||||
///
|
||||
/// Throwing version of Device::ChangeState().
|
||||
void ChangeStateOrThrow(Transition transition)
|
||||
{
|
||||
if(!ChangeState(transition)) {
|
||||
auto const err = MakeErrorCode(ErrorCode::DeviceChangeStateFailed);
|
||||
throw std::system_error(err.value(),
|
||||
err.category(),
|
||||
tools::ToString("Invalid transition: ", transition));
|
||||
}
|
||||
}
|
||||
/// @brief Request a device state transition
|
||||
/// @param transition state transition
|
||||
/// @throws when the transition could not have been scheduled
|
||||
///
|
||||
/// Throwing version of Device::ChangeState().
|
||||
void ChangeStateOrThrow(std::string const& transition)
|
||||
{
|
||||
ChangeStateOrThrow(GetTransition(transition));
|
||||
}
|
||||
|
||||
/// @brief waits for the next state (any) to occur
|
||||
State WaitForNextState() { return fStateQueue.WaitForNext(); }
|
||||
@@ -552,6 +502,8 @@ class Device
|
||||
/// @param state state to wait for
|
||||
void WaitForState(const std::string& state) { WaitForState(GetState(state)); }
|
||||
|
||||
void TransitionTo(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)
|
||||
@@ -636,10 +588,7 @@ class Device
|
||||
void ResetWrapper();
|
||||
|
||||
/// Notifies transports to cease any blocking activity
|
||||
void InterruptTransports();
|
||||
|
||||
/// Notifies transports to resume any blocking activity
|
||||
void ResumeTransports();
|
||||
void UnblockTransports();
|
||||
|
||||
/// Shuts down the transports and the device
|
||||
void Exit() {}
|
||||
@@ -673,12 +622,15 @@ class Device
|
||||
|
||||
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 fTransportMtx; ///< guards access to transports container
|
||||
std::mutex fTransitionMtx;
|
||||
bool fTransitioning;
|
||||
};
|
||||
|
||||
} // namespace fair::mq
|
||||
|
@@ -152,7 +152,7 @@ auto DeviceRunner::Run() -> int
|
||||
fDevice->RegisterChannelEndpoints();
|
||||
if (fConfig.Count("print-channels")) {
|
||||
fDevice->PrintRegisteredChannels();
|
||||
fDevice->ChangeStateOrThrow(fair::mq::Transition::End);
|
||||
fDevice->ChangeState(fair::mq::Transition::End);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ auto DeviceRunner::Run() -> int
|
||||
if (fConfig.Count("version")) {
|
||||
LOGV(info, verylow) << "FairMQ version: " << FAIRMQ_GIT_VERSION;
|
||||
LOGV(info, verylow) << "User device version: " << fDevice->GetVersion();
|
||||
fDevice->ChangeStateOrThrow(fair::mq::Transition::End);
|
||||
fDevice->ChangeState(fair::mq::Transition::End);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
39
fairmq/Error.cxx
Normal file
39
fairmq/Error.cxx
Normal file
@@ -0,0 +1,39 @@
|
||||
/********************************************************************************
|
||||
* 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, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#include "Error.h"
|
||||
|
||||
namespace fair::mq {
|
||||
|
||||
const char* ErrorCategory::name() const noexcept { return "fairmq"; }
|
||||
|
||||
std::string ErrorCategory::message(int ev) const
|
||||
{
|
||||
switch (static_cast<ErrorCode>(ev)) {
|
||||
case ErrorCode::OperationInProgress:
|
||||
return "async operation already in progress";
|
||||
case ErrorCode::OperationTimeout:
|
||||
return "async operation timed out";
|
||||
case ErrorCode::OperationCanceled:
|
||||
return "async operation canceled";
|
||||
case ErrorCode::DeviceChangeStateFailed:
|
||||
return "failed to change state of a fairmq device";
|
||||
case ErrorCode::DeviceGetPropertiesFailed:
|
||||
return "failed to get fairmq device properties";
|
||||
case ErrorCode::DeviceSetPropertiesFailed:
|
||||
return "failed to set fairmq device properties";
|
||||
default:
|
||||
return "(unrecognized error)";
|
||||
}
|
||||
}
|
||||
|
||||
const ErrorCategory errorCategory{};
|
||||
|
||||
std::error_code MakeErrorCode(ErrorCode e) { return {static_cast<int>(e), errorCategory}; }
|
||||
|
||||
} // namespace fair::mq
|
@@ -38,34 +38,14 @@ enum class ErrorCode
|
||||
DeviceSetPropertiesFailed
|
||||
};
|
||||
|
||||
std::error_code MakeErrorCode(ErrorCode);
|
||||
|
||||
struct ErrorCategory : std::error_category
|
||||
{
|
||||
const char* name() const noexcept override { return "fairmq"; }
|
||||
std::string message(int ev) const override
|
||||
{
|
||||
switch (static_cast<ErrorCode>(ev)) {
|
||||
case ErrorCode::OperationInProgress:
|
||||
return "async operation already in progress";
|
||||
case ErrorCode::OperationTimeout:
|
||||
return "async operation timed out";
|
||||
case ErrorCode::OperationCanceled:
|
||||
return "async operation canceled";
|
||||
case ErrorCode::DeviceChangeStateFailed:
|
||||
return "failed to change state of a fairmq device";
|
||||
case ErrorCode::DeviceGetPropertiesFailed:
|
||||
return "failed to get fairmq device properties";
|
||||
case ErrorCode::DeviceSetPropertiesFailed:
|
||||
return "failed to set fairmq device properties";
|
||||
default:
|
||||
return "(unrecognized error)";
|
||||
}
|
||||
}
|
||||
const char* name() const noexcept override;
|
||||
std::string message(int ev) const override;
|
||||
};
|
||||
|
||||
static ErrorCategory ec;
|
||||
|
||||
inline std::error_code MakeErrorCode(ErrorCode e) { return {static_cast<int>(e), ec}; }
|
||||
|
||||
} // namespace fair::mq
|
||||
|
||||
namespace std {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2014-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* Copyright (C) 2014-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
@@ -38,12 +38,9 @@ struct Parts
|
||||
template<typename... Ps>
|
||||
Parts(Ps&&... parts)
|
||||
{
|
||||
fParts.reserve(sizeof...(Ps));
|
||||
AddPart(std::forward<Ps>(parts)...);
|
||||
}
|
||||
|
||||
[[deprecated("Avoid owning raw pointer args, use AddPart(MessagePtr) instead.")]]
|
||||
void AddPart(Message* msg) { fParts.push_back(MessagePtr(msg)); }
|
||||
void AddPart(MessagePtr msg) { fParts.push_back(std::move(msg)); }
|
||||
|
||||
template<typename... Ts>
|
||||
@@ -63,15 +60,9 @@ struct Parts
|
||||
}
|
||||
}
|
||||
|
||||
Message& operator[](size_type index) { return *(fParts[index]); }
|
||||
Message const& operator[](size_type index) const { return *(fParts[index]); }
|
||||
// TODO: For consistency with the STL interfaces, operator[] should not dereference,
|
||||
// but I have no good idea how to fix this.
|
||||
// reference operator[](size_type index) { return fParts[index]; }
|
||||
// const_reference operator[](size_type index) const { return fParts[index]; }
|
||||
reference operator[](size_type index) { return fParts[index]; }
|
||||
const_reference operator[](size_type index) const { return fParts[index]; }
|
||||
|
||||
[[deprecated("Redundant, dereference at call site e.g. '*(parts.At(index))' instead.")]]
|
||||
Message& AtRef(size_type index) { return *(fParts.at(index)); }
|
||||
reference At(size_type index) { return fParts.at(index); }
|
||||
const_reference At(size_type index) const { return fParts.at(index); }
|
||||
|
||||
|
@@ -22,6 +22,7 @@ using namespace std;
|
||||
using fair::mq::Plugin;
|
||||
using fair::mq::tools::ToString;
|
||||
using fair::mq::tools::ToStrVector;
|
||||
namespace fs = boost::filesystem;
|
||||
namespace po = boost::program_options;
|
||||
namespace dll = boost::dll;
|
||||
using boost::optional;
|
||||
@@ -170,7 +171,7 @@ auto fair::mq::PluginManager::LoadPluginPrelinkedDynamic(const string& pluginNam
|
||||
}
|
||||
|
||||
auto fair::mq::PluginManager::SearchPluginFile(const string& pluginName) const
|
||||
-> fs::path
|
||||
-> boost::filesystem::path
|
||||
{
|
||||
for (const auto& searchPath : SearchPaths()) {
|
||||
for (const auto& libPrefix : {fgkLibPrefix, fgkLibPrefixAlt}) {
|
||||
@@ -180,7 +181,7 @@ auto fair::mq::PluginManager::SearchPluginFile(const string& pluginName) const
|
||||
libPrefix,
|
||||
pluginName,
|
||||
boost::dll::detail::shared_library_impl::suffix().native());
|
||||
auto const found = fs::exists(file);
|
||||
auto const found = boost::filesystem::exists(file);
|
||||
if (found) {
|
||||
return file;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2017-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
@@ -13,16 +13,9 @@
|
||||
#include <fairmq/PluginServices.h>
|
||||
#include <fairmq/tools/Strings.h>
|
||||
|
||||
#if FAIRMQ_HAS_STD_FILESYSTEM
|
||||
#include <filesystem>
|
||||
namespace fs = std::filesystem;
|
||||
#else
|
||||
#define BOOST_FILESYSTEM_VERSION 3
|
||||
#define BOOST_FILESYSTEM_NO_DEPRECATED
|
||||
#include <boost/filesystem.hpp>
|
||||
namespace fs = ::boost::filesystem;
|
||||
#endif
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/dll/import.hpp>
|
||||
@@ -67,13 +60,13 @@ class PluginManager
|
||||
LOG(debug) << "Shutting down Plugin Manager";
|
||||
}
|
||||
|
||||
auto SetSearchPaths(const std::vector<fs::path>&) -> void;
|
||||
auto AppendSearchPath(const fs::path&) -> void;
|
||||
auto PrependSearchPath(const fs::path&) -> void;
|
||||
auto SearchPaths() const -> const std::vector<fs::path>& { return fSearchPaths; }
|
||||
auto SetSearchPaths(const std::vector<boost::filesystem::path>&) -> void;
|
||||
auto AppendSearchPath(const boost::filesystem::path&) -> void;
|
||||
auto PrependSearchPath(const boost::filesystem::path&) -> void;
|
||||
auto SearchPaths() const -> const std::vector<boost::filesystem::path>& { return fSearchPaths; }
|
||||
struct BadSearchPath : std::invalid_argument { using std::invalid_argument::invalid_argument; };
|
||||
|
||||
auto SearchPluginFile(const std::string&) const -> fs::path;
|
||||
auto SearchPluginFile(const std::string&) const -> boost::filesystem::path;
|
||||
struct PluginNotFound : std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
auto LoadPlugin(const std::string& pluginName) -> void;
|
||||
auto LoadPlugins(const std::vector<std::string>& pluginNames) -> void { for(const auto& pluginName : pluginNames) { LoadPlugin(pluginName); } }
|
||||
@@ -95,33 +88,18 @@ class PluginManager
|
||||
auto WaitForPluginsToReleaseDeviceControl() -> void { fPluginServices->WaitForReleaseDeviceControl(); }
|
||||
|
||||
private:
|
||||
static auto ValidateSearchPath(const fs::path&) -> void;
|
||||
static auto ValidateSearchPath(const boost::filesystem::path&) -> void;
|
||||
|
||||
auto LoadPluginPrelinkedDynamic(const std::string& pluginName) -> void;
|
||||
auto LoadPluginDynamic(const std::string& pluginName) -> void;
|
||||
auto LoadPluginStatic(const std::string& pluginName) -> void;
|
||||
#if FAIRMQ_HAS_STD_FILESYSTEM
|
||||
template<typename T>
|
||||
static auto AdaptPathType(T&& path)
|
||||
{
|
||||
if constexpr(std::is_same_v<T, std::filesystem::path>) {
|
||||
return boost::filesystem::path(std::forward<T>(path));
|
||||
} else {
|
||||
return std::forward<T>(path);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
template<typename FirstArg, typename... Args>
|
||||
auto LoadSymbols(const std::string& pluginName, FirstArg&& farg, Args&&... args) -> void
|
||||
template<typename... Args>
|
||||
auto LoadSymbols(const std::string& pluginName, Args&&... args) -> void
|
||||
{
|
||||
using namespace boost::dll;
|
||||
using fair::mq::tools::ToString;
|
||||
|
||||
#if FAIRMQ_HAS_STD_FILESYSTEM
|
||||
auto lib = shared_library{AdaptPathType(std::forward<FirstArg>(farg)), std::forward<Args>(args)...};
|
||||
#else
|
||||
auto lib = shared_library{std::forward<FirstArg>(farg), std::forward<Args>(args)...};
|
||||
#endif
|
||||
auto lib = shared_library{std::forward<Args>(args)...};
|
||||
fgDLLKeepAlive.push_back(lib);
|
||||
|
||||
fPluginFactories[pluginName] = import_alias<PluginFactory>(
|
||||
@@ -143,7 +121,7 @@ class PluginManager
|
||||
|
||||
static const std::string fgkLibPrefix;
|
||||
static const std::string fgkLibPrefixAlt;
|
||||
std::vector<fs::path> fSearchPaths;
|
||||
std::vector<boost::filesystem::path> fSearchPaths;
|
||||
static std::vector<boost::dll::shared_library> fgDLLKeepAlive; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
std::map<std::string, std::function<PluginFactory>> fPluginFactories;
|
||||
std::unique_ptr<PluginServices> fPluginServices;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2014-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* Copyright (C) 2014-2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
@@ -8,15 +8,7 @@
|
||||
|
||||
#include <fairmq/Properties.h>
|
||||
|
||||
#if FAIRMQ_HAS_STD_FILESYSTEM
|
||||
#include <filesystem>
|
||||
namespace fs = std::filesystem;
|
||||
#else
|
||||
#define BOOST_FILESYSTEM_VERSION 3
|
||||
#define BOOST_FILESYSTEM_NO_DEPRECATED
|
||||
#include <boost/filesystem.hpp>
|
||||
namespace fs = ::boost::filesystem;
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using boost::any_cast;
|
||||
@@ -92,12 +84,7 @@ unordered_map<type_index, function<pair<string, string>(const Property&)>> Prope
|
||||
{ type_index(typeid(long double)), [](const Property& p) { return getStringPair<long double>(p, "long double"); } },
|
||||
{ type_index(typeid(bool)), [](const Property& p) { stringstream ss; ss << boolalpha << any_cast<bool>(p); return pair<string, string>{ ss.str(), "bool" }; } },
|
||||
{ type_index(typeid(vector<bool>)), [](const Property& p) { stringstream ss; ss << boolalpha << any_cast<vector<bool>>(p); return pair<string, string>{ ss.str(), "vector<bool>>" }; } },
|
||||
{ type_index(typeid(fs::path)), [](const Property& p) { return getStringPair<fs::path>(p,
|
||||
#if FAIRMQ_HAS_STD_FILESYSTEM
|
||||
"std::filesystem::path"); } },
|
||||
#else
|
||||
"boost::filesystem::path"); } },
|
||||
#endif
|
||||
{ type_index(typeid(boost::filesystem::path)), [](const Property& p) { return getStringPair<boost::filesystem::path>(p, "boost::filesystem::path"); } },
|
||||
{ type_index(typeid(vector<char>)), [](const Property& p) { return getStringPair<vector<char>>(p, "vector<char>"); } },
|
||||
{ type_index(typeid(vector<signed char>)), [](const Property& p) { return getStringPair<vector<signed char>>(p, "vector<signed char>"); } },
|
||||
{ type_index(typeid(vector<unsigned char>)), [](const Property& p) { return getStringPair<vector<unsigned char>>(p, "vector<unsigned char>"); } },
|
||||
@@ -113,12 +100,7 @@ unordered_map<type_index, function<pair<string, string>(const Property&)>> Prope
|
||||
{ type_index(typeid(vector<float>)), [](const Property& p) { return getStringPair<vector<float>>(p, "vector<float>"); } },
|
||||
{ type_index(typeid(vector<double>)), [](const Property& p) { return getStringPair<vector<double>>(p, "vector<double>"); } },
|
||||
{ type_index(typeid(vector<long double>)), [](const Property& p) { return getStringPair<vector<long double>>(p, "vector<long double>"); } },
|
||||
{ type_index(typeid(vector<fs::path>)), [](const Property& p) { return getStringPair<vector<fs::path>>(p,
|
||||
#if FAIRMQ_HAS_STD_FILESYSTEM
|
||||
"vector<std::filesystem::path>"); } },
|
||||
#else
|
||||
"vector<boost::filesystem::path>"); } },
|
||||
#endif
|
||||
{ type_index(typeid(vector<boost::filesystem::path>)), [](const Property& p) { return getStringPair<vector<boost::filesystem::path>>(p, "vector<boost::filesystem::path>"); } },
|
||||
};
|
||||
|
||||
unordered_map<type_index, void(*)(const EventManager&, const string&, const Property&)> PropertyHelper::fEventEmitters = {
|
||||
@@ -140,7 +122,7 @@ unordered_map<type_index, void(*)(const EventManager&, const string&, const Prop
|
||||
{ type_index(typeid(long double)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, long double>(k, any_cast<long double>(p)); } },
|
||||
{ type_index(typeid(bool)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, bool>(k, any_cast<bool>(p)); } },
|
||||
{ type_index(typeid(vector<bool>)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, vector<bool>>(k, any_cast<vector<bool>>(p)); } },
|
||||
{ type_index(typeid(fs::path)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, fs::path>(k, any_cast<fs::path>(p)); } },
|
||||
{ type_index(typeid(boost::filesystem::path)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, boost::filesystem::path>(k, any_cast<boost::filesystem::path>(p)); } },
|
||||
{ type_index(typeid(vector<char>)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, vector<char>>(k, any_cast<vector<char>>(p)); } },
|
||||
{ type_index(typeid(vector<signed char>)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, vector<signed char>>(k, any_cast<vector<signed char>>(p)); } },
|
||||
{ type_index(typeid(vector<unsigned char>)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, vector<unsigned char>>(k, any_cast<vector<unsigned char>>(p)); } },
|
||||
@@ -156,7 +138,7 @@ unordered_map<type_index, void(*)(const EventManager&, const string&, const Prop
|
||||
{ type_index(typeid(vector<float>)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, vector<float>>(k, any_cast<vector<float>>(p)); } },
|
||||
{ type_index(typeid(vector<double>)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, vector<double>>(k, any_cast<vector<double>>(p)); } },
|
||||
{ type_index(typeid(vector<long double>)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, vector<long double>>(k, any_cast<vector<long double>>(p)); } },
|
||||
{ type_index(typeid(vector<fs::path>)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, vector<fs::path>>(k, any_cast<vector<fs::path>>(p)); } },
|
||||
{ type_index(typeid(vector<boost::filesystem::path>)), [](const EventManager& em, const string& k, const Property& p) { em.Emit<PropertyChange, vector<boost::filesystem::path>>(k, any_cast<vector<boost::filesystem::path>>(p)); } },
|
||||
};
|
||||
|
||||
} // namespace fair::mq
|
||||
|
@@ -157,13 +157,6 @@ struct Machine_ : public state_machine_def<Machine_>
|
||||
}
|
||||
}
|
||||
|
||||
void CallStatePrep(const State state) const
|
||||
{
|
||||
if (!fStatePrepSignal.empty()) {
|
||||
fStatePrepSignal(state);
|
||||
}
|
||||
}
|
||||
|
||||
void CallNewTransitionCallbacks(const Transition transition) const
|
||||
{
|
||||
if (!fNewTransitionSignal.empty()) {
|
||||
@@ -182,7 +175,6 @@ struct Machine_ : public state_machine_def<Machine_>
|
||||
|
||||
boost::signals2::signal<void(const State)> fStateChangeSignal;
|
||||
boost::signals2::signal<void(const State)> fStateHandleSignal;
|
||||
boost::signals2::signal<void(const State)> fStatePrepSignal;
|
||||
boost::signals2::signal<void(const Transition)> fNewTransitionSignal;
|
||||
unordered_map<string, boost::signals2::connection> fStateChangeSignalsMap;
|
||||
unordered_map<string, boost::signals2::connection> fNewTransitionSignalsMap;
|
||||
@@ -206,7 +198,6 @@ struct Machine_ : public state_machine_def<Machine_>
|
||||
}
|
||||
}
|
||||
|
||||
CallStatePrep(fState);
|
||||
CallStateChangeCallbacks(fState);
|
||||
CallStateHandler(fState);
|
||||
}
|
||||
@@ -322,16 +313,6 @@ void StateMachine::UnsubscribeFromStateChange(const string& key)
|
||||
}
|
||||
}
|
||||
|
||||
void StateMachine::PrepareState(std::function<void(const State)> callback)
|
||||
{
|
||||
auto fsm = static_pointer_cast<FairMQFSM>(fFsm);
|
||||
if (fsm->fStatePrepSignal.empty()) {
|
||||
fsm->fStatePrepSignal.connect(callback);
|
||||
} else {
|
||||
LOG(error) << "state preparation handler is already set";
|
||||
}
|
||||
}
|
||||
|
||||
void StateMachine::HandleStates(function<void(const State)> callback)
|
||||
{
|
||||
auto fsm = static_pointer_cast<FairMQFSM>(fFsm);
|
||||
@@ -345,9 +326,6 @@ void StateMachine::HandleStates(function<void(const State)> callback)
|
||||
void StateMachine::StopHandlingStates()
|
||||
{
|
||||
auto fsm = static_pointer_cast<FairMQFSM>(fFsm);
|
||||
if (!fsm->fStatePrepSignal.empty()) {
|
||||
fsm->fStatePrepSignal.disconnect_all_slots();
|
||||
}
|
||||
if (!fsm->fStateHandleSignal.empty()) {
|
||||
fsm->fStateHandleSignal.disconnect_all_slots();
|
||||
}
|
||||
|
@@ -35,7 +35,6 @@ class StateMachine
|
||||
void SubscribeToStateChange(const std::string& key, std::function<void(const State)> callback);
|
||||
void UnsubscribeFromStateChange(const std::string& key);
|
||||
|
||||
void PrepareState(std::function<void(const State)> callback);
|
||||
void HandleStates(std::function<void(const State)> callback);
|
||||
void StopHandlingStates();
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2017-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* Copyright (C) 2017-2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
@@ -10,7 +10,6 @@
|
||||
#define FAIR_MQ_TOOLS_H
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include <fairmq/tools/Compiler.h>
|
||||
#include <fairmq/tools/CppSTL.h>
|
||||
#include <fairmq/tools/Exceptions.h>
|
||||
#include <fairmq/tools/InstanceLimit.h>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2017-2023 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,6 +9,9 @@
|
||||
#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 <fairlogger/Logger.h>
|
||||
#include <fairmq/Tools.h>
|
||||
#include <memory>
|
||||
@@ -36,12 +39,20 @@ auto TransportFactory::CreateTransportFactory(const string& type,
|
||||
} else if (type == "shmem") {
|
||||
return make_shared<shmem::TransportFactory>(finalId, config);
|
||||
}
|
||||
#ifdef BUILD_OFI_TRANSPORT
|
||||
else if (type == "ofi") {
|
||||
return make_shared<ofi::TransportFactory>(finalId, config);
|
||||
}
|
||||
#endif /* BUILD_OFI_TRANSPORT */
|
||||
else {
|
||||
LOG(error) << "Unavailable transport requested: "
|
||||
<< "\"" << type << "\""
|
||||
<< ". Available are: "
|
||||
<< "\"zeromq\","
|
||||
<< "\"shmem\""
|
||||
#ifdef BUILD_OFI_TRANSPORT
|
||||
<< ", and \"ofi\""
|
||||
#endif /* BUILD_OFI_TRANSPORT */
|
||||
<< ". Exiting.";
|
||||
throw TransportFactoryError(tools::ToString("Unavailable transport requested: ", type));
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2014-2023 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,8 @@ enum class Transport
|
||||
{
|
||||
DEFAULT,
|
||||
ZMQ,
|
||||
SHM
|
||||
SHM,
|
||||
OFI
|
||||
};
|
||||
|
||||
struct TransportError : std::runtime_error
|
||||
@@ -33,13 +34,15 @@ struct TransportError : std::runtime_error
|
||||
static const std::unordered_map<std::string, Transport> TransportTypes{
|
||||
{"default", Transport::DEFAULT},
|
||||
{"zeromq", Transport::ZMQ},
|
||||
{"shmem", Transport::SHM}
|
||||
{"shmem", Transport::SHM},
|
||||
{"ofi", Transport::OFI}
|
||||
};
|
||||
|
||||
static const std::unordered_map<Transport, std::string> TransportNames{
|
||||
{Transport::DEFAULT, "default"},
|
||||
{Transport::ZMQ, "zeromq"},
|
||||
{Transport::SHM, "shmem"}
|
||||
{Transport::SHM, "shmem"},
|
||||
{Transport::OFI, "ofi"}
|
||||
};
|
||||
|
||||
inline std::string TransportName(Transport transport) { return TransportNames.at(transport); }
|
||||
@@ -58,7 +61,11 @@ inline std::ostream& operator<<(std::ostream& os, const Transport& transport)
|
||||
|
||||
inline auto GetEnabledTransports() -> std::vector<Transport>
|
||||
{
|
||||
#ifdef BUILD_OFI_TRANSPORT
|
||||
return {Transport::ZMQ, Transport::SHM, Transport::OFI};
|
||||
#else
|
||||
return {Transport::ZMQ, Transport::SHM};
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace fair::mq
|
||||
|
@@ -19,7 +19,7 @@
|
||||
#define FAIRMQ_GIT_DATE "@PROJECT_GIT_DATE@"
|
||||
#define FAIRMQ_REPO_URL "https://github.com/FairRootGroup/FairMQ"
|
||||
#define FAIRMQ_LICENSE "LGPL-3.0"
|
||||
#define FAIRMQ_COPYRIGHT "2012-2023 GSI"
|
||||
#define FAIRMQ_COPYRIGHT "2012-2022 GSI"
|
||||
#define FAIRMQ_BUILD_TYPE "@CMAKE_BUILD_TYPE@"
|
||||
|
||||
#endif // FAIR_MQ_VERSION_H
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2014-2023 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, *
|
||||
@@ -47,7 +47,7 @@ class Merger : public Device
|
||||
|
||||
std::vector<Channel*> chans;
|
||||
|
||||
for (auto& chan : GetChannels().at(fInChannelName)) {
|
||||
for (auto& chan : fChannels.at(fInChannelName)) {
|
||||
chans.push_back(&chan);
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2014-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* Copyright (C) 2014-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
|
@@ -63,7 +63,6 @@ echo "Usage: startBenchmark [message size=1000000] [number of iterations=0] [tra
|
||||
|
||||
SAMPLER="fairmq-bsampler"
|
||||
SAMPLER+=" --id bsampler1"
|
||||
SAMPLER+=" --shm-monitor true"
|
||||
#SAMPLER+=" --io-threads 2"
|
||||
#SAMPLER+=" --control static"
|
||||
SAMPLER+=" --transport $transport"
|
||||
@@ -82,7 +81,6 @@ echo "pid: $!"
|
||||
|
||||
SINK="fairmq-sink"
|
||||
SINK+=" --id sink1"
|
||||
SINK+=" --shm-monitor true"
|
||||
#SINK+=" --io-threads 2"
|
||||
#SINK+=" --control static"
|
||||
SINK+=" --transport $transport"
|
||||
|
130
fairmq/ofi/Context.cxx
Normal file
130
fairmq/ofi/Context.cxx
Normal file
@@ -0,0 +1,130 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2018-2022 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 <arpa/inet.h>
|
||||
#include <asiofi/version.hpp>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <fairlogger/Logger.h>
|
||||
#include <fairmq/ofi/Context.h>
|
||||
#include <fairmq/tools/Strings.h>
|
||||
#include <memory>
|
||||
#include <netinet/in.h>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <sys/socket.h>
|
||||
|
||||
namespace fair::mq::ofi
|
||||
{
|
||||
|
||||
using namespace std;
|
||||
|
||||
Context::Context(mq::TransportFactory& sendFactory,
|
||||
mq::TransportFactory& receiveFactory,
|
||||
int numberIoThreads)
|
||||
: fIoWork(fIoContext)
|
||||
, fReceiveFactory(receiveFactory)
|
||||
, fSendFactory(sendFactory)
|
||||
, fSizeHint(0)
|
||||
{
|
||||
InitThreadPool(numberIoThreads);
|
||||
}
|
||||
|
||||
auto Context::InitThreadPool(int numberIoThreads) -> void
|
||||
{
|
||||
assert(numberIoThreads > 0);
|
||||
|
||||
for (int i = 1; i <= numberIoThreads; ++i) {
|
||||
fThreadPool.emplace_back([&, i, numberIoThreads]{
|
||||
try {
|
||||
LOG(debug) << "OFI transport: I/O thread #" << i << " of " << numberIoThreads << " started";
|
||||
fIoContext.run();
|
||||
LOG(debug) << "OFI transport: I/O thread #" << i << " of " << numberIoThreads << " stopped";
|
||||
} catch (const std::exception& e) {
|
||||
LOG(error) << "OFI transport: Uncaught exception in I/O thread #" << i << ": " << e.what();
|
||||
} catch (...) {
|
||||
LOG(error) << "OFI transport: Uncaught exception in I/O thread #" << i;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
auto Context::Reset() -> void
|
||||
{
|
||||
// TODO "Linger", rethink this
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
|
||||
fIoContext.stop();
|
||||
}
|
||||
|
||||
Context::~Context()
|
||||
{
|
||||
for (auto& thread : fThreadPool)
|
||||
thread.join();
|
||||
}
|
||||
|
||||
auto Context::GetAsiofiVersion() const -> string
|
||||
{
|
||||
return ASIOFI_VERSION;
|
||||
}
|
||||
|
||||
auto Context::ConvertAddress(std::string address) -> Address
|
||||
{
|
||||
string protocol, ip;
|
||||
unsigned int port = 0;
|
||||
regex address_regex("^([a-z]+)://([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+):([0-9]+).*");
|
||||
smatch address_result;
|
||||
if (regex_match(address, address_result, address_regex)) {
|
||||
protocol = address_result[1];
|
||||
ip = address_result[2];
|
||||
port = stoul(address_result[3]);
|
||||
// LOG(debug) << "Parsed '" << protocol << "', '" << ip << "', '" << port << "' fields from '" << address << "'";
|
||||
} else {
|
||||
throw ContextError(tools::ToString("Wrong format: Address must be in format prot://ip:port"));
|
||||
}
|
||||
|
||||
return { protocol, ip, port };
|
||||
}
|
||||
|
||||
auto Context::ConvertAddress(Address address) -> sockaddr_in
|
||||
{
|
||||
sockaddr_in sa;
|
||||
if (inet_pton(AF_INET, address.Ip.c_str(), &(sa.sin_addr)) != 1)
|
||||
throw ContextError(tools::ToString("Failed to convert given IP '", address.Ip, "' to struct in_addr, reason: ", strerror(errno)));
|
||||
sa.sin_port = htons(address.Port);
|
||||
sa.sin_family = AF_INET;
|
||||
|
||||
return sa;
|
||||
}
|
||||
|
||||
auto Context::ConvertAddress(sockaddr_in address) -> Address
|
||||
{
|
||||
return {"tcp", inet_ntoa(address.sin_addr), ntohs(address.sin_port)};
|
||||
}
|
||||
|
||||
auto Context::VerifyAddress(const std::string& address) -> Address
|
||||
{
|
||||
auto addr = ConvertAddress(address);
|
||||
|
||||
if (!(addr.Protocol == "tcp" || addr.Protocol == "verbs"))
|
||||
throw ContextError("Wrong protocol: Supported protocols are: tcp:// and verbs://");
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
auto Context::MakeReceiveMessage(size_t size) -> MessagePtr
|
||||
{
|
||||
return fReceiveFactory.CreateMessage(size);
|
||||
}
|
||||
|
||||
auto Context::MakeSendMessage(size_t size) -> MessagePtr
|
||||
{
|
||||
return fSendFactory.CreateMessage(size);
|
||||
}
|
||||
|
||||
} // namespace fair::mq::ofi
|
92
fairmq/ofi/Context.h
Normal file
92
fairmq/ofi/Context.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/********************************************************************************
|
||||
* 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" *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef FAIR_MQ_OFI_CONTEXT_H
|
||||
#define FAIR_MQ_OFI_CONTEXT_H
|
||||
|
||||
#include <asio/io_context.hpp>
|
||||
#include <asiofi/domain.hpp>
|
||||
#include <asiofi/fabric.hpp>
|
||||
#include <asiofi/info.hpp>
|
||||
#include <fairlogger/Logger.h>
|
||||
#include <fairmq/TransportFactory.h>
|
||||
#include <memory>
|
||||
#include <netinet/in.h>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace fair::mq::ofi
|
||||
{
|
||||
|
||||
enum class ConnectionType : bool { Bind, Connect };
|
||||
|
||||
struct Address {
|
||||
std::string Protocol;
|
||||
std::string Ip;
|
||||
unsigned int Port;
|
||||
friend auto operator<<(std::ostream& os, const Address& a) -> std::ostream&
|
||||
{
|
||||
return os << a.Protocol << "://" << a.Ip << ":" << a.Port;
|
||||
}
|
||||
friend auto operator==(const Address& lhs, const Address& rhs) -> bool
|
||||
{
|
||||
return (lhs.Protocol == rhs.Protocol) && (lhs.Ip == rhs.Ip) && (lhs.Port == rhs.Port);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Context Context.h <fairmq/ofi/Context.h>
|
||||
* @brief Transport-wide context
|
||||
*
|
||||
* @todo TODO insert long description
|
||||
*/
|
||||
class Context
|
||||
{
|
||||
public:
|
||||
Context(mq::TransportFactory& sendFactory,
|
||||
mq::TransportFactory& receiveFactory,
|
||||
int numberIoThreads = 1);
|
||||
Context(const Context&) = delete;
|
||||
Context(Context&&) = delete;
|
||||
Context& operator=(const Context&) = delete;
|
||||
Context& operator=(Context&&) = delete;
|
||||
~Context();
|
||||
|
||||
auto GetAsiofiVersion() const -> std::string;
|
||||
auto GetIoContext() -> asio::io_context& { return fIoContext; }
|
||||
static auto ConvertAddress(std::string address) -> Address;
|
||||
static auto ConvertAddress(Address address) -> sockaddr_in;
|
||||
static auto ConvertAddress(sockaddr_in address) -> Address;
|
||||
static auto VerifyAddress(const std::string& address) -> Address;
|
||||
auto Interrupt() -> void { LOG(debug) << "OFI transport: Interrupted (NOOP - not implemented)."; }
|
||||
auto Resume() -> void { LOG(debug) << "OFI transport: Resumed (NOOP - not implemented)."; }
|
||||
auto Reset() -> void;
|
||||
auto MakeReceiveMessage(size_t size) -> MessagePtr;
|
||||
auto MakeSendMessage(size_t size) -> MessagePtr;
|
||||
auto GetSizeHint() -> size_t { return fSizeHint; }
|
||||
auto SetSizeHint(size_t size) -> void { fSizeHint = size; }
|
||||
|
||||
private:
|
||||
asio::io_context fIoContext;
|
||||
asio::io_context::work fIoWork;
|
||||
std::vector<std::thread> fThreadPool;
|
||||
mq::TransportFactory& fReceiveFactory;
|
||||
mq::TransportFactory& fSendFactory;
|
||||
size_t fSizeHint;
|
||||
|
||||
auto InitThreadPool(int numberIoThreads) -> void;
|
||||
}; /* class Context */
|
||||
|
||||
struct ContextError : std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
|
||||
} // namespace fair::mq::ofi
|
||||
|
||||
#endif /* FAIR_MQ_OFI_CONTEXT_H */
|
112
fairmq/ofi/ControlMessages.h
Normal file
112
fairmq/ofi/ControlMessages.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/********************************************************************************
|
||||
* 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" *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef FAIR_MQ_OFI_CONTROLMESSAGES_H
|
||||
#define FAIR_MQ_OFI_CONTROLMESSAGES_H
|
||||
|
||||
#include <asio/buffer.hpp>
|
||||
#include <cstdint>
|
||||
#include <fairlogger/Logger.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
#include <type_traits>
|
||||
|
||||
namespace asio
|
||||
{
|
||||
|
||||
template<typename PodType>
|
||||
auto buffer(const PodType& obj) -> asio::const_buffer
|
||||
{
|
||||
return asio::const_buffer(static_cast<const void*>(&obj), sizeof(PodType));
|
||||
}
|
||||
|
||||
} // namespace asio
|
||||
|
||||
namespace fair::mq::ofi
|
||||
{
|
||||
|
||||
enum class ControlMessageType
|
||||
{
|
||||
Empty = 1,
|
||||
PostBuffer,
|
||||
PostMultiPartStartBuffer
|
||||
};
|
||||
|
||||
struct Empty
|
||||
{};
|
||||
|
||||
struct PostBuffer
|
||||
{
|
||||
uint64_t size; // buffer size (size_t)
|
||||
};
|
||||
|
||||
struct PostMultiPartStartBuffer
|
||||
{
|
||||
uint32_t numParts; // buffer size (size_t)
|
||||
uint64_t size; // buffer size (size_t)
|
||||
};
|
||||
|
||||
union ControlMessageContent
|
||||
{
|
||||
PostBuffer postBuffer;
|
||||
PostMultiPartStartBuffer postMultiPartStartBuffer;
|
||||
};
|
||||
|
||||
struct ControlMessage
|
||||
{
|
||||
ControlMessageType type;
|
||||
ControlMessageContent msg;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using unique_ptr = std::unique_ptr<T, std::function<void(T*)>>;
|
||||
|
||||
template<typename T, typename... Args>
|
||||
auto MakeControlMessageWithPmr(std::pmr::memory_resource& pmr, Args&&... args)
|
||||
-> ofi::unique_ptr<ControlMessage>
|
||||
{
|
||||
void* mem = pmr.allocate(sizeof(ControlMessage));
|
||||
ControlMessage* ctrl = new (mem) ControlMessage();
|
||||
|
||||
if (std::is_same<T, PostBuffer>::value) {
|
||||
ctrl->type = ControlMessageType::PostBuffer;
|
||||
ctrl->msg.postBuffer = PostBuffer(std::forward<Args>(args)...);
|
||||
} else if (std::is_same<T, PostMultiPartStartBuffer>::value) {
|
||||
ctrl->type = ControlMessageType::PostMultiPartStartBuffer;
|
||||
ctrl->msg.postMultiPartStartBuffer = PostMultiPartStartBuffer(std::forward<Args>(args)...);
|
||||
} else if (std::is_same<T, Empty>::value) {
|
||||
ctrl->type = ControlMessageType::Empty;
|
||||
}
|
||||
|
||||
return ofi::unique_ptr<ControlMessage>(ctrl, [&pmr](ControlMessage* p) {
|
||||
p->~ControlMessage();
|
||||
pmr.deallocate(p, sizeof(T));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
auto MakeControlMessage(Args&&... args) -> ControlMessage
|
||||
{
|
||||
ControlMessage ctrl;
|
||||
|
||||
if (std::is_same<T, PostBuffer>::value) {
|
||||
ctrl.type = ControlMessageType::PostBuffer;
|
||||
} else if (std::is_same<T, PostMultiPartStartBuffer>::value) {
|
||||
ctrl.type = ControlMessageType::PostMultiPartStartBuffer;
|
||||
} else if (std::is_same<T, Empty>::value) {
|
||||
ctrl.type = ControlMessageType::Empty;
|
||||
}
|
||||
ctrl.msg = T(std::forward<Args>(args)...);
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
} // namespace fair::mq::ofi
|
||||
|
||||
#endif /* FAIR_MQ_OFI_CONTROLMESSAGES_H */
|
199
fairmq/ofi/Message.cxx
Normal file
199
fairmq/ofi/Message.cxx
Normal file
@@ -0,0 +1,199 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2018-2022 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 <asiofi.hpp>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <fairlogger/Logger.h>
|
||||
#include <fairmq/ofi/Message.h>
|
||||
#include <zmq.h>
|
||||
|
||||
namespace fair::mq::ofi
|
||||
{
|
||||
|
||||
using namespace std;
|
||||
|
||||
Message::Message(pmr::memory_resource* pmr)
|
||||
: fInitialSize(0)
|
||||
, fSize(0)
|
||||
, fData(nullptr)
|
||||
, fFreeFunction(nullptr)
|
||||
, fHint(nullptr)
|
||||
, fPmr(pmr)
|
||||
{
|
||||
}
|
||||
|
||||
Message::Message(pmr::memory_resource* pmr, Alignment /* alignment */)
|
||||
: fInitialSize(0)
|
||||
, fSize(0)
|
||||
, fData(nullptr)
|
||||
, fFreeFunction(nullptr)
|
||||
, fHint(nullptr)
|
||||
, fPmr(pmr)
|
||||
{
|
||||
}
|
||||
|
||||
Message::Message(pmr::memory_resource* pmr, const size_t size)
|
||||
: fInitialSize(size)
|
||||
, fSize(size)
|
||||
, fData(nullptr)
|
||||
, fFreeFunction(nullptr)
|
||||
, fHint(nullptr)
|
||||
, fPmr(pmr)
|
||||
{
|
||||
if (size) {
|
||||
fData = fPmr->allocate(size);
|
||||
assert(fData);
|
||||
}
|
||||
}
|
||||
|
||||
Message::Message(pmr::memory_resource* pmr, const size_t size, Alignment /* alignment */)
|
||||
: fInitialSize(size)
|
||||
, fSize(size)
|
||||
, fData(nullptr)
|
||||
, fFreeFunction(nullptr)
|
||||
, fHint(nullptr)
|
||||
, fPmr(pmr)
|
||||
{
|
||||
if (size) {
|
||||
fData = fPmr->allocate(size);
|
||||
assert(fData);
|
||||
}
|
||||
}
|
||||
|
||||
Message::Message(pmr::memory_resource* pmr,
|
||||
void* data,
|
||||
const size_t size,
|
||||
FreeFn* ffn,
|
||||
void* hint)
|
||||
: fInitialSize(size)
|
||||
, fSize(size)
|
||||
, fData(data)
|
||||
, fFreeFunction(ffn)
|
||||
, fHint(hint)
|
||||
, fPmr(pmr)
|
||||
{}
|
||||
|
||||
Message::Message(pmr::memory_resource* /*pmr*/,
|
||||
fair::mq::UnmanagedRegionPtr& /*region*/,
|
||||
void* /*data*/,
|
||||
const size_t /*size*/,
|
||||
void* /*hint*/)
|
||||
{
|
||||
throw MessageError{"Not yet implemented."};
|
||||
}
|
||||
|
||||
auto Message::Rebuild() -> void
|
||||
{
|
||||
if (fFreeFunction) {
|
||||
fFreeFunction(fData, fHint);
|
||||
} else {
|
||||
if (fData) {
|
||||
fPmr->deallocate(fData, fSize);
|
||||
}
|
||||
}
|
||||
fData = nullptr;
|
||||
fInitialSize = 0;
|
||||
fSize = 0;
|
||||
fFreeFunction = nullptr;
|
||||
fHint = nullptr;
|
||||
}
|
||||
|
||||
auto Message::Rebuild(Alignment /* alignment */) -> void
|
||||
{
|
||||
// TODO: implement alignment
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
auto Message::Rebuild(size_t size) -> void
|
||||
{
|
||||
if (fFreeFunction) {
|
||||
fFreeFunction(fData, fHint);
|
||||
} else {
|
||||
if (fData) {
|
||||
fPmr->deallocate(fData, fSize);
|
||||
}
|
||||
}
|
||||
if (size) {
|
||||
fData = fPmr->allocate(size);
|
||||
assert(fData);
|
||||
} else {
|
||||
fData = nullptr;
|
||||
}
|
||||
fInitialSize = size;
|
||||
fSize = size;
|
||||
fFreeFunction = nullptr;
|
||||
fHint = nullptr;
|
||||
}
|
||||
|
||||
auto Message::Rebuild(size_t size, Alignment /* alignment */) -> void
|
||||
{
|
||||
// TODO: implement alignment
|
||||
Rebuild(size);
|
||||
}
|
||||
|
||||
auto Message::Rebuild(void* /*data*/, size_t size, FreeFn* ffn, void* hint) -> void
|
||||
{
|
||||
if (fFreeFunction) {
|
||||
fFreeFunction(fData, fHint);
|
||||
} else {
|
||||
if (fData) {
|
||||
fPmr->deallocate(fData, fSize);
|
||||
}
|
||||
}
|
||||
if (size) {
|
||||
fData = fPmr->allocate(size);
|
||||
assert(fData);
|
||||
} else {
|
||||
fData = nullptr;
|
||||
}
|
||||
assert(fData);
|
||||
fInitialSize = size;
|
||||
fSize = size;
|
||||
fFreeFunction = ffn;
|
||||
fHint = hint;
|
||||
}
|
||||
|
||||
auto Message::GetData() const -> void*
|
||||
{
|
||||
return fData;
|
||||
}
|
||||
|
||||
auto Message::GetSize() const -> size_t
|
||||
{
|
||||
return fSize;
|
||||
}
|
||||
|
||||
auto Message::SetUsedSize(size_t size) -> bool
|
||||
{
|
||||
if (size == fSize) {
|
||||
return true;
|
||||
} else if (size <= fSize) {
|
||||
throw MessageError{"Message size shrinking not yet implemented."};
|
||||
} else {
|
||||
throw MessageError{"Cannot grow message size."};
|
||||
}
|
||||
}
|
||||
|
||||
auto Message::Copy(const fair::mq::Message& /*msg*/) -> void
|
||||
{
|
||||
throw MessageError{"Not yet implemented."};
|
||||
}
|
||||
|
||||
Message::~Message()
|
||||
{
|
||||
if (fFreeFunction) {
|
||||
fFreeFunction(fData, fHint);
|
||||
} else {
|
||||
if (fData) {
|
||||
fPmr->deallocate(fData, fSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fair::mq::ofi
|
81
fairmq/ofi/Message.h
Normal file
81
fairmq/ofi/Message.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2018-2022 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_OFI_MESSAGE_H
|
||||
#define FAIR_MQ_OFI_MESSAGE_H
|
||||
|
||||
#include <asiofi.hpp>
|
||||
#include <atomic>
|
||||
#include <cstddef> // size_t
|
||||
#include <fairmq/Message.h>
|
||||
#include <fairmq/Transports.h>
|
||||
#include <fairmq/UnmanagedRegion.h>
|
||||
#include <memory_resource>
|
||||
#include <zmq.h>
|
||||
|
||||
namespace fair::mq::ofi
|
||||
{
|
||||
|
||||
/**
|
||||
* @class Message Message.h <fairmq/ofi/Message.h>
|
||||
* @brief
|
||||
*
|
||||
* @todo TODO insert long description
|
||||
*/
|
||||
class Message final : public fair::mq::Message
|
||||
{
|
||||
public:
|
||||
Message(std::pmr::memory_resource* pmr);
|
||||
Message(std::pmr::memory_resource* pmr, Alignment alignment);
|
||||
Message(std::pmr::memory_resource* pmr, size_t size);
|
||||
Message(std::pmr::memory_resource* pmr, size_t size, Alignment alignment);
|
||||
Message(std::pmr::memory_resource* pmr,
|
||||
void* data,
|
||||
size_t size,
|
||||
FreeFn* ffn,
|
||||
void* hint = nullptr);
|
||||
Message(std::pmr::memory_resource* pmr,
|
||||
fair::mq::UnmanagedRegionPtr& region,
|
||||
void* data,
|
||||
size_t size,
|
||||
void* hint = 0);
|
||||
|
||||
Message(const Message&) = delete;
|
||||
Message(Message&&) = delete;
|
||||
Message& operator=(const Message&) = delete;
|
||||
Message& operator=(Message&&) = delete;
|
||||
|
||||
auto Rebuild() -> void override;
|
||||
auto Rebuild(Alignment alignment) -> void override;
|
||||
auto Rebuild(size_t size) -> void override;
|
||||
auto Rebuild(size_t size, Alignment alignment) -> void override;
|
||||
auto Rebuild(void* data, size_t size, FreeFn* ffn, void* hint = nullptr) -> void override;
|
||||
|
||||
auto GetData() const -> void* override;
|
||||
auto GetSize() const -> size_t override;
|
||||
|
||||
auto SetUsedSize(size_t size) -> bool override;
|
||||
|
||||
auto GetType() const -> fair::mq::Transport override { return fair::mq::Transport::OFI; }
|
||||
|
||||
auto Copy(const fair::mq::Message& msg) -> void override;
|
||||
|
||||
~Message() override;
|
||||
|
||||
private:
|
||||
size_t fInitialSize;
|
||||
size_t fSize;
|
||||
void* fData;
|
||||
FreeFn* fFreeFunction;
|
||||
void* fHint;
|
||||
std::pmr::memory_resource* fPmr;
|
||||
}; /* class Message */
|
||||
|
||||
} // namespace fair::mq::ofi
|
||||
|
||||
#endif /* FAIR_MQ_OFI_MESSAGE_H */
|
680
fairmq/ofi/Socket.cxx
Normal file
680
fairmq/ofi/Socket.cxx
Normal file
@@ -0,0 +1,680 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2018-2022 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 <fairmq/ofi/ControlMessages.h>
|
||||
#include <fairmq/ofi/Socket.h>
|
||||
#include <fairmq/ofi/TransportFactory.h>
|
||||
#include <fairmq/tools/Strings.h>
|
||||
#include <fairlogger/Logger.h>
|
||||
|
||||
#include <asiofi.hpp>
|
||||
#include <asio/buffer.hpp>
|
||||
#include <asio/dispatch.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
namespace fair::mq::ofi
|
||||
{
|
||||
|
||||
using namespace std;
|
||||
|
||||
Socket::Socket(Context& context, const string& type, const string& name, const string& id /*= ""*/)
|
||||
: fContext(context)
|
||||
, fOfiInfo(nullptr)
|
||||
, fOfiFabric(nullptr)
|
||||
, fOfiDomain(nullptr)
|
||||
, fPassiveEndpoint(nullptr)
|
||||
, fDataEndpoint(nullptr)
|
||||
, fControlEndpoint(nullptr)
|
||||
, fId(id + "." + name + "." + type)
|
||||
, fBytesTx(0)
|
||||
, fBytesRx(0)
|
||||
, fMessagesTx(0)
|
||||
, fMessagesRx(0)
|
||||
, fMultiPartRecvCounter(-1)
|
||||
, fSendPushSem(fContext.GetIoContext(), 384)
|
||||
, fSendPopSem(fContext.GetIoContext(), 0)
|
||||
, fRecvPushSem(fContext.GetIoContext(), 384)
|
||||
, fRecvPopSem(fContext.GetIoContext(), 0)
|
||||
, fNeedOfiMemoryRegistration(false)
|
||||
{
|
||||
if (type != "pair") {
|
||||
throw SocketError{tools::ToString("Socket type '", type, "' not implemented for ofi transport.")};
|
||||
}
|
||||
}
|
||||
|
||||
auto Socket::InitOfi(Address addr) -> void
|
||||
{
|
||||
if (!fOfiInfo) {
|
||||
assert(!fOfiFabric);
|
||||
assert(!fOfiDomain);
|
||||
|
||||
asiofi::hints hints;
|
||||
if (addr.Protocol == "tcp") {
|
||||
hints.set_provider("sockets");
|
||||
} else if (addr.Protocol == "verbs") {
|
||||
hints.set_provider("verbs");
|
||||
}
|
||||
if (fRemoteAddr == addr) {
|
||||
fOfiInfo = make_unique<asiofi::info>(addr.Ip.c_str(), std::to_string(addr.Port).c_str(), 0, hints);
|
||||
} else {
|
||||
fOfiInfo = make_unique<asiofi::info>(addr.Ip.c_str(), std::to_string(addr.Port).c_str(), FI_SOURCE, hints);
|
||||
}
|
||||
|
||||
LOG(debug) << "OFI transport (" << fId << "): " << *fOfiInfo;
|
||||
|
||||
fOfiFabric = make_unique<asiofi::fabric>(*fOfiInfo);
|
||||
|
||||
fOfiDomain = make_unique<asiofi::domain>(*fOfiFabric);
|
||||
}
|
||||
}
|
||||
|
||||
auto Socket::Bind(const string& addr) -> bool
|
||||
try {
|
||||
fLocalAddr = Context::VerifyAddress(addr);
|
||||
if (fLocalAddr.Protocol == "verbs") {
|
||||
fNeedOfiMemoryRegistration = true;
|
||||
}
|
||||
|
||||
InitOfi(fLocalAddr);
|
||||
|
||||
fPassiveEndpoint = make_unique<asiofi::passive_endpoint>(fContext.GetIoContext(), *fOfiFabric);
|
||||
//fPassiveEndpoint->set_local_address(Context::ConvertAddress(fLocalAddr));
|
||||
|
||||
BindControlEndpoint();
|
||||
|
||||
return true;
|
||||
}
|
||||
// TODO catch the correct ofi error
|
||||
catch (const SilentSocketError& e)
|
||||
{
|
||||
// do not print error in this case, this is handled by fair::mq::Device
|
||||
// in case no connection could be established after trying a number of random ports from a range.
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG(error) << "OFI transport: " << e.what();
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG(error) << "OFI transport: Unknown exception in ofi::Socket::Bind";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Socket::BindControlEndpoint() -> void
|
||||
{
|
||||
assert(!fControlEndpoint);
|
||||
|
||||
fPassiveEndpoint->listen([&](asiofi::info&& info) {
|
||||
LOG(debug) << "OFI transport (" << fId
|
||||
<< "): control band connection request received. Accepting ...";
|
||||
fControlEndpoint = make_unique<asiofi::connected_endpoint>(
|
||||
fContext.GetIoContext(), *fOfiDomain, info);
|
||||
fControlEndpoint->enable();
|
||||
fControlEndpoint->accept([&]() {
|
||||
LOG(debug) << "OFI transport (" << fId << "): control band connection accepted.";
|
||||
|
||||
BindDataEndpoint();
|
||||
});
|
||||
});
|
||||
|
||||
LOG(debug) << "OFI transport (" << fId << "): control band bound to " << fLocalAddr;
|
||||
}
|
||||
|
||||
auto Socket::BindDataEndpoint() -> void
|
||||
{
|
||||
assert(!fDataEndpoint);
|
||||
|
||||
fPassiveEndpoint->listen([&](asiofi::info&& info) {
|
||||
LOG(debug) << "OFI transport (" << fId
|
||||
<< "): data band connection request received. Accepting ...";
|
||||
fDataEndpoint = make_unique<asiofi::connected_endpoint>(
|
||||
fContext.GetIoContext(), *fOfiDomain, info);
|
||||
fDataEndpoint->enable();
|
||||
fDataEndpoint->accept([&]() {
|
||||
LOG(debug) << "OFI transport (" << fId << "): data band connection accepted.";
|
||||
|
||||
if (fContext.GetSizeHint()) {
|
||||
asio::post(fContext.GetIoContext(),
|
||||
std::bind(&Socket::SendQueueReaderStatic, this));
|
||||
asio::post(fContext.GetIoContext(),
|
||||
std::bind(&Socket::RecvQueueReaderStatic, this));
|
||||
} else {
|
||||
asio::post(fContext.GetIoContext(),
|
||||
std::bind(&Socket::SendQueueReader, this));
|
||||
asio::post(fContext.GetIoContext(),
|
||||
std::bind(&Socket::RecvControlQueueReader, this));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
LOG(debug) << "OFI transport (" << fId << "): data band bound to " << fLocalAddr;
|
||||
}
|
||||
|
||||
auto Socket::Connect(const string& address) -> bool
|
||||
try {
|
||||
fRemoteAddr = Context::VerifyAddress(address);
|
||||
if (fRemoteAddr.Protocol == "verbs") {
|
||||
fNeedOfiMemoryRegistration = true;
|
||||
}
|
||||
|
||||
InitOfi(fRemoteAddr);
|
||||
|
||||
ConnectEndpoint(fControlEndpoint, Band::Control);
|
||||
ConnectEndpoint(fDataEndpoint, Band::Data);
|
||||
|
||||
if (fContext.GetSizeHint()) {
|
||||
asio::post(fContext.GetIoContext(), std::bind(&Socket::SendQueueReaderStatic, this));
|
||||
asio::post(fContext.GetIoContext(), std::bind(&Socket::RecvQueueReaderStatic, this));
|
||||
} else {
|
||||
asio::post(fContext.GetIoContext(), std::bind(&Socket::SendQueueReader, this));
|
||||
asio::post(fContext.GetIoContext(), std::bind(&Socket::RecvControlQueueReader, this));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const SilentSocketError& e)
|
||||
{
|
||||
// do not print error in this case, this is handled by fair::mq::Device
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG(error) << "OFI transport: " << e.what();
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG(error) << "OFI transport: Unknown exception in ofi::Socket::Connect";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Socket::ConnectEndpoint(std::unique_ptr<asiofi::connected_endpoint>& endpoint, Band type) -> void
|
||||
{
|
||||
assert(!endpoint);
|
||||
|
||||
std::string band(type == Band::Control ? "control" : "data");
|
||||
|
||||
endpoint = make_unique<asiofi::connected_endpoint>(fContext.GetIoContext(), *fOfiDomain);
|
||||
endpoint->enable();
|
||||
|
||||
LOG(debug) << "OFI transport (" << fId << "): Sending " << band << " band connection request to " << fRemoteAddr;
|
||||
|
||||
std::mutex mtx;
|
||||
std::condition_variable cv;
|
||||
bool notified(false), connected(false);
|
||||
|
||||
while (true) {
|
||||
endpoint->connect(Context::ConvertAddress(fRemoteAddr), [&, band](asiofi::eq::event event) {
|
||||
// LOG(debug) << "OFI transport (" << fId << "): " << band << " band conn event happened";
|
||||
std::unique_lock<std::mutex> lk2(mtx);
|
||||
notified = true;
|
||||
if (event == asiofi::eq::event::connected) {
|
||||
LOG(debug) << "OFI transport (" << fId << "): " << band << " band connected.";
|
||||
connected = true;
|
||||
} else {
|
||||
// LOG(debug) << "OFI transport (" << fId << "): " << band << " band connection refused. Trying again.";
|
||||
}
|
||||
lk2.unlock();
|
||||
cv.notify_one();
|
||||
});
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mtx);
|
||||
cv.wait(lk, [&] { return notified; });
|
||||
|
||||
if (connected) {
|
||||
break;
|
||||
} else {
|
||||
notified = false;
|
||||
lk.unlock();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Socket::Send(MessagePtr& msg, int /*timeout*/) -> int64_t
|
||||
{
|
||||
// timeout argument not yet implemented
|
||||
|
||||
std::vector<MessagePtr> msgVec;
|
||||
msgVec.reserve(1);
|
||||
msgVec.emplace_back(std::move(msg));
|
||||
|
||||
return Send(msgVec);
|
||||
}
|
||||
|
||||
auto Socket::Send(std::vector<MessagePtr>& msgVec, int /*timeout*/) -> int64_t
|
||||
try {
|
||||
// timeout argument not yet implemented
|
||||
|
||||
int size(0);
|
||||
for (auto& msg : msgVec) {
|
||||
size += msg->GetSize();
|
||||
}
|
||||
|
||||
fSendPushSem.wait();
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(fSendQueueMutex);
|
||||
fSendQueue.emplace(std::move(msgVec));
|
||||
}
|
||||
fSendPopSem.signal();
|
||||
|
||||
return size;
|
||||
} catch (const std::exception& e) {
|
||||
LOG(error) << e.what();
|
||||
return static_cast<int64_t>(TransferCode::error);
|
||||
}
|
||||
|
||||
auto Socket::SendQueueReader() -> void
|
||||
{
|
||||
fSendPopSem.async_wait([&] {
|
||||
// Read msg from send queue
|
||||
std::unique_lock<std::mutex> lk(fSendQueueMutex);
|
||||
std::vector<MessagePtr> msgVec(std::move(fSendQueue.front()));
|
||||
fSendQueue.pop();
|
||||
lk.unlock();
|
||||
|
||||
bool postMultiPartStartBuffer = msgVec.size() > 1;
|
||||
for (auto& msg : msgVec) {
|
||||
// Create control message
|
||||
ofi::unique_ptr<ControlMessage> ctrl(nullptr);
|
||||
if (postMultiPartStartBuffer) {
|
||||
postMultiPartStartBuffer = false;
|
||||
ctrl = MakeControlMessageWithPmr<PostMultiPartStartBuffer>(fControlMemPool);
|
||||
ctrl->msg.postMultiPartStartBuffer.numParts = msgVec.size();
|
||||
ctrl->msg.postMultiPartStartBuffer.size = msg->GetSize();
|
||||
} else {
|
||||
ctrl = MakeControlMessageWithPmr<PostBuffer>(fControlMemPool);
|
||||
ctrl->msg.postBuffer.size = msg->GetSize();
|
||||
}
|
||||
|
||||
// Send control message
|
||||
asio::mutable_buffer ctrlMsg(ctrl.get(), sizeof(ControlMessage));
|
||||
|
||||
if (fNeedOfiMemoryRegistration) {
|
||||
asiofi::memory_region mr(*fOfiDomain, ctrlMsg, asiofi::mr::access::send);
|
||||
auto desc = mr.desc();
|
||||
fControlEndpoint->send(ctrlMsg,
|
||||
desc,
|
||||
[&, ctrl2 = std::move(ctrlMsg), mr2 = std::move(mr)](
|
||||
asio::mutable_buffer) mutable {});
|
||||
} else {
|
||||
fControlEndpoint->send(
|
||||
ctrlMsg, [&, ctrl2 = std::move(ctrl)](asio::mutable_buffer) mutable {});
|
||||
}
|
||||
|
||||
// Send data message
|
||||
const auto size = msg->GetSize();
|
||||
|
||||
if (size) {
|
||||
asio::mutable_buffer buffer(msg->GetData(), size);
|
||||
|
||||
if (fNeedOfiMemoryRegistration) {
|
||||
asiofi::memory_region mr(*fOfiDomain, buffer, asiofi::mr::access::send);
|
||||
auto desc = mr.desc();
|
||||
|
||||
fDataEndpoint->send(buffer,
|
||||
desc,
|
||||
[&, size, msg2 = std::move(msg), mr2 = std::move(mr)](
|
||||
asio::mutable_buffer) mutable {
|
||||
fBytesTx += size;
|
||||
fMessagesTx++;
|
||||
fSendPushSem.signal();
|
||||
});
|
||||
} else {
|
||||
fDataEndpoint->send(
|
||||
buffer, [&, size, msg2 = std::move(msg)](asio::mutable_buffer) mutable {
|
||||
fBytesTx += size;
|
||||
fMessagesTx++;
|
||||
fSendPushSem.signal();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
++fMessagesTx;
|
||||
fSendPushSem.signal();
|
||||
}
|
||||
}
|
||||
|
||||
asio::dispatch(fContext.GetIoContext(), std::bind(&Socket::SendQueueReader, this));
|
||||
});
|
||||
}
|
||||
|
||||
auto Socket::SendQueueReaderStatic() -> void
|
||||
{
|
||||
fSendPopSem.async_wait([&] {
|
||||
// Read msg from send queue
|
||||
std::unique_lock<std::mutex> lk(fSendQueueMutex);
|
||||
std::vector<MessagePtr> msgVec(std::move(fSendQueue.front()));
|
||||
fSendQueue.pop();
|
||||
lk.unlock();
|
||||
|
||||
bool postMultiPartStartBuffer = msgVec.size() > 1;
|
||||
if (postMultiPartStartBuffer) {
|
||||
throw SocketError{tools::ToString("Multipart API not supported in static size mode.")};
|
||||
}
|
||||
|
||||
MessagePtr& msg = msgVec[0];
|
||||
|
||||
// Send data message
|
||||
const auto size = msg->GetSize();
|
||||
|
||||
if (size) {
|
||||
asio::mutable_buffer buffer(msg->GetData(), size);
|
||||
|
||||
if (fNeedOfiMemoryRegistration) {
|
||||
asiofi::memory_region mr(*fOfiDomain, buffer, asiofi::mr::access::send);
|
||||
auto desc = mr.desc();
|
||||
|
||||
fDataEndpoint->send(buffer,
|
||||
desc,
|
||||
[&, size, msg2 = std::move(msg), mr2 = std::move(mr)](
|
||||
asio::mutable_buffer) mutable {
|
||||
fBytesTx += size;
|
||||
fMessagesTx++;
|
||||
fSendPushSem.signal();
|
||||
});
|
||||
} else {
|
||||
fDataEndpoint->send(
|
||||
buffer, [&, size, msg2 = std::move(msg)](asio::mutable_buffer) mutable {
|
||||
fBytesTx += size;
|
||||
fMessagesTx++;
|
||||
fSendPushSem.signal();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
++fMessagesTx;
|
||||
fSendPushSem.signal();
|
||||
}
|
||||
|
||||
asio::dispatch(fContext.GetIoContext(), std::bind(&Socket::SendQueueReaderStatic, this));
|
||||
});
|
||||
}
|
||||
|
||||
auto Socket::Receive(MessagePtr& msg, int /*timeout*/) -> int64_t
|
||||
try {
|
||||
// timeout argument not yet implemented
|
||||
|
||||
fRecvPopSem.wait();
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(fRecvQueueMutex);
|
||||
msg = std::move(fRecvQueue.front().front());
|
||||
fRecvQueue.pop();
|
||||
}
|
||||
fRecvPushSem.signal();
|
||||
|
||||
int size(msg->GetSize());
|
||||
fBytesRx += size;
|
||||
++fMessagesRx;
|
||||
|
||||
return size;
|
||||
} catch (const std::exception& e) {
|
||||
LOG(error) << e.what();
|
||||
return static_cast<int>(TransferCode::error);
|
||||
}
|
||||
|
||||
auto Socket::Receive(std::vector<MessagePtr>& msgVec, int /*timeout*/) -> int64_t
|
||||
try {
|
||||
// timeout argument not yet implemented
|
||||
|
||||
fRecvPopSem.wait();
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(fRecvQueueMutex);
|
||||
msgVec = std::move(fRecvQueue.front());
|
||||
fRecvQueue.pop();
|
||||
}
|
||||
fRecvPushSem.signal();
|
||||
|
||||
int64_t size(0);
|
||||
for (auto& msg : msgVec) {
|
||||
size += msg->GetSize();
|
||||
}
|
||||
fBytesRx += size;
|
||||
++fMessagesRx;
|
||||
|
||||
return size;
|
||||
} catch (const std::exception& e) {
|
||||
LOG(error) << e.what();
|
||||
return static_cast<int64_t>(TransferCode::error);
|
||||
}
|
||||
|
||||
auto Socket::RecvControlQueueReader() -> void
|
||||
{
|
||||
fRecvPushSem.async_wait([&] {
|
||||
// Receive control message
|
||||
ofi::unique_ptr<ControlMessage> ctrl(MakeControlMessageWithPmr<Empty>(fControlMemPool));
|
||||
asio::mutable_buffer ctrlMsg(ctrl.get(), sizeof(ControlMessage));
|
||||
|
||||
if (fNeedOfiMemoryRegistration) {
|
||||
asiofi::memory_region mr(*fOfiDomain, ctrlMsg, asiofi::mr::access::recv);
|
||||
auto desc = mr.desc();
|
||||
|
||||
fControlEndpoint->recv(
|
||||
ctrlMsg,
|
||||
desc,
|
||||
[&, ctrl2 = std::move(ctrl), mr2 = std::move(mr)](
|
||||
asio::mutable_buffer) mutable { OnRecvControl(std::move(ctrl2)); });
|
||||
} else {
|
||||
fControlEndpoint->recv(
|
||||
ctrlMsg, [&, ctrl2 = std::move(ctrl)](asio::mutable_buffer) mutable {
|
||||
OnRecvControl(std::move(ctrl2));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
auto Socket::OnRecvControl(ofi::unique_ptr<ControlMessage> ctrl) -> void
|
||||
{
|
||||
// Check control message type
|
||||
auto size(0);
|
||||
if (ctrl->type == ControlMessageType::PostMultiPartStartBuffer) {
|
||||
size = ctrl->msg.postMultiPartStartBuffer.size;
|
||||
if (fMultiPartRecvCounter == -1) {
|
||||
fMultiPartRecvCounter = ctrl->msg.postMultiPartStartBuffer.numParts;
|
||||
assert(fInflightMultiPartMessage.empty());
|
||||
fInflightMultiPartMessage.reserve(ctrl->msg.postMultiPartStartBuffer.numParts);
|
||||
} else {
|
||||
throw SocketError{tools::ToString(
|
||||
"OFI transport: Received control start of new multi part message without completed "
|
||||
"reception of previous multi part message. Number of parts missing: ",
|
||||
fMultiPartRecvCounter)};
|
||||
}
|
||||
} else if (ctrl->type == ControlMessageType::PostBuffer) {
|
||||
size = ctrl->msg.postBuffer.size;
|
||||
} else {
|
||||
throw SocketError{tools::ToString("OFI transport: Unknown control message type: '",
|
||||
static_cast<int>(ctrl->type))};
|
||||
}
|
||||
|
||||
// Receive data
|
||||
auto msg = fContext.MakeReceiveMessage(size);
|
||||
|
||||
if (size) {
|
||||
asio::mutable_buffer buffer(msg->GetData(), size);
|
||||
|
||||
if (fNeedOfiMemoryRegistration) {
|
||||
asiofi::memory_region mr(*fOfiDomain, buffer, asiofi::mr::access::recv);
|
||||
auto desc = mr.desc();
|
||||
|
||||
fDataEndpoint->recv(
|
||||
buffer,
|
||||
desc,
|
||||
[&, msg2 = std::move(msg), mr2 = std::move(mr)](
|
||||
asio::mutable_buffer) mutable { DataMessageReceived(std::move(msg2)); });
|
||||
|
||||
} else {
|
||||
fDataEndpoint->recv(buffer,
|
||||
[&, msg2 = std::move(msg)](asio::mutable_buffer) mutable {
|
||||
DataMessageReceived(std::move(msg2));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
DataMessageReceived(std::move(msg));
|
||||
}
|
||||
|
||||
asio::dispatch(fContext.GetIoContext(),
|
||||
std::bind(&Socket::RecvControlQueueReader, this));
|
||||
}
|
||||
|
||||
auto Socket::RecvQueueReaderStatic() -> void
|
||||
{
|
||||
fRecvPushSem.async_wait([&] {
|
||||
static size_t size = fContext.GetSizeHint();
|
||||
// Receive data
|
||||
auto msg = fContext.MakeReceiveMessage(size);
|
||||
|
||||
if (size) {
|
||||
asio::mutable_buffer buffer(msg->GetData(), size);
|
||||
|
||||
if (fNeedOfiMemoryRegistration) {
|
||||
asiofi::memory_region mr(*fOfiDomain, buffer, asiofi::mr::access::recv);
|
||||
auto desc = mr.desc();
|
||||
|
||||
fDataEndpoint->recv(buffer,
|
||||
desc,
|
||||
[&, msg2 = std::move(msg), mr2 = std::move(mr)](
|
||||
asio::mutable_buffer) mutable {
|
||||
DataMessageReceived(std::move(msg2));
|
||||
});
|
||||
|
||||
} else {
|
||||
fDataEndpoint->recv(
|
||||
buffer, [&, msg2 = std::move(msg)](asio::mutable_buffer) mutable {
|
||||
DataMessageReceived(std::move(msg2));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
DataMessageReceived(std::move(msg));
|
||||
}
|
||||
|
||||
asio::dispatch(fContext.GetIoContext(),
|
||||
std::bind(&Socket::RecvQueueReaderStatic, this));
|
||||
});
|
||||
}
|
||||
|
||||
auto Socket::DataMessageReceived(MessagePtr msg) -> void
|
||||
{
|
||||
if (fMultiPartRecvCounter > 0) {
|
||||
--fMultiPartRecvCounter;
|
||||
fInflightMultiPartMessage.push_back(std::move(msg));
|
||||
}
|
||||
|
||||
if (fMultiPartRecvCounter == 0) {
|
||||
std::unique_lock<std::mutex> lk(fRecvQueueMutex);
|
||||
fRecvQueue.push(std::move(fInflightMultiPartMessage));
|
||||
lk.unlock();
|
||||
fMultiPartRecvCounter = -1;
|
||||
fRecvPopSem.signal();
|
||||
} else if (fMultiPartRecvCounter == -1) {
|
||||
std::vector<MessagePtr> msgVec;
|
||||
msgVec.push_back(std::move(msg));
|
||||
std::unique_lock<std::mutex> lk(fRecvQueueMutex);
|
||||
fRecvQueue.push(std::move(msgVec));
|
||||
lk.unlock();
|
||||
fRecvPopSem.signal();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
auto Socket::Close() -> void {}
|
||||
|
||||
auto Socket::SetOption(const string& /*option*/, const void* /*value*/, size_t /*valueSize*/) -> void
|
||||
{
|
||||
// if (zmq_setsockopt(fControlSocket, GetConstant(option), value, valueSize) < 0) {
|
||||
// throw SocketError{tools::ToString("Failed setting socket option, reason: ", zmq_strerror(errno))};
|
||||
// }
|
||||
}
|
||||
|
||||
auto Socket::GetOption(const string& /*option*/, void* /*value*/, size_t* /*valueSize*/) -> void
|
||||
{
|
||||
// if (zmq_getsockopt(fControlSocket, GetConstant(option), value, valueSize) < 0) {
|
||||
// throw SocketError{tools::ToString("Failed getting socket option, reason: ", zmq_strerror(errno))};
|
||||
// }
|
||||
}
|
||||
|
||||
void Socket::SetLinger(int /*value*/)
|
||||
{
|
||||
LOG(debug) << "OFI transport (" << fId << "): Not yet implemented.";
|
||||
}
|
||||
|
||||
int Socket::GetLinger() const
|
||||
{
|
||||
LOG(debug) << "OFI transport (" << fId << "): Not yet implemented.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Socket::SetSndBufSize(int /*value*/)
|
||||
{
|
||||
LOG(debug) << "OFI transport (" << fId << "): Not yet implemented.";
|
||||
}
|
||||
|
||||
int Socket::GetSndBufSize() const
|
||||
{
|
||||
LOG(debug) << "OFI transport (" << fId << "): Not yet implemented.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Socket::SetRcvBufSize(int /*value*/)
|
||||
{
|
||||
LOG(debug) << "OFI transport (" << fId << "): Not yet implemented.";
|
||||
}
|
||||
|
||||
int Socket::GetRcvBufSize() const
|
||||
{
|
||||
LOG(debug) << "OFI transport (" << fId << "): Not yet implemented.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Socket::SetSndKernelSize(int /*value*/)
|
||||
{
|
||||
LOG(debug) << "OFI transport (" << fId << "): Not yet implemented.";
|
||||
}
|
||||
|
||||
int Socket::GetSndKernelSize() const
|
||||
{
|
||||
LOG(debug) << "OFI transport (" << fId << "): Not yet implemented.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Socket::SetRcvKernelSize(int /*value*/)
|
||||
{
|
||||
LOG(debug) << "OFI transport (" << fId << "): Not yet implemented.";
|
||||
}
|
||||
|
||||
int Socket::GetRcvKernelSize() const
|
||||
{
|
||||
LOG(debug) << "OFI transport (" << fId << "): Not yet implemented.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto Socket::GetConstant(const string& /*constant*/) -> int
|
||||
{
|
||||
LOG(debug) << "OFI transport: Not yet implemented.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
Socket::~Socket()
|
||||
{
|
||||
try {
|
||||
Close(); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
|
||||
} catch (SocketError& e) {
|
||||
LOG(error) << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fair::mq::ofi
|
125
fairmq/ofi/Socket.h
Normal file
125
fairmq/ofi/Socket.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2018-2022 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_OFI_SOCKET_H
|
||||
#define FAIR_MQ_OFI_SOCKET_H
|
||||
|
||||
#include <fairmq/Message.h>
|
||||
#include <fairmq/Socket.h>
|
||||
#include <fairmq/ofi/Context.h>
|
||||
#include <fairmq/ofi/ControlMessages.h>
|
||||
|
||||
#include <asiofi/connected_endpoint.hpp>
|
||||
#include <asiofi/memory_resources.hpp>
|
||||
#include <asiofi/passive_endpoint.hpp>
|
||||
#include <asiofi/semaphore.hpp>
|
||||
#include <memory> // unique_ptr
|
||||
#include <mutex>
|
||||
|
||||
|
||||
namespace fair::mq::ofi
|
||||
{
|
||||
|
||||
/**
|
||||
* @class Socket Socket.h <fairmq/ofi/Socket.h>
|
||||
* @brief
|
||||
*
|
||||
* @todo TODO insert long description
|
||||
*/
|
||||
class Socket final : public fair::mq::Socket
|
||||
{
|
||||
public:
|
||||
Socket(Context& context, const std::string& type, const std::string& name, const std::string& id = "");
|
||||
Socket(const Socket&) = delete;
|
||||
Socket(Socket&&) = delete;
|
||||
Socket& operator=(const Socket&) = delete;
|
||||
Socket& operator=(Socket&&) = delete;
|
||||
|
||||
auto GetId() const -> std::string override { return fId; }
|
||||
|
||||
auto Events(uint32_t *events) -> int override { *events = 0; return -1; }
|
||||
auto Bind(const std::string& address) -> bool override;
|
||||
auto Connect(const std::string& address) -> bool override;
|
||||
|
||||
auto Send(MessagePtr& msg, int timeout = 0) -> int64_t override;
|
||||
auto Receive(MessagePtr& msg, int timeout = 0) -> int64_t override;
|
||||
auto Send(std::vector<MessagePtr>& msgVec, int timeout = 0) -> int64_t override;
|
||||
auto Receive(std::vector<MessagePtr>& msgVec, int timeout = 0) -> int64_t override;
|
||||
|
||||
auto GetSocket() const -> void* { return nullptr; }
|
||||
|
||||
void SetLinger(int value) override;
|
||||
int GetLinger() const override;
|
||||
void SetSndBufSize(int value) override;
|
||||
int GetSndBufSize() const override;
|
||||
void SetRcvBufSize(int value) override;
|
||||
int GetRcvBufSize() const override;
|
||||
void SetSndKernelSize(int value) override;
|
||||
int GetSndKernelSize() const override;
|
||||
void SetRcvKernelSize(int value) override;
|
||||
int GetRcvKernelSize() const override;
|
||||
|
||||
auto Close() -> void override;
|
||||
|
||||
auto SetOption(const std::string& option, const void* value, size_t valueSize) -> void override;
|
||||
auto GetOption(const std::string& option, void* value, size_t* valueSize) -> void override;
|
||||
|
||||
auto GetBytesTx() const -> unsigned long override { return fBytesTx; }
|
||||
auto GetBytesRx() const -> unsigned long override { return fBytesRx; }
|
||||
auto GetMessagesTx() const -> unsigned long override { return fMessagesTx; }
|
||||
auto GetMessagesRx() const -> unsigned long override { return fMessagesRx; }
|
||||
|
||||
auto GetNumberOfConnectedPeers() const -> unsigned long override
|
||||
{
|
||||
throw SocketError("not yet implemented");
|
||||
}
|
||||
|
||||
static auto GetConstant(const std::string& constant) -> int;
|
||||
|
||||
~Socket() override;
|
||||
|
||||
private:
|
||||
Context& fContext;
|
||||
asiofi::allocated_pool_resource fControlMemPool;
|
||||
std::unique_ptr<asiofi::info> fOfiInfo;
|
||||
std::unique_ptr<asiofi::fabric> fOfiFabric;
|
||||
std::unique_ptr<asiofi::domain> fOfiDomain;
|
||||
std::unique_ptr<asiofi::passive_endpoint> fPassiveEndpoint;
|
||||
std::unique_ptr<asiofi::connected_endpoint> fDataEndpoint, fControlEndpoint;
|
||||
std::string fId;
|
||||
std::atomic<unsigned long> fBytesTx;
|
||||
std::atomic<unsigned long> fBytesRx;
|
||||
std::atomic<unsigned long> fMessagesTx;
|
||||
std::atomic<unsigned long> fMessagesRx;
|
||||
Address fRemoteAddr;
|
||||
Address fLocalAddr;
|
||||
std::mutex fSendQueueMutex, fRecvQueueMutex;
|
||||
std::queue<std::vector<MessagePtr>> fSendQueue, fRecvQueue;
|
||||
std::vector<MessagePtr> fInflightMultiPartMessage;
|
||||
int64_t fMultiPartRecvCounter;
|
||||
asiofi::synchronized_semaphore fSendPushSem, fSendPopSem, fRecvPushSem, fRecvPopSem;
|
||||
std::atomic<bool> fNeedOfiMemoryRegistration;
|
||||
|
||||
auto InitOfi(Address addr) -> void;
|
||||
auto BindControlEndpoint() -> void;
|
||||
auto BindDataEndpoint() -> void;
|
||||
enum class Band { Control, Data };
|
||||
auto ConnectEndpoint(std::unique_ptr<asiofi::connected_endpoint>& endpoint, Band type) -> void;
|
||||
auto SendQueueReader() -> void;
|
||||
auto SendQueueReaderStatic() -> void;
|
||||
auto RecvControlQueueReader() -> void;
|
||||
auto RecvQueueReaderStatic() -> void;
|
||||
auto OnRecvControl(ofi::unique_ptr<ControlMessage> ctrl) -> void;
|
||||
auto DataMessageReceived(MessagePtr msg) -> void;
|
||||
}; /* class Socket */
|
||||
|
||||
struct SilentSocketError : SocketError { using SocketError::SocketError; };
|
||||
|
||||
} // namespace fair::mq::ofi
|
||||
|
||||
#endif /* FAIR_MQ_OFI_SOCKET_H */
|
218
fairmq/ofi/TransportFactory.h
Normal file
218
fairmq/ofi/TransportFactory.h
Normal file
@@ -0,0 +1,218 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2018-2022 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_OFI_TRANSPORTFACTORY_H
|
||||
#define FAIR_MQ_OFI_TRANSPORTFACTORY_H
|
||||
|
||||
#include <asiofi.hpp>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <fairlogger/Logger.h>
|
||||
#include <fairmq/Channel.h>
|
||||
#include <fairmq/Message.h>
|
||||
#include <fairmq/Poller.h>
|
||||
#include <fairmq/ProgOptions.h>
|
||||
#include <fairmq/Socket.h>
|
||||
#include <fairmq/TransportFactory.h>
|
||||
#include <fairmq/Transports.h>
|
||||
#include <fairmq/ofi/Context.h>
|
||||
#include <fairmq/ofi/Message.h>
|
||||
#include <fairmq/ofi/Socket.h>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace fair::mq::ofi {
|
||||
|
||||
/**
|
||||
* @class TransportFactory TransportFactory.h <fairmq/ofi/TransportFactory.h>
|
||||
* @brief FairMQ transport factory for the ofi transport
|
||||
*
|
||||
* @todo TODO insert long description
|
||||
*/
|
||||
struct TransportFactory final : mq::TransportFactory
|
||||
{
|
||||
TransportFactory(std::string const& id = "", ProgOptions const* config = nullptr)
|
||||
: mq::TransportFactory(id)
|
||||
, fContext(*this, *this, 1)
|
||||
{
|
||||
try {
|
||||
LOG(debug) << "OFI transport: asiofi (" << fContext.GetAsiofiVersion() << ")";
|
||||
|
||||
if (config) {
|
||||
fContext.SetSizeHint(config->GetProperty<size_t>("ofi-size-hint", 0));
|
||||
}
|
||||
} catch (ContextError& e) {
|
||||
throw TransportFactoryError(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
TransportFactory(const TransportFactory&) = delete;
|
||||
TransportFactory(TransportFactory&&) = delete;
|
||||
TransportFactory& operator=(const TransportFactory&) = delete;
|
||||
TransportFactory& operator=(TransportFactory&&) = delete;
|
||||
~TransportFactory() override = default;
|
||||
|
||||
auto CreateMessage() -> std::unique_ptr<mq::Message> override
|
||||
{
|
||||
return std::make_unique<Message>(&fMemoryResource);
|
||||
}
|
||||
|
||||
auto CreateMessage(Alignment /*alignment*/) -> std::unique_ptr<mq::Message> override
|
||||
{
|
||||
// TODO Do not ignore alignment
|
||||
return std::make_unique<Message>(&fMemoryResource);
|
||||
}
|
||||
|
||||
auto CreateMessage(std::size_t size) -> std::unique_ptr<mq::Message> override
|
||||
{
|
||||
return std::make_unique<Message>(&fMemoryResource, size);
|
||||
}
|
||||
|
||||
auto CreateMessage(std::size_t size, Alignment /*alignment*/)
|
||||
-> std::unique_ptr<mq::Message> override
|
||||
{
|
||||
// TODO Do not ignore alignment
|
||||
return std::make_unique<Message>(&fMemoryResource, size);
|
||||
}
|
||||
|
||||
auto CreateMessage(void* data, std::size_t size, FreeFn* ffn, void* hint = nullptr)
|
||||
-> std::unique_ptr<mq::Message> override
|
||||
{
|
||||
return std::make_unique<Message>(&fMemoryResource, data, size, ffn, hint);
|
||||
}
|
||||
|
||||
auto CreateMessage(std::unique_ptr<mq::UnmanagedRegion>& region,
|
||||
void* data,
|
||||
std::size_t size,
|
||||
void* hint = nullptr) -> std::unique_ptr<mq::Message> override
|
||||
{
|
||||
return std::make_unique<Message>(&fMemoryResource, region, data, size, hint);
|
||||
}
|
||||
|
||||
auto CreateSocket(std::string const& type, std::string const& name)
|
||||
-> std::unique_ptr<mq::Socket> override
|
||||
{
|
||||
return std::make_unique<Socket>(fContext, type, name, GetId());
|
||||
}
|
||||
|
||||
auto CreatePoller(std::vector<mq::Channel> const& /*channels*/) const
|
||||
-> std::unique_ptr<mq::Poller> override
|
||||
{
|
||||
throw std::runtime_error("Not yet implemented (Poller).");
|
||||
}
|
||||
|
||||
auto CreatePoller(std::vector<mq::Channel*> const& /*channels*/) const
|
||||
-> std::unique_ptr<mq::Poller> override
|
||||
{
|
||||
throw std::runtime_error("Not yet implemented (Poller).");
|
||||
}
|
||||
|
||||
auto CreatePoller(
|
||||
std::unordered_map<std::string, std::vector<Channel>> const& /*channelsMap*/,
|
||||
std::vector<std::string> const& /*channelList*/) const
|
||||
-> std::unique_ptr<mq::Poller> override
|
||||
{
|
||||
throw std::runtime_error("Not yet implemented (Poller).");
|
||||
}
|
||||
|
||||
auto CreateUnmanagedRegion(std::size_t /*size*/,
|
||||
RegionCallback /*callback = nullptr*/,
|
||||
std::string const& /*path = ""*/,
|
||||
int /*flags = 0*/,
|
||||
RegionConfig /*cfg = RegionConfig()*/)
|
||||
-> std::unique_ptr<mq::UnmanagedRegion> override
|
||||
{
|
||||
throw std::runtime_error("Not yet implemented UMR.");
|
||||
}
|
||||
|
||||
auto CreateUnmanagedRegion(std::size_t /*size*/,
|
||||
RegionBulkCallback /*callback = nullptr*/,
|
||||
std::string const& /*path = ""*/,
|
||||
int /*flags = 0*/,
|
||||
RegionConfig /*cfg = RegionConfig()*/)
|
||||
-> std::unique_ptr<mq::UnmanagedRegion> override
|
||||
{
|
||||
throw std::runtime_error("Not yet implemented UMR.");
|
||||
}
|
||||
|
||||
auto CreateUnmanagedRegion(std::size_t /*size*/,
|
||||
int64_t /*userFlags*/,
|
||||
RegionCallback /*callback = nullptr*/,
|
||||
std::string const& /*path = ""*/,
|
||||
int /*flags = 0*/,
|
||||
RegionConfig /*cfg = RegionConfig()*/)
|
||||
-> std::unique_ptr<mq::UnmanagedRegion> override
|
||||
{
|
||||
throw std::runtime_error("Not yet implemented UMR.");
|
||||
}
|
||||
|
||||
auto CreateUnmanagedRegion(std::size_t /*size*/,
|
||||
int64_t /*userFlags*/,
|
||||
RegionBulkCallback /*callback = nullptr*/,
|
||||
std::string const& /*path = ""*/,
|
||||
int /*flags = 0*/,
|
||||
RegionConfig /*cfg = RegionConfig()*/)
|
||||
-> std::unique_ptr<mq::UnmanagedRegion> override
|
||||
{
|
||||
throw std::runtime_error("Not yet implemented UMR.");
|
||||
}
|
||||
|
||||
auto CreateUnmanagedRegion(std::size_t /*size*/,
|
||||
RegionCallback /*callback*/,
|
||||
RegionConfig /*cfg*/)
|
||||
-> std::unique_ptr<mq::UnmanagedRegion> override
|
||||
{
|
||||
throw std::runtime_error("Not yet implemented UMR.");
|
||||
}
|
||||
|
||||
auto CreateUnmanagedRegion(std::size_t /*size*/,
|
||||
RegionBulkCallback /*callback*/,
|
||||
RegionConfig /*cfg*/)
|
||||
-> std::unique_ptr<mq::UnmanagedRegion> override
|
||||
{
|
||||
throw std::runtime_error("Not yet implemented UMR.");
|
||||
}
|
||||
|
||||
auto SubscribeToRegionEvents(RegionEventCallback /*callback*/) -> void override
|
||||
{
|
||||
throw std::runtime_error("Not yet implemented.");
|
||||
}
|
||||
|
||||
auto SubscribedToRegionEvents() -> bool override
|
||||
{
|
||||
throw std::runtime_error("Not yet implemented.");
|
||||
}
|
||||
|
||||
auto UnsubscribeFromRegionEvents() -> void override
|
||||
{
|
||||
throw std::runtime_error("Not yet implemented.");
|
||||
}
|
||||
|
||||
auto GetRegionInfo() -> std::vector<RegionInfo> override
|
||||
{
|
||||
LOG(error) << "GetRegionInfo not yet implemented for OFI, returning empty vector";
|
||||
return std::vector<RegionInfo>();
|
||||
}
|
||||
|
||||
auto GetType() const -> Transport override { return Transport::OFI; }
|
||||
|
||||
void Interrupt() override { fContext.Interrupt(); }
|
||||
void Resume() override { fContext.Resume(); }
|
||||
void Reset() override { fContext.Reset(); }
|
||||
|
||||
private:
|
||||
mutable Context fContext;
|
||||
asiofi::allocated_pool_resource fMemoryResource;
|
||||
}; /* class TransportFactory */
|
||||
|
||||
} // namespace fair::mq::ofi
|
||||
|
||||
#endif /* FAIR_MQ_OFI_TRANSPORTFACTORY_H */
|
33
fairmq/plugins/PMIx/CMakeLists.txt
Normal file
33
fairmq/plugins/PMIx/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2019-2022 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(plugin FairMQPlugin_pmix)
|
||||
add_library(${plugin} SHARED
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/PMIxPlugin.cxx
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/PMIxPlugin.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/PMIxCommands.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/PMIx.hpp
|
||||
)
|
||||
target_compile_features(${plugin} PUBLIC cxx_std_17)
|
||||
target_link_libraries(${plugin} PUBLIC FairMQ PMIx::libpmix)
|
||||
target_include_directories(${plugin} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set_target_properties(${plugin} PROPERTIES
|
||||
CXX_VISIBILITY_PRESET hidden
|
||||
OUTPUT_NAME "${PROJECT_NAME_LOWER}-plugin-pmix"
|
||||
)
|
||||
|
||||
set(exe fairmq-pmix-command-ui)
|
||||
add_executable(${exe} ${CMAKE_CURRENT_SOURCE_DIR}/runPMIxCommandUI.cxx)
|
||||
target_link_libraries(${exe} FairMQ PMIx::libpmix)
|
||||
target_include_directories(${exe} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
install(TARGETS ${plugin} ${exe}
|
||||
EXPORT ${PROJECT_EXPORT_SET}
|
||||
LIBRARY DESTINATION ${PROJECT_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||
)
|
317
fairmq/plugins/PMIx/PMIx.hpp
Normal file
317
fairmq/plugins/PMIx/PMIx.hpp
Normal file
@@ -0,0 +1,317 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2019 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 PMIX_HPP
|
||||
#define PMIX_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <pmix.h>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// C++ PMIx v2.2 API
|
||||
namespace pmix
|
||||
{
|
||||
|
||||
struct runtime_error : std::runtime_error
|
||||
{
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
using status = pmix_status_t;
|
||||
|
||||
using nspace = pmix_nspace_t;
|
||||
|
||||
using key = pmix_key_t;
|
||||
|
||||
using data_type = pmix_data_type_t;
|
||||
|
||||
struct rank
|
||||
{
|
||||
enum named : pmix_rank_t
|
||||
{
|
||||
undef = PMIX_RANK_UNDEF,
|
||||
wildcard = PMIX_RANK_WILDCARD,
|
||||
local_node = PMIX_RANK_LOCAL_NODE
|
||||
};
|
||||
|
||||
explicit rank(pmix_rank_t r)
|
||||
: m_value(r)
|
||||
{}
|
||||
|
||||
operator pmix_rank_t() { return m_value; }
|
||||
|
||||
private:
|
||||
pmix_rank_t m_value;
|
||||
};
|
||||
|
||||
struct proc : pmix_proc_t
|
||||
{
|
||||
proc() { PMIX_PROC_CONSTRUCT(static_cast<pmix_proc_t*>(this)); }
|
||||
~proc() { PMIX_PROC_DESTRUCT(static_cast<pmix_proc_t*>(this)); }
|
||||
|
||||
proc(pmix::nspace ns, pmix::rank r)
|
||||
{
|
||||
PMIX_PROC_LOAD(static_cast<pmix_proc_t*>(this), ns, static_cast<pmix_rank_t>(r));
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const proc& p)
|
||||
{
|
||||
return os << p.nspace << "_" << p.rank;
|
||||
}
|
||||
};
|
||||
|
||||
struct value : pmix_value_t
|
||||
{
|
||||
value() { PMIX_VALUE_CONSTRUCT(static_cast<pmix_value_t*>(this)); }
|
||||
~value() { PMIX_VALUE_DESTRUCT(static_cast<pmix_value_t*>(this)); }
|
||||
|
||||
value(const value& rhs)
|
||||
{
|
||||
status rc;
|
||||
auto lhs(static_cast<pmix_value_t*>(this));
|
||||
PMIX_VALUE_XFER(rc, lhs, static_cast<pmix_value_t*>(const_cast<value*>(&rhs)));
|
||||
|
||||
if (rc != PMIX_SUCCESS) {
|
||||
throw runtime_error("pmix::value copy ctor failed: rc=" + std::to_string(rc));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
explicit value(T)
|
||||
{
|
||||
throw runtime_error("Given value type not supported or not yet implemented.");
|
||||
}
|
||||
|
||||
explicit value(const char* val)
|
||||
{
|
||||
PMIX_VALUE_LOAD(static_cast<pmix_value_t*>(this), const_cast<char*>(val), PMIX_STRING);
|
||||
}
|
||||
|
||||
explicit value(const std::string& val)
|
||||
{
|
||||
PMIX_VALUE_LOAD(
|
||||
static_cast<pmix_value_t*>(this), const_cast<char*>(val.c_str()), PMIX_STRING);
|
||||
}
|
||||
|
||||
explicit value(int val)
|
||||
{
|
||||
PMIX_VALUE_LOAD(static_cast<pmix_value_t*>(this), &val, PMIX_INT);
|
||||
}
|
||||
|
||||
explicit value(pmix_data_array_t* val)
|
||||
{
|
||||
PMIX_VALUE_LOAD(static_cast<pmix_value_t*>(this), val, PMIX_DATA_ARRAY);
|
||||
}
|
||||
};
|
||||
|
||||
struct info : pmix_info_t
|
||||
{
|
||||
info() { PMIX_INFO_CONSTRUCT(static_cast<pmix_info_t*>(this)); }
|
||||
~info() { PMIX_INFO_DESTRUCT(static_cast<pmix_info_t*>(this)); }
|
||||
|
||||
template<typename... Args>
|
||||
info(const std::string& k, Args&&... args)
|
||||
{
|
||||
(void)strncpy(key, k.c_str(), PMIX_MAX_KEYLEN);
|
||||
flags = 0;
|
||||
|
||||
pmix::value rhs(std::forward<Args>(args)...);
|
||||
auto lhs(&value);
|
||||
status rc;
|
||||
PMIX_VALUE_XFER(rc, lhs, static_cast<pmix_value_t*>(&rhs));
|
||||
|
||||
if (rc != PMIX_SUCCESS) {
|
||||
throw runtime_error("pmix::info ctor failed: rc=" + std::to_string(rc));
|
||||
}
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const info& i)
|
||||
{
|
||||
return os << "key=" << i.key << ",value='" << i.value.data.string << "'";
|
||||
}
|
||||
|
||||
info(const info& rhs)
|
||||
{
|
||||
PMIX_INFO_XFER(static_cast<pmix_info_t*>(this),
|
||||
static_cast<pmix_info_t*>(const_cast<info*>(&rhs)));
|
||||
}
|
||||
};
|
||||
|
||||
struct pdata : pmix_pdata_t
|
||||
{
|
||||
pdata() { PMIX_PDATA_CONSTRUCT(static_cast<pmix_pdata_t*>(this)); }
|
||||
~pdata() { PMIX_PDATA_DESTRUCT(static_cast<pmix_pdata_t*>(this)); }
|
||||
|
||||
pdata(const pdata& rhs)
|
||||
{
|
||||
PMIX_PDATA_XFER(static_cast<pmix_pdata_t*>(this),
|
||||
static_cast<pmix_pdata_t*>(const_cast<pdata*>(&rhs)));
|
||||
}
|
||||
|
||||
auto set_key(const std::string& new_key) -> void
|
||||
{
|
||||
(void)strncpy(key, new_key.c_str(), PMIX_MAX_KEYLEN);
|
||||
}
|
||||
};
|
||||
|
||||
auto init(const std::vector<info>& info = {}) -> proc
|
||||
{
|
||||
proc res;
|
||||
status rc;
|
||||
|
||||
rc = PMIx_Init(&res, const_cast<pmix::info*>(info.data()), info.size());
|
||||
if (rc != PMIX_SUCCESS) {
|
||||
throw runtime_error("pmix::init() failed: rc=" + std::to_string(rc));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
auto initialized() -> bool { return !!PMIx_Initialized(); }
|
||||
|
||||
auto get_version() -> std::string { return {PMIx_Get_version()}; }
|
||||
|
||||
auto finalize(const std::vector<info>& info = {}) -> void
|
||||
{
|
||||
status rc;
|
||||
|
||||
rc = PMIx_Finalize(info.data(), info.size());
|
||||
if (rc != PMIX_SUCCESS) {
|
||||
throw runtime_error("pmix::finalize() failed: rc=" + std::to_string(rc));
|
||||
}
|
||||
}
|
||||
|
||||
auto publish(const std::vector<info>& info) -> void
|
||||
{
|
||||
status rc;
|
||||
|
||||
rc = PMIx_Publish(info.data(), info.size());
|
||||
if (rc != PMIX_SUCCESS) {
|
||||
throw runtime_error("pmix::publish() failed: rc=" + std::to_string(rc));
|
||||
}
|
||||
}
|
||||
|
||||
auto fence(const std::vector<proc>& procs = {}, const std::vector<info>& info = {}) -> void
|
||||
{
|
||||
status rc;
|
||||
|
||||
rc = PMIx_Fence(procs.data(), procs.size(), info.data(), info.size());
|
||||
if (rc != PMIX_SUCCESS) {
|
||||
throw runtime_error("pmix::fence() failed: rc=" + std::to_string(rc));
|
||||
}
|
||||
}
|
||||
|
||||
auto lookup(std::vector<pdata>& pdata, const std::vector<info>& info = {}) -> void
|
||||
{
|
||||
status rc;
|
||||
|
||||
rc = PMIx_Lookup(pdata.data(), pdata.size(), info.data(), info.size());
|
||||
if (rc != PMIX_SUCCESS) {
|
||||
throw runtime_error("pmix::lookup() failed: rc=" + std::to_string(rc));
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_info(const std::string& name, pmix::proc& process)
|
||||
{
|
||||
pmix_value_t* v;
|
||||
|
||||
pmix::status rc = PMIx_Get(&process, name.c_str(), nullptr, 0, &v);
|
||||
if (rc == PMIX_SUCCESS) {
|
||||
std::stringstream ss;
|
||||
|
||||
switch (v->type) {
|
||||
case PMIX_SIZE: ss << static_cast<size_t>(v->data.size) << " (size_t)"; break;
|
||||
case PMIX_INT: ss << static_cast<int>(v->data.integer) << " (int)"; break;
|
||||
case PMIX_INT8: ss << static_cast<int8_t>(v->data.int8) << " (int8_t)"; break;
|
||||
case PMIX_INT16: ss << static_cast<int16_t>(v->data.int16) << " (int16_t)"; break;
|
||||
case PMIX_INT32: ss << static_cast<int32_t>(v->data.int32) << " (int32_t)"; break;
|
||||
case PMIX_INT64: ss << static_cast<int64_t>(v->data.int64) << " (int64_t)"; break;
|
||||
case PMIX_UINT: ss << static_cast<unsigned int>(v->data.uint) << " (unsigned int)"; break;
|
||||
case PMIX_UINT8: ss << static_cast<uint8_t>(v->data.uint8) << " (uint8_t)"; break;
|
||||
case PMIX_UINT16: ss << static_cast<uint16_t>(v->data.uint16) << " (uint16_t)"; break;
|
||||
case PMIX_UINT32: ss << static_cast<uint32_t>(v->data.uint32) << " (uint32_t)"; break;
|
||||
case PMIX_UINT64: ss << static_cast<uint64_t>(v->data.uint64) << " (uint64_t)"; break;
|
||||
case PMIX_FLOAT: ss << static_cast<float>(v->data.fval) << " (float)"; break;
|
||||
case PMIX_DOUBLE: ss << static_cast<double>(v->data.dval) << " (double)"; break;
|
||||
case PMIX_PID: ss << static_cast<pid_t>(v->data.pid) << " (pid_t)"; break;
|
||||
case PMIX_STRING: ss << static_cast<char*>(v->data.string) << " (string)"; break;
|
||||
case PMIX_PROC_RANK: ss << static_cast<uint32_t>(v->data.rank) << " (pmix_rank_t)"; break;
|
||||
case PMIX_PROC: ss << "proc.nspace: " << static_cast<pmix_proc_t*>(v->data.proc)->nspace
|
||||
<< ", proc.rank: " << static_cast<pmix_proc_t*>(v->data.proc)->rank << " (pmix_proc_t*)"; break;
|
||||
default:
|
||||
ss << "unknown type: " << v->type;
|
||||
break;
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
} else if (rc == PMIX_ERR_NOT_FOUND) {
|
||||
// LOG(error) << "PMIx_Get failed: PMIX_ERR_NOT_FOUND";
|
||||
return "";
|
||||
} else {
|
||||
// LOG(error) << "PMIx_Get failed: " << rc;
|
||||
return "<undefined>";
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_value_str(const pmix_value_t& v)
|
||||
{
|
||||
switch (v.type) {
|
||||
case PMIX_BOOL: return std::to_string(static_cast<bool>(v.data.flag));
|
||||
case PMIX_SIZE: return std::to_string(static_cast<size_t>(v.data.size));
|
||||
case PMIX_INT: return std::to_string(static_cast<int>(v.data.integer));
|
||||
case PMIX_INT8: return std::to_string(static_cast<int8_t>(v.data.int8));
|
||||
case PMIX_INT16: return std::to_string(static_cast<int16_t>(v.data.int16));
|
||||
case PMIX_INT32: return std::to_string(static_cast<int32_t>(v.data.int32));
|
||||
case PMIX_INT64: return std::to_string(static_cast<int64_t>(v.data.int64));
|
||||
case PMIX_UINT: return std::to_string(static_cast<unsigned int>(v.data.uint));
|
||||
case PMIX_UINT8: return std::to_string(static_cast<uint8_t>(v.data.uint8));
|
||||
case PMIX_UINT16: return std::to_string(static_cast<uint16_t>(v.data.uint16));
|
||||
case PMIX_UINT32: return std::to_string(static_cast<uint32_t>(v.data.uint32));
|
||||
case PMIX_UINT64: return std::to_string(static_cast<uint64_t>(v.data.uint64));
|
||||
case PMIX_FLOAT: return std::to_string(static_cast<float>(v.data.fval));
|
||||
case PMIX_DOUBLE: return std::to_string(static_cast<double>(v.data.dval));
|
||||
case PMIX_PID: return std::to_string(static_cast<pid_t>(v.data.pid));
|
||||
case PMIX_STRING: return static_cast<char*>(v.data.string);
|
||||
case PMIX_PROC_RANK: return std::to_string(static_cast<uint32_t>(v.data.rank));
|
||||
case PMIX_POINTER: { std::stringstream ss; ss << static_cast<void*>(v.data.ptr); return ss.str(); }
|
||||
case PMIX_DATA_ARRAY: {
|
||||
if (v.data.darray->type == PMIX_PROC) {
|
||||
std::stringstream ss;
|
||||
ss << "[";
|
||||
for (size_t i = 0; i < v.data.darray->size; ++i) {
|
||||
ss << static_cast<pmix_proc_t*>(static_cast<pmix_data_array_t*>(v.data.darray)->array)[0].nspace;
|
||||
ss << "_";
|
||||
ss << static_cast<pmix_proc_t*>(static_cast<pmix_data_array_t*>(v.data.darray)->array)[0].rank;
|
||||
|
||||
if (i < v.data.darray->size - 1) {
|
||||
ss << ",";
|
||||
}
|
||||
}
|
||||
ss << "]";
|
||||
return ss.str();
|
||||
} else {
|
||||
return "UNKNOWN TYPE IN DATA ARRAY";
|
||||
}
|
||||
}
|
||||
default: return "UNKNOWN TYPE";
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace pmix */
|
||||
|
||||
#endif /* PMIX_HPP */
|
291
fairmq/plugins/PMIx/PMIxCommands.h
Normal file
291
fairmq/plugins/PMIx/PMIxCommands.h
Normal file
@@ -0,0 +1,291 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2019 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 PMIXCOMMANDS_H
|
||||
#define PMIXCOMMANDS_H
|
||||
|
||||
#include "PMIx.hpp"
|
||||
|
||||
#include <fairlogger/Logger.h>
|
||||
#include <fairmq/tools/Semaphore.h>
|
||||
#include <memory> // make_unique
|
||||
#include <string>
|
||||
|
||||
namespace pmix
|
||||
{
|
||||
|
||||
std::array<std::string, 47> typeNames =
|
||||
{
|
||||
{
|
||||
"PMIX_UNDEF",
|
||||
"PMIX_BOOL",
|
||||
"PMIX_BYTE",
|
||||
"PMIX_STRING",
|
||||
"PMIX_SIZE",
|
||||
"PMIX_PID",
|
||||
"PMIX_INT",
|
||||
"PMIX_INT8",
|
||||
"PMIX_INT16",
|
||||
"PMIX_INT32",
|
||||
"PMIX_INT64",
|
||||
"PMIX_UINT",
|
||||
"PMIX_UINT8",
|
||||
"PMIX_UINT16",
|
||||
"PMIX_UINT32",
|
||||
"PMIX_UINT64",
|
||||
"PMIX_FLOAT",
|
||||
"PMIX_DOUBLE",
|
||||
"PMIX_TIMEVAL",
|
||||
"PMIX_TIME",
|
||||
"PMIX_STATUS",
|
||||
"PMIX_VALUE",
|
||||
"PMIX_PROC",
|
||||
"PMIX_APP",
|
||||
"PMIX_INFO",
|
||||
"PMIX_PDATA",
|
||||
"PMIX_BUFFER",
|
||||
"PMIX_BYTE_OBJECT",
|
||||
"PMIX_KVAL",
|
||||
"PMIX_MODEX",
|
||||
"PMIX_PERSIST",
|
||||
"PMIX_POINTER",
|
||||
"PMIX_SCOPE",
|
||||
"PMIX_DATA_RANGE",
|
||||
"PMIX_COMMAND",
|
||||
"PMIX_INFO_DIRECTIVES",
|
||||
"PMIX_DATA_TYPE",
|
||||
"PMIX_PROC_STATE",
|
||||
"PMIX_PROC_INFO",
|
||||
"PMIX_DATA_ARRAY",
|
||||
"PMIX_PROC_RANK",
|
||||
"PMIX_QUERY",
|
||||
"PMIX_COMPRESSED_STRING",
|
||||
"PMIX_ALLOC_DIRECTIVE",
|
||||
"PMIX_INFO_ARRAY",
|
||||
"PMIX_IOF_CHANNEL",
|
||||
"PMIX_ENVAR"
|
||||
}
|
||||
};
|
||||
|
||||
enum class Command : int
|
||||
{
|
||||
general = PMIX_EXTERNAL_ERR_BASE,
|
||||
error = PMIX_EXTERNAL_ERR_BASE - 1
|
||||
};
|
||||
|
||||
|
||||
class Commands
|
||||
{
|
||||
public:
|
||||
Commands(const proc& process)
|
||||
: fProcess(process)
|
||||
, fSubscribed(false)
|
||||
{
|
||||
}
|
||||
|
||||
~Commands()
|
||||
{
|
||||
Unsubscribe();
|
||||
}
|
||||
|
||||
void Subscribe(std::function<void(const std::string& msg, const proc& sender)> callback)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
|
||||
LOG(debug) << "PMIxCommands: Subscribing...";
|
||||
|
||||
fCallback = callback;
|
||||
std::array<pmix::status, 1> codes;
|
||||
codes[0] = static_cast<int>(pmix::Command::general);
|
||||
|
||||
PMIX_INFO_LOAD(&(fInfos[0]), PMIX_EVENT_RETURN_OBJECT, this, PMIX_POINTER);
|
||||
|
||||
PMIx_Register_event_handler(codes.data(), codes.size(),
|
||||
fInfos.data(), fInfos.size(),
|
||||
&Commands::Handler,
|
||||
&Commands::EventHandlerRegistration,
|
||||
this);
|
||||
fBlocker.Wait();
|
||||
LOG(debug) << "PMIxCommands: Subscribing complete!";
|
||||
}
|
||||
|
||||
void Unsubscribe()
|
||||
{
|
||||
if (fSubscribed) {
|
||||
LOG(debug) << "PMIxCommands: Unsubscribing...";
|
||||
PMIx_Deregister_event_handler(fHandlerRef, &Commands::EventHandlerDeregistration, this);
|
||||
fBlocker.Wait();
|
||||
LOG(debug) << "PMIxCommands: Unsubscribing complete!";
|
||||
} else {
|
||||
LOG(debug) << "Unsubscribe() is called while no subscription is active";
|
||||
}
|
||||
}
|
||||
|
||||
struct Holder
|
||||
{
|
||||
Holder() : fData(nullptr) {}
|
||||
~Holder() { PMIX_DATA_ARRAY_FREE(fData); }
|
||||
|
||||
std::vector<pmix::info> fInfos;
|
||||
pmix_data_array_t* fData;
|
||||
};
|
||||
|
||||
void Send(const std::string& msg)
|
||||
{
|
||||
std::vector<pmix::info>* infos = new std::vector<pmix::info>();
|
||||
infos->emplace_back("fairmq.cmd", msg);
|
||||
PMIx_Notify_event(static_cast<int>(pmix::Command::general),
|
||||
&fProcess,
|
||||
PMIX_RANGE_NAMESPACE,
|
||||
infos->data(), infos->size(),
|
||||
&Commands::OpCompleteCallback<std::vector<pmix::info>>,
|
||||
infos);
|
||||
}
|
||||
|
||||
void Send(const std::string& msg, rank rank)
|
||||
{
|
||||
pmix::proc destination(fProcess);
|
||||
destination.rank = rank;
|
||||
Send(msg, {destination});
|
||||
}
|
||||
|
||||
void Send(const std::string& msg, const std::vector<proc>& destination)
|
||||
{
|
||||
std::unique_ptr<Holder> holder = std::make_unique<Holder>();
|
||||
|
||||
PMIX_DATA_ARRAY_CREATE(holder->fData, destination.size(), PMIX_PROC);
|
||||
memcpy(holder->fData->array, destination.data(), destination.size() * sizeof(pmix_proc_t));
|
||||
// LOG(warn) << "OLOG: " << msg << " > " << static_cast<pmix_proc_t*>(holder->fData->array)[0].nspace << ": " << static_cast<pmix_proc_t*>(holder->fData->array)[0].rank;
|
||||
holder->fInfos.emplace_back(PMIX_EVENT_CUSTOM_RANGE, holder->fData);
|
||||
// LOG(warn) << msg << " // packed range: " << static_cast<pmix_proc_t*>(static_cast<pmix_data_array_t*>(holder->fInfos.at(0).value.data.darray)->array)[0].nspace << "_" << static_cast<pmix_proc_t*>(static_cast<pmix_data_array_t*>(holder->fInfos.at(0).value.data.darray)->array)[0].rank;
|
||||
// LOG(warn) << msg << " // packed range.type: " << pmix::typeNames.at(holder->fInfos.at(0).value.type);
|
||||
// LOG(warn) << msg << " // packed range.array.type: " << pmix::typeNames.at(static_cast<pmix_data_array_t*>(holder->fInfos.at(0).value.data.darray)->type);
|
||||
// LOG(warn) << msg << " // packed range.array.size: " << static_cast<pmix_data_array_t*>(holder->fInfos.at(0).value.data.darray)->size;
|
||||
// LOG(warn) << holder->fInfos.size();
|
||||
holder->fInfos.emplace_back("fairmq.cmd", msg);
|
||||
// LOG(warn) << msg << " // packed msg: " << holder->fInfos.at(1).value.data.string;
|
||||
// LOG(warn) << msg << " // packed msg.type: " << pmix::typeNames.at(holder->fInfos.at(1).value.type);
|
||||
// LOG(warn) << holder->fInfos.size();
|
||||
|
||||
PMIx_Notify_event(static_cast<int>(pmix::Command::general),
|
||||
&fProcess,
|
||||
PMIX_RANGE_CUSTOM,
|
||||
holder->fInfos.data(), holder->fInfos.size(),
|
||||
&Commands::OpCompleteCallback<Holder>,
|
||||
holder.get());
|
||||
holder.release();
|
||||
}
|
||||
|
||||
private:
|
||||
static void EventHandlerRegistration(pmix_status_t s, size_t handlerRef, void* obj)
|
||||
{
|
||||
if (s == PMIX_SUCCESS) {
|
||||
LOG(debug) << "Successfully registered event handler, reference = " << static_cast<unsigned long>(handlerRef);
|
||||
static_cast<Commands*>(obj)->fHandlerRef = handlerRef;
|
||||
static_cast<Commands*>(obj)->fSubscribed = true;
|
||||
} else {
|
||||
LOG(error) << "Could not register PMIx event handler, status = " << s;
|
||||
}
|
||||
static_cast<Commands*>(obj)->fBlocker.Signal();
|
||||
}
|
||||
|
||||
static void EventHandlerDeregistration(pmix_status_t s, void* obj)
|
||||
{
|
||||
if (s == PMIX_SUCCESS) {
|
||||
LOG(debug) << "Successfully deregistered event handler, reference = " << static_cast<Commands*>(obj)->fHandlerRef;
|
||||
static_cast<Commands*>(obj)->fSubscribed = false;
|
||||
} else {
|
||||
LOG(error) << "Could not deregister PMIx event handler, reference = " << static_cast<Commands*>(obj)->fHandlerRef << ", status = " << s;
|
||||
}
|
||||
static_cast<Commands*>(obj)->fBlocker.Signal();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void OpCompleteCallback(pmix_status_t s, void* data)
|
||||
{
|
||||
if (s == PMIX_SUCCESS) {
|
||||
// LOG(info) << "Operation completed successfully";
|
||||
} else {
|
||||
LOG(error) << "Could not complete operation, status = " << s;
|
||||
}
|
||||
if (data) {
|
||||
// LOG(warn) << "Destroying event data...";
|
||||
delete static_cast<T*>(data);
|
||||
}
|
||||
}
|
||||
|
||||
static void Handler(size_t handlerId,
|
||||
pmix_status_t s,
|
||||
const pmix_proc_t* src,
|
||||
pmix_info_t info[], size_t ninfo,
|
||||
pmix_info_t[] /* results */, size_t nresults,
|
||||
pmix_event_notification_cbfunc_fn_t cbfunc,
|
||||
void* cbdata)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Event handler called with "
|
||||
<< "status: " << s << ", "
|
||||
<< "source: " << src->nspace << "_" << src->rank << ", "
|
||||
<< "ninfo: " << ninfo << ", "
|
||||
<< "nresults: " << nresults << ", "
|
||||
<< "handlerId: " << handlerId;
|
||||
|
||||
std::string msg;
|
||||
|
||||
Commands* obj = nullptr;
|
||||
|
||||
if (ninfo > 0) {
|
||||
ss << ":\n";
|
||||
for (size_t i = 0; i < ninfo; ++i) {
|
||||
ss << " [" << i << "]: key: '" << info[i].key
|
||||
<< "', value: '" << pmix::get_value_str(info[i].value)
|
||||
<< "', value.type: '" << pmix::typeNames.at(info[i].value.type)
|
||||
<< "', flags: " << info[i].flags;
|
||||
|
||||
if (std::strcmp(info[i].key, "fairmq.cmd") == 0) {
|
||||
msg = pmix::get_value_str(info[i].value);
|
||||
}
|
||||
|
||||
if (std::strcmp(info[i].key, PMIX_EVENT_RETURN_OBJECT) == 0) {
|
||||
obj = static_cast<Commands*>(info[i].value.data.ptr);
|
||||
}
|
||||
|
||||
if (i < ninfo - 1) {
|
||||
ss << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (obj != nullptr) {
|
||||
if (static_cast<Commands*>(obj)->fProcess.rank != src->rank) {
|
||||
// LOG(warn) << ss.str();
|
||||
static_cast<Commands*>(obj)->fCallback(msg, proc(const_cast<char*>(src->nspace), rank(src->rank)));
|
||||
} else {
|
||||
// LOG(trace) << "suppressing message from itself";
|
||||
}
|
||||
} else {
|
||||
LOG(error) << "ERROR";
|
||||
}
|
||||
|
||||
if (cbfunc != nullptr) {
|
||||
cbfunc(PMIX_SUCCESS, nullptr, 0, nullptr, nullptr, cbdata);
|
||||
}
|
||||
}
|
||||
|
||||
const proc& fProcess;
|
||||
size_t fHandlerRef;
|
||||
std::function<void(const std::string& msg, const proc& sender)> fCallback;
|
||||
std::array<pmix_info_t, 1> fInfos;
|
||||
bool fSubscribed;
|
||||
fair::mq::tools::SharedSemaphore fBlocker;
|
||||
};
|
||||
|
||||
} /* namespace pmix */
|
||||
|
||||
#endif /* PMIXCOMMANDS_H */
|
205
fairmq/plugins/PMIx/PMIxPlugin.cxx
Normal file
205
fairmq/plugins/PMIx/PMIxPlugin.cxx
Normal file
@@ -0,0 +1,205 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2019-2022 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 "PMIxPlugin.h"
|
||||
|
||||
#include <fairmq/tools/Strings.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <cstdint> // UINT32_MAX
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace fair::mq::plugins
|
||||
{
|
||||
|
||||
PMIxPlugin::PMIxPlugin(const string& name,
|
||||
const Plugin::Version version,
|
||||
const string& maintainer,
|
||||
const string& homepage,
|
||||
PluginServices* pluginServices)
|
||||
: Plugin(name, version, maintainer, homepage, pluginServices)
|
||||
, fProcess(Init())
|
||||
, fPid(getpid())
|
||||
, fPMIxClient(tools::ToString("PMIx client(pid=", fPid, ") "))
|
||||
, fDeviceId(string(fProcess.nspace) + "_" + to_string(fProcess.rank))
|
||||
// , fLastExternalController(UINT32_MAX)
|
||||
, fExitingAckedByLastExternalController(false)
|
||||
, fCurrentState(DeviceState::Idle)
|
||||
, fLastState(DeviceState::Idle)
|
||||
{
|
||||
TakeDeviceControl();
|
||||
LOG(debug) << PMIxClient() << "pmix::init() OK: " << fProcess << ", version=" << pmix::get_version();
|
||||
SetProperty<string>("id", fDeviceId);
|
||||
|
||||
Fence("pmix::init");
|
||||
|
||||
// LOG(info) << "PMIX_EXTERNAL_ERR_BASE: " << PMIX_EXTERNAL_ERR_BASE;
|
||||
|
||||
// job level infos
|
||||
// LOG(info) << "PMIX_SESSION_ID: " << pmix::getInfo(PMIX_SESSION_ID, fProcess);
|
||||
// LOG(info) << "PMIX_UNIV_SIZE: " << pmix::getInfo(PMIX_UNIV_SIZE, fProcess);
|
||||
// LOG(info) << "PMIX_JOB_SIZE: " << pmix::getInfo(PMIX_JOB_SIZE, fProcess);
|
||||
// LOG(info) << "PMIX_JOB_NUM_APPS: " << pmix::getInfo(PMIX_JOB_NUM_APPS, fProcess);
|
||||
// LOG(info) << "PMIX_APP_SIZE: " << pmix::getInfo(PMIX_APP_SIZE, fProcess);
|
||||
// LOG(info) << "PMIX_MAX_PROCS: " << pmix::getInfo(PMIX_MAX_PROCS, fProcess);
|
||||
// LOG(info) << "PMIX_NUM_NODES: " << pmix::getInfo(PMIX_NUM_NODES, fProcess);
|
||||
// LOG(info) << "PMIX_CLUSTER_ID: " << pmix::getInfo(PMIX_CLUSTER_ID, fProcess);
|
||||
// LOG(info) << "PMIX_NSPACE: " << pmix::getInfo(PMIX_NSPACE, fProcess);
|
||||
// LOG(info) << "PMIX_JOBID: " << pmix::getInfo(PMIX_JOBID, fProcess);
|
||||
// LOG(info) << "PMIX_NODE_LIST: " << pmix::getInfo(PMIX_NODE_LIST, fProcess);
|
||||
// LOG(info) << "PMIX_ALLOCATED_NODELIST: " << pmix::getInfo(PMIX_ALLOCATED_NODELIST, fProcess);
|
||||
// LOG(info) << "PMIX_NPROC_OFFSET: " << pmix::getInfo(PMIX_NPROC_OFFSET, fProcess);
|
||||
// LOG(info) << "PMIX_LOCALLDR: " << pmix::getInfo(PMIX_LOCALLDR, fProcess);
|
||||
// LOG(info) << "PMIX_APPLDR: " << pmix::getInfo(PMIX_APPLDR, fProcess);
|
||||
|
||||
// // per-node information
|
||||
// LOG(info) << "PMIX_NODE_SIZE: " << pmix::getInfo(PMIX_NODE_SIZE, fProcess);
|
||||
// LOG(info) << "PMIX_LOCAL_SIZE: " << pmix::getInfo(PMIX_LOCAL_SIZE, fProcess);
|
||||
// LOG(info) << "PMIX_AVAIL_PHYS_MEMORY: " << pmix::getInfo(PMIX_AVAIL_PHYS_MEMORY, fProcess);
|
||||
|
||||
// // per-process information
|
||||
// LOG(info) << "PMIX_PROCID: " << pmix::getInfo(PMIX_PROCID, fProcess);
|
||||
// LOG(info) << "PMIX_APPNUM: " << pmix::getInfo(PMIX_APPNUM, fProcess);
|
||||
// LOG(info) << "PMIX_LOCAL_RANK: " << pmix::getInfo(PMIX_LOCAL_RANK, fProcess);
|
||||
// LOG(info) << "PMIX_NODE_RANK: " << pmix::getInfo(PMIX_NODE_RANK, fProcess);
|
||||
// LOG(info) << "PMIX_RANK: " << pmix::getInfo(PMIX_RANK, fProcess);
|
||||
// LOG(info) << "PMIX_GLOBAL_RANK: " << pmix::getInfo(PMIX_GLOBAL_RANK, fProcess);
|
||||
// LOG(info) << "PMIX_APP_RANK: " << pmix::getInfo(PMIX_APP_RANK, fProcess);
|
||||
|
||||
SubscribeToDeviceStateChange([this](DeviceState newState) {
|
||||
switch (newState) {
|
||||
case DeviceState::Bound:
|
||||
Publish();
|
||||
break;
|
||||
case DeviceState::Connecting:
|
||||
Lookup();
|
||||
break;
|
||||
case DeviceState::Exiting:
|
||||
ReleaseDeviceControl();
|
||||
UnsubscribeFromDeviceStateChange();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
lock_guard<mutex> lock{fStateChangeSubscriberMutex};
|
||||
fLastState = fCurrentState;
|
||||
fCurrentState = newState;
|
||||
// for (auto subscriberId : fStateChangeSubscribers) {
|
||||
// LOG(debug) << "Publishing state-change: " << fLastState << "->" << newState << " to " << subscriberId;
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
PMIxPlugin::~PMIxPlugin()
|
||||
{
|
||||
LOG(debug) << "Destroying PMIxPlugin";
|
||||
ReleaseDeviceControl();
|
||||
while (pmix::initialized()) {
|
||||
try {
|
||||
pmix::finalize();
|
||||
LOG(debug) << PMIxClient() << "pmix::finalize() OK";
|
||||
} catch (const pmix::runtime_error& e) {
|
||||
LOG(debug) << PMIxClient() << "pmix::finalize() failed: " << e.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto PMIxPlugin::Init() -> pmix::proc
|
||||
{
|
||||
if (!pmix::initialized()) {
|
||||
return pmix::init();
|
||||
} else {
|
||||
throw runtime_error("trying to initialize PMIx while it is already initialized");
|
||||
}
|
||||
}
|
||||
|
||||
auto PMIxPlugin::Publish() -> void
|
||||
{
|
||||
auto channels(GetChannelInfo());
|
||||
vector<pmix::info> info;
|
||||
|
||||
for (const auto& c : channels) {
|
||||
string methodKey("chans." + c.first + "." + to_string(c.second - 1) + ".method");
|
||||
if (GetProperty<string>(methodKey) == "bind") {
|
||||
for (int i = 0; i < c.second; ++i) {
|
||||
string addressKey("chans." + c.first + "." + to_string(i) + ".address");
|
||||
info.emplace_back(addressKey, GetProperty<string>(addressKey));
|
||||
LOG(debug) << PMIxClient() << info.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info.size() > 0) {
|
||||
pmix::publish(info);
|
||||
LOG(debug) << PMIxClient() << "pmix::publish() OK: published " << info.size()
|
||||
<< " binding channels.";
|
||||
}
|
||||
}
|
||||
|
||||
auto PMIxPlugin::Fence() -> void
|
||||
{
|
||||
pmix::proc all(fProcess);
|
||||
all.rank = pmix::rank::wildcard;
|
||||
|
||||
pmix::fence({all});
|
||||
}
|
||||
|
||||
auto PMIxPlugin::Fence(const std::string& label) -> void
|
||||
{
|
||||
Fence();
|
||||
LOG(debug) << PMIxClient() << "pmix::fence() [" << label << "] OK";
|
||||
}
|
||||
|
||||
auto PMIxPlugin::Lookup() -> void
|
||||
{
|
||||
auto channels(GetChannelInfo());
|
||||
for (const auto& c : channels) {
|
||||
string methodKey("chans." + c.first + "." + to_string(c.second - 1) + ".method");
|
||||
if (GetProperty<string>(methodKey) == "connect") {
|
||||
for (int i = 0; i < c.second; ++i) {
|
||||
vector<pmix::pdata> pdata;
|
||||
string addressKey("chans." + c.first + "." + to_string(i) + ".address");
|
||||
pdata.emplace_back();
|
||||
pdata.back().set_key(addressKey);
|
||||
vector<pmix::info> info;
|
||||
info.emplace_back(PMIX_WAIT, static_cast<int>(pdata.size()));
|
||||
|
||||
if (pdata.size() > 0) {
|
||||
pmix::lookup(pdata, info);
|
||||
LOG(debug) << PMIxClient() << "pmix::lookup() OK";
|
||||
}
|
||||
|
||||
for (const auto& p : pdata) {
|
||||
if (p.value.type == PMIX_UNDEF) {
|
||||
LOG(debug) << PMIxClient() << "pmix::lookup() not found: key=" << p.key;
|
||||
} else if (p.value.type == PMIX_STRING) {
|
||||
LOG(debug) << PMIxClient() << "pmix::lookup() found:"
|
||||
<< " key=" << p.key << ",value=" << p.value.data.string;
|
||||
SetProperty<string>(p.key, p.value.data.string);
|
||||
} else {
|
||||
LOG(debug) << PMIxClient() << "pmix::lookup() wrong type returned: "
|
||||
<< "key=" << p.key << ",type=" << p.value.type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto PMIxPlugin::WaitForExitingAck() -> void
|
||||
{
|
||||
unique_lock<mutex> lock(fStateChangeSubscriberMutex);
|
||||
fExitingAcked.wait_for(lock, chrono::milliseconds(1000), [this]() {
|
||||
return fExitingAckedByLastExternalController;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fair::mq::plugins
|
87
fairmq/plugins/PMIx/PMIxPlugin.h
Normal file
87
fairmq/plugins/PMIx/PMIxPlugin.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2019-2022 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_PLUGINS_PMIX
|
||||
#define FAIR_MQ_PLUGINS_PMIX
|
||||
|
||||
#include "PMIx.hpp"
|
||||
#include "PMIxCommands.h"
|
||||
|
||||
#include <fairmq/Plugin.h>
|
||||
#include <fairmq/Version.h>
|
||||
#include <fairlogger/Logger.h>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
namespace fair::mq::plugins
|
||||
{
|
||||
|
||||
class PMIxPlugin : public Plugin
|
||||
{
|
||||
public:
|
||||
PMIxPlugin(const std::string& name,
|
||||
const Plugin::Version version,
|
||||
const std::string& maintainer,
|
||||
const std::string& homepage,
|
||||
PluginServices* pluginServices);
|
||||
~PMIxPlugin();
|
||||
|
||||
auto PMIxClient() const -> std::string { return fPMIxClient; };
|
||||
|
||||
private:
|
||||
pmix::proc fProcess;
|
||||
pid_t fPid;
|
||||
std::string fPMIxClient;
|
||||
std::string fDeviceId;
|
||||
|
||||
std::set<uint32_t> fStateChangeSubscribers;
|
||||
// uint32_t fLastExternalController;
|
||||
bool fExitingAckedByLastExternalController;
|
||||
std::condition_variable fExitingAcked;
|
||||
std::mutex fStateChangeSubscriberMutex;
|
||||
|
||||
DeviceState fCurrentState;
|
||||
DeviceState fLastState;
|
||||
|
||||
auto Init() -> pmix::proc;
|
||||
auto Publish() -> void;
|
||||
auto Fence() -> void;
|
||||
auto Fence(const std::string& label) -> void;
|
||||
auto Lookup() -> void;
|
||||
|
||||
auto WaitForExitingAck() -> void;
|
||||
};
|
||||
|
||||
Plugin::ProgOptions PMIxProgramOptions()
|
||||
{
|
||||
boost::program_options::options_description options("PMIx Plugin");
|
||||
options.add_options()
|
||||
("pmix-dummy", boost::program_options::value<int>()->default_value(0), "Dummy.");
|
||||
return options;
|
||||
}
|
||||
|
||||
REGISTER_FAIRMQ_PLUGIN(
|
||||
PMIxPlugin, // Class name
|
||||
pmix, // Plugin name (string, lower case chars only)
|
||||
(Plugin::Version{FAIRMQ_VERSION_MAJOR,
|
||||
FAIRMQ_VERSION_MINOR,
|
||||
FAIRMQ_VERSION_PATCH}), // Version
|
||||
"FairRootGroup <fairroot@gsi.de>", // Maintainer
|
||||
"https://github.com/FairRootGroup/FairMQ", // Homepage
|
||||
PMIxProgramOptions // custom program options for the plugin
|
||||
)
|
||||
|
||||
} // namespace fair::mq::plugins
|
||||
|
||||
#endif /* FAIR_MQ_PLUGINS_PMIX */
|
131
fairmq/plugins/PMIx/runPMIxCommandUI.cxx
Normal file
131
fairmq/plugins/PMIx/runPMIxCommandUI.cxx
Normal file
@@ -0,0 +1,131 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2014-2022 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 <fairmq/States.h>
|
||||
|
||||
#include <fairlogger/Logger.h>
|
||||
|
||||
#include "PMIx.hpp"
|
||||
#include "PMIxCommands.h"
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <termios.h> // raw mode console input
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
const std::map<fair::mq::Transition, fair::mq::State> expected =
|
||||
{
|
||||
{ fair::mq::Transition::InitDevice, fair::mq::State::InitializingDevice },
|
||||
{ fair::mq::Transition::CompleteInit, fair::mq::State::Initialized },
|
||||
{ fair::mq::Transition::Bind, fair::mq::State::Bound },
|
||||
{ fair::mq::Transition::Connect, fair::mq::State::DeviceReady },
|
||||
{ fair::mq::Transition::InitTask, fair::mq::State::Ready },
|
||||
{ fair::mq::Transition::Run, fair::mq::State::Running },
|
||||
{ fair::mq::Transition::Stop, fair::mq::State::Ready },
|
||||
{ fair::mq::Transition::ResetTask, fair::mq::State::DeviceReady },
|
||||
{ fair::mq::Transition::ResetDevice, fair::mq::State::Idle },
|
||||
{ fair::mq::Transition::End, fair::mq::State::Exiting }
|
||||
};
|
||||
|
||||
struct MiniTopo
|
||||
{
|
||||
explicit MiniTopo(unsigned int n)
|
||||
: fState(n, fair::mq::State::Ok)
|
||||
{}
|
||||
|
||||
void WaitFor(const fair::mq::State state)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(fMtx);
|
||||
|
||||
fCV.wait(lk, [&](){
|
||||
unsigned int count = std::count_if(fState.cbegin(), fState.cend(), [=](const auto& s) {
|
||||
return s == state;
|
||||
});
|
||||
|
||||
bool result = count == fState.size();
|
||||
cout << "expecting " << state << " for " << fState.size() << " devices, found " << count << ", condition: " << result << endl;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
void Update(uint32_t rank, const fair::mq::State state)
|
||||
{
|
||||
try {
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(fMtx);
|
||||
fState.at(rank - 1) = state;
|
||||
}
|
||||
fCV.notify_one();
|
||||
} catch (const std::exception& e) {
|
||||
LOG(error) << "Exception in Update: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
vector<fair::mq::State> fState;
|
||||
std::mutex fMtx;
|
||||
std::condition_variable fCV;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try {
|
||||
unsigned int numDevices;
|
||||
|
||||
bpo::options_description options("Common options");
|
||||
options.add_options()
|
||||
("number-devices,n", bpo::value<unsigned int>(&numDevices)->default_value(0), "Number of devices (will be removed in the future)")
|
||||
("help,h", "Produce help message");
|
||||
|
||||
bpo::variables_map vm;
|
||||
bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm);
|
||||
|
||||
if (vm.count("help")) {
|
||||
cout << "FairMQ DDS Command UI" << endl << options << endl;
|
||||
cout << "Commands: [c] check state, [o] dump config, [h] help, [r] run, [s] stop, [t] reset task, [d] reset device, [q] end, [j] init task, [i] init device, [k] complete init, [b] bind, [x] connect" << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
bpo::notify(vm);
|
||||
|
||||
fair::Logger::SetConsoleSeverity(fair::Severity::debug);
|
||||
fair::Logger::SetConsoleColor(true);
|
||||
fair::Logger::SetVerbosity(fair::Verbosity::low);
|
||||
|
||||
pmix::proc process;
|
||||
|
||||
if (!pmix::initialized()) {
|
||||
process = pmix::init();
|
||||
LOG(warn) << "pmix::init() OK: " << process << ", version=" << pmix::get_version();
|
||||
}
|
||||
|
||||
pmix::proc all(process);
|
||||
all.rank = pmix::rank::wildcard;
|
||||
pmix::fence({all});
|
||||
LOG(warn) << "pmix::fence() [pmix::init] OK";
|
||||
|
||||
MiniTopo topo(numDevices);
|
||||
} catch (exception& e) {
|
||||
LOG(error) << "Error: " << e.what();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
LOG(warn) << "exiting";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2017-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* Copyright (C) 2017-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
@@ -40,7 +40,7 @@ Config::Config(const string& name, Plugin::Version version, const string& mainta
|
||||
LOG(debug) << "channel-config: Parsing channel configuration";
|
||||
SetProperties(SuboptParser(GetProperty<vector<string>>("channel-config"), idForParser));
|
||||
} else {
|
||||
LOG(info) << "fair::mq::plugins::Config: no channels configuration provided via --mq-config or --channel-config";
|
||||
LOG(warn) << "fair::mq::plugins::Config: no channels configuration provided via --mq-config or --channel-config";
|
||||
}
|
||||
} catch (exception& e) {
|
||||
LOG(error) << e.what();
|
||||
@@ -74,8 +74,9 @@ Plugin::ProgOptions ConfigPluginProgramOptions()
|
||||
("shm-throw-bad-alloc", po::value<bool >()->default_value(true), "Shared memory: throw fair::mq::MessageBadAlloc if cannot allocate a message (retry if false).")
|
||||
("bad-alloc-max-attempts", po::value<int >(), "Maximum number of allocation attempts before throwing fair::mq::MessageBadAlloc. -1 is infinite. There is always at least one attempt, so 0 has safe effect as 1.")
|
||||
("bad-alloc-attempt-interval", po::value<int >()->default_value(50), "Interval between attempts if cannot allocate a message (in ms).")
|
||||
("shm-monitor", po::value<bool >()->default_value(false), "Shared memory: run monitor daemon.")
|
||||
("shm-monitor", po::value<bool >()->default_value(true), "Shared memory: run monitor daemon.")
|
||||
("shm-no-cleanup", po::value<bool >()->default_value(false), "Shared memory: do not cleanup the memory when last device leaves.")
|
||||
("ofi-size-hint", po::value<size_t >()->default_value(0), "EXPERIMENTAL: OFI size hint for the allocator.")
|
||||
("rate", po::value<float >()->default_value(0.), "Rate for conditional run loop (Hz).")
|
||||
("session", po::value<string >()->default_value("default"), "Session name.")
|
||||
("config-key", po::value<string >(), "Use provided value instead of device id for fetching the configuration from JSON file.")
|
||||
|
@@ -489,6 +489,12 @@ auto Control::RunShutdownSequence() -> void
|
||||
case DeviceState::Running:
|
||||
ChangeDeviceState(DeviceStateTransition::Stop);
|
||||
break;
|
||||
case DeviceState::Binding:
|
||||
case DeviceState::Connecting:
|
||||
case DeviceState::InitializingTask:
|
||||
case DeviceState::ResettingTask:
|
||||
case DeviceState::ResettingDevice:
|
||||
ChangeDeviceState(DeviceStateTransition::Auto);
|
||||
default:
|
||||
// LOG(debug) << "Controller ignoring event: " << nextState;
|
||||
break;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2014-2023 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, *
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include <fairlogger/Logger.h>
|
||||
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/interprocess/ipc/message_queue.hpp>
|
||||
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||
#include <boost/interprocess/sync/interprocess_condition.hpp>
|
||||
@@ -27,7 +29,6 @@
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include <algorithm> // max
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstddef> // max_align_t
|
||||
#include <cstdlib> // getenv
|
||||
@@ -206,22 +207,22 @@ class Manager
|
||||
|
||||
fEventCounter = fManagementSegment.find<EventCounter>(unique_instance).first;
|
||||
if (fEventCounter) {
|
||||
LOG(trace) << "event counter found: " << fEventCounter->fCount;
|
||||
LOG(debug) << "event counter found: " << fEventCounter->fCount;
|
||||
} else {
|
||||
LOG(trace) << "no event counter found, creating one and initializing with 0";
|
||||
LOG(debug) << "no event counter found, creating one and initializing with 0";
|
||||
fEventCounter = fManagementSegment.construct<EventCounter>(unique_instance)(0);
|
||||
LOG(trace) << "initialized event counter with: " << fEventCounter->fCount;
|
||||
LOG(debug) << "initialized event counter with: " << fEventCounter->fCount;
|
||||
}
|
||||
|
||||
fDeviceCounter = fManagementSegment.find<DeviceCounter>(unique_instance).first;
|
||||
if (fDeviceCounter) {
|
||||
LOG(trace) << "device counter found, with value of " << fDeviceCounter->fCount << ". incrementing.";
|
||||
LOG(debug) << "device counter found, with value of " << fDeviceCounter->fCount << ". incrementing.";
|
||||
(fDeviceCounter->fCount)++;
|
||||
LOG(trace) << "incremented device counter, now: " << fDeviceCounter->fCount;
|
||||
LOG(debug) << "incremented device counter, now: " << fDeviceCounter->fCount;
|
||||
} else {
|
||||
LOG(trace) << "no device counter found, creating one and initializing with 1";
|
||||
LOG(debug) << "no device counter found, creating one and initializing with 1";
|
||||
fDeviceCounter = fManagementSegment.construct<DeviceCounter>(unique_instance)(1);
|
||||
LOG(trace) << "initialized device counter with: " << fDeviceCounter->fCount;
|
||||
LOG(debug) << "initialized device counter with: " << fDeviceCounter->fCount;
|
||||
}
|
||||
|
||||
fShmSegments = fManagementSegment.find_or_construct<Uint16SegmentInfoHashMap>(unique_instance)(fShmVoidAlloc);
|
||||
@@ -264,10 +265,10 @@ class Manager
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG(debug) << (createdSegment ? "Created" : "Opened") << " managed shared memory segment " << "fmq_" << fShmId << "_m_" << fSegmentId
|
||||
<< ". Size: " << boost::apply_visitor(SegmentSize(), fSegments.at(fSegmentId)) << " bytes."
|
||||
<< " Available: " << boost::apply_visitor(SegmentFreeMemory(), fSegments.at(fSegmentId)) << " bytes."
|
||||
<< " Allocation algorithm: " << allocationAlgorithm;
|
||||
LOG(debug) << "Created/opened shared memory segment '" << "fmq_" << fShmId << "_m_" << fSegmentId << "'."
|
||||
<< " Size: " << boost::apply_visitor(SegmentSize(), fSegments.at(fSegmentId)) << " bytes."
|
||||
<< " Available: " << boost::apply_visitor(SegmentFreeMemory(), fSegments.at(fSegmentId)) << " bytes."
|
||||
<< " Allocation algorithm: " << allocationAlgorithm;
|
||||
} catch (interprocess_exception& bie) {
|
||||
LOG(error) << "Failed to create/open shared memory segment '" << "fmq_" << fShmId << "_m_" << fSegmentId << "': " << bie.what();
|
||||
throw TransportError(tools::ToString("Failed to create/open shared memory segment '", "fmq_", fShmId, "_m_", fSegmentId, "': ", bie.what()));
|
||||
@@ -394,26 +395,20 @@ class Manager
|
||||
|
||||
const uint16_t id = cfg.id.value();
|
||||
|
||||
std::lock_guard<std::mutex> lock(fLocalRegionsMtx);
|
||||
|
||||
UnmanagedRegion* region = nullptr;
|
||||
|
||||
auto it = fRegions.find(id);
|
||||
if (it != fRegions.end()) {
|
||||
region = it->second.get();
|
||||
if (region->fControlling) {
|
||||
LOG(error) << "Unmanaged Region with id " << id << " already exists. Only unique IDs per session are allowed.";
|
||||
throw TransportError(tools::ToString("Unmanaged Region with id ", id, " already exists. Only unique IDs per session are allowed."));
|
||||
}
|
||||
|
||||
LOG(debug) << "Unmanaged region (view) already present, promoting to controller";
|
||||
region->BecomeController(cfg);
|
||||
} else {
|
||||
auto res = fRegions.emplace(id, std::make_unique<UnmanagedRegion>(fShmId, size, true, cfg));
|
||||
bool newRegionCreated = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fLocalRegionsMtx);
|
||||
auto res = fRegions.emplace(id, std::make_unique<UnmanagedRegion>(fShmId, size, false, cfg));
|
||||
newRegionCreated = res.second;
|
||||
region = res.first->second.get();
|
||||
}
|
||||
// LOG(debug) << "Created region with id '" << id << "', path: '" << cfg.path << "', flags: '" << cfg.creationFlags << "'";
|
||||
|
||||
if (!newRegionCreated) {
|
||||
region->fRemote = false; // TODO: this should be more clear, refactor it.
|
||||
}
|
||||
|
||||
// start ack receiver only if a callback has been provided.
|
||||
if (callback || bulkCallback) {
|
||||
region->SetCallbacks(callback, bulkCallback);
|
||||
@@ -434,7 +429,7 @@ class Manager
|
||||
}
|
||||
}
|
||||
|
||||
UnmanagedRegion* GetRegionFromCache(uint16_t id)
|
||||
UnmanagedRegion* GetRegion(uint16_t id)
|
||||
{
|
||||
// NOTE: gcc optimizations. Prevent loading tls addresses many times in the fast path
|
||||
const auto &lTlCache = fTlRegionCache;
|
||||
@@ -448,39 +443,41 @@ class Manager
|
||||
}
|
||||
}
|
||||
|
||||
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> shmLock(*fShmMtx);
|
||||
// slow path: check invalidation
|
||||
if (lTlCacheGen != fRegionsGen) {
|
||||
fTlRegionCache.fRegionsTLCache.clear();
|
||||
}
|
||||
|
||||
auto* lRegion = GetRegion(id);
|
||||
std::lock_guard<std::mutex> lock(fLocalRegionsMtx);
|
||||
auto* lRegion = GetRegionUnsafe(id, shmLock);
|
||||
fTlRegionCache.fRegionsTLCache.emplace_back(std::make_tuple(lRegion, id, fShmId64));
|
||||
fTlRegionCache.fRegionsTLCacheGen = fRegionsGen;
|
||||
return lRegion;
|
||||
}
|
||||
|
||||
UnmanagedRegion* GetRegion(uint16_t id)
|
||||
UnmanagedRegion* GetRegionUnsafe(uint16_t id, boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex>& lockedShmLock)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(fLocalRegionsMtx);
|
||||
// remote region could actually be a local one if a message originates from this device (has been sent out and returned)
|
||||
auto it = fRegions.find(id);
|
||||
if (it != fRegions.end()) {
|
||||
return it->second.get();
|
||||
} else {
|
||||
try {
|
||||
RegionConfig cfg;
|
||||
// get region info
|
||||
{
|
||||
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> shmLock(*fShmMtx);
|
||||
RegionInfo regionInfo = fShmRegions->at(id);
|
||||
cfg.id = id;
|
||||
cfg.creationFlags = regionInfo.fCreationFlags;
|
||||
cfg.path = regionInfo.fPath.c_str();
|
||||
}
|
||||
RegionInfo regionInfo = fShmRegions->at(id);
|
||||
// safe to unlock now - no shm container accessed after this
|
||||
lockedShmLock.unlock();
|
||||
RegionConfig cfg;
|
||||
cfg.id = id;
|
||||
cfg.creationFlags = regionInfo.fCreationFlags;
|
||||
cfg.path = regionInfo.fPath.c_str();
|
||||
// LOG(debug) << "Located remote region with id '" << id << "', path: '" << cfg.path << "', flags: '" << cfg.creationFlags << "'";
|
||||
|
||||
auto r = fRegions.emplace(id, std::make_unique<UnmanagedRegion>(fShmId, 0, false, std::move(cfg)));
|
||||
auto r = fRegions.emplace(id, std::make_unique<UnmanagedRegion>(fShmId, 0, true, std::move(cfg)));
|
||||
r.first->second->InitializeQueues();
|
||||
r.first->second->StartAckSender();
|
||||
lockedShmLock.lock();
|
||||
return r.first->second.get();
|
||||
} catch (std::out_of_range& oor) {
|
||||
LOG(error) << "Could not get remote region with id '" << id << "'. Does the region creator run with the same session id?";
|
||||
@@ -496,10 +493,10 @@ class Manager
|
||||
void RemoveRegion(uint16_t id)
|
||||
{
|
||||
try {
|
||||
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> shmLock(*fShmMtx);
|
||||
std::lock_guard<std::mutex> lock(fLocalRegionsMtx);
|
||||
fRegions.at(id)->StopAcks();
|
||||
{
|
||||
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> shmLock(*fShmMtx);
|
||||
if (fRegions.at(id)->RemoveOnDestruction()) {
|
||||
fShmRegions->at(id).fDestroyed = true;
|
||||
(fEventCounter->fCount)++;
|
||||
@@ -515,73 +512,44 @@ class Manager
|
||||
std::vector<fair::mq::RegionInfo> GetRegionInfo()
|
||||
{
|
||||
std::vector<fair::mq::RegionInfo> result;
|
||||
std::map<uint64_t, RegionConfig> regionCfgs;
|
||||
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> shmLock(*fShmMtx);
|
||||
|
||||
{
|
||||
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> shmLock(*fShmMtx);
|
||||
|
||||
for (const auto& [segmentId, segmentInfo] : *fShmSegments) {
|
||||
// make sure any segments in the session are found
|
||||
GetSegment(segmentId);
|
||||
try {
|
||||
fair::mq::RegionInfo info;
|
||||
info.managed = true;
|
||||
info.id = segmentId;
|
||||
info.event = RegionEvent::created;
|
||||
info.ptr = boost::apply_visitor(SegmentAddress(), fSegments.at(segmentId));
|
||||
info.size = boost::apply_visitor(SegmentSize(), fSegments.at(segmentId));
|
||||
result.push_back(info);
|
||||
} catch (const std::out_of_range& oor) {
|
||||
LOG(error) << "could not find segment with id " << segmentId;
|
||||
LOG(error) << oor.what();
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [regionId, regionInfo] : *fShmRegions) {
|
||||
for (const auto& e : *fShmSegments) {
|
||||
// make sure any segments in the session are found
|
||||
GetSegment(e.first);
|
||||
try {
|
||||
fair::mq::RegionInfo info;
|
||||
info.managed = false;
|
||||
info.id = regionId;
|
||||
info.flags = regionInfo.fUserFlags;
|
||||
info.event = regionInfo.fDestroyed ? RegionEvent::destroyed : RegionEvent::created;
|
||||
if (info.event == RegionEvent::created) {
|
||||
RegionConfig cfg;
|
||||
cfg.id = info.id;
|
||||
cfg.creationFlags = regionInfo.fCreationFlags;
|
||||
cfg.path = regionInfo.fPath.c_str();
|
||||
regionCfgs.emplace(info.id, cfg);
|
||||
// fill the ptr+size info after shmLock is released, to avoid constructing local region under it
|
||||
} else {
|
||||
info.ptr = nullptr;
|
||||
info.size = 0;
|
||||
}
|
||||
info.managed = true;
|
||||
info.id = e.first;
|
||||
info.event = RegionEvent::created;
|
||||
info.ptr = boost::apply_visitor(SegmentAddress(), fSegments.at(e.first));
|
||||
info.size = boost::apply_visitor(SegmentSize(), fSegments.at(e.first));
|
||||
result.push_back(info);
|
||||
} catch (const std::out_of_range& oor) {
|
||||
LOG(error) << "could not find segment with id " << e.first;
|
||||
LOG(error) << oor.what();
|
||||
}
|
||||
}
|
||||
|
||||
// do another iteration outside of shm lock, to fill ptr+size of unmanaged regions
|
||||
for (auto& info : result) {
|
||||
if (!info.managed && info.event == RegionEvent::created) {
|
||||
auto cfgIt = regionCfgs.find(info.id);
|
||||
if (cfgIt != regionCfgs.end()) {
|
||||
UnmanagedRegion* region = nullptr;
|
||||
std::lock_guard<std::mutex> lock(fLocalRegionsMtx);
|
||||
auto it = fRegions.find(info.id);
|
||||
if (it != fRegions.end()) {
|
||||
region = it->second.get();
|
||||
} else {
|
||||
auto r = fRegions.emplace(cfgIt->first, std::make_unique<UnmanagedRegion>(fShmId, 0, false, cfgIt->second));
|
||||
region = r.first->second.get();
|
||||
region->InitializeQueues();
|
||||
region->StartAckSender();
|
||||
}
|
||||
|
||||
for (const auto& e : *fShmRegions) {
|
||||
fair::mq::RegionInfo info;
|
||||
info.managed = false;
|
||||
info.id = e.first;
|
||||
info.flags = e.second.fUserFlags;
|
||||
info.event = e.second.fDestroyed ? RegionEvent::destroyed : RegionEvent::created;
|
||||
if (info.event == RegionEvent::created) {
|
||||
auto region = GetRegionUnsafe(info.id, shmLock);
|
||||
if (region) {
|
||||
info.ptr = region->GetData();
|
||||
info.size = region->GetSize();
|
||||
} else {
|
||||
info.ptr = nullptr;
|
||||
info.size = 0;
|
||||
throw std::runtime_error(tools::ToString("GetRegionInfo() could not get region with id '", info.id, "'"));
|
||||
}
|
||||
} else {
|
||||
info.ptr = nullptr;
|
||||
info.size = 0;
|
||||
}
|
||||
result.push_back(info);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -752,9 +720,6 @@ class Manager
|
||||
if (fBadAllocMaxAttempts >= 0 && ++numAttempts >= fBadAllocMaxAttempts) {
|
||||
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))));
|
||||
}
|
||||
if (numAttempts == 1 && fBadAllocMaxAttempts > 1) {
|
||||
LOG(warn) << 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)), ". Will try ", (fBadAllocMaxAttempts > 1 ? (std::to_string(fBadAllocMaxAttempts - 1)) + " more times" : " until success"), ", in ", fBadAllocAttemptIntervalInMs, "ms intervals");
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(fBadAllocAttemptIntervalInMs));
|
||||
if (Interrupted()) {
|
||||
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))));
|
||||
|
@@ -195,7 +195,7 @@ class Message final : public fair::mq::Message
|
||||
fLocalPtr = nullptr;
|
||||
}
|
||||
} else {
|
||||
fRegionPtr = fManager.GetRegionFromCache(fMeta.fRegionId);
|
||||
fRegionPtr = fManager.GetRegion(fMeta.fRegionId);
|
||||
if (fRegionPtr) {
|
||||
fLocalPtr = reinterpret_cast<char*>(fRegionPtr->GetData()) + fMeta.fHandle;
|
||||
} else {
|
||||
@@ -365,7 +365,7 @@ class Message final : public fair::mq::Message
|
||||
void ReleaseUnmanagedRegionBlock()
|
||||
{
|
||||
if (!fRegionPtr) {
|
||||
fRegionPtr = fManager.GetRegionFromCache(fMeta.fRegionId);
|
||||
fRegionPtr = fManager.GetRegion(fMeta.fRegionId);
|
||||
}
|
||||
|
||||
if (fRegionPtr) {
|
||||
|
@@ -23,7 +23,6 @@
|
||||
#include <boost/interprocess/ipc/message_queue.hpp>
|
||||
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
@@ -534,88 +533,6 @@ unsigned long Monitor::GetFreeMemory(const SessionId& sessionId, uint16_t segmen
|
||||
return GetFreeMemory(shmId, segmentId);
|
||||
}
|
||||
|
||||
bool Monitor::SegmentIsPresent(const ShmId& shmId, uint16_t segmentId)
|
||||
{
|
||||
using namespace boost::interprocess;
|
||||
try {
|
||||
bipc::managed_shared_memory managementSegment(bipc::open_read_only, std::string("fmq_" + shmId.shmId + "_mng").c_str());
|
||||
Uint16SegmentInfoHashMap* shmSegments = managementSegment.find<Uint16SegmentInfoHashMap>(unique_instance).first;
|
||||
|
||||
if (!shmSegments) {
|
||||
LOG(error) << "Found management segment, but could not locate segment info";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = shmSegments->find(segmentId);
|
||||
if (it != shmSegments->end()) {
|
||||
try {
|
||||
if (it->second.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) {
|
||||
RBTreeBestFitSegment segment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + std::to_string(segmentId)).c_str());
|
||||
} else {
|
||||
SimpleSeqFitSegment segment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + std::to_string(segmentId)).c_str());
|
||||
}
|
||||
} catch (bie&) {
|
||||
LOG(error) << "Could not find segment with id '" << segmentId << "' for shmId '" << shmId.shmId << "'";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
LOG(error) << "Could not find segment info for segment id '" << segmentId << "' for shmId '" << shmId.shmId << "'";
|
||||
return false;
|
||||
}
|
||||
} catch (bie&) {
|
||||
LOG(error) << "Could not find management segment for shmid '" << shmId.shmId << "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool Monitor::SegmentIsPresent(const SessionId& sessionId, uint16_t segmentId)
|
||||
{
|
||||
ShmId shmId{makeShmIdStr(sessionId.sessionId)};
|
||||
return SegmentIsPresent(shmId, segmentId);
|
||||
}
|
||||
|
||||
bool Monitor::RegionIsPresent(const ShmId& shmId, uint16_t regionId)
|
||||
{
|
||||
using namespace boost::interprocess;
|
||||
try {
|
||||
bipc::managed_shared_memory managementSegment(bipc::open_read_only, std::string("fmq_" + shmId.shmId + "_mng").c_str());
|
||||
Uint16RegionInfoHashMap* shmRegions = managementSegment.find<Uint16RegionInfoHashMap>(bipc::unique_instance).first;
|
||||
|
||||
if (!shmRegions) {
|
||||
LOG(error) << "Found management segment, but could not locate region info";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string regionFileName("fmq_" + shmId.shmId + "_rg_" + to_string(regionId));
|
||||
|
||||
auto it = shmRegions->find(regionId);
|
||||
if (it != shmRegions->end()) {
|
||||
try {
|
||||
if (it->second.fPath.empty()) {
|
||||
shared_memory_object object(open_only, regionFileName.c_str(), read_only);
|
||||
}
|
||||
} catch (bie&) {
|
||||
LOG(error) << "Could not find region with id '" << regionId << "' for shmId '" << shmId.shmId << "'";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
LOG(error) << "Could not find region info for region id '" << regionId << "' for shmId '" << shmId.shmId << "'";
|
||||
return false;
|
||||
}
|
||||
} catch (bie&) {
|
||||
LOG(error) << "Could not find management segment for shmid '" << shmId.shmId << "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool Monitor::RegionIsPresent(const SessionId& sessionId, uint16_t regionId)
|
||||
{
|
||||
ShmId shmId{makeShmIdStr(sessionId.sessionId)};
|
||||
return RegionIsPresent(shmId, regionId);
|
||||
}
|
||||
|
||||
void Monitor::PrintHelp()
|
||||
{
|
||||
LOG(info) << "controls: [x] close memory, "
|
||||
@@ -670,7 +587,7 @@ std::vector<std::pair<std::string, bool>> Monitor::Cleanup(const ShmId& shmIdT,
|
||||
string path = info.fPath.c_str();
|
||||
int flags = info.fCreationFlags;
|
||||
if (verbose) {
|
||||
LOG(info) << "Found UnmanagedRegion with id: " << id << ", path: '" << path << "', flags: " << flags << ", fDestroyed: " << info.fDestroyed << ".";
|
||||
LOG(info) << "Found RegionInfo with path: '" << path << "', flags: " << flags << ", fDestroyed: " << info.fDestroyed << ".";
|
||||
}
|
||||
if (!path.empty()) {
|
||||
result.emplace_back(Remove<bipc::file_mapping>(path + "fmq_" + shmId + "_rg_" + to_string(id), verbose));
|
||||
|
@@ -119,7 +119,7 @@ class Monitor
|
||||
/// @param sessionId session id
|
||||
static std::unordered_map<uint16_t, std::vector<BufferDebugInfo>> GetDebugInfo(const SessionId& sessionId);
|
||||
/// @brief Returns the amount of free memory in the specified segment
|
||||
/// @param shmId shmem id
|
||||
/// @param sessionId shmem id
|
||||
/// @param segmentId segment id
|
||||
/// @throws MonitorError
|
||||
static unsigned long GetFreeMemory(const ShmId& shmId, uint16_t segmentId);
|
||||
@@ -128,23 +128,6 @@ class Monitor
|
||||
/// @param segmentId segment id
|
||||
/// @throws MonitorError
|
||||
static unsigned long GetFreeMemory(const SessionId& sessionId, uint16_t segmentId);
|
||||
/// @brief Checks if a given segment can be opened
|
||||
/// @param shmId shmem id
|
||||
/// @param segmentId segment id
|
||||
static bool SegmentIsPresent(const ShmId& shmId, uint16_t segmentId);
|
||||
/// @brief Checks if a given segment can be opened
|
||||
/// @param sessionId session id
|
||||
/// @param segmentId segment id
|
||||
static bool SegmentIsPresent(const SessionId& sessionId, uint16_t segmentId);
|
||||
/// @brief Checks if a given region can be opened
|
||||
/// @param shmId shmem id
|
||||
/// @param regionId region id
|
||||
static bool RegionIsPresent(const ShmId& shmId, uint16_t regionId);
|
||||
/// @brief Checks if a given region can be opened
|
||||
/// @param sessionId session id
|
||||
/// @param regionId region id
|
||||
static bool RegionIsPresent(const SessionId& sessionId, uint16_t regionId);
|
||||
|
||||
|
||||
static bool PrintShm(const ShmId& shmId);
|
||||
static void ListAll(const std::string& path);
|
||||
|
@@ -54,13 +54,3 @@ The Monitor class can also be used independently from the supplied executable, a
|
||||
## Troubleshooting
|
||||
|
||||
Bus Error (SIGBUS) can occur if the transport tries to access shared memory that is not accessible. One reason could be because the used memory in the segment exceeds the capacity or available memory of the shmem filesystem (capacity is by default set to half of RAM on Linux).
|
||||
|
||||
## Shared Memory cleanup
|
||||
|
||||
On a graceful shutdown of all devices, shared memory transport removes all created shared memory files. In case of a crash however, the cleanup cannot be guaranteed. Following possibilites are available to perform cleanup in case of crashes:
|
||||
|
||||
- For execution with Slurm a [job_container](https://slurm.schedmd.com/job_container.conf.html) can be used, which can isolate the shm and remove them upon job finish. This is currently the recommended approach.
|
||||
- a [trap](https://www.man7.org/linux/man-pages/man1/trap.1p.html) in an executing script on ERR/EXIT with a call to `fairmq-shmmonitor -c -s <sessionid>`. This would not work if the script is killed with -SIGKILL or similar fashion where it cannot call the trap.
|
||||
- [CTest cleanup fixture](https://cmake.org/cmake/help/latest/prop_test/FIXTURES_CLEANUP.html) with a call to `fairmq-shmmonitor -c -s <sessionid>`. This would work for an ongoing ctest run, but would not be called if a test run is interrupted, e.g. by SIGINT.
|
||||
- manual cleanup of the files listed [above](#shared-memory-objects--files).
|
||||
- Launch devices with `--shm-monitor true`. This will launch a daemon. The daemon will then listen for heartbeats from devices (every 100ms) and if none are received within 2000ms, will clean the memory. This is unreliable because the daemon can also be killed by a strict enough controller. But also if for some reason there are significant delays in the heartbeats, shmem could end up being cleaned before it should be.
|
||||
|
@@ -11,9 +11,8 @@
|
||||
#include "Common.h"
|
||||
#include "Manager.h"
|
||||
#include "Message.h"
|
||||
#include <fairmq/Error.h>
|
||||
#include <fairmq/Message.h>
|
||||
#include <fairmq/Socket.h>
|
||||
#include <fairmq/Message.h>
|
||||
#include <fairmq/tools/Strings.h>
|
||||
#include <fairmq/zeromq/Common.h>
|
||||
|
||||
@@ -126,21 +125,16 @@ class Socket final : public fair::mq::Socket
|
||||
return zmq::Connect(fSocket, address, fId);
|
||||
}
|
||||
|
||||
int64_t Send(mq::MessagePtr& msg, int timeout = -1) override
|
||||
int64_t Send(MessagePtr& msg, int timeout = -1) override
|
||||
{
|
||||
auto msgPtr = msg.get();
|
||||
if (!msgPtr) {
|
||||
return static_cast<int>(TransferCode::error);
|
||||
}
|
||||
assertm(dynamic_cast<shmem::Message*>(msgPtr), "given mq::Message is a shmem::Message"); // NOLINT
|
||||
auto shmMsg = static_cast<shmem::Message*>(msgPtr); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
|
||||
|
||||
int flags = 0;
|
||||
if (timeout == 0) {
|
||||
flags = ZMQ_DONTWAIT;
|
||||
}
|
||||
int elapsed = 0;
|
||||
|
||||
Message* shmMsg = static_cast<Message*>(msg.get());
|
||||
|
||||
while (true) {
|
||||
int nbytes = zmq_send(fSocket, &(shmMsg->fMeta), sizeof(MetaHeader), flags);
|
||||
if (nbytes > 0) {
|
||||
@@ -219,12 +213,7 @@ class Socket final : public fair::mq::Socket
|
||||
MetaHeader* metas = static_cast<MetaHeader*>(zmqMsg.Data());
|
||||
|
||||
for (auto& msg : msgVec) {
|
||||
auto msgPtr = msg.get();
|
||||
if (!msgPtr) {
|
||||
return static_cast<int>(TransferCode::error);
|
||||
}
|
||||
assertm(dynamic_cast<shmem::Message*>(msgPtr), "given mq::Message is a shmem::Message"); // NOLINT
|
||||
auto shmMsg = static_cast<shmem::Message*>(msgPtr); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
|
||||
Message* shmMsg = static_cast<Message*>(msg.get());
|
||||
std::memcpy(metas++, &(shmMsg->fMeta), sizeof(MetaHeader));
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2014-2023 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, *
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <fairlogger/Logger.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||
#include <boost/interprocess/file_mapping.hpp>
|
||||
@@ -43,19 +44,19 @@ struct UnmanagedRegion
|
||||
friend class Monitor;
|
||||
|
||||
UnmanagedRegion(const std::string& shmId, uint16_t id, uint64_t size)
|
||||
: UnmanagedRegion(shmId, size, true, makeRegionConfig(id))
|
||||
: UnmanagedRegion(shmId, size, false, makeRegionConfig(id))
|
||||
{}
|
||||
|
||||
UnmanagedRegion(const std::string& shmId, uint64_t size, RegionConfig cfg)
|
||||
: UnmanagedRegion(shmId, size, true, std::move(cfg))
|
||||
: UnmanagedRegion(shmId, size, false, std::move(cfg))
|
||||
{}
|
||||
|
||||
UnmanagedRegion(const std::string& shmId, RegionConfig cfg)
|
||||
: UnmanagedRegion(shmId, cfg.size, true, std::move(cfg))
|
||||
: UnmanagedRegion(shmId, cfg.size, false, std::move(cfg))
|
||||
{}
|
||||
|
||||
UnmanagedRegion(const std::string& shmId, uint64_t size, bool controlling, RegionConfig cfg)
|
||||
: fControlling(controlling)
|
||||
UnmanagedRegion(const std::string& shmId, uint64_t size, bool remote, RegionConfig cfg)
|
||||
: fRemote(remote)
|
||||
, fRemoveOnDestruction(cfg.removeOnDestruction)
|
||||
, fLinger(cfg.linger)
|
||||
, fStopAcks(false)
|
||||
@@ -72,15 +73,11 @@ struct UnmanagedRegion
|
||||
|
||||
// TODO: refactor this
|
||||
cfg.size = size;
|
||||
const uint16_t id = cfg.id.value();
|
||||
bool created = false;
|
||||
|
||||
LOG(debug) << "UnmanagedRegion(): " << fName << " (" << (fControlling ? "controller" : "viewer") << ")";
|
||||
|
||||
if (!cfg.path.empty()) {
|
||||
fName = std::string(cfg.path + fName);
|
||||
|
||||
if (fControlling) {
|
||||
if (!fRemote) {
|
||||
// create a file
|
||||
std::filebuf fbuf;
|
||||
if (fbuf.open(fName, std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::binary)) {
|
||||
@@ -95,30 +92,23 @@ struct UnmanagedRegion
|
||||
if (!fFile) {
|
||||
LOG(error) << "Failed to initialize file: " << fName;
|
||||
LOG(error) << "errno: " << errno << ": " << strerror(errno);
|
||||
throw TransportError(tools::ToString("Failed to initialize file for shared memory region: ", strerror(errno)));
|
||||
throw std::runtime_error(tools::ToString("Failed to initialize file for shared memory region: ", strerror(errno)));
|
||||
}
|
||||
fFileMapping = file_mapping(fName.c_str(), read_write);
|
||||
LOG(debug) << "UnmanagedRegion(): initialized file: " << fName;
|
||||
LOG(debug) << "shmem: initialized file: " << fName;
|
||||
fRegion = mapped_region(fFileMapping, read_write, 0, size, 0, cfg.creationFlags);
|
||||
} else {
|
||||
try {
|
||||
// if opening fails, create
|
||||
try {
|
||||
fShmemObject = shared_memory_object(open_only, fName.c_str(), read_write);
|
||||
created = false;
|
||||
} catch (interprocess_exception& e) {
|
||||
if (fControlling) {
|
||||
LOG(debug) << "Could not open controlling shared_memory_object for region " << id << ": " << e.what() << ", creating...";
|
||||
fShmemObject = shared_memory_object(create_only, fName.c_str(), read_write);
|
||||
fShmemObject.truncate(size);
|
||||
created = true;
|
||||
} else {
|
||||
LOG(error) << "Could not open view for shared_memory_object for region " << id << ": " << e.what();
|
||||
throw TransportError(tools::ToString("Could not open view for shared_memory_object for region ", id, ": ", e.what()));
|
||||
}
|
||||
LOG(debug) << "Could not open " << (remote ? "remote" : "local") << " shared_memory_object for region id '" << cfg.id.value() << "': " << e.what() << ", creating...";
|
||||
fShmemObject = shared_memory_object(create_only, fName.c_str(), read_write);
|
||||
fShmemObject.truncate(size);
|
||||
}
|
||||
} catch (interprocess_exception& e) {
|
||||
LOG(error) << "Failed initializing shared_memory_object for region id " << id << ": " << e.what();
|
||||
LOG(error) << "Failed " << (remote ? "opening" : "creating") << " shared_memory_object for region id '" << cfg.id.value() << "': " << e.what();
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -129,27 +119,27 @@ struct UnmanagedRegion
|
||||
throw TransportError(tools::ToString("Created/opened region size (", fRegion.get_size(), ") does not match configured size (", size, ")"));
|
||||
}
|
||||
} catch (interprocess_exception& e) {
|
||||
LOG(error) << "Failed mapping shared_memory_object for region id " << id << ": " << e.what();
|
||||
LOG(error) << "Failed mapping shared_memory_object for region id '" << cfg.id.value() << "': " << e.what();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg.lock) {
|
||||
LOG(debug) << "Locking region " << id << "...";
|
||||
LOG(debug) << "Locking region " << cfg.id.value() << "...";
|
||||
Lock();
|
||||
LOG(debug) << "Successfully locked region " << id << ".";
|
||||
LOG(debug) << "Successfully locked region " << cfg.id.value() << ".";
|
||||
}
|
||||
if (cfg.zero) {
|
||||
LOG(debug) << "Zeroing free memory of region " << id << "...";
|
||||
LOG(debug) << "Zeroing free memory of region " << cfg.id.value() << "...";
|
||||
Zero();
|
||||
LOG(debug) << "Successfully zeroed free memory of region " << id << ".";
|
||||
LOG(debug) << "Successfully zeroed free memory of region " << cfg.id.value() << ".";
|
||||
}
|
||||
|
||||
if (fControlling && created) {
|
||||
if (!remote) {
|
||||
Register(shmId, cfg);
|
||||
}
|
||||
|
||||
LOG(debug) << (created ? "Created" : "Opened") << " unmanaged shared memory region: " << fName << " (" << (fControlling ? "controller" : "viewer") << ")";
|
||||
LOG(trace) << "shmem: initialized region: " << fName << " (" << (remote ? "remote" : "local") << ")";
|
||||
}
|
||||
|
||||
UnmanagedRegion() = delete;
|
||||
@@ -159,13 +149,6 @@ struct UnmanagedRegion
|
||||
UnmanagedRegion& operator=(const UnmanagedRegion&) = delete;
|
||||
UnmanagedRegion& operator=(UnmanagedRegion&&) = delete;
|
||||
|
||||
void BecomeController(RegionConfig& cfg)
|
||||
{
|
||||
fControlling = true;
|
||||
fLinger = cfg.linger;
|
||||
fRemoveOnDestruction = cfg.removeOnDestruction;
|
||||
}
|
||||
|
||||
void Zero()
|
||||
{
|
||||
memset(fRegion.get_address(), 0x00, fRegion.get_size());
|
||||
@@ -188,7 +171,6 @@ struct UnmanagedRegion
|
||||
|
||||
~UnmanagedRegion()
|
||||
{
|
||||
LOG(debug) << "~UnmanagedRegion(): " << fName << " (" << (fControlling ? "controller" : "viewer") << ")";
|
||||
fStopAcks = true;
|
||||
|
||||
if (fAcksSender.joinable()) {
|
||||
@@ -196,7 +178,7 @@ struct UnmanagedRegion
|
||||
fAcksSender.join();
|
||||
}
|
||||
|
||||
if (fControlling) {
|
||||
if (!fRemote) {
|
||||
if (fAcksReceiver.joinable()) {
|
||||
fAcksReceiver.join();
|
||||
}
|
||||
@@ -222,14 +204,14 @@ struct UnmanagedRegion
|
||||
fclose(fFile);
|
||||
}
|
||||
} else {
|
||||
// LOG(debug) << "Region queue '" << fQueueName << "' is viewer, no cleanup necessary";
|
||||
// LOG(debug) << "Region queue '" << fQueueName << "' is remote, no cleanup necessary";
|
||||
}
|
||||
|
||||
// LOG(debug) << "Region '" << fName << "' (" << (fControlling ? "controller" : "viewer") << ") destructed.";
|
||||
// LOG(debug) << "Region '" << fName << "' (" << (fRemote ? "remote" : "local") << ") destructed.";
|
||||
}
|
||||
|
||||
private:
|
||||
bool fControlling;
|
||||
bool fRemote;
|
||||
bool fRemoveOnDestruction;
|
||||
uint32_t fLinger;
|
||||
std::atomic<bool> fStopAcks;
|
||||
@@ -261,7 +243,6 @@ struct UnmanagedRegion
|
||||
static void Register(const std::string& shmId, const RegionConfig& cfg)
|
||||
{
|
||||
using namespace boost::interprocess;
|
||||
LOG(debug) << "Registering unmanaged shared memory region with id " << cfg.id.value();
|
||||
managed_shared_memory mngSegment(open_or_create, std::string("fmq_" + shmId + "_mng").c_str(), kManagementSegmentSize);
|
||||
VoidAlloc alloc(mngSegment.get_segment_manager());
|
||||
|
||||
@@ -269,14 +250,10 @@ struct UnmanagedRegion
|
||||
|
||||
EventCounter* eventCounter = mngSegment.find_or_construct<EventCounter>(unique_instance)(0);
|
||||
|
||||
auto it = shmRegions->find(cfg.id.value());
|
||||
if (it != shmRegions->end()) {
|
||||
LOG(error) << "Unmanaged Region with id " << cfg.id.value() << " has already been registered. Only unique IDs per session are allowed.";
|
||||
throw TransportError(tools::ToString("Unmanaged Region with id ", cfg.id.value(), " has already been registered. Only unique IDs per session are allowed."));
|
||||
bool newShmRegionCreated = shmRegions->emplace(cfg.id.value(), RegionInfo(cfg.path.c_str(), cfg.creationFlags, cfg.userFlags, cfg.size, alloc)).second;
|
||||
if (newShmRegionCreated) {
|
||||
(eventCounter->fCount)++;
|
||||
}
|
||||
|
||||
shmRegions->emplace(cfg.id.value(), RegionInfo(cfg.path.c_str(), cfg.creationFlags, cfg.userFlags, cfg.size, alloc));
|
||||
(eventCounter->fCount)++;
|
||||
}
|
||||
|
||||
void SetCallbacks(RegionCallback callback, RegionBulkCallback bulkCallback)
|
||||
|
@@ -40,9 +40,9 @@ class UnmanagedRegionImpl final : public fair::mq::UnmanagedRegion
|
||||
, fRegion(nullptr)
|
||||
, fRegionId(0)
|
||||
{
|
||||
auto [regionPtr, regionId] = fManager.CreateRegion(size, callback, bulkCallback, std::move(cfg));
|
||||
fRegion = regionPtr;
|
||||
fRegionId = regionId;
|
||||
auto result = fManager.CreateRegion(size, callback, bulkCallback, std::move(cfg));
|
||||
fRegion = result.first;
|
||||
fRegionId = result.second;
|
||||
}
|
||||
|
||||
UnmanagedRegionImpl(const UnmanagedRegionImpl&) = delete;
|
||||
|
@@ -1,14 +0,0 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2022 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_TOOLS_COMPILER_H
|
||||
#define FAIR_MQ_TOOLS_COMPILER_H
|
||||
|
||||
#define __FAIRMQ_ALWAYS_INLINE __attribute__((always_inline)) inline
|
||||
|
||||
#endif /* FAIR_MQ_TOOLS_COMPILER_H */
|
@@ -1,5 +1,5 @@
|
||||
/********************************************************************************
|
||||
* Copyright (C) 2017-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
@@ -10,8 +10,8 @@
|
||||
#include <fairmq/tools/Strings.h>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/process.hpp>
|
||||
#include <chrono>
|
||||
#include <csignal> // kill, signals
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
@@ -77,10 +77,8 @@ execute_result execute(const string& cmd, const string& prefix, const string& in
|
||||
bp::async_pipe errorPipe(ios);
|
||||
|
||||
const string delimiter = "\n";
|
||||
ba::steady_timer inputTimer(ios);
|
||||
inputTimer.expires_after(std::chrono::milliseconds(1000)); // NOLINT
|
||||
ba::steady_timer signalTimer(ios);
|
||||
signalTimer.expires_after(std::chrono::milliseconds(2000)); // NOLINT
|
||||
ba::deadline_timer inputTimer(ios, boost::posix_time::milliseconds(1000)); // NOLINT
|
||||
ba::deadline_timer signalTimer(ios, boost::posix_time::milliseconds(2000)); // NOLINT
|
||||
|
||||
// child process
|
||||
bp::child c(cmd, bp::std_out > outputPipe, bp::std_err > errorPipe, bp::std_in < inputPipe);
|
||||
|
157
meta_update.py
157
meta_update.py
@@ -1,157 +0,0 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright (C) 2021-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
|
||||
from argparse import ArgumentParser
|
||||
import json
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
class Manipulator(object):
|
||||
def __str__(self):
|
||||
return self.__class__.__name__
|
||||
|
||||
def load(self, filename=None):
|
||||
if filename is None:
|
||||
filename = self.default_filename
|
||||
with open(filename, 'rb') as fp:
|
||||
self.data = json.load(fp, object_pairs_hook=OrderedDict)
|
||||
|
||||
def save(self, filename=None, indent=2):
|
||||
if filename is None:
|
||||
filename = self.default_filename
|
||||
with open(filename, 'w', encoding='utf8') as fp:
|
||||
json.dump(self.data, fp, indent=indent)
|
||||
fp.write('\n')
|
||||
|
||||
@staticmethod
|
||||
def _dict_entry_cmp(dict1, dict2, field1, field2=None):
|
||||
if field2 is None:
|
||||
field2 = field1
|
||||
if (field1 in dict1) and (field2 in dict2):
|
||||
return dict1[field1] == dict2[field2]
|
||||
else:
|
||||
return False
|
||||
|
||||
def _handle_person_list_file(self, filename, field_name, **kwargs):
|
||||
fp = open(filename, 'r', encoding='utf8')
|
||||
person_list = self.data.setdefault(field_name, [])
|
||||
for i, line in enumerate(fp, start=0):
|
||||
line = line.strip()
|
||||
m = self.findregex.match(line)
|
||||
if m is None:
|
||||
raise RuntimeError("Could not analyze line %r" % line)
|
||||
found_entry = self._find_person_entry(person_list, m.groupdict())
|
||||
entry = self.update_person_entry(found_entry, m.groupdict(),
|
||||
**kwargs)
|
||||
if found_entry is None:
|
||||
person_list.insert(i, entry)
|
||||
|
||||
|
||||
class CodeMetaManipulator(Manipulator):
|
||||
default_filename = 'codemeta.json'
|
||||
findregex = re.compile(r'^(?P<familyName>[-\w\s]*[-\w]),\s*'
|
||||
r'(?P<givenName>[-\w\s]*[-\w])\s*'
|
||||
r'(?:<(?P<email>\S+@\S+)>)?\s*'
|
||||
r'(\[(?P<orcid>\S+)\])?$')
|
||||
|
||||
@classmethod
|
||||
def _find_person_entry(cls, person_list, matchdict):
|
||||
# orcid is unique
|
||||
for entry in person_list:
|
||||
if cls._dict_entry_cmp(entry, matchdict, '@id', 'orcid'):
|
||||
return entry
|
||||
for entry in person_list:
|
||||
if cls._dict_entry_cmp(entry, matchdict, 'email'):
|
||||
return entry
|
||||
if cls._dict_entry_cmp(entry, matchdict, 'familyName') \
|
||||
and cls._dict_entry_cmp(entry, matchdict, 'givenName'):
|
||||
return entry
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def update_person_entry(entry, matchdict):
|
||||
if entry is None:
|
||||
entry = OrderedDict()
|
||||
entry['@type'] = 'Person'
|
||||
for field in ('orcid', 'givenName', 'familyName', 'email'):
|
||||
val = matchdict.get(field, None)
|
||||
if val is not None:
|
||||
if field == 'orcid':
|
||||
entry['@id'] = val
|
||||
else:
|
||||
entry[field] = val
|
||||
return entry
|
||||
|
||||
def update_authors(self):
|
||||
self._handle_person_list_file('AUTHORS', 'author')
|
||||
self._handle_person_list_file('CONTRIBUTORS', 'contributor')
|
||||
|
||||
def version(self, new_version):
|
||||
self.data['softwareVersion'] = new_version
|
||||
|
||||
|
||||
class ZenodoManipulator(Manipulator):
|
||||
default_filename = '.zenodo.json'
|
||||
findregex = re.compile(r'^(?P<name>[-\w\s,]*[-\w])\s*'
|
||||
r'(?:<(?P<email>\S+@\S+)>)?\s*'
|
||||
r'(\[https://orcid\.org/(?P<orcid>\S+)\])?$')
|
||||
|
||||
@classmethod
|
||||
def _find_person_entry(cls, person_list, matchdict):
|
||||
# Match on orcid first
|
||||
for entry in person_list:
|
||||
if cls._dict_entry_cmp(entry, matchdict, 'orcid'):
|
||||
return entry
|
||||
for entry in person_list:
|
||||
if cls._dict_entry_cmp(entry, matchdict, 'name'):
|
||||
return entry
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def update_person_entry(entry, matchdict, contributor_type=None):
|
||||
if entry is None:
|
||||
entry = OrderedDict()
|
||||
if contributor_type:
|
||||
entry['type'] = contributor_type
|
||||
for field in ('name', 'orcid'):
|
||||
val = matchdict.get(field, None)
|
||||
if val is not None:
|
||||
entry[field] = val
|
||||
return entry
|
||||
|
||||
def update_authors(self):
|
||||
self._handle_person_list_file('AUTHORS', 'creators')
|
||||
self._handle_person_list_file('CONTRIBUTORS', 'contributors',
|
||||
contributor_type='Other')
|
||||
|
||||
def save(self, filename=None):
|
||||
super().save(filename, 4)
|
||||
|
||||
def version(self, new_version):
|
||||
self.data['version'] = new_version
|
||||
|
||||
|
||||
def main():
|
||||
parser = ArgumentParser(description='Update codemeta.json and '
|
||||
'.zenodo.json')
|
||||
parser.add_argument('--set-version', dest='newversion')
|
||||
args = parser.parse_args()
|
||||
|
||||
for manipulator in (CodeMetaManipulator(), ZenodoManipulator()):
|
||||
try:
|
||||
manipulator.load()
|
||||
except FileNotFoundError as e:
|
||||
print('*** Skipping {}: {}'.format(manipulator, e))
|
||||
continue
|
||||
if args.newversion is not None:
|
||||
manipulator.version(args.newversion)
|
||||
manipulator.update_authors()
|
||||
manipulator.save()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@@ -1,5 +1,5 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2014-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
|
||||
# Copyright (C) 2014-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
|
||||
# #
|
||||
# This software is distributed under the terms of the #
|
||||
# GNU Lesser General Public Licence (LGPL) version 3, #
|
||||
@@ -16,13 +16,12 @@ if(FairLogger_VERSION VERSION_LESS 1.9.0 AND FairLogger_VERSION VERSION_GREATER_
|
||||
LIST(APPEND definitions FAIR_MIN_SEVERITY=trace)
|
||||
endif()
|
||||
|
||||
if(definitions)
|
||||
set(definitions DEFINITIONS ${definitions})
|
||||
if(BUILD_OFI_TRANSPORT)
|
||||
LIST(APPEND definitions BUILD_OFI_TRANSPORT)
|
||||
endif()
|
||||
|
||||
if(ENABLE_SANITIZER_LEAK)
|
||||
get_filename_component(lsan_supps "${CMAKE_CURRENT_SOURCE_DIR}/leak_sanitizer_suppressions.txt" ABSOLUTE)
|
||||
set(environment ENVIRONMENT "LSAN_OPTIONS=set:suppressions=${lsan_supps}")
|
||||
if(definitions)
|
||||
set(definitions DEFINITIONS ${definitions})
|
||||
endif()
|
||||
|
||||
add_testhelper(runTestDevice
|
||||
@@ -68,13 +67,11 @@ add_testsuite(Protocols
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 20
|
||||
${definitions}
|
||||
${environment}
|
||||
)
|
||||
|
||||
add_testsuite(Parts
|
||||
SOURCES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/runner.cxx
|
||||
parts/_add_part.cxx
|
||||
parts/_iterator_interface.cxx
|
||||
|
||||
LINKS FairMQ
|
||||
@@ -82,7 +79,6 @@ add_testsuite(Parts
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/parts
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 20
|
||||
${environment}
|
||||
)
|
||||
|
||||
add_testsuite(Message
|
||||
@@ -96,7 +92,6 @@ add_testsuite(Message
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 20
|
||||
${definitions}
|
||||
${environment}
|
||||
)
|
||||
|
||||
add_testsuite(Region
|
||||
@@ -111,7 +106,6 @@ add_testsuite(Region
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 20
|
||||
${definitions}
|
||||
${environment}
|
||||
)
|
||||
|
||||
add_testsuite(Device
|
||||
@@ -134,7 +128,6 @@ add_testsuite(Device
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/device
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 20
|
||||
${environment}
|
||||
)
|
||||
|
||||
set(VERSION_MAJOR 1)
|
||||
@@ -180,7 +173,6 @@ add_testsuite(Plugins
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS FairMQPlugin_test_dummy fairmq-plugin-test_dummy2
|
||||
TIMEOUT 20
|
||||
${environment}
|
||||
)
|
||||
|
||||
add_testsuite(PluginsPrelinked
|
||||
@@ -192,7 +184,6 @@ add_testsuite(PluginsPrelinked
|
||||
INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 20
|
||||
${environment}
|
||||
)
|
||||
|
||||
add_testsuite(PluginServices
|
||||
@@ -206,7 +197,6 @@ add_testsuite(PluginServices
|
||||
INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 20
|
||||
${environment}
|
||||
)
|
||||
|
||||
add_testsuite(EventManager
|
||||
@@ -218,7 +208,6 @@ add_testsuite(EventManager
|
||||
INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 20
|
||||
${environment}
|
||||
)
|
||||
|
||||
add_testsuite(Properties
|
||||
@@ -231,7 +220,6 @@ add_testsuite(Properties
|
||||
INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 20
|
||||
${environment}
|
||||
)
|
||||
|
||||
# add_testsuite(StateMachine
|
||||
@@ -254,7 +242,6 @@ add_testsuite(Tools
|
||||
INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 20
|
||||
${environment}
|
||||
)
|
||||
|
||||
add_testsuite(Channel
|
||||
@@ -266,7 +253,6 @@ add_testsuite(Channel
|
||||
INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 20
|
||||
${environment}
|
||||
)
|
||||
|
||||
add_testsuite(Transport
|
||||
@@ -281,7 +267,6 @@ add_testsuite(Transport
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 20
|
||||
${definitions}
|
||||
${environment}
|
||||
)
|
||||
|
||||
add_testsuite(Poller
|
||||
@@ -294,7 +279,6 @@ add_testsuite(Poller
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 20
|
||||
${definitions}
|
||||
${environment}
|
||||
)
|
||||
|
||||
add_testsuite(MemoryResources
|
||||
@@ -307,5 +291,4 @@ add_testsuite(MemoryResources
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
TIMEOUT 20
|
||||
${definitions}
|
||||
${environment}
|
||||
)
|
||||
|
@@ -102,7 +102,6 @@ auto testConnectedPeers(std::string const& transport)
|
||||
|
||||
ProgOptions config;
|
||||
config.SetProperty<string>("session", tools::Uuid());
|
||||
config.SetProperty<bool>("shm-monitor", true);
|
||||
string const address(tools::ToString("ipc://", config.GetProperty<string>("session")));
|
||||
unsigned long constexpr zero(0), one(1);
|
||||
auto factory(TransportFactory::CreateTransportFactory(transport, tools::Uuid(), &config));
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user