Compare commits

...

63 Commits

Author SHA1 Message Date
Dennis Klein
dcea48fcee
fix: parse errors
```
/test/memory_resources/_memory_resources.cxx: In member function ‘virtual void {anonymous}::MemoryResources_allocator_Test::TestBody()’:
/test/memory_resources/_memory_resources.cxx:104:12: error: parse error in template argument list
  104 |     config.SetProperty<string>("session", to_string(session));
      |            ^~~~~~~~~~~~~~~~~~~
/test/memory_resources/_memory_resources.cxx:104:31: error: no matching function for call to ‘fair::mq::ProgOptions::SetProperty<<expression error> >(const char [8], std::string)’
  104 |     config.SetProperty<string>("session", to_string(session));
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /home/dklein/projects/FairMQ2/test/memory_resources/_memory_resources.cxx:11:
/fairmq/ProgOptions.h:269:6: note: candidate: ‘template<class T> void fair::mq::ProgOptions::SetProperty(const std::string&, T)’
  269 | void fair::mq::ProgOptions::SetProperty(const std::string& key, T val)
      |      ^~~~
/fairmq/ProgOptions.h:269:6: note:   template argument deduction/substitution failed:
/test/memory_resources/_memory_resources.cxx:104:31: error: template argument 1 is invalid
  104 |     config.SetProperty<string>("session", to_string(session));
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/test/memory_resources/_memory_resources.cxx: In member function ‘virtual void {anonymous}::MemoryResources_getMessage_Test::TestBody()’:
/test/memory_resources/_memory_resources.cxx:132:12: error: parse error in template argument list
  132 |     config.SetProperty<string>("session", to_string(session));
      |            ^~~~~~~~~~~~~~~~~~~
/test/memory_resources/_memory_resources.cxx:132:31: error: no matching function for call to ‘fair::mq::ProgOptions::SetProperty<<expression error> >(const char [8], std::string)’
  132 |     config.SetProperty<string>("session", to_string(session));
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/fairmq/ProgOptions.h:269:6: note: candidate: ‘template<class T> void fair::mq::ProgOptions::SetProperty(const std::string&, T)’
  269 | void fair::mq::ProgOptions::SetProperty(const std::string& key, T val)
      |      ^~~~
/fairmq/ProgOptions.h:269:6: note:   template argument deduction/substitution failed:
/test/memory_resources/_memory_resources.cxx:132:31: error: template argument 1 is invalid
  132 |     config.SetProperty<string>("session", to_string(session));
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
2025-06-13 08:17:53 +02:00
Giulio Eulisse
67dcf77a7f De-boostify: use std::pmr from C++17 2025-06-13 08:02:06 +02:00
Alexey Rybalchenko
24e7a5b8d0 Make shmem headers public 2025-03-17 15:16:46 +01:00
Giulio Eulisse
c11506e958
feat(EventManager): Out of line some methods 2025-01-23 15:35:26 +01:00
Dennis Klein
e4f258c9ea fix: Update copyright 2025-01-09 17:09:57 +01:00
Dennis Klein
324a27a2e1 fix(tools): No longer use removed alias io_service
Deprecated via d3bbf3756d
and removed via 49fcd03434
in Boost 1.87 or Asio 1.33.
2025-01-09 17:09:57 +01:00
Dennis Klein
c80f97b338 fix(tools): No longer use removed query API
Deprecated via 74fe2b8e14
and removed via e916bdfb1a
in Boost 1.87 or Asio 1.33.
2025-01-09 17:09:57 +01:00
Dennis Klein
76824fee36 build(googletest): Update metadata 2025-01-09 17:09:57 +01:00
dependabot[bot]
d2e4679dc8 build(deps): bump extern/googletest from 530d5c8 to 7d76a23
Bumps [extern/googletest](https://github.com/google/googletest) from `530d5c8` to `7d76a23`.
- [Release notes](https://github.com/google/googletest/releases)
- [Commits](530d5c8c84...7d76a231b0)

---
updated-dependencies:
- dependency-name: extern/googletest
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-07 18:24:47 +01:00
Alexey Rybalchenko
6bb70bd519 Update Mac CI nodes 2025-01-07 17:42:16 +01:00
Giulio Eulisse
fe2127e12f Reduce bloat due to statics
Avoid disseminating every compile unit including Message.h with TransportNames and
TransportTypes and the associated unordered_map helper methods (e.g.
murmur_hash).
2025-01-07 17:41:30 +01:00
Giulio Eulisse
41165cf16b Out of line ProgOption::SetProperty for int and std::string
The specializations are common enough to show up in O2 compilation profiles
and they are not time critical (once per run at max).
2025-01-07 17:34:22 +01:00
Dennis Klein
8fe95e644e ci: Update 2024-08-20 15:56:21 +02:00
Dennis Klein
6628a231e2 build: Adopt all CMake policies up to 3.30
Modernizing to the policy range syntax supported by
[`cmake_minimum_required`](https://cmake.org/cmake/help/latest/command/cmake_minimum_required.html)
since CMake 3.12.
2024-08-20 15:56:21 +02:00
Giulio Eulisse
91b31f0799 Hide actual container from the API 2024-05-23 15:54:24 +02:00
Alexey Rybalchenko
39cb021827 Add 'no control' controller 2024-02-19 22:09:54 +01:00
Alexey Rybalchenko
36b48f5594 Update MacOS CI entires 2024-02-16 13:12:40 +01:00
Alexey Rybalchenko
0e221b28b8 shm: use node_allocator for ref counts 2024-01-25 10:45:34 +01:00
Alexey Rybalchenko
1ee0977df4 shm: use (de)allocate_one() for ref counts 2024-01-25 10:45:34 +01:00
Alexey Rybalchenko
24d578a4ba shm: extend monitor output for refCount region 2024-01-25 10:45:34 +01:00
Christian Tacke
ce1a4499cc ci: Check codemeta/zenodo with AUTHORS/CONTRIBUTORS
If AUTHORS or CONTRIBUTORS are changed,
check that the changes are merged into codemeta.json,
and .zenodo.json.
2023-12-20 16:51:13 +01:00
Dennis Klein
7d009f0915 docs: Update installation section 2023-12-14 13:15:18 +00:00
Dennis Klein
b70b181c38
ci: Create devcontainer.json 2023-12-14 13:40:47 +01:00
dependabot[bot]
94602d23b3 build(deps): bump extern/googletest from a1cc8c5 to 530d5c8
Bumps [extern/googletest](https://github.com/google/googletest) from `a1cc8c5` to `530d5c8`.
- [Release notes](https://github.com/google/googletest/releases)
- [Commits](a1cc8c5519...530d5c8c84)

---
updated-dependencies:
- dependency-name: extern/googletest
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-14 13:35:01 +01:00
Dennis Klein
41ac755c57
ci: Fix dependabot gitsubmodule directory 2023-12-13 15:48:21 +01:00
Dennis Klein
6d4a82427b
Update dependabot.yml 2023-12-13 15:31:14 +01:00
Dennis Klein
0966dee55d
build: Enable dependabot 2023-12-13 15:26:44 +01:00
Christian Tacke
b649356c5a chore: upgrade checkout step to v4 2023-12-13 13:34:35 +01:00
Alexey Rybalchenko
2df3d909fa shm: when refCount segment size is zero, fallback to old behaviour
, which is to store reference counts inside the main data segment
2023-11-29 19:21:42 +01:00
Alexey Rybalchenko
05a2ae6a31 example: configure new script too 2023-11-29 19:21:42 +01:00
Alexey Rybalchenko
58ffdfd1f4 Remove unused ctor and constant 2023-11-29 19:21:42 +01:00
Alexey Rybalchenko
addfd071bb Fix incorrect parameters in region example scripts 2023-11-24 14:19:21 +01:00
Alexey Rybalchenko
2d27abc533 Examples: add a script for externally created region 2023-11-24 14:19:21 +01:00
Alexey Rybalchenko
faf577086a shm: fix initialization of rc segment when region is created externally 2023-11-24 14:19:21 +01:00
Alexey Rybalchenko
ff1f9b94ef shm: include rcCountSegment free memory in the monitor output 2023-11-24 14:19:21 +01:00
Alexey Rybalchenko
34e8a24c86 Examples: use multipart in the region example 2023-11-15 12:52:14 +01:00
Alexey Rybalchenko
7567a10513 shm: Bump the ref segment size 10x 2023-11-15 12:52:14 +01:00
Alexey Rybalchenko
424e22b41a shm: Throw RefCountBadAlloc if insufficient space in the ref count segment 2023-11-15 12:52:14 +01:00
Dennis Klein
961eca5276 test(PluginServices): state change subscription thread-safety 2023-11-10 13:13:13 +01:00
Alexey Rybalchenko
fbb6577625 StateMachine: Guard access to subscription containers 2023-11-10 13:13:13 +01:00
Alexey Rybalchenko
6122010694 Fix address clashes in tests 2023-10-24 15:22:21 +02:00
Giulio Eulisse
b40db42196 Use std::move rather than just move
Apparently:

  "warning: unqualified call to 'std::move' [-Wunqualified-std-cast-call]"

is default in new XCode.
2023-10-23 08:00:23 +02:00
Giulio Eulisse
f732b87def Drop unused variable
The else clause at the end makes the postincrement impossible.
2023-10-23 08:00:23 +02:00
Alexey Rybalchenko
f05a09da5a shm: Message: refactor ctors 2023-10-19 19:16:00 +02:00
Alexey Rybalchenko
5aa6c99442 shm: remove alignment member from Message 2023-10-19 19:16:00 +02:00
Alexey Rybalchenko
3c714fd9e0 Message::SetUsedSize: add optional alignment argument, to avoid storing alignment with the msg object 2023-10-19 19:16:00 +02:00
Alexey Rybalchenko
1b7532a520 Refactor shm::Message to contain sorted members of MetaHeader
Move the members of MetaHeader flat into shmem::Message and sort them by
size to reduce the size of the class.
2023-10-19 19:16:00 +02:00
Alexey Rybalchenko
f092b94c96
Update comment 2023-10-04 11:25:47 +02:00
Alexey Rybalchenko
8d28824489 Shm: Use MakeShmName to construct shm object names 2023-09-29 11:18:24 +02:00
Alexey Rybalchenko
4310d07ed1 deduplicate ipc address in a test 2023-09-29 11:18:24 +02:00
Alexey Rybalchenko
7bd31f8ff0 apply readability-else-after-return 2023-09-29 11:18:24 +02:00
Alexey Rybalchenko
1a0ab3a4e2 shm: Ref counting for unmanaged regions in a dedicated segment 2023-09-29 11:18:24 +02:00
Alexey Rybalchenko
cacf69d5f6 Replace boost::variant with std::variant 2023-09-29 11:18:24 +02:00
Alexey Rybalchenko
46f50a10ea Add example with ref-counted copy from unmanaged region 2023-09-29 11:18:24 +02:00
Alexey Rybalchenko
68038c4693 shm: Move ShmHeader into Common.h 2023-09-29 11:18:24 +02:00
Dennis Klein
1036e204d0 docs: Add "releaseNotes" field to codemeta 2023-09-11 18:07:29 +02:00
Dennis Klein
fddbbc1732 docs: Add "softwareVersion" field to codemeta 2023-09-11 18:01:42 +02:00
Giulio Eulisse
3c1723fc54 Allow sorting StateChange callbacks
If the key of the callback is a number, it will be used to invoke
callbacks with the correct ordering.
2023-09-06 09:49:30 +02:00
Christian Tacke
c3418cc7b8 chore: Run meta_update.py 2023-08-08 16:26:10 +02:00
Christian Tacke
cc00c5a6f1 docs: Add "readme" field to codemeta
A link to an introduction for people who are not experts in
the field.
2023-08-08 16:26:10 +02:00
Christian Tacke
e6bb14f535 ci: Check codemeta.json
Use the eOSSR tooling to validate our codemeta.json file
2023-08-08 16:26:10 +02:00
Christian Tacke
b18d60372c codemeta: Add GSI as "maintainer"
GSI is providing resources for maintaining FairMQ. So let's
document this in codemeta.json.
2023-08-08 16:21:48 +02:00
Giulio Eulisse
7ceccdeaa6 Print actual address we are trying to bind. 2023-06-29 12:28:23 +02:00
59 changed files with 1180 additions and 609 deletions

View File

@ -0,0 +1,5 @@
{
"image": "ghcr.io/fairrootgroup/fairmq-dev/fedora-38:latest",
"features": {
}
}

12
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,12 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "dev"
schedule:
interval: "monthly"
- package-ecosystem: "gitsubmodule"
directory: "/"
target-branch: "dev"
schedule:
interval: "monthly"

29
.github/workflows/check_metadata.yaml vendored Normal file
View File

@ -0,0 +1,29 @@
# SPDX-FileCopyrightText: 2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH, Darmstadt, Germany
#
# SPDX-License-Identifier: CC0-1.0
name: Check AUTHORS and CONTRIBUTORS in metadata
on:
push:
paths:
- AUTHORS
- CONTRIBUTORS
- codemeta.json
- .zenodo.json
pull_request:
paths:
- AUTHORS
- CONTRIBUTORS
- codemeta.json
- .zenodo.json
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Try updating metadata
run: python meta_update.py
- name: Check for Updates
run: git diff --exit-code

View File

@ -0,0 +1,21 @@
name: validate codemeta
on:
push:
paths:
- codemeta.json
- .github/workflows/codemeta_validate.yaml
pull_request:
paths:
- codemeta.json
- .github/workflows/codemeta_validate.yaml
jobs:
build:
runs-on: ubuntu-latest
container:
image: gitlab-registry.in2p3.fr/escape2020/wp3/eossr:v1.0
steps:
- uses: actions/checkout@v4
- name: validate codemeta
run: eossr-metadata-validator codemeta.json

View File

@ -1,6 +1,7 @@
{ {
"creators": [ "creators": [
{ {
"orcid": "0000-0002-8071-4497",
"name": "Al-Turany, Mohammad" "name": "Al-Turany, Mohammad"
}, },
{ {

View File

@ -1,5 +1,5 @@
################################################################################ ################################################################################
# Copyright (C) 2018-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # # Copyright (C) 2018-2024 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
# # # #
# This software is distributed under the terms of the # # This software is distributed under the terms of the #
# GNU Lesser General Public Licence (LGPL) version 3, # # GNU Lesser General Public Licence (LGPL) version 3, #
@ -8,8 +8,7 @@
# Project ###################################################################### # Project ######################################################################
cmake_minimum_required(VERSION 3.15 FATAL_ERROR) cmake_minimum_required(VERSION 3.15...3.30 FATAL_ERROR)
cmake_policy(VERSION 3.15...3.26)
list(PREPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) list(PREPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
include(GitHelper) include(GitHelper)

10
Jenkinsfile vendored
View File

@ -43,7 +43,7 @@ def jobMatrix(String type, List specs) {
sh "bash ${jobscript}" sh "bash ${jobscript}"
} else { // selector == "slurm" } else { // selector == "slurm"
def imageurl = "oras://ghcr.io/fairrootgroup/fairmq-dev/${os}-${ver}-sif:latest" def imageurl = "oras://ghcr.io/fairrootgroup/fairmq-dev/${os}-${ver}-sif:latest"
def execopts = "--net --ipc --uts --pid -B/shared" def execopts = "--ipc --uts --pid -B/shared"
def containercmd = "singularity exec ${execopts} ${imageurl} bash -l -c \\\"${ctestcmd} ${extra}\\\"" def containercmd = "singularity exec ${execopts} ${imageurl} bash -l -c \\\"${ctestcmd} ${extra}\\\""
sh """\ sh """\
echo \"echo \\\"*** Job started at .......: \\\$(date -R)\\\"\" >> ${jobscript} echo \"echo \\\"*** Job started at .......: \\\$(date -R)\\\"\" >> ${jobscript}
@ -87,14 +87,18 @@ pipeline{
def builds = jobMatrix('build', [ def builds = jobMatrix('build', [
[os: 'ubuntu', ver: '20.04', arch: 'x86_64', compiler: 'gcc-9'], [os: 'ubuntu', ver: '20.04', arch: 'x86_64', compiler: 'gcc-9'],
[os: 'ubuntu', ver: '22.04', arch: 'x86_64', compiler: 'gcc-11'], [os: 'ubuntu', ver: '22.04', arch: 'x86_64', compiler: 'gcc-11'],
[os: 'ubuntu', ver: '24.04', arch: 'x86_64', compiler: 'gcc-13'],
[os: 'fedora', ver: '33', arch: 'x86_64', compiler: 'gcc-10'], [os: 'fedora', ver: '33', arch: 'x86_64', compiler: 'gcc-10'],
[os: 'fedora', ver: '34', arch: 'x86_64', compiler: 'gcc-11'], [os: 'fedora', ver: '34', arch: 'x86_64', compiler: 'gcc-11'],
[os: 'fedora', ver: '35', 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: '36', arch: 'x86_64', compiler: 'gcc-12'],
[os: 'fedora', ver: '37', 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: 'fedora', ver: '38', arch: 'x86_64', compiler: 'gcc-13'],
[os: 'macos', ver: '12', arch: 'x86_64', compiler: 'apple-clang-14'], [os: 'fedora', ver: '39', arch: 'x86_64', compiler: 'gcc-13'],
[os: 'macos', ver: '13', arch: 'arm64', compiler: 'apple-clang-14'], [os: 'fedora', ver: '40', arch: 'x86_64', compiler: 'gcc-14'],
[os: 'macos', ver: '14', arch: 'x86_64', compiler: 'apple-clang-16'],
[os: 'macos', ver: '15', arch: 'x86_64', compiler: 'apple-clang-16'],
[os: 'macos', ver: '15', arch: 'arm64', compiler: 'apple-clang-16'],
]) ])
def all_debug = "-DCMAKE_BUILD_TYPE=Debug" def all_debug = "-DCMAKE_BUILD_TYPE=Debug"

View File

@ -45,9 +45,9 @@ Recommended:
```bash ```bash
git clone https://github.com/FairRootGroup/FairMQ fairmq_source git clone https://github.com/FairRootGroup/FairMQ fairmq_source
cmake -S fairmq_source -B fairmq_build -GNinja -DCMAKE_BUILD_TYPE=Release cmake -S fairmq_source -B fairmq_build -GNinja -DCMAKE_BUILD_TYPE=Release [-DBUILD_TESTING=ON]
cmake --build fairmq_build cmake --build fairmq_build
ctest --test-dir fairmq_build --output-on-failure --schedule-random -j<ncpus> [ctest --test-dir fairmq_build --output-on-failure --schedule-random -j<ncpus>] # needs -DBUILD_TESTING=ON
cmake --install fairmq_build --prefix $(pwd)/fairmq_install cmake --install fairmq_build --prefix $(pwd)/fairmq_install
``` ```
@ -56,6 +56,24 @@ Please consult the [manpages of your CMake version](https://cmake.org/cmake/help
If dependencies are not installed in standard system directories, you can hint the installation location via If dependencies are not installed in standard system directories, you can hint the installation location via
`-DCMAKE_PREFIX_PATH=...` or per dependency via `-D{DEPENDENCY}_ROOT=...` (`*_ROOT` variables can also be environment variables). `-DCMAKE_PREFIX_PATH=...` or per dependency via `-D{DEPENDENCY}_ROOT=...` (`*_ROOT` variables can also be environment variables).
## Installation via Spack
Prerequisite: [Spack](https://spack.readthedocs.io/en/latest/getting_started.html)
```bash
spack info fairmq # inspect build options
spack install fairmq # build latest packaged version with default options
```
Build FairMQ's dependencies via Spack for development:
```bash
git clone -b dev https://github.com/FairRootGroup/FairMQ fairmq_source
spack --env fairmq_source install # installs deps declared in fairmq_source/spack.yaml
spack env activate fairmq_source # sets $CMAKE_PREFIX_PATH which is used by CMake to find FairMQ's deps
cmake -S fairmq_source -B fairmq_build -GNinja -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON
# develop, compile, test
spack env deactivate # at end of dev session, or simply close the shell
```
## Usage ## Usage

View File

@ -1,5 +1,5 @@
################################################################################ ################################################################################
# Copyright (C) 2018-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # # Copyright (C) 2018-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
# # # #
# This software is distributed under the terms of the # # This software is distributed under the terms of the #
# GNU Lesser General Public Licence (LGPL) version 3, # # GNU Lesser General Public Licence (LGPL) version 3, #
@ -41,7 +41,7 @@ if(BUILD_TESTING)
endif() endif()
find_package2(BUNDLED GTest REQUIRED) find_package2(BUNDLED GTest REQUIRED)
if(GTest_BUNDLED) if(GTest_BUNDLED)
set(GTest_VERSION "Apr 8 2022 @a1cc8c55") set(GTest_VERSION "Dec 26 2024 @7d76a23")
set(GTest_PREFIX "<bundled>") set(GTest_PREFIX "<bundled>")
endif() endif()
endif() endif()

View File

@ -6,12 +6,23 @@
"license": "./COPYRIGHT", "license": "./COPYRIGHT",
"datePublished": "2018-04-15", "datePublished": "2018-04-15",
"developmentStatus": "active", "developmentStatus": "active",
"softwareVersion": "master",
"releaseNotes": "https://github.com/FairRootGroup/FairMQ/releases",
"codeRepository": "https://github.com/FairRootGroup/FairMQ/", "codeRepository": "https://github.com/FairRootGroup/FairMQ/",
"readme": "https://github.com/FairRootGroup/FairMQ/#readme",
"issueTracker": "https://github.com/FairRootGroup/FairMQ/issues", "issueTracker": "https://github.com/FairRootGroup/FairMQ/issues",
"identifier": "https://doi.org/10.5281/zenodo.1689985", "identifier": "https://doi.org/10.5281/zenodo.1689985",
"maintainer": [
{
"@type": "ResearchOrganisation",
"@id": "https://ror.org/02k8cbn47",
"name": "GSI Helmholtz Centre for Heavy Ion Research"
}
],
"author": [ "author": [
{ {
"@type": "Person", "@type": "Person",
"@id": "https://orcid.org/0000-0002-8071-4497",
"givenName": "Mohammad", "givenName": "Mohammad",
"familyName": "Al-Turany" "familyName": "Al-Turany"
}, },

View File

@ -61,7 +61,7 @@ function(add_example)
set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq) set(FAIRMQ_BIN_DIR ${CMAKE_BINARY_DIR}/fairmq)
foreach(script IN LISTS scripts) foreach(script IN LISTS scripts)
set(script_file "${script_prefix}-${script}.sh") set(script_file "${script_prefix}-${script}.sh")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${script_file}.in" "${CMAKE_CURRENT_BINARY_DIR}/${script_file}") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${script_file}.in" "${CMAKE_CURRENT_BINARY_DIR}/${script_file}" @ONLY)
endforeach() endforeach()
if(ARG_CONFIG) if(ARG_CONFIG)
@ -119,7 +119,7 @@ function(add_example)
set(FAIRMQ_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR}/fairmq) set(FAIRMQ_BIN_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_BINDIR}/fairmq)
foreach(script IN LISTS scripts) foreach(script IN LISTS scripts)
set(script_file "${script_prefix}-${script}.sh") set(script_file "${script_prefix}-${script}.sh")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${script_file}.in" "${CMAKE_CURRENT_BINARY_DIR}/${script_file}_install") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${script_file}.in" "${CMAKE_CURRENT_BINARY_DIR}/${script_file}_install" @ONLY)
install( install(
PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/${script_file}_install" PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/${script_file}_install"
DESTINATION ${PROJECT_INSTALL_BINDIR} DESTINATION ${PROJECT_INSTALL_BINDIR}

View File

@ -7,5 +7,6 @@
################################################################################ ################################################################################
add_example(NAME region add_example(NAME region
DEVICE sampler sink keep-alive DEVICE sampler processor sink keep-alive
SCRIPT region region-advanced region-advanced-external
) )

View File

@ -0,0 +1,95 @@
#!/bin/bash
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
transport=${1:-shmem}
msgSize=${2:-1000000}
SAMPLER="fairmq-ex-region-sampler"
SAMPLER+=" --id sampler1"
# SAMPLER+=" --sampling-rate 10"
SAMPLER+=" --severity debug"
SAMPLER+=" --msg-size $msgSize"
SAMPLER+=" --transport $transport"
SAMPLER+=" --shmid 1"
SAMPLER+=" --shm-monitor false"
SAMPLER+=" --rc-segment-size 200000000"
SAMPLER+=" --external-region true"
SAMPLER+=" --shm-no-cleanup true"
SAMPLER+=" --chan-name data1"
SAMPLER+=" --channel-config name=data1,type=push,method=bind,address=tcp://127.0.0.1:7777"
xterm -geometry 90x60+0+0 -hold -e @EX_BIN_DIR@/$SAMPLER &
PROCESSOR1="fairmq-ex-region-processor"
PROCESSOR1+=" --id processor1"
PROCESSOR1+=" --severity debug"
PROCESSOR1+=" --transport $transport"
PROCESSOR1+=" --shmid 1"
PROCESSOR1+=" --shm-segment-id 1"
PROCESSOR1+=" --shm-monitor false"
PROCESSOR1+=" --shm-no-cleanup true"
PROCESSOR1+=" --channel-config name=data1,type=pull,method=connect,address=tcp://127.0.0.1:7777"
PROCESSOR1+=" name=data2,type=push,method=bind,address=tcp://127.0.0.1:7778"
PROCESSOR1+=" name=data3,type=push,method=bind,address=tcp://127.0.0.1:7779"
xterm -geometry 90x40+550+40 -hold -e @EX_BIN_DIR@/$PROCESSOR1 &
PROCESSOR2="fairmq-ex-region-processor"
PROCESSOR2+=" --id processor2"
PROCESSOR2+=" --severity debug"
PROCESSOR2+=" --transport $transport"
PROCESSOR2+=" --shmid 1"
PROCESSOR2+=" --shm-segment-id 2"
PROCESSOR2+=" --shm-monitor false"
PROCESSOR2+=" --shm-no-cleanup true"
PROCESSOR2+=" --channel-config name=data1,type=pull,method=connect,address=tcp://127.0.0.1:7777"
PROCESSOR2+=" name=data2,type=push,method=bind,address=tcp://127.0.0.1:7788"
PROCESSOR2+=" name=data3,type=push,method=bind,address=tcp://127.0.0.1:7789"
xterm -geometry 90x40+550+600 -hold -e @EX_BIN_DIR@/$PROCESSOR2 &
SINK1_1="fairmq-ex-region-sink"
SINK1_1+=" --id sink1_1"
SINK1_1+=" --severity debug"
SINK1_1+=" --chan-name data2"
SINK1_1+=" --transport $transport"
SINK1_1+=" --shmid 1"
SINK1_1+=" --shm-segment-id 1"
SINK1_1+=" --shm-monitor false"
SINK1_1+=" --shm-no-cleanup true"
SINK1_1+=" --channel-config name=data2,type=pull,method=connect,address=tcp://127.0.0.1:7778"
xterm -geometry 90x20+1100+0 -hold -e @EX_BIN_DIR@/$SINK1_1 &
SINK1_2="fairmq-ex-region-sink"
SINK1_2+=" --id sink1_2"
SINK1_2+=" --severity debug"
SINK1_2+=" --chan-name data3"
SINK1_2+=" --transport $transport"
SINK1_2+=" --shmid 1"
SINK1_2+=" --shm-segment-id 1"
SINK1_2+=" --shm-monitor false"
SINK1_2+=" --shm-no-cleanup true"
SINK1_2+=" --channel-config name=data3,type=pull,method=connect,address=tcp://127.0.0.1:7779"
xterm -geometry 90x20+1100+300 -hold -e @EX_BIN_DIR@/$SINK1_2 &
SINK2_1="fairmq-ex-region-sink"
SINK2_1+=" --id sink2_1"
SINK2_1+=" --severity debug"
SINK2_1+=" --chan-name data2"
SINK2_1+=" --transport $transport"
SINK2_1+=" --shmid 1"
SINK2_1+=" --shm-segment-id 2"
SINK2_1+=" --shm-monitor false"
SINK2_1+=" --shm-no-cleanup true"
SINK2_1+=" --channel-config name=data2,type=pull,method=connect,address=tcp://127.0.0.1:7788"
xterm -geometry 90x20+1100+600 -hold -e @EX_BIN_DIR@/$SINK2_1 &
SINK2_2="fairmq-ex-region-sink"
SINK2_2+=" --id sink2_2"
SINK2_2+=" --severity debug"
SINK2_2+=" --chan-name data3"
SINK2_2+=" --transport $transport"
SINK2_2+=" --shmid 1"
SINK2_2+=" --shm-segment-id 2"
SINK2_2+=" --shm-monitor false"
SINK2_2+=" --shm-no-cleanup true"
SINK2_2+=" --channel-config name=data3,type=pull,method=connect,address=tcp://127.0.0.1:7789"
xterm -geometry 90x20+1100+900 -hold -e @EX_BIN_DIR@/$SINK2_2 &

View File

@ -0,0 +1,80 @@
#!/bin/bash
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
transport=${1:-shmem}
msgSize=${2:-1000000}
SAMPLER="fairmq-ex-region-sampler"
SAMPLER+=" --id sampler1"
# SAMPLER+=" --sampling-rate 10"
SAMPLER+=" --severity debug"
SAMPLER+=" --msg-size $msgSize"
SAMPLER+=" --transport $transport"
#SAMPLER+=" --rc-segment-size 0"
SAMPLER+=" --shm-monitor true"
SAMPLER+=" --chan-name data1"
SAMPLER+=" --channel-config name=data1,type=push,method=bind,address=tcp://127.0.0.1:7777"
xterm -geometry 90x60+0+0 -hold -e @EX_BIN_DIR@/$SAMPLER &
PROCESSOR1="fairmq-ex-region-processor"
PROCESSOR1+=" --id processor1"
PROCESSOR1+=" --severity debug"
PROCESSOR1+=" --transport $transport"
PROCESSOR1+=" --shm-segment-id 1"
PROCESSOR1+=" --shm-monitor true"
PROCESSOR1+=" --channel-config name=data1,type=pull,method=connect,address=tcp://127.0.0.1:7777"
PROCESSOR1+=" name=data2,type=push,method=bind,address=tcp://127.0.0.1:7778"
PROCESSOR1+=" name=data3,type=push,method=bind,address=tcp://127.0.0.1:7779"
xterm -geometry 90x40+550+40 -hold -e @EX_BIN_DIR@/$PROCESSOR1 &
PROCESSOR2="fairmq-ex-region-processor"
PROCESSOR2+=" --id processor2"
PROCESSOR2+=" --severity debug"
PROCESSOR2+=" --transport $transport"
PROCESSOR2+=" --shm-segment-id 2"
PROCESSOR2+=" --shm-monitor true"
PROCESSOR2+=" --channel-config name=data1,type=pull,method=connect,address=tcp://127.0.0.1:7777"
PROCESSOR2+=" name=data2,type=push,method=bind,address=tcp://127.0.0.1:7788"
PROCESSOR2+=" name=data3,type=push,method=bind,address=tcp://127.0.0.1:7789"
xterm -geometry 90x40+550+600 -hold -e @EX_BIN_DIR@/$PROCESSOR2 &
SINK1_1="fairmq-ex-region-sink"
SINK1_1+=" --id sink1_1"
SINK1_1+=" --severity debug"
SINK1_1+=" --chan-name data2"
SINK1_1+=" --transport $transport"
SINK1_1+=" --shm-segment-id 1"
SINK1_1+=" --shm-monitor true"
SINK1_1+=" --channel-config name=data2,type=pull,method=connect,address=tcp://127.0.0.1:7778"
xterm -geometry 90x20+1100+0 -hold -e @EX_BIN_DIR@/$SINK1_1 &
SINK1_2="fairmq-ex-region-sink"
SINK1_2+=" --id sink1_2"
SINK1_2+=" --severity debug"
SINK1_2+=" --chan-name data3"
SINK1_2+=" --transport $transport"
SINK1_2+=" --shm-segment-id 1"
SINK1_2+=" --shm-monitor true"
SINK1_2+=" --channel-config name=data3,type=pull,method=connect,address=tcp://127.0.0.1:7779"
xterm -geometry 90x20+1100+300 -hold -e @EX_BIN_DIR@/$SINK1_2 &
SINK2_1="fairmq-ex-region-sink"
SINK2_1+=" --id sink2_1"
SINK2_1+=" --severity debug"
SINK2_1+=" --chan-name data2"
SINK2_1+=" --transport $transport"
SINK2_1+=" --shm-segment-id 2"
SINK2_1+=" --shm-monitor true"
SINK2_1+=" --channel-config name=data2,type=pull,method=connect,address=tcp://127.0.0.1:7788"
xterm -geometry 90x20+1100+600 -hold -e @EX_BIN_DIR@/$SINK2_1 &
SINK2_2="fairmq-ex-region-sink"
SINK2_2+=" --id sink2_2"
SINK2_2+=" --severity debug"
SINK2_2+=" --chan-name data3"
SINK2_2+=" --transport $transport"
SINK2_2+=" --shm-segment-id 2"
SINK2_2+=" --shm-monitor true"
SINK2_2+=" --channel-config name=data3,type=pull,method=connect,address=tcp://127.0.0.1:7789"
xterm -geometry 90x20+1100+900 -hold -e @EX_BIN_DIR@/$SINK2_2 &

View File

@ -2,16 +2,8 @@
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@ export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
transport="shmem" transport=${1:-shmem}
msgSize="1000000" msgSize=${2:-1000000}
if [[ $1 =~ ^[a-z]+$ ]]; then
transport=$1
fi
if [[ $2 =~ ^[0-9]+$ ]]; then
msgSize=$1
fi
SAMPLER="fairmq-ex-region-sampler" SAMPLER="fairmq-ex-region-sampler"
SAMPLER+=" --id sampler1" SAMPLER+=" --id sampler1"

View File

@ -95,10 +95,11 @@ struct ShmManager
uint64_t size = stoull(conf.at(1)); uint64_t size = stoull(conf.at(1));
fair::mq::RegionConfig cfg; fair::mq::RegionConfig cfg;
cfg.id = id; cfg.id = id;
cfg.rcSegmentSize = 0;
cfg.size = size; cfg.size = size;
regionCfgs.push_back(cfg); regionCfgs.push_back(cfg);
auto ret = regions.emplace(id, make_unique<fair::mq::shmem::UnmanagedRegion>(shmId, id, size)); auto ret = regions.emplace(id, make_unique<fair::mq::shmem::UnmanagedRegion>(shmId, cfg));
fair::mq::shmem::UnmanagedRegion& region = *(ret.first->second); fair::mq::shmem::UnmanagedRegion& region = *(ret.first->second);
LOG(info) << "Created unamanged region " << id << " of size " << region.GetSize() LOG(info) << "Created unamanged region " << id << " of size " << region.GetSize()
<< ", starting at " << region.GetData() << ". Locking..."; << ", starting at " << region.GetData() << ". Locking...";

View File

@ -0,0 +1,80 @@
/********************************************************************************
* Copyright (C) 2014-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include <fairmq/Device.h>
#include <fairmq/runDevice.h>
#include <memory>
namespace bpo = boost::program_options;
using namespace std;
using namespace fair::mq;
namespace {
struct Processor : Device
{
void InitTask() override
{
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
GetChannel("data1", 0).Transport()->SubscribeToRegionEvents([](RegionInfo info) {
LOG(info) << "Region event: " << info.event << ": "
<< (info.managed ? "managed" : "unmanaged") << ", id: " << info.id
<< ", ptr: " << info.ptr << ", size: " << info.size
<< ", flags: " << info.flags;
});
}
void Run() override
{
Channel& dataIn = GetChannel("data1", 0);
Channel& dataOut1 = GetChannel("data2", 0);
Channel& dataOut2 = GetChannel("data3", 0);
while (!NewStatePending()) {
fair::mq::Parts inParts;
dataIn.Receive(inParts);
fair::mq::Parts outParts1;
fair::mq::Parts outParts2;
for (const auto& inPart : inParts) {
outParts1.AddPart(NewMessage());
outParts1.fParts.back()->Copy(*inPart);
outParts2.AddPart(NewMessage());
outParts2.fParts.back()->Copy(*inPart);
}
dataOut1.Send(outParts1);
dataOut2.Send(outParts2);
if (fMaxIterations > 0 && ++fNumIterations >= fMaxIterations) {
LOG(info) << "Configured max number of iterations reached. Leaving RUNNING state.";
break;
}
}
}
void ResetTask() override
{
GetChannel("data1", 0).Transport()->UnsubscribeFromRegionEvents();
}
private:
uint64_t fMaxIterations = 0;
uint64_t fNumIterations = 0;
};
} // namespace
void addCustomOptions(bpo::options_description& options)
{
options.add_options()("max-iterations", bpo::value<uint64_t>()->default_value(0), "Maximum number of iterations of Run/ConditionalRun/OnData (0 - infinite)");
}
unique_ptr<Device> getDevice(ProgOptions& /*config*/) { return make_unique<Processor>(); }

View File

@ -8,6 +8,7 @@
#include <fairmq/Device.h> #include <fairmq/Device.h>
#include <fairmq/runDevice.h> #include <fairmq/runDevice.h>
#include <fairmq/tools/RateLimit.h>
#include <cstdint> #include <cstdint>
#include <mutex> #include <mutex>
@ -23,8 +24,11 @@ struct Sampler : fair::mq::Device
fMsgSize = fConfig->GetProperty<int>("msg-size"); fMsgSize = fConfig->GetProperty<int>("msg-size");
fLinger = fConfig->GetProperty<uint32_t>("region-linger"); fLinger = fConfig->GetProperty<uint32_t>("region-linger");
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations"); fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
fChanName = fConfig->GetProperty<std::string>("chan-name");
fSamplingRate = fConfig->GetProperty<float>("sampling-rate");
fRCSegmentSize = fConfig->GetProperty<uint64_t>("rc-segment-size");
GetChannel("data", 0).Transport()->SubscribeToRegionEvents([](fair::mq::RegionInfo info) { GetChannel(fChanName, 0).Transport()->SubscribeToRegionEvents([](fair::mq::RegionInfo info) {
LOG(info) << "Region event: " << info.event << ": " LOG(info) << "Region event: " << info.event << ": "
<< (info.managed ? "managed" : "unmanaged") << (info.managed ? "managed" : "unmanaged")
<< ", id: " << info.id << ", id: " << info.id
@ -42,8 +46,9 @@ struct Sampler : fair::mq::Device
} }
regionCfg.lock = !fExternalRegion; // mlock region after creation regionCfg.lock = !fExternalRegion; // mlock region after creation
regionCfg.zero = !fExternalRegion; // zero region content after creation regionCfg.zero = !fExternalRegion; // zero region content after creation
regionCfg.rcSegmentSize = fRCSegmentSize; // size of the corresponding reference count segment
fRegion = fair::mq::UnmanagedRegionPtr(NewUnmanagedRegionFor( fRegion = fair::mq::UnmanagedRegionPtr(NewUnmanagedRegionFor(
"data", // region is created using the transport of this channel... fChanName, // region is created using the transport of this channel...
0, // ... and this sub-channel 0, // ... and this sub-channel
10000000, // region size 10000000, // region size
[this](const std::vector<fair::mq::RegionBlock>& blocks) { // callback to be called when message buffers no longer needed by transport [this](const std::vector<fair::mq::RegionBlock>& blocks) { // callback to be called when message buffers no longer needed by transport
@ -59,22 +64,33 @@ struct Sampler : fair::mq::Device
void Run() override void Run() override
{ {
fair::mq::tools::RateLimiter rateLimiter(fSamplingRate);
while (!NewStatePending()) { while (!NewStatePending()) {
fair::mq::MessagePtr msg(NewMessageFor("data", // channel fair::mq::Parts parts;
// make 64 parts
for (int i = 0; i < 64; ++i) {
parts.AddPart(NewMessageFor(
fChanName, // channel
0, // sub-channel 0, // sub-channel
fRegion, // region fRegion, // region
fRegion->GetData(), // ptr within region fRegion->GetData(), // ptr within region
fMsgSize, // offset from ptr fMsgSize, // offset from ptr
nullptr // hint nullptr // hint
)); ));
}
std::lock_guard<std::mutex> lock(fMtx); std::lock_guard<std::mutex> lock(fMtx);
++fNumUnackedMsgs; fNumUnackedMsgs += parts.Size();
if (Send(msg, "data", 0) > 0) { if (Send(parts, fChanName, 0) > 0) {
if (fMaxIterations > 0 && ++fNumIterations >= fMaxIterations) { if (fMaxIterations > 0 && ++fNumIterations >= fMaxIterations) {
LOG(info) << "Configured maximum number of iterations reached. Stopping sending."; LOG(info) << "Configured maximum number of iterations reached. Stopping sending.";
break; break;
} }
if (fSamplingRate > 0.001) {
rateLimiter.maybe_sleep();
}
} }
} }
@ -99,7 +115,7 @@ struct Sampler : fair::mq::Device
void ResetTask() override void ResetTask() override
{ {
fRegion.reset(); fRegion.reset();
GetChannel("data", 0).Transport()->UnsubscribeFromRegionEvents(); GetChannel(fChanName, 0).Transport()->UnsubscribeFromRegionEvents();
} }
private: private:
@ -108,18 +124,24 @@ struct Sampler : fair::mq::Device
uint32_t fLinger = 100; uint32_t fLinger = 100;
uint64_t fMaxIterations = 0; uint64_t fMaxIterations = 0;
uint64_t fNumIterations = 0; uint64_t fNumIterations = 0;
uint64_t fRCSegmentSize = 10000000;
fair::mq::UnmanagedRegionPtr fRegion = nullptr; fair::mq::UnmanagedRegionPtr fRegion = nullptr;
std::mutex fMtx; std::mutex fMtx;
uint64_t fNumUnackedMsgs = 0; uint64_t fNumUnackedMsgs = 0;
std::string fChanName;
float fSamplingRate = 0.;
}; };
void addCustomOptions(bpo::options_description& options) void addCustomOptions(bpo::options_description& options)
{ {
options.add_options() options.add_options()
("chan-name", bpo::value<std::string>()->default_value("data"), "name of the output channel")
("msg-size", bpo::value<int>()->default_value(1000), "Message size in bytes") ("msg-size", bpo::value<int>()->default_value(1000), "Message size in bytes")
("sampling-rate", bpo::value<float>()->default_value(0.), "Sampling rate (Hz).")
("region-linger", bpo::value<uint32_t>()->default_value(100), "Linger period for regions") ("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)") ("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"); ("external-region", bpo::value<bool>()->default_value(false), "Use region created by another process")
("rc-segment-size", bpo::value<uint64_t>()->default_value(10000000), "Size of the reference count segment for Unamanged Region");
} }
std::unique_ptr<fair::mq::Device> getDevice(fair::mq::ProgOptions& /*config*/) std::unique_ptr<fair::mq::Device> getDevice(fair::mq::ProgOptions& /*config*/)

View File

@ -22,7 +22,8 @@ struct Sink : Device
{ {
// Get the fMaxIterations value from the command line options (via fConfig) // Get the fMaxIterations value from the command line options (via fConfig)
fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations"); fMaxIterations = fConfig->GetProperty<uint64_t>("max-iterations");
GetChannel("data", 0).Transport()->SubscribeToRegionEvents([](RegionInfo info) { fChanName = fConfig->GetProperty<std::string>("chan-name");
GetChannel(fChanName, 0).Transport()->SubscribeToRegionEvents([](RegionInfo info) {
LOG(info) << "Region event: " << info.event << ": " LOG(info) << "Region event: " << info.event << ": "
<< (info.managed ? "managed" : "unmanaged") << ", id: " << info.id << (info.managed ? "managed" : "unmanaged") << ", id: " << info.id
<< ", ptr: " << info.ptr << ", size: " << info.size << ", ptr: " << info.ptr << ", size: " << info.size
@ -32,15 +33,11 @@ struct Sink : Device
void Run() override void Run() override
{ {
Channel& dataInChannel = GetChannel("data", 0); Channel& dataIn = GetChannel(fChanName, 0);
while (!NewStatePending()) { while (!NewStatePending()) {
auto msg(dataInChannel.Transport()->CreateMessage()); fair::mq::Parts parts;
dataInChannel.Receive(msg); dataIn.Receive(parts);
// void* ptr = msg->GetData();
// char* cptr = static_cast<char*>(ptr);
// LOG(info) << "check: " << cptr[3];
if (fMaxIterations > 0 && ++fNumIterations >= fMaxIterations) { if (fMaxIterations > 0 && ++fNumIterations >= fMaxIterations) {
LOG(info) << "Configured max number of iterations reached. Leaving RUNNING state."; LOG(info) << "Configured max number of iterations reached. Leaving RUNNING state.";
@ -51,22 +48,22 @@ struct Sink : Device
void ResetTask() override void ResetTask() override
{ {
GetChannel("data", 0).Transport()->UnsubscribeFromRegionEvents(); GetChannel(fChanName, 0).Transport()->UnsubscribeFromRegionEvents();
} }
private: private:
uint64_t fMaxIterations = 0; uint64_t fMaxIterations = 0;
uint64_t fNumIterations = 0; uint64_t fNumIterations = 0;
std::string fChanName;
}; };
} // namespace } // namespace
void addCustomOptions(bpo::options_description& options) void addCustomOptions(bpo::options_description& options)
{ {
options.add_options()( options.add_options()
"max-iterations", ("chan-name", bpo::value<std::string>()->default_value("data"), "name of the input channel")
bpo::value<uint64_t>()->default_value(0), ("max-iterations", bpo::value<uint64_t>()->default_value(0), "Maximum number of iterations of Run/ConditionalRun/OnData (0 - infinite)");
"Maximum number of iterations of Run/ConditionalRun/OnData (0 - infinite)");
} }
unique_ptr<Device> getDevice(ProgOptions& /*config*/) { return make_unique<Sink>(); } unique_ptr<Device> getDevice(ProgOptions& /*config*/) { return make_unique<Sink>(); }

2
extern/googletest vendored

@ -1 +1 @@
Subproject commit a1cc8c55195661a58ad60c3bb062a0b9c302710d Subproject commit 7d76a231b0e29caf86e68d1df858308cd53b2a66

View File

@ -1,5 +1,5 @@
################################################################################ ################################################################################
# Copyright (C) 2012-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # # Copyright (C) 2012-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
# # # #
# This software is distributed under the terms of the # # This software is distributed under the terms of the #
# GNU Lesser General Public Licence (LGPL) version 3, # # GNU Lesser General Public Licence (LGPL) version 3, #
@ -63,14 +63,21 @@ if(BUILD_FAIRMQ)
Tools.h Tools.h
TransportFactory.h TransportFactory.h
Transports.h Transports.h
TransportEnum.h
UnmanagedRegion.h UnmanagedRegion.h
options/FairMQProgOptions.h options/FairMQProgOptions.h
runDevice.h runDevice.h
runFairMQDevice.h runFairMQDevice.h
shmem/Common.h shmem/Common.h
shmem/Manager.h
shmem/Message.h
shmem/Monitor.h shmem/Monitor.h
shmem/Poller.h
shmem/Segment.h shmem/Segment.h
shmem/Socket.h
shmem/TransportFactory.h
shmem/UnmanagedRegion.h shmem/UnmanagedRegion.h
shmem/UnmanagedRegionImpl.h
tools/Compiler.h tools/Compiler.h
tools/CppSTL.h tools/CppSTL.h
tools/Exceptions.h tools/Exceptions.h
@ -95,12 +102,6 @@ if(BUILD_FAIRMQ)
plugins/Builtin.h plugins/Builtin.h
plugins/config/Config.h plugins/config/Config.h
plugins/control/Control.h plugins/control/Control.h
shmem/Message.h
shmem/Poller.h
shmem/UnmanagedRegionImpl.h
shmem/Socket.h
shmem/TransportFactory.h
shmem/Manager.h
zeromq/Common.h zeromq/Common.h
zeromq/Context.h zeromq/Context.h
zeromq/Message.h zeromq/Message.h
@ -118,6 +119,7 @@ if(BUILD_FAIRMQ)
Channel.cxx Channel.cxx
Device.cxx Device.cxx
DeviceRunner.cxx DeviceRunner.cxx
EventManager.cxx
JSONParser.cxx JSONParser.cxx
MemoryResources.cxx MemoryResources.cxx
Plugin.cxx Plugin.cxx

View File

@ -12,6 +12,7 @@
#include <fairmq/Channel.h> #include <fairmq/Channel.h>
#include <fairmq/Properties.h> #include <fairmq/Properties.h>
#include <fairmq/Tools.h> #include <fairmq/Tools.h>
#include <fairmq/Transports.h>
#include <random> #include <random>
#include <regex> #include <regex>
#include <set> #include <set>
@ -383,4 +384,10 @@ bool Channel::BindEndpoint(string& endpoint)
} }
} }
std::string Channel::GetTransportName() const { return TransportName(fTransportType); }
Transport Channel::GetTransportType() const { return fTransportType; }
void Channel::UpdateTransport(const std::string& transport) { fTransportType = TransportType(transport); Invalidate(); }
} // namespace fair::mq } // namespace fair::mq

View File

@ -14,7 +14,7 @@
#include <fairmq/Properties.h> #include <fairmq/Properties.h>
#include <fairmq/Socket.h> #include <fairmq/Socket.h>
#include <fairmq/TransportFactory.h> #include <fairmq/TransportFactory.h>
#include <fairmq/Transports.h> #include <fairmq/TransportEnum.h>
#include <fairmq/UnmanagedRegion.h> #include <fairmq/UnmanagedRegion.h>
#include <cstdint> // int64_t #include <cstdint> // int64_t
@ -145,11 +145,11 @@ class Channel
/// Get channel transport name ("default", "zeromq" or "shmem") /// Get channel transport name ("default", "zeromq" or "shmem")
/// @return Returns channel transport name (e.g. "default", "zeromq" or "shmem") /// @return Returns channel transport name (e.g. "default", "zeromq" or "shmem")
std::string GetTransportName() const { return TransportName(fTransportType); } std::string GetTransportName() const;
/// Get channel transport type /// Get channel transport type
/// @return Returns channel transport type /// @return Returns channel transport type
mq::Transport GetTransportType() const { return fTransportType; } mq::Transport GetTransportType() const;
/// Get socket send buffer size (in number of messages) /// Get socket send buffer size (in number of messages)
/// @return Returns socket send buffer size (in number of messages) /// @return Returns socket send buffer size (in number of messages)
@ -221,7 +221,7 @@ class Channel
/// Set channel transport /// Set channel transport
/// @param transport transport string ("default", "zeromq" or "shmem") /// @param transport transport string ("default", "zeromq" or "shmem")
void UpdateTransport(const std::string& transport) { fTransportType = TransportType(transport); Invalidate(); } void UpdateTransport(const std::string& transport);
/// Set socket send buffer size /// Set socket send buffer size
/// @param sndBufSize Socket send buffer size (in number of messages) /// @param sndBufSize Socket send buffer size (in number of messages)
@ -438,7 +438,7 @@ class Channel
} }
void CheckSendCompatibility(Parts& parts) { CheckSendCompatibility(parts.fParts); } void CheckSendCompatibility(Parts& parts) { CheckSendCompatibility(parts.fParts); }
void CheckSendCompatibility(std::vector<MessagePtr>& msgVec) void CheckSendCompatibility(Parts::container & msgVec)
{ {
for (auto& msg : msgVec) { for (auto& msg : msgVec) {
if (fTransportType != msg->GetType()) { if (fTransportType != msg->GetType()) {
@ -468,7 +468,7 @@ class Channel
} }
void CheckReceiveCompatibility(Parts& parts) { CheckReceiveCompatibility(parts.fParts); } void CheckReceiveCompatibility(Parts& parts) { CheckReceiveCompatibility(parts.fParts); }
void CheckReceiveCompatibility(std::vector<MessagePtr>& msgVec) void CheckReceiveCompatibility(Parts::container& msgVec)
{ {
for (auto& msg : msgVec) { for (auto& msg : msgVec) {
if (fTransportType != msg->GetType()) { if (fTransportType != msg->GetType()) {

View File

@ -9,6 +9,7 @@
// FairMQ // FairMQ
#include <fairmq/Device.h> #include <fairmq/Device.h>
#include <fairmq/Tools.h> #include <fairmq/Tools.h>
#include <fairmq/Transports.h>
// boost // boost
#include <boost/algorithm/string.hpp> // join/split #include <boost/algorithm/string.hpp> // join/split
@ -176,7 +177,6 @@ void Device::InitWrapper()
// Fill the uninitialized channel containers // Fill the uninitialized channel containers
for (auto& channel : GetChannels()) { for (auto& channel : GetChannels()) {
int subChannelIndex = 0;
for (auto& subChannel : channel.second) { for (auto& subChannel : channel.second) {
// set channel transport // set channel transport
LOG(debug) << "Initializing transport for channel " << subChannel.fName << ": " << TransportNames.at(subChannel.fTransportType); LOG(debug) << "Initializing transport for channel " << subChannel.fName << ": " << TransportNames.at(subChannel.fTransportType);
@ -208,8 +208,6 @@ void Device::InitWrapper()
LOG(error) << "Cannot update configuration. Socket method (bind/connect) for channel '" << subChannel.fName << "' not specified."; LOG(error) << "Cannot update configuration. Socket method (bind/connect) for channel '" << subChannel.fName << "' not specified.";
throw runtime_error(tools::ToString("Cannot update configuration. Socket method (bind/connect) for channel ", subChannel.fName, " not specified.")); throw runtime_error(tools::ToString("Cannot update configuration. Socket method (bind/connect) for channel ", subChannel.fName, " not specified."));
} }
subChannelIndex++;
} }
} }
@ -288,7 +286,7 @@ void Device::AttachChannels(vector<Channel*>& chans)
// remove the channel from the uninitialized container // remove the channel from the uninitialized container
itr = chans.erase(itr); itr = chans.erase(itr);
} else { } else {
LOG(error) << "failed to attach channel " << (*itr)->fName << " (" << (*itr)->fMethod << ")"; LOG(error) << "failed to attach channel " << (*itr)->fName << " (" << (*itr)->fMethod << " on " << (*itr)->fAddress << ")";
++itr; ++itr;
} }
} else { } else {

View File

@ -19,7 +19,7 @@
#include <fairmq/StateQueue.h> #include <fairmq/StateQueue.h>
#include <fairmq/Tools.h> #include <fairmq/Tools.h>
#include <fairmq/TransportFactory.h> #include <fairmq/TransportFactory.h>
#include <fairmq/Transports.h> #include <fairmq/TransportEnum.h>
#include <fairmq/UnmanagedRegion.h> #include <fairmq/UnmanagedRegion.h>
// logger // logger

20
fairmq/EventManager.cxx Normal file
View File

@ -0,0 +1,20 @@
/********************************************************************************
* Copyright (C) 2025 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 "EventManager.h"
#include <string>
#include <typeindex>
template std::shared_ptr<
fair::mq::EventManager::Signal<fair::mq::PropertyChangeAsString, std::string>>
fair::mq::EventManager::GetSignal<fair::mq::PropertyChangeAsString, std::string>(
const std::pair<std::type_index, std::type_index>& key) const;
template void fair::mq::EventManager::Subscribe<fair::mq::PropertyChangeAsString, std::string>(
const std::string& subscriber,
std::function<void(typename fair::mq::PropertyChangeAsString::KeyType, std::string)>);

View File

@ -1,5 +1,5 @@
/******************************************************************************** /********************************************************************************
* Copyright (C) 2014-2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Copyright (C) 2014-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* * * *
* This software is distributed under the terms of the * * This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, * * GNU Lesser General Public Licence (LGPL) version 3, *
@ -57,27 +57,8 @@ class EventManager
template<typename E, typename ...Args> template<typename E, typename ...Args>
using Signal = boost::signals2::signal<void(typename E::KeyType, Args...)>; using Signal = boost::signals2::signal<void(typename E::KeyType, Args...)>;
template<typename E, typename ...Args> template<typename E, typename... Args>
auto Subscribe(const std::string& subscriber, std::function<void(typename E::KeyType, Args...)> callback) -> void auto Subscribe(const std::string& subscriber, std::function<void(typename E::KeyType, Args...)> callback) -> void;
{
const std::type_index event_type_index{typeid(E)};
const std::type_index callback_type_index{typeid(std::function<void(typename E::KeyType, Args...)>)};
const auto signalsKey = std::make_pair(event_type_index, callback_type_index);
const auto connectionsKey = std::make_pair(subscriber, signalsKey);
const auto connection = GetSignal<E, Args...>(signalsKey)->connect(callback);
{
std::lock_guard<std::mutex> lock{fMutex};
if (fConnections.find(connectionsKey) != fConnections.end())
{
fConnections.at(connectionsKey).disconnect();
fConnections.erase(connectionsKey);
}
fConnections.insert({connectionsKey, connection});
}
}
template<typename E, typename ...Args> template<typename E, typename ...Args>
auto Unsubscribe(const std::string& subscriber) -> void auto Unsubscribe(const std::string& subscriber) -> void
@ -119,12 +100,17 @@ class EventManager
mutable std::mutex fMutex; mutable std::mutex fMutex;
template<typename E, typename ...Args> template<typename E, typename ...Args>
auto GetSignal(const SignalsKey& key) const -> std::shared_ptr<Signal<E, Args...>> auto GetSignal(const SignalsKey& key) const -> std::shared_ptr<Signal<E, Args...>>;
{ }; /* class EventManager */
struct PropertyChangeAsString : Event<std::string> {};
template<typename E, typename... Args>
auto EventManager::GetSignal(const SignalsKey& key) const -> std::shared_ptr<Signal<E, Args...>>
{
std::lock_guard<std::mutex> lock{fMutex}; std::lock_guard<std::mutex> lock{fMutex};
if (fSignals.find(key) == fSignals.end()) if (fSignals.find(key) == fSignals.end()) {
{
// wrapper is needed because boost::signals2::signal is neither copyable nor movable // wrapper is needed because boost::signals2::signal is neither copyable nor movable
// and I don't know how else to insert it into the map // and I don't know how else to insert it into the map
auto signal = std::make_shared<Signal<E, Args...>>(); auto signal = std::make_shared<Signal<E, Args...>>();
@ -132,8 +118,40 @@ class EventManager
} }
return boost::any_cast<std::shared_ptr<Signal<E, Args...>>>(fSignals.at(key)); return boost::any_cast<std::shared_ptr<Signal<E, Args...>>>(fSignals.at(key));
}
template<typename E, typename... Args>
auto EventManager::Subscribe(const std::string& subscriber,
std::function<void(typename E::KeyType, Args...)> callback) -> void
{
const std::type_index event_type_index{typeid(E)};
const std::type_index callback_type_index{
typeid(std::function<void(typename E::KeyType, Args...)>)};
const auto signalsKey = std::make_pair(event_type_index, callback_type_index);
const auto connectionsKey = std::make_pair(subscriber, signalsKey);
const auto connection = GetSignal<E, Args...>(signalsKey)->connect(callback);
{
std::lock_guard<std::mutex> lock{fMutex};
if (fConnections.find(connectionsKey) != fConnections.end()) {
fConnections.at(connectionsKey).disconnect();
fConnections.erase(connectionsKey);
} }
}; /* class EventManager */ fConnections.insert({connectionsKey, connection});
}
}
extern template std::shared_ptr<
fair::mq::EventManager::Signal<fair::mq::PropertyChangeAsString, std::string>>
fair::mq::EventManager::GetSignal<fair::mq::PropertyChangeAsString, std::string>(
const std::pair<std::type_index, std::type_index>& key) const;
extern template void
fair::mq::EventManager::Subscribe<fair::mq::PropertyChangeAsString, std::string>(
const std::string& subscriber,
std::function<void(typename fair::mq::PropertyChangeAsString::KeyType, std::string)>);
} // namespace fair::mq } // namespace fair::mq

View File

@ -17,7 +17,7 @@
#include <boost/container/container_fwd.hpp> #include <boost/container/container_fwd.hpp>
#include <boost/container/flat_map.hpp> #include <boost/container/flat_map.hpp>
#include <boost/container/pmr/memory_resource.hpp> #include <memory_resource>
#include <cstring> #include <cstring>
#include <fairmq/Message.h> #include <fairmq/Message.h>
#include <stdexcept> #include <stdexcept>
@ -27,7 +27,7 @@ namespace fair::mq {
class TransportFactory; class TransportFactory;
using byte = unsigned char; using byte = unsigned char;
namespace pmr = boost::container::pmr; namespace pmr = std::pmr;
/// All FairMQ related memory resources need to inherit from this interface /// All FairMQ related memory resources need to inherit from this interface
/// class for the /// class for the

View File

@ -10,7 +10,7 @@
#define FAIR_MQ_MESSAGE_H #define FAIR_MQ_MESSAGE_H
#include <cstddef> // for size_t #include <cstddef> // for size_t
#include <fairmq/Transports.h> #include <fairmq/TransportEnum.h>
#include <memory> // unique_ptr #include <memory> // unique_ptr
#include <stdexcept> #include <stdexcept>
@ -46,7 +46,7 @@ struct Message
virtual void* GetData() const = 0; virtual void* GetData() const = 0;
virtual size_t GetSize() const = 0; virtual size_t GetSize() const = 0;
virtual bool SetUsedSize(size_t size) = 0; virtual bool SetUsedSize(size_t size, Alignment alignment = Alignment{0}) = 0;
virtual Transport GetType() const = 0; virtual Transport GetType() const = 0;
TransportFactory* GetTransport() { return fTransport; } TransportFactory* GetTransport() { return fTransport; }
@ -76,6 +76,11 @@ struct MessageBadAlloc : std::runtime_error
using std::runtime_error::runtime_error; using std::runtime_error::runtime_error;
}; };
struct RefCountBadAlloc : std::runtime_error
{
using std::runtime_error::runtime_error;
};
} // namespace fair::mq } // namespace fair::mq
using fairmq_free_fn [[deprecated("Use fair::mq::FreeFn")]] = fair::mq::FreeFn; using fairmq_free_fn [[deprecated("Use fair::mq::FreeFn")]] = fair::mq::FreeFn;

View File

@ -448,3 +448,6 @@ void ProgOptions::PrintOptionsRaw() const
} }
} // namespace fair::mq } // namespace fair::mq
template void fair::mq::ProgOptions::SetProperty<std::string>(const std::string& key, std::string val);
template void fair::mq::ProgOptions::SetProperty<int>(const std::string& key, int val);

View File

@ -129,17 +129,7 @@ class ProgOptions
/// @param key /// @param key
/// @param val /// @param val
template<typename T> template<typename T>
void SetProperty(const std::string& key, T val) void SetProperty(const std::string& key, T val);
{
std::unique_lock<std::mutex> lock(fMtx);
SetVarMapValue<typename std::decay<T>::type>(key, val);
lock.unlock();
fEvents.Emit<fair::mq::PropertyChange, typename std::decay<T>::type>(key, val);
fEvents.Emit<fair::mq::PropertyChangeAsString, std::string>(key, GetPropertyAsString(key));
}
/// @brief Updates an existing config property (or fails if it doesn't exist) /// @brief Updates an existing config property (or fails if it doesn't exist)
/// @param key /// @param key
@ -275,5 +265,20 @@ class ProgOptions
}; };
} // namespace fair::mq } // namespace fair::mq
template <typename T>
void fair::mq::ProgOptions::SetProperty(const std::string& key, T val)
{
std::unique_lock<std::mutex> lock(fMtx);
SetVarMapValue<typename std::decay<T>::type>(key, val);
lock.unlock();
fEvents.Emit<fair::mq::PropertyChange, typename std::decay<T>::type>(key, val);
fEvents.Emit<fair::mq::PropertyChangeAsString, std::string>(key, GetPropertyAsString(key));
}
extern template void fair::mq::ProgOptions::SetProperty<int>(const std::string& key, int val);
extern template void fair::mq::ProgOptions::SetProperty<std::string>(const std::string& key, std::string val);
#endif /* FAIR_MQ_PROGOPTIONS_H */ #endif /* FAIR_MQ_PROGOPTIONS_H */

View File

@ -1,5 +1,5 @@
/******************************************************************************** /********************************************************************************
* Copyright (C) 2014-2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Copyright (C) 2014-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* * * *
* This software is distributed under the terms of the * * This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, * * GNU Lesser General Public Licence (LGPL) version 3, *
@ -29,7 +29,6 @@ using Property = boost::any;
using Properties = std::map<std::string, Property>; using Properties = std::map<std::string, Property>;
struct PropertyChange : Event<std::string> {}; struct PropertyChange : Event<std::string> {};
struct PropertyChangeAsString : Event<std::string> {};
class PropertyHelper class PropertyHelper
{ {

View File

@ -52,8 +52,8 @@ struct Socket
virtual int64_t Send(MessagePtr& msg, int timeout = -1) = 0; virtual int64_t Send(MessagePtr& msg, int timeout = -1) = 0;
virtual int64_t Receive(MessagePtr& msg, int timeout = -1) = 0; virtual int64_t Receive(MessagePtr& msg, int timeout = -1) = 0;
virtual int64_t Send(std::vector<std::unique_ptr<Message>>& msgVec, int timeout = -1) = 0; virtual int64_t Send(Parts::container& msgVec, int timeout = -1) = 0;
virtual int64_t Receive(std::vector<std::unique_ptr<Message>>& msgVec, int timeout = -1) = 0; virtual int64_t Receive(Parts::container & msgVec, int timeout = -1) = 0;
virtual int64_t Send(Parts& parts, int timeout = -1) { return Send(parts.fParts, timeout); } virtual int64_t Send(Parts& parts, int timeout = -1) { return Send(parts.fParts, timeout); }
virtual int64_t Receive(Parts& parts, int timeout = -1) { return Receive(parts.fParts, timeout); } virtual int64_t Receive(Parts& parts, int timeout = -1) { return Receive(parts.fParts, timeout); }

View File

@ -177,6 +177,7 @@ struct Machine_ : public state_machine_def<Machine_>
atomic<bool> fLastTransitionResult; atomic<bool> fLastTransitionResult;
mutex fStateMtx; mutex fStateMtx;
mutex fSubscriptionsMtx;
atomic<bool> fNewStatePending; atomic<bool> fNewStatePending;
condition_variable fNewStatePendingCV; condition_variable fNewStatePendingCV;
@ -310,12 +311,17 @@ try {
void StateMachine::SubscribeToStateChange(const string& key, function<void(const State)> callback) void StateMachine::SubscribeToStateChange(const string& key, function<void(const State)> callback)
{ {
static_pointer_cast<FairMQFSM>(fFsm)->fStateChangeSignalsMap.insert({key, static_pointer_cast<FairMQFSM>(fFsm)->fStateChangeSignal.connect(callback)}); // Check if the key has a integer value as prefix, if yes, decode it.
int i = strtol(key.c_str(), nullptr, 10);
auto fsm = static_pointer_cast<FairMQFSM>(fFsm);
lock_guard<mutex> lock(fsm->fSubscriptionsMtx);
fsm->fStateChangeSignalsMap.insert({key, fsm->fStateChangeSignal.connect(i, callback)});
} }
void StateMachine::UnsubscribeFromStateChange(const string& key) void StateMachine::UnsubscribeFromStateChange(const string& key)
{ {
auto fsm = static_pointer_cast<FairMQFSM>(fFsm); auto fsm = static_pointer_cast<FairMQFSM>(fFsm);
lock_guard<mutex> lock(fsm->fSubscriptionsMtx);
if (fsm->fStateChangeSignalsMap.count(key)) { if (fsm->fStateChangeSignalsMap.count(key)) {
fsm->fStateChangeSignalsMap.at(key).disconnect(); fsm->fStateChangeSignalsMap.at(key).disconnect();
fsm->fStateChangeSignalsMap.erase(key); fsm->fStateChangeSignalsMap.erase(key);
@ -355,12 +361,15 @@ void StateMachine::StopHandlingStates()
void StateMachine::SubscribeToNewTransition(const string& key, function<void(const Transition)> callback) void StateMachine::SubscribeToNewTransition(const string& key, function<void(const Transition)> callback)
{ {
static_pointer_cast<FairMQFSM>(fFsm)->fNewTransitionSignalsMap.insert({key, static_pointer_cast<FairMQFSM>(fFsm)->fNewTransitionSignal.connect(callback)}); auto fsm = static_pointer_cast<FairMQFSM>(fFsm);
lock_guard<mutex> lock(fsm->fSubscriptionsMtx);
fsm->fNewTransitionSignalsMap.insert({key, fsm->fNewTransitionSignal.connect(callback)});
} }
void StateMachine::UnsubscribeFromNewTransition(const string& key) void StateMachine::UnsubscribeFromNewTransition(const string& key)
{ {
auto fsm = static_pointer_cast<FairMQFSM>(fFsm); auto fsm = static_pointer_cast<FairMQFSM>(fFsm);
lock_guard<mutex> lock(fsm->fSubscriptionsMtx);
if (fsm->fNewTransitionSignalsMap.count(key)) { if (fsm->fNewTransitionSignalsMap.count(key)) {
fsm->fNewTransitionSignalsMap.at(key).disconnect(); fsm->fNewTransitionSignalsMap.at(key).disconnect();
fsm->fNewTransitionSignalsMap.erase(key); fsm->fNewTransitionSignalsMap.erase(key);

22
fairmq/TransportEnum.h Normal file
View File

@ -0,0 +1,22 @@
/********************************************************************************
* Copyright (C) 2014-2025 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_TRANSPORTENUMS_H
#define FAIR_MQ_TRANSPORTENUMS_H
namespace fair::mq {
enum class Transport {
DEFAULT,
ZMQ,
SHM
};
}
#endif // FAIR_MQ_TRANSPORTENUMS_H

View File

@ -14,7 +14,7 @@
#include <fairmq/Message.h> #include <fairmq/Message.h>
#include <fairmq/Poller.h> #include <fairmq/Poller.h>
#include <fairmq/Socket.h> #include <fairmq/Socket.h>
#include <fairmq/Transports.h> #include <fairmq/TransportEnum.h>
#include <fairmq/UnmanagedRegion.h> #include <fairmq/UnmanagedRegion.h>
#include <memory> // shared_ptr #include <memory> // shared_ptr
#include <stdexcept> #include <stdexcept>

View File

@ -10,6 +10,7 @@
#define FAIR_MQ_TRANSPORTS_H #define FAIR_MQ_TRANSPORTS_H
#include <fairmq/tools/Strings.h> #include <fairmq/tools/Strings.h>
#include <fairmq/TransportEnum.h>
#include <memory> #include <memory>
#include <ostream> #include <ostream>
#include <stdexcept> #include <stdexcept>
@ -18,13 +19,6 @@
namespace fair::mq { namespace fair::mq {
enum class Transport
{
DEFAULT,
ZMQ,
SHM
};
struct TransportError : std::runtime_error struct TransportError : std::runtime_error
{ {
using std::runtime_error::runtime_error; using std::runtime_error::runtime_error;

View File

@ -9,7 +9,7 @@
#ifndef FAIR_MQ_UNMANAGEDREGION_H #ifndef FAIR_MQ_UNMANAGEDREGION_H
#define FAIR_MQ_UNMANAGEDREGION_H #define FAIR_MQ_UNMANAGEDREGION_H
#include <fairmq/Transports.h> #include <fairmq/TransportEnum.h>
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstdint> // uint32_t #include <cstdint> // uint32_t
@ -134,6 +134,7 @@ struct RegionConfig
int creationFlags = 0; /// flags passed to the underlying transport on region creation int creationFlags = 0; /// flags passed to the underlying transport on region creation
int64_t userFlags = 0; /// custom flags that have no effect on the transport, but can be retrieved from the region by the user int64_t userFlags = 0; /// custom flags that have no effect on the transport, but can be retrieved from the region by the user
uint64_t size = 0; /// region size uint64_t size = 0; /// region size
uint64_t rcSegmentSize = 100000000; /// size of the segment that stores reference counts when "soft"-copying the messages
std::string path = ""; /// file path, if the region is backed by a file std::string path = ""; /// file path, if the region is backed by a file
std::optional<uint16_t> id = std::nullopt; /// region id std::optional<uint16_t> id = std::nullopt; /// region id
uint32_t linger = 100; /// delay in ms before region destruction to collect outstanding events uint32_t linger = 100; /// delay in ms before region destruction to collect outstanding events

View File

@ -1,5 +1,5 @@
/******************************************************************************** /********************************************************************************
* Copyright (C) 2018-2022 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Copyright (C) 2018-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* * * *
* This software is distributed under the terms of the * * This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, * * GNU Lesser General Public Licence (LGPL) version 3, *
@ -19,7 +19,7 @@
#define FAIRMQ_GIT_DATE "@PROJECT_GIT_DATE@" #define FAIRMQ_GIT_DATE "@PROJECT_GIT_DATE@"
#define FAIRMQ_REPO_URL "https://github.com/FairRootGroup/FairMQ" #define FAIRMQ_REPO_URL "https://github.com/FairRootGroup/FairMQ"
#define FAIRMQ_LICENSE "LGPL-3.0" #define FAIRMQ_LICENSE "LGPL-3.0"
#define FAIRMQ_COPYRIGHT "2012-2023 GSI" #define FAIRMQ_COPYRIGHT "2012-2025 GSI"
#define FAIRMQ_BUILD_TYPE "@CMAKE_BUILD_TYPE@" #define FAIRMQ_BUILD_TYPE "@CMAKE_BUILD_TYPE@"
#endif // FAIR_MQ_VERSION_H #endif // FAIR_MQ_VERSION_H

View File

@ -65,13 +65,17 @@ Control::Control(const string& name, Plugin::Version version, const string& main
}); });
try { try {
TakeDeviceControl();
auto control = GetProperty<string>("control"); auto control = GetProperty<string>("control");
if (control != "none") {
TakeDeviceControl();
}
if (control == "static") { if (control == "static") {
LOG(debug) << "Running builtin controller: static"; LOG(debug) << "Running builtin controller: static";
fControllerThread = thread(&Control::StaticMode, this); fControllerThread = thread(&Control::StaticMode, this);
} else if (control == "none") {
LOG(debug) << "Builtin controller: disabled";
} else if (control == "gui") { } else if (control == "gui") {
LOG(debug) << "Running builtin controller: gui"; LOG(debug) << "Running builtin controller: gui";
fControllerThread = thread(&Control::GUIMode, this); fControllerThread = thread(&Control::GUIMode, this);
@ -142,7 +146,7 @@ auto ControlPluginProgramOptions() -> Plugin::ProgOptions
namespace po = boost::program_options; namespace po = boost::program_options;
auto pluginOptions = po::options_description{"Control (builtin) Plugin"}; auto pluginOptions = po::options_description{"Control (builtin) Plugin"};
pluginOptions.add_options() pluginOptions.add_options()
("control", po::value<string>()->default_value("dynamic"), "Control mode, 'static' or 'dynamic' (aliases for dynamic are external and interactive)") ("control", po::value<string>()->default_value("dynamic"), "Control mode, 'static' or 'dynamic' (aliases for dynamic are external and interactive), 'none', 'gui'")
("catch-signals", po::value<int >()->default_value(1), "Enable signal handling (1/0)."); ("catch-signals", po::value<int >()->default_value(1), "Enable signal handling (1/0).");
return pluginOptions; return pluginOptions;
} }
@ -271,11 +275,11 @@ auto Control::InteractiveMode() -> void
try { try {
RunStartupSequence(); RunStartupSequence();
if(!fDeviceShutdownRequested) { if (!fDeviceShutdownRequested) {
RunREPL(); RunREPL();
} }
if(!fDeviceShutdownRequested) { if (!fDeviceShutdownRequested) {
RunShutdownSequence(); RunShutdownSequence();
} }
} catch (PluginServices::DeviceControlError& e) { } catch (PluginServices::DeviceControlError& e) {
@ -404,7 +408,7 @@ try {
// or for device shutdown request (Ctrl-C) // or for device shutdown request (Ctrl-C)
fStateQueue.WaitForNextOrCustom([this]{ return fDeviceShutdownRequested.load(); }); fStateQueue.WaitForNextOrCustom([this]{ return fDeviceShutdownRequested.load(); });
if(!fDeviceShutdownRequested) { if (!fDeviceShutdownRequested) {
RunShutdownSequence(); RunShutdownSequence();
} }
} catch (PluginServices::DeviceControlError& e) { } catch (PluginServices::DeviceControlError& e) {
@ -421,7 +425,7 @@ try {
// Wait for device shutdown request (Ctrl-C) // Wait for device shutdown request (Ctrl-C)
fStateQueue.WaitForCustom([this]{ return fDeviceShutdownRequested.load(); }); fStateQueue.WaitForCustom([this]{ return fDeviceShutdownRequested.load(); });
if(!fDeviceShutdownRequested) { if (!fDeviceShutdownRequested) {
RunShutdownSequence(); RunShutdownSequence();
} }
} catch (PluginServices::DeviceControlError& e) { } catch (PluginServices::DeviceControlError& e) {

View File

@ -13,6 +13,8 @@
#include <functional> // std::equal_to #include <functional> // std::equal_to
#include <boost/functional/hash.hpp> #include <boost/functional/hash.hpp>
// #include <boost/interprocess/allocators/adaptive_pool.hpp>
#include <boost/interprocess/allocators/node_allocator.hpp>
#include <boost/interprocess/allocators/allocator.hpp> #include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/map.hpp> #include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/string.hpp> #include <boost/interprocess/containers/string.hpp>
@ -21,10 +23,12 @@
#include <boost/interprocess/managed_shared_memory.hpp> #include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/mem_algo/simple_seq_fit.hpp> #include <boost/interprocess/mem_algo/simple_seq_fit.hpp>
#include <boost/unordered_map.hpp> #include <boost/unordered_map.hpp>
#include <boost/variant.hpp> #include <variant>
#include <sys/types.h> #include <sys/types.h>
#include <fairmq/tools/Strings.h>
namespace fair::mq::shmem namespace fair::mq::shmem
{ {
@ -41,6 +45,36 @@ using RBTreeBestFitSegment = boost::interprocess::basic_managed_shared_memory<ch
boost::interprocess::null_index>; boost::interprocess::null_index>;
// boost::interprocess::iset_index>; // boost::interprocess::iset_index>;
inline std::string MakeShmName(const std::string& shmId, const std::string& type) {
return std::string("fmq_" + shmId + "_" + type);
}
inline std::string MakeShmName(const std::string& shmId, const std::string& type, int index) {
return std::string(MakeShmName(shmId, type) + "_" + std::to_string(index));
}
struct RefCount
{
explicit RefCount(uint16_t c)
: count(c)
{}
uint16_t Get() { return count.load(); }
uint16_t Increment() { return count.fetch_add(1); }
uint16_t Decrement() { return count.fetch_sub(1); }
std::atomic<uint16_t> count;
};
// Number of nodes allocated at once when the allocator runs out of nodes.
static constexpr size_t numNodesPerBlock = 4096;
// Maximum number of totally free blocks that the adaptive node pool will hold.
// The rest of the totally free blocks will be deallocated with the segment manager.
// static constexpr size_t maxFreeBlocks = 2;
using RefCountPool = boost::interprocess::node_allocator<RefCount, boost::interprocess::managed_shared_memory::segment_manager, numNodesPerBlock>;
// using RefCountPool = boost::interprocess::adaptive_pool<RefCount, boost::interprocess::managed_shared_memory::segment_manager, numNodesPerBlock, maxFreeBlocks>;
using SegmentManager = boost::interprocess::managed_shared_memory::segment_manager; using SegmentManager = boost::interprocess::managed_shared_memory::segment_manager;
using VoidAlloc = boost::interprocess::allocator<void, SegmentManager>; using VoidAlloc = boost::interprocess::allocator<void, SegmentManager>;
using CharAlloc = boost::interprocess::allocator<char, SegmentManager>; using CharAlloc = boost::interprocess::allocator<char, SegmentManager>;
@ -48,6 +82,90 @@ using Str = boost::interprocess::basic_string<char, std::char_traits<
using StrAlloc = boost::interprocess::allocator<Str, SegmentManager>; using StrAlloc = boost::interprocess::allocator<Str, SegmentManager>;
using StrVector = boost::interprocess::vector<Str, StrAlloc>; using StrVector = boost::interprocess::vector<Str, StrAlloc>;
// ShmHeader stores user buffer alignment and the reference count in the following structure:
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// The alignment of Hdr depends on the alignment of std::atomic and is stored in the first entry
struct ShmHeader
{
struct Hdr
{
uint16_t userOffset;
std::atomic<uint16_t> refCount;
};
static Hdr* HdrPtr(char* ptr)
{
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// ^
return reinterpret_cast<Hdr*>(ptr + sizeof(uint16_t) + *(reinterpret_cast<uint16_t*>(ptr)));
}
static uint16_t HdrPartSize() // [HdrOffset(uint16_t)][Hdr alignment][Hdr]
{
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// <--------------------------------------->
return sizeof(uint16_t) + alignof(Hdr) + sizeof(Hdr);
}
static std::atomic<uint16_t>& RefCountPtr(char* ptr)
{
// get the ref count ptr from the Hdr
return HdrPtr(ptr)->refCount;
}
static uint16_t UserOffset(char* ptr)
{
return HdrPartSize() + HdrPtr(ptr)->userOffset;
}
static char* UserPtr(char* ptr)
{
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// ^
return ptr + HdrPartSize() + HdrPtr(ptr)->userOffset;
}
static uint16_t RefCount(char* ptr) { return RefCountPtr(ptr).load(); }
static uint16_t IncrementRefCount(char* ptr) { return RefCountPtr(ptr).fetch_add(1); }
static uint16_t DecrementRefCount(char* ptr) { return RefCountPtr(ptr).fetch_sub(1); }
static size_t FullSize(size_t size, size_t alignment)
{
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// <--------------------------------------------------------------------------->
return HdrPartSize() + alignment + size;
}
static void Construct(char* ptr, size_t alignment)
{
// place the Hdr in the aligned location, fill it and store its offset to HdrOffset
// the address alignment should be at least 2
assert(reinterpret_cast<uintptr_t>(ptr) % 2 == 0);
// offset to the beginning of the Hdr. store it in the beginning
uint16_t hdrOffset = alignof(Hdr) - ((reinterpret_cast<uintptr_t>(ptr) + sizeof(uint16_t)) % alignof(Hdr));
memcpy(ptr, &hdrOffset, sizeof(hdrOffset));
// offset to the beginning of the user buffer, store in Hdr together with the ref count
uint16_t userOffset = alignment - ((reinterpret_cast<uintptr_t>(ptr) + HdrPartSize()) % alignment);
new(ptr + sizeof(uint16_t) + hdrOffset) Hdr{ userOffset, std::atomic<uint16_t>(1) };
}
static void Destruct(char* ptr) { RefCountPtr(ptr).~atomic(); }
};
struct MetaHeader
{
size_t fSize; // size of the shm buffer
size_t fHint; // user-defined value, given by the user on message creation and returned to the user on "buffer no longer needed"-callbacks
boost::interprocess::managed_shared_memory::handle_t fHandle; // handle to shm buffer, convertible to shm buffer ptr
mutable boost::interprocess::managed_shared_memory::handle_t fShared; // handle to the buffer storing the ref count for shared buffers
uint16_t fRegionId; // id of the unmanaged region
mutable uint16_t fSegmentId; // id of the managed segment
bool fManaged; // true = managed segment, false = unmanaged region
};
enum class AllocationAlgorithm : int enum class AllocationAlgorithm : int
{ {
rbtree_best_fit, rbtree_best_fit,
@ -56,19 +174,12 @@ enum class AllocationAlgorithm : int
struct RegionInfo struct RegionInfo
{ {
RegionInfo(const VoidAlloc& alloc) RegionInfo(const char* path, int flags, uint64_t userFlags, uint64_t size, uint64_t rcSegmentSize, const VoidAlloc& alloc)
: fPath("", alloc)
, fCreationFlags(0)
, fUserFlags(0)
, fSize(0)
, fDestroyed(false)
{}
RegionInfo(const char* path, int flags, uint64_t userFlags, uint64_t size, const VoidAlloc& alloc)
: fPath(path, alloc) : fPath(path, alloc)
, fCreationFlags(flags) , fCreationFlags(flags)
, fUserFlags(userFlags) , fUserFlags(userFlags)
, fSize(size) , fSize(size)
, fRCSegmentSize(rcSegmentSize)
, fDestroyed(false) , fDestroyed(false)
{} {}
@ -76,6 +187,7 @@ struct RegionInfo
int fCreationFlags; int fCreationFlags;
uint64_t fUserFlags; uint64_t fUserFlags;
uint64_t fSize; uint64_t fSize;
uint64_t fRCSegmentSize;
bool fDestroyed; bool fDestroyed;
}; };
@ -143,17 +255,6 @@ struct RegionCounter
std::atomic<uint16_t> fCount; std::atomic<uint16_t> fCount;
}; };
struct MetaHeader
{
size_t fSize;
size_t fHint;
boost::interprocess::managed_shared_memory::handle_t fHandle;
mutable boost::interprocess::managed_shared_memory::handle_t fShared;
uint16_t fRegionId;
mutable uint16_t fSegmentId;
bool fManaged;
};
#ifdef FAIRMQ_DEBUG_MODE #ifdef FAIRMQ_DEBUG_MODE
struct MsgCounter struct MsgCounter
{ {
@ -219,73 +320,7 @@ std::string makeShmIdStr(const std::string& sessionId);
std::string makeShmIdStr(uint64_t val); std::string makeShmIdStr(uint64_t val);
uint64_t makeShmIdUint64(const std::string& sessionId); uint64_t makeShmIdUint64(const std::string& sessionId);
struct SegmentBufferShrink
struct SegmentSize : public boost::static_visitor<size_t>
{
template<typename S>
size_t operator()(S& s) const { return s.get_size(); }
};
struct SegmentAddress : public boost::static_visitor<void*>
{
template<typename S>
void* operator()(S& s) const { return s.get_address(); }
};
struct SegmentMemoryZeroer : public boost::static_visitor<>
{
template<typename S>
void operator()(S& s) const { s.zero_free_memory(); }
};
struct SegmentFreeMemory : public boost::static_visitor<size_t>
{
template<typename S>
size_t operator()(S& s) const { return s.get_free_memory(); }
};
struct SegmentHandleFromAddress : public boost::static_visitor<boost::interprocess::managed_shared_memory::handle_t>
{
SegmentHandleFromAddress(const void* _ptr) : ptr(_ptr) {}
template<typename S>
boost::interprocess::managed_shared_memory::handle_t operator()(S& s) const { return s.get_handle_from_address(ptr); }
const void* ptr;
};
struct SegmentAddressFromHandle : public boost::static_visitor<char*>
{
SegmentAddressFromHandle(const boost::interprocess::managed_shared_memory::handle_t _handle) : handle(_handle) {}
template<typename S>
char* operator()(S& s) const { return reinterpret_cast<char*>(s.get_address_from_handle(handle)); }
const boost::interprocess::managed_shared_memory::handle_t handle;
};
struct SegmentAllocate : public boost::static_visitor<char*>
{
SegmentAllocate(const size_t _size) : size(_size) {}
template<typename S>
char* operator()(S& s) const { return reinterpret_cast<char*>(s.allocate(size)); }
const size_t size;
};
struct SegmentAllocateAligned : public boost::static_visitor<void*>
{
SegmentAllocateAligned(const size_t _size, const size_t _alignment) : size(_size), alignment(_alignment) {}
template<typename S>
void* operator()(S& s) const { return s.allocate_aligned(size, alignment); }
const size_t size;
const size_t alignment;
};
struct SegmentBufferShrink : public boost::static_visitor<char*>
{ {
SegmentBufferShrink(const size_t _new_size, char* _local_ptr) SegmentBufferShrink(const size_t _new_size, char* _local_ptr)
: new_size(_new_size) : new_size(_new_size)
@ -303,16 +338,6 @@ struct SegmentBufferShrink : public boost::static_visitor<char*>
mutable char* local_ptr; mutable char* local_ptr;
}; };
struct SegmentDeallocate : public boost::static_visitor<>
{
SegmentDeallocate(char* _ptr) : ptr(_ptr) {}
template<typename S>
void operator()(S& s) const { return s.deallocate(ptr); }
char* ptr;
};
} // namespace fair::mq::shmem } // namespace fair::mq::shmem
#endif /* FAIR_MQ_SHMEM_COMMON_H_ */ #endif /* FAIR_MQ_SHMEM_COMMON_H_ */

View File

@ -24,7 +24,6 @@
#include <boost/interprocess/sync/interprocess_condition.hpp> #include <boost/interprocess/sync/interprocess_condition.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp> #include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/named_mutex.hpp> #include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/variant.hpp>
#include <algorithm> // max #include <algorithm> // max
#include <chrono> #include <chrono>
@ -42,6 +41,7 @@
#include <tuple> #include <tuple>
#include <unordered_map> #include <unordered_map>
#include <utility> // pair #include <utility> // pair
#include <variant>
#include <vector> #include <vector>
#include <unistd.h> // getuid #include <unistd.h> // getuid
@ -51,79 +51,6 @@
namespace fair::mq::shmem namespace fair::mq::shmem
{ {
// ShmHeader stores user buffer alignment and the reference count in the following structure:
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// The alignment of Hdr depends on the alignment of std::atomic and is stored in the first entry
struct ShmHeader
{
struct Hdr
{
uint16_t userOffset;
std::atomic<uint16_t> refCount;
};
static Hdr* HdrPtr(char* ptr)
{
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// ^
return reinterpret_cast<Hdr*>(ptr + sizeof(uint16_t) + *(reinterpret_cast<uint16_t*>(ptr)));
}
static uint16_t HdrPartSize() // [HdrOffset(uint16_t)][Hdr alignment][Hdr]
{
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// <--------------------------------------->
return sizeof(uint16_t) + alignof(Hdr) + sizeof(Hdr);
}
static std::atomic<uint16_t>& RefCountPtr(char* ptr)
{
// get the ref count ptr from the Hdr
return HdrPtr(ptr)->refCount;
}
static uint16_t UserOffset(char* ptr)
{
return HdrPartSize() + HdrPtr(ptr)->userOffset;
}
static char* UserPtr(char* ptr)
{
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// ^
return ptr + HdrPartSize() + HdrPtr(ptr)->userOffset;
}
static uint16_t RefCount(char* ptr) { return RefCountPtr(ptr).load(); }
static uint16_t IncrementRefCount(char* ptr) { return RefCountPtr(ptr).fetch_add(1); }
static uint16_t DecrementRefCount(char* ptr) { return RefCountPtr(ptr).fetch_sub(1); }
static size_t FullSize(size_t size, size_t alignment)
{
// [HdrOffset(uint16_t)][Hdr alignment][Hdr][user buffer alignment][user buffer]
// <--------------------------------------------------------------------------->
return HdrPartSize() + alignment + size;
}
static void Construct(char* ptr, size_t alignment)
{
// place the Hdr in the aligned location, fill it and store its offset to HdrOffset
// the address alignment should be at least 2
assert(reinterpret_cast<uintptr_t>(ptr) % 2 == 0);
// offset to the beginning of the Hdr. store it in the beginning
uint16_t hdrOffset = alignof(Hdr) - ((reinterpret_cast<uintptr_t>(ptr) + sizeof(uint16_t)) % alignof(Hdr));
memcpy(ptr, &hdrOffset, sizeof(hdrOffset));
// offset to the beginning of the user buffer, store in Hdr together with the ref count
uint16_t userOffset = alignment - ((reinterpret_cast<uintptr_t>(ptr) + HdrPartSize()) % alignment);
new(ptr + sizeof(uint16_t) + hdrOffset) Hdr{ userOffset, std::atomic<uint16_t>(1) };
}
static void Destruct(char* ptr) { RefCountPtr(ptr).~atomic(); }
};
class Manager class Manager
{ {
public: public:
@ -131,7 +58,7 @@ class Manager
: fShmId64(config ? config->GetProperty<uint64_t>("shmid", makeShmIdUint64(sessionName)) : makeShmIdUint64(sessionName)) : fShmId64(config ? config->GetProperty<uint64_t>("shmid", makeShmIdUint64(sessionName)) : makeShmIdUint64(sessionName))
, fShmId(makeShmIdStr(fShmId64)) , fShmId(makeShmIdStr(fShmId64))
, fSegmentId(config ? config->GetProperty<uint16_t>("shm-segment-id", 0) : 0) , fSegmentId(config ? config->GetProperty<uint16_t>("shm-segment-id", 0) : 0)
, fManagementSegment(boost::interprocess::open_or_create, std::string("fmq_" + fShmId + "_mng").c_str(), kManagementSegmentSize) , fManagementSegment(boost::interprocess::open_or_create, MakeShmName(fShmId, "mng").c_str(), kManagementSegmentSize)
, fShmVoidAlloc(fManagementSegment.get_segment_manager()) , fShmVoidAlloc(fManagementSegment.get_segment_manager())
, fShmMtx(fManagementSegment.find_or_construct<boost::interprocess::interprocess_mutex>(boost::interprocess::unique_instance)()) , fShmMtx(fManagementSegment.find_or_construct<boost::interprocess::interprocess_mutex>(boost::interprocess::unique_instance)())
, fNumObservedEvents(0) , fNumObservedEvents(0)
@ -231,7 +158,7 @@ class Manager
bool createdSegment = false; bool createdSegment = false;
try { try {
std::string segmentName("fmq_" + fShmId + "_m_" + std::to_string(fSegmentId)); std::string segmentName = MakeShmName(fShmId, "m", fSegmentId);
auto it = fShmSegments->find(fSegmentId); auto it = fShmSegments->find(fSegmentId);
if (it == fShmSegments->end()) { if (it == fShmSegments->end()) {
// no segment with given id exists, creating // no segment with given id exists, creating
@ -266,8 +193,8 @@ class Manager
} }
} }
LOG(debug) << (createdSegment ? "Created" : "Opened") << " managed shared memory segment " << "fmq_" << fShmId << "_m_" << fSegmentId LOG(debug) << (createdSegment ? "Created" : "Opened") << " managed shared memory segment " << "fmq_" << fShmId << "_m_" << fSegmentId
<< ". Size: " << boost::apply_visitor(SegmentSize(), fSegments.at(fSegmentId)) << " bytes." << ". Size: " << std::visit([](auto& s) { return s.get_size(); }, fSegments.at(fSegmentId)) << " bytes."
<< " Available: " << boost::apply_visitor(SegmentFreeMemory(), fSegments.at(fSegmentId)) << " bytes." << " Available: " << std::visit([](auto& s) { return s.get_free_memory(); }, fSegments.at(fSegmentId)) << " bytes."
<< " Allocation algorithm: " << allocationAlgorithm; << " Allocation algorithm: " << allocationAlgorithm;
} catch (interprocess_exception& bie) { } catch (interprocess_exception& bie) {
LOG(error) << "Failed to create/open shared memory segment '" << "fmq_" << fShmId << "_m_" << fSegmentId << "': " << bie.what(); LOG(error) << "Failed to create/open shared memory segment '" << "fmq_" << fShmId << "_m_" << fSegmentId << "': " << bie.what();
@ -305,14 +232,16 @@ class Manager
void ZeroSegment(uint16_t id) void ZeroSegment(uint16_t id)
{ {
LOG(debug) << "Zeroing the managed segment free memory..."; LOG(debug) << "Zeroing the managed segment free memory...";
boost::apply_visitor(SegmentMemoryZeroer(), fSegments.at(id)); std::visit([](auto& s) { return s.zero_free_memory(); }, fSegments.at(id));
LOG(debug) << "Successfully zeroed the managed segment free memory."; LOG(debug) << "Successfully zeroed the managed segment free memory.";
} }
void MlockSegment(uint16_t id) void MlockSegment(uint16_t id)
{ {
LOG(debug) << "Locking the managed segment memory pages..."; LOG(debug) << "Locking the managed segment memory pages...";
if (mlock(boost::apply_visitor(SegmentAddress(), fSegments.at(id)), boost::apply_visitor(SegmentSize(), fSegments.at(id))) == -1) { if (mlock(
std::visit([](auto& s) { return s.get_address(); }, fSegments.at(id)),
std::visit([](auto& s) { return s.get_size(); }, fSegments.at(id))) == -1) {
LOG(error) << "Could not lock the managed segment memory. Code: " << errno << ", reason: " << strerror(errno); LOG(error) << "Could not lock the managed segment memory. Code: " << errno << ", reason: " << strerror(errno);
throw TransportError(tools::ToString("Could not lock the managed segment memory: ", strerror(errno))); throw TransportError(tools::ToString("Could not lock the managed segment memory: ", strerror(errno)));
} }
@ -327,7 +256,7 @@ class Manager
{ {
using namespace boost::interprocess; using namespace boost::interprocess;
try { try {
named_mutex monitorStatus(open_only, std::string("fmq_" + id + "_ms").c_str()); named_mutex monitorStatus(open_only, MakeShmName(id, "ms").c_str());
LOG(debug) << "Found fairmq-shmmonitor for shared memory id " << id; LOG(debug) << "Found fairmq-shmmonitor for shared memory id " << id;
} catch (interprocess_exception&) { } catch (interprocess_exception&) {
LOG(debug) << "no fairmq-shmmonitor found for shared memory id " << id << ", starting..."; LOG(debug) << "no fairmq-shmmonitor found for shared memory id " << id << ", starting...";
@ -336,7 +265,7 @@ class Manager
int numTries = 0; int numTries = 0;
do { do {
try { try {
named_mutex monitorStatus(open_only, std::string("fmq_" + id + "_ms").c_str()); named_mutex monitorStatus(open_only, MakeShmName(id, "ms").c_str());
LOG(debug) << "Started fairmq-shmmonitor for shared memory id " << id; LOG(debug) << "Started fairmq-shmmonitor for shared memory id " << id;
break; break;
} catch (interprocess_exception&) { } catch (interprocess_exception&) {
@ -410,6 +339,12 @@ class Manager
LOG(debug) << "Unmanaged region (view) already present, promoting to controller"; LOG(debug) << "Unmanaged region (view) already present, promoting to controller";
region->BecomeController(cfg); region->BecomeController(cfg);
} else { } else {
// we need to update local config, if the region information already exists
auto info = fShmRegions->find(id);
if (info != fShmRegions->end()) {
cfg.rcSegmentSize = info->second.fRCSegmentSize;
}
auto res = fRegions.emplace(id, std::make_unique<UnmanagedRegion>(fShmId, size, true, cfg)); auto res = fRegions.emplace(id, std::make_unique<UnmanagedRegion>(fShmId, size, true, cfg));
region = res.first->second.get(); region = res.first->second.get();
} }
@ -466,7 +401,8 @@ class Manager
auto it = fRegions.find(id); auto it = fRegions.find(id);
if (it != fRegions.end()) { if (it != fRegions.end()) {
return it->second.get(); return it->second.get();
} else { }
try { try {
RegionConfig cfg; RegionConfig cfg;
// get region info // get region info
@ -475,6 +411,7 @@ class Manager
RegionInfo regionInfo = fShmRegions->at(id); RegionInfo regionInfo = fShmRegions->at(id);
cfg.id = id; cfg.id = id;
cfg.creationFlags = regionInfo.fCreationFlags; cfg.creationFlags = regionInfo.fCreationFlags;
cfg.rcSegmentSize = regionInfo.fRCSegmentSize;
cfg.path = regionInfo.fPath.c_str(); cfg.path = regionInfo.fPath.c_str();
} }
// LOG(debug) << "Located remote region with id '" << id << "', path: '" << cfg.path << "', flags: '" << cfg.creationFlags << "'"; // LOG(debug) << "Located remote region with id '" << id << "', path: '" << cfg.path << "', flags: '" << cfg.creationFlags << "'";
@ -492,7 +429,6 @@ class Manager
return nullptr; return nullptr;
} }
} }
}
void RemoveRegion(uint16_t id) void RemoveRegion(uint16_t id)
{ {
@ -529,8 +465,8 @@ class Manager
info.managed = true; info.managed = true;
info.id = segmentId; info.id = segmentId;
info.event = RegionEvent::created; info.event = RegionEvent::created;
info.ptr = boost::apply_visitor(SegmentAddress(), fSegments.at(segmentId)); info.ptr = std::visit([](auto& s) { return s.get_address(); }, fSegments.at(segmentId));
info.size = boost::apply_visitor(SegmentSize(), fSegments.at(segmentId)); info.size = std::visit([](auto& s) { return s.get_size(); }, fSegments.at(segmentId));
result.push_back(info); result.push_back(info);
} catch (const std::out_of_range& oor) { } catch (const std::out_of_range& oor) {
LOG(error) << "could not find segment with id " << segmentId; LOG(error) << "could not find segment with id " << segmentId;
@ -549,6 +485,7 @@ class Manager
cfg.id = info.id; cfg.id = info.id;
cfg.creationFlags = regionInfo.fCreationFlags; cfg.creationFlags = regionInfo.fCreationFlags;
cfg.path = regionInfo.fPath.c_str(); cfg.path = regionInfo.fPath.c_str();
cfg.rcSegmentSize = regionInfo.fRCSegmentSize;
regionCfgs.emplace(info.id, cfg); regionCfgs.emplace(info.id, cfg);
// fill the ptr+size info after shmLock is released, to avoid constructing local region under it // fill the ptr+size info after shmLock is released, to avoid constructing local region under it
} else { } else {
@ -710,9 +647,9 @@ class Manager
using namespace boost::interprocess; using namespace boost::interprocess;
if (segmentInfo.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) { if (segmentInfo.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) {
fSegments.emplace(id, RBTreeBestFitSegment(open_only, std::string("fmq_" + fShmId + "_m_" + std::to_string(id)).c_str())); fSegments.emplace(id, RBTreeBestFitSegment(open_only, MakeShmName(fShmId, "m", id).c_str()));
} else { } else {
fSegments.emplace(id, SimpleSeqFitSegment(open_only, std::string("fmq_" + fShmId + "_m_" + std::to_string(id)).c_str())); fSegments.emplace(id, SimpleSeqFitSegment(open_only, MakeShmName(fShmId, "m", id).c_str()));
} }
} catch (std::out_of_range& oor) { } catch (std::out_of_range& oor) {
LOG(error) << "Could not get segment with id '" << id << "': " << oor.what(); LOG(error) << "Could not get segment with id '" << id << "': " << oor.what();
@ -724,11 +661,11 @@ class Manager
boost::interprocess::managed_shared_memory::handle_t GetHandleFromAddress(const void* ptr, uint16_t segmentId) const boost::interprocess::managed_shared_memory::handle_t GetHandleFromAddress(const void* ptr, uint16_t segmentId) const
{ {
return boost::apply_visitor(SegmentHandleFromAddress(ptr), fSegments.at(segmentId)); return std::visit([ptr](auto& s) { return s.get_handle_from_address(ptr); }, fSegments.at(segmentId));
} }
char* GetAddressFromHandle(const boost::interprocess::managed_shared_memory::handle_t handle, uint16_t segmentId) const char* GetAddressFromHandle(const boost::interprocess::managed_shared_memory::handle_t handle, uint16_t segmentId) const
{ {
return boost::apply_visitor(SegmentAddressFromHandle(handle), fSegments.at(segmentId)); return std::visit([handle](auto& s) { return reinterpret_cast<char*>(s.get_address_from_handle(handle)); }, fSegments.at(segmentId));
} }
char* Allocate(size_t size, size_t alignment = 0) char* Allocate(size_t size, size_t alignment = 0)
@ -741,24 +678,32 @@ class Manager
while (!ptr) { while (!ptr) {
try { try {
size_t segmentSize = boost::apply_visitor(SegmentSize(), fSegments.at(fSegmentId)); size_t segmentSize = std::visit([](auto& s) { return s.get_size(); }, fSegments.at(fSegmentId));
if (fullSize > segmentSize) { if (fullSize > segmentSize) {
throw MessageBadAlloc(tools::ToString("Requested message size (", fullSize, ") exceeds segment size (", segmentSize, ")")); throw MessageBadAlloc(tools::ToString("Requested message size (", fullSize, ") exceeds segment size (", segmentSize, ")"));
} }
ptr = boost::apply_visitor(SegmentAllocate{fullSize}, fSegments.at(fSegmentId)); ptr = std::visit([fullSize](auto& s) { return reinterpret_cast<char*>(s.allocate(fullSize)); }, fSegments.at(fSegmentId));
ShmHeader::Construct(ptr, alignment); ShmHeader::Construct(ptr, alignment);
} catch (boost::interprocess::bad_alloc& ba) { } catch (boost::interprocess::bad_alloc& ba) {
// LOG(warn) << "Shared memory full..."; // LOG(warn) << "Shared memory full...";
if (fBadAllocMaxAttempts >= 0 && ++numAttempts >= fBadAllocMaxAttempts) { 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)))); throw MessageBadAlloc(tools::ToString("shmem: could not create a message of size ", size,
", alignment: ", (alignment != 0) ? std::to_string(alignment) : "default",
", free memory: ", std::visit([](auto& s) { return s.get_free_memory(); }, fSegments.at(fSegmentId))));
} }
if (numAttempts == 1 && fBadAllocMaxAttempts > 1) { 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"); LOG(warn) << tools::ToString("shmem: could not create a message of size ", size,
", alignment: ", (alignment != 0) ? std::to_string(alignment) : "default",
", free memory: ", std::visit([](auto& s) { return s.get_free_memory(); }, 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)); std::this_thread::sleep_for(std::chrono::milliseconds(fBadAllocAttemptIntervalInMs));
if (Interrupted()) { 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)))); throw MessageBadAlloc(tools::ToString("shmem: could not create a message of size ", size,
", alignment: ", (alignment != 0) ? std::to_string(alignment) : "default",
", free memory: ", std::visit([](auto& s) { return s.get_free_memory(); }, fSegments.at(fSegmentId))));
} else { } else {
continue; continue;
} }
@ -792,12 +737,12 @@ class Manager
} }
#endif #endif
ShmHeader::Destruct(ptr); ShmHeader::Destruct(ptr);
boost::apply_visitor(SegmentDeallocate(ptr), fSegments.at(segmentId)); std::visit([ptr](auto& s) { s.deallocate(ptr); }, fSegments.at(segmentId));
} }
char* ShrinkInPlace(size_t newSize, char* localPtr, uint16_t segmentId) char* ShrinkInPlace(size_t newSize, char* localPtr, uint16_t segmentId)
{ {
return boost::apply_visitor(SegmentBufferShrink(newSize, localPtr), fSegments.at(segmentId)); return std::visit(SegmentBufferShrink(newSize, localPtr), fSegments.at(segmentId));
} }
uint16_t GetSegmentId() const { return fSegmentId; } uint16_t GetSegmentId() const { return fSegmentId; }
@ -845,7 +790,7 @@ class Manager
uint64_t fShmId64; uint64_t fShmId64;
std::string fShmId; std::string fShmId;
uint16_t fSegmentId; uint16_t fSegmentId;
std::unordered_map<uint16_t, boost::variant<RBTreeBestFitSegment, SimpleSeqFitSegment>> fSegments; // TODO: refactor to use Segment class std::unordered_map<uint16_t, std::variant<RBTreeBestFitSegment, SimpleSeqFitSegment>> fSegments; // TODO: refactor to use Segment class
boost::interprocess::managed_shared_memory fManagementSegment; // TODO: refactor to use ManagementSegment class boost::interprocess::managed_shared_memory fManagementSegment; // TODO: refactor to use ManagementSegment class
VoidAlloc fShmVoidAlloc; VoidAlloc fShmVoidAlloc;
boost::interprocess::interprocess_mutex* fShmMtx; boost::interprocess::interprocess_mutex* fShmMtx;

View File

@ -14,6 +14,7 @@
#include "UnmanagedRegionImpl.h" #include "UnmanagedRegionImpl.h"
#include <fairmq/Message.h> #include <fairmq/Message.h>
#include <fairmq/UnmanagedRegion.h> #include <fairmq/UnmanagedRegion.h>
#include <fairmq/Transports.h>
#include <fairlogger/Logger.h> #include <fairlogger/Logger.h>
@ -36,60 +37,34 @@ class Message final : public fair::mq::Message
public: public:
Message(Manager& manager, fair::mq::TransportFactory* factory = nullptr) Message(Manager& manager, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory) : Message(manager, Alignment{0}, factory)
, fManager(manager) {}
, fQueued(false)
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId(), true}
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
{
fManager.IncrementMsgCounter();
}
Message(Manager& manager, Alignment alignment, fair::mq::TransportFactory* factory = nullptr) Message(Manager& manager, Alignment /* alignment */, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory) : fair::mq::Message(factory)
, fManager(manager) , fManager(manager)
, fQueued(false) , fSegmentId(fManager.GetSegmentId())
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId(), true}
, fAlignment(alignment.alignment)
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
{ {
fManager.IncrementMsgCounter(); fManager.IncrementMsgCounter();
} }
Message(Manager& manager, const size_t size, fair::mq::TransportFactory* factory = nullptr) Message(Manager& manager, const size_t size, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory) : Message(manager, size, Alignment{0}, factory)
, fManager(manager) {}
, fQueued(false)
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId(), true}
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
{
InitializeChunk(size);
fManager.IncrementMsgCounter();
}
Message(Manager& manager, const size_t size, Alignment alignment, fair::mq::TransportFactory* factory = nullptr) Message(Manager& manager, const size_t size, Alignment alignment, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory) : fair::mq::Message(factory)
, fManager(manager) , fManager(manager)
, fQueued(false) , fSegmentId(fManager.GetSegmentId())
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId(), true}
, fAlignment(alignment.alignment)
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
{ {
InitializeChunk(size, fAlignment); InitializeChunk(size, alignment.alignment);
fManager.IncrementMsgCounter(); fManager.IncrementMsgCounter();
} }
Message(Manager& manager, void* data, const size_t size, fair::mq::FreeFn* ffn, void* hint = nullptr, fair::mq::TransportFactory* factory = nullptr) Message(Manager& manager, void* data, const size_t size, fair::mq::FreeFn* ffn, void* hint = nullptr, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory) : fair::mq::Message(factory)
, fManager(manager) , fManager(manager)
, fQueued(false) , fSegmentId(fManager.GetSegmentId())
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId(), true}
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
{ {
if (InitializeChunk(size)) { if (InitializeChunk(size)) {
std::memcpy(fLocalPtr, data, size); std::memcpy(fLocalPtr, data, size);
@ -105,10 +80,12 @@ class Message final : public fair::mq::Message
Message(Manager& manager, UnmanagedRegionPtr& region, void* data, const size_t size, void* hint = 0, fair::mq::TransportFactory* factory = nullptr) Message(Manager& manager, UnmanagedRegionPtr& region, void* data, const size_t size, void* hint = 0, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory) : fair::mq::Message(factory)
, fManager(manager) , fManager(manager)
, fQueued(false)
, fMeta{size, reinterpret_cast<size_t>(hint), -1, -1, static_cast<UnmanagedRegionImpl*>(region.get())->fRegionId, fManager.GetSegmentId(), false}
, fRegionPtr(nullptr)
, fLocalPtr(static_cast<char*>(data)) , fLocalPtr(static_cast<char*>(data))
, fSize(size)
, fHint(reinterpret_cast<size_t>(hint))
, fRegionId(static_cast<UnmanagedRegionImpl*>(region.get())->fRegionId)
, fSegmentId(fManager.GetSegmentId())
, fManaged(false)
{ {
if (region->GetType() != GetType()) { if (region->GetType() != GetType()) {
LOG(error) << "region type (" << region->GetType() << ") does not match message type (" << GetType() << ")"; LOG(error) << "region type (" << region->GetType() << ") does not match message type (" << GetType() << ")";
@ -117,7 +94,7 @@ class Message final : public fair::mq::Message
if (reinterpret_cast<const char*>(data) >= reinterpret_cast<const char*>(region->GetData()) && if (reinterpret_cast<const char*>(data) >= reinterpret_cast<const char*>(region->GetData()) &&
reinterpret_cast<const char*>(data) <= reinterpret_cast<const char*>(region->GetData()) + region->GetSize()) { reinterpret_cast<const char*>(data) <= reinterpret_cast<const char*>(region->GetData()) + region->GetSize()) {
fMeta.fHandle = (boost::interprocess::managed_shared_memory::handle_t)(reinterpret_cast<const char*>(data) - reinterpret_cast<const char*>(region->GetData())); fHandle = (boost::interprocess::managed_shared_memory::handle_t)(reinterpret_cast<const char*>(data) - reinterpret_cast<const char*>(region->GetData()));
} else { } else {
LOG(error) << "trying to create region message with data from outside the region"; LOG(error) << "trying to create region message with data from outside the region";
throw TransportError("trying to create region message with data from outside the region"); throw TransportError("trying to create region message with data from outside the region");
@ -128,10 +105,13 @@ class Message final : public fair::mq::Message
Message(Manager& manager, MetaHeader& hdr, fair::mq::TransportFactory* factory = nullptr) Message(Manager& manager, MetaHeader& hdr, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory) : fair::mq::Message(factory)
, fManager(manager) , fManager(manager)
, fQueued(false) , fSize(hdr.fSize)
, fMeta{hdr} , fHint(hdr.fHint)
, fRegionPtr(nullptr) , fHandle(hdr.fHandle)
, fLocalPtr(nullptr) , fShared(hdr.fShared)
, fRegionId(hdr.fRegionId)
, fSegmentId(hdr.fSegmentId)
, fManaged(hdr.fManaged)
{ {
fManager.IncrementMsgCounter(); fManager.IncrementMsgCounter();
} }
@ -147,11 +127,10 @@ class Message final : public fair::mq::Message
fQueued = false; fQueued = false;
} }
void Rebuild(Alignment alignment) override void Rebuild(Alignment /* alignment */) override
{ {
CloseMessage(); CloseMessage();
fQueued = false; fQueued = false;
fAlignment = alignment.alignment;
} }
void Rebuild(size_t size) override void Rebuild(size_t size) override
@ -165,8 +144,7 @@ class Message final : public fair::mq::Message
{ {
CloseMessage(); CloseMessage();
fQueued = false; fQueued = false;
fAlignment = alignment.alignment; InitializeChunk(size, alignment.alignment);
InitializeChunk(size, fAlignment);
} }
void Rebuild(void* data, size_t size, fair::mq::FreeFn* ffn, void* hint = nullptr) override void Rebuild(void* data, size_t size, fair::mq::FreeFn* ffn, void* hint = nullptr) override
@ -187,17 +165,17 @@ class Message final : public fair::mq::Message
void* GetData() const override void* GetData() const override
{ {
if (!fLocalPtr) { if (!fLocalPtr) {
if (fMeta.fManaged) { if (fManaged) {
if (fMeta.fSize > 0) { if (fSize > 0) {
fManager.GetSegment(fMeta.fSegmentId); fManager.GetSegment(fSegmentId);
fLocalPtr = ShmHeader::UserPtr(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId)); fLocalPtr = ShmHeader::UserPtr(fManager.GetAddressFromHandle(fHandle, fSegmentId));
} else { } else {
fLocalPtr = nullptr; fLocalPtr = nullptr;
} }
} else { } else {
fRegionPtr = fManager.GetRegionFromCache(fMeta.fRegionId); fRegionPtr = fManager.GetRegionFromCache(fRegionId);
if (fRegionPtr) { if (fRegionPtr) {
fLocalPtr = reinterpret_cast<char*>(fRegionPtr->GetData()) + fMeta.fHandle; fLocalPtr = reinterpret_cast<char*>(fRegionPtr->GetData()) + fHandle;
} else { } else {
// LOG(warn) << "could not get pointer from a region message"; // LOG(warn) << "could not get pointer from a region message";
fLocalPtr = nullptr; fLocalPtr = nullptr;
@ -208,37 +186,41 @@ class Message final : public fair::mq::Message
return static_cast<void*>(fLocalPtr); return static_cast<void*>(fLocalPtr);
} }
size_t GetSize() const override { return fMeta.fSize; } size_t GetSize() const override { return fSize; }
bool SetUsedSize(size_t newSize) override bool SetUsedSize(size_t newSize, Alignment alignment = Alignment{0}) override
{ {
if (newSize == fMeta.fSize) { if (newSize == fSize) {
return true; return true;
} else if (newSize == 0) { } else if (newSize == 0) {
Deallocate(); Deallocate();
return true; return true;
} else if (newSize <= fMeta.fSize) { } else if (newSize <= fSize) {
try { try {
char* oldPtr = fManager.GetAddressFromHandle(fHandle, fSegmentId);
try { try {
char* oldPtr = fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId);
uint16_t userOffset = ShmHeader::UserOffset(oldPtr); uint16_t userOffset = ShmHeader::UserOffset(oldPtr);
char* ptr = fManager.ShrinkInPlace(userOffset + newSize, oldPtr, fMeta.fSegmentId); char* ptr = fManager.ShrinkInPlace(userOffset + newSize, oldPtr, fSegmentId);
fLocalPtr = ShmHeader::UserPtr(ptr); fLocalPtr = ShmHeader::UserPtr(ptr);
fMeta.fSize = newSize; fSize = newSize;
return true; return true;
} catch (boost::interprocess::bad_alloc& e) { } catch (boost::interprocess::bad_alloc& e) {
// if shrinking fails (can happen due to boost alignment requirements): // if shrinking fails (can happen due to boost alignment requirements):
// unused size >= 1000000 bytes: reallocate fully // unused size >= 1000000 bytes: reallocate fully
// unused size < 1000000 bytes: simply reset the size and keep the rest of the buffer until message destruction // unused size < 1000000 bytes: simply reset the size and keep the rest of the buffer until message destruction
if (fMeta.fSize - newSize >= 1000000) { if (fSize - newSize >= 1000000) {
char* ptr = fManager.Allocate(newSize, fAlignment); if (alignment.alignment == 0) {
// if no alignment is provided, take the minimum alignment of the old pointer, but no more than 4096
alignment.alignment = 1 << std::min(__builtin_ctz(reinterpret_cast<size_t>(oldPtr)), 12);
}
char* ptr = fManager.Allocate(newSize, alignment.alignment);
char* userPtr = ShmHeader::UserPtr(ptr); char* userPtr = ShmHeader::UserPtr(ptr);
std::memcpy(userPtr, fLocalPtr, newSize); std::memcpy(userPtr, fLocalPtr, newSize);
fManager.Deallocate(fMeta.fHandle, fMeta.fSegmentId); fManager.Deallocate(fHandle, fSegmentId);
fLocalPtr = userPtr; fLocalPtr = userPtr;
fMeta.fHandle = fManager.GetHandleFromAddress(ptr, fMeta.fSegmentId); fHandle = fManager.GetHandleFromAddress(ptr, fSegmentId);
} }
fMeta.fSize = newSize; fSize = newSize;
return true; return true;
} }
} catch (boost::interprocess::interprocess_exception& e) { } catch (boost::interprocess::interprocess_exception& e) {
@ -255,123 +237,178 @@ class Message final : public fair::mq::Message
uint16_t GetRefCount() const uint16_t GetRefCount() const
{ {
if (fMeta.fHandle < 0) { if (fHandle < 0) {
return 1; return 1;
} }
if (fMeta.fManaged) { // managed segment if (fManaged) { // managed segment
fManager.GetSegment(fMeta.fSegmentId); fManager.GetSegment(fSegmentId);
return ShmHeader::RefCount(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId)); return ShmHeader::RefCount(fManager.GetAddressFromHandle(fHandle, fSegmentId));
} else { // unmanaged region
if (fMeta.fShared < 0) { // UR msg is not yet shared
return 1;
} else {
fManager.GetSegment(fMeta.fSegmentId);
return ShmHeader::RefCount(fManager.GetAddressFromHandle(fMeta.fShared, fMeta.fSegmentId));
} }
if (fShared < 0) { // UR msg is not yet shared
return 1;
}
fRegionPtr = fManager.GetRegionFromCache(fRegionId);
if (!fRegionPtr) {
throw TransportError(tools::ToString("Cannot get unmanaged region with id ", fRegionId));
}
if (fRegionPtr->fRcSegmentSize > 0) {
return fRegionPtr->GetRefCountAddressFromHandle(fShared)->Get();
} else {
fManager.GetSegment(fSegmentId);
return ShmHeader::RefCount(fManager.GetAddressFromHandle(fShared, fSegmentId));
} }
} }
void Copy(const fair::mq::Message& other) override void Copy(const fair::mq::Message& other) override
{ {
const Message& otherMsg = static_cast<const Message&>(other); const Message& otherMsg = static_cast<const Message&>(other);
if (otherMsg.fMeta.fHandle < 0) {
// if the other message is not initialized, close this one too and return // if the other message is not initialized, close this one too and return
if (otherMsg.fHandle < 0) {
CloseMessage(); CloseMessage();
return; return;
} }
if (fMeta.fHandle >= 0) {
// if this msg is already initialized, close it first // if this msg is already initialized, close it first
if (fHandle >= 0) {
CloseMessage(); CloseMessage();
} }
if (otherMsg.fMeta.fManaged) { // managed segment // increment ref count
fMeta = otherMsg.fMeta; if (otherMsg.fManaged) { // msg in managed segment
fManager.GetSegment(fMeta.fSegmentId); fManager.GetSegment(otherMsg.fSegmentId);
ShmHeader::IncrementRefCount(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId)); ShmHeader::IncrementRefCount(fManager.GetAddressFromHandle(otherMsg.fHandle, otherMsg.fSegmentId));
} else { // unmanaged region } else { // msg in unmanaged region
if (otherMsg.fMeta.fShared < 0) { // if UR msg is not yet shared fRegionPtr = fManager.GetRegionFromCache(otherMsg.fRegionId);
// TODO: minimize the size to 0 and don't create extra space for user buffer alignment if (!fRegionPtr) {
throw TransportError(tools::ToString("Cannot get unmanaged region with id ", otherMsg.fRegionId));
}
if (fRegionPtr->fRcSegmentSize > 0) {
if (otherMsg.fShared < 0) {
// UR msg not yet shared, create the reference counting object with count 2
try {
otherMsg.fShared = fRegionPtr->HandleFromAddress(&(fRegionPtr->MakeRefCount(2)));
} catch (boost::interprocess::bad_alloc& ba) {
throw RefCountBadAlloc(tools::ToString("Insufficient space in the reference count segment ", otherMsg.fRegionId, ", original exception: bad_alloc: ", ba.what()));
}
} else {
fRegionPtr->GetRefCountAddressFromHandle(otherMsg.fShared)->Increment();
}
} else { // if RefCount segment size is 0, store the ref count in the managed segment
if (otherMsg.fShared < 0) { // if UR msg is not yet shared
char* ptr = fManager.Allocate(2, 0); char* ptr = fManager.Allocate(2, 0);
// point the fShared in the unmanaged region message to the refCount holder // point the fShared in the unmanaged region message to the refCount holder
otherMsg.fMeta.fShared = fManager.GetHandleFromAddress(ptr, fMeta.fSegmentId); otherMsg.fShared = fManager.GetHandleFromAddress(ptr, fSegmentId);
// the message needs to be able to locate in which segment the refCount is stored // the message needs to be able to locate in which segment the refCount is stored
otherMsg.fMeta.fSegmentId = fMeta.fSegmentId; otherMsg.fSegmentId = fSegmentId;
// point this message to the same content as the unmanaged region message
fMeta = otherMsg.fMeta;
// increment the refCount
ShmHeader::IncrementRefCount(ptr); ShmHeader::IncrementRefCount(ptr);
} else { // if the UR msg is already shared } else { // if the UR msg is already shared
fMeta = otherMsg.fMeta; fManager.GetSegment(otherMsg.fSegmentId);
fManager.GetSegment(fMeta.fSegmentId); ShmHeader::IncrementRefCount(fManager.GetAddressFromHandle(otherMsg.fShared, otherMsg.fSegmentId));
ShmHeader::IncrementRefCount(fManager.GetAddressFromHandle(fMeta.fShared, fMeta.fSegmentId));
} }
} }
} }
// copy meta data
fSize = otherMsg.fSize;
fHint = otherMsg.fHint;
fHandle = otherMsg.fHandle;
fShared = otherMsg.fShared;
fRegionId = otherMsg.fRegionId;
fSegmentId = otherMsg.fSegmentId;
fManaged = otherMsg.fManaged;
}
~Message() override { CloseMessage(); } ~Message() override { CloseMessage(); }
private: private:
Manager& fManager; Manager& fManager;
bool fQueued; mutable UnmanagedRegion* fRegionPtr = nullptr;
MetaHeader fMeta; mutable char* fLocalPtr = nullptr;
size_t fAlignment; size_t fSize = 0; // size of the shm buffer
mutable UnmanagedRegion* fRegionPtr; size_t fHint = 0; // user-defined value, given by the user on message creation and returned to the user on "buffer no longer needed"-callbacks
mutable char* fLocalPtr; boost::interprocess::managed_shared_memory::handle_t fHandle = -1; // handle to shm buffer, convertible to shm buffer ptr
mutable boost::interprocess::managed_shared_memory::handle_t fShared = -1; // handle to the buffer storing the ref count for shared buffers
uint16_t fRegionId = 0; // id of the unmanaged region
mutable uint16_t fSegmentId; // id of the managed segment
bool fManaged = true; // true = managed segment, false = unmanaged region
bool fQueued = false;
void SetMeta(const MetaHeader& meta)
{
fSize = meta.fSize;
fHint = meta.fHint;
fHandle = meta.fHandle;
fShared = meta.fShared;
fRegionId = meta.fRegionId;
fSegmentId = meta.fSegmentId;
fManaged = meta.fManaged;
}
char* InitializeChunk(const size_t size, size_t alignment = 0) char* InitializeChunk(const size_t size, size_t alignment = 0)
{ {
if (size == 0) { if (size == 0) {
fMeta.fSize = 0; fSize = 0;
return fLocalPtr; return fLocalPtr;
} }
char* ptr = fManager.Allocate(size, alignment); char* ptr = fManager.Allocate(size, alignment);
fMeta.fHandle = fManager.GetHandleFromAddress(ptr, fMeta.fSegmentId); fHandle = fManager.GetHandleFromAddress(ptr, fSegmentId);
fMeta.fSize = size; fSize = size;
fLocalPtr = ShmHeader::UserPtr(ptr); fLocalPtr = ShmHeader::UserPtr(ptr);
return fLocalPtr; return fLocalPtr;
} }
void Deallocate() void Deallocate()
{ {
if (fMeta.fHandle >= 0 && !fQueued) { if (fHandle >= 0 && !fQueued) {
if (fMeta.fManaged) { // managed segment if (fManaged) { // managed segment
fManager.GetSegment(fMeta.fSegmentId); fManager.GetSegment(fSegmentId);
uint16_t refCount = ShmHeader::DecrementRefCount(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId)); uint16_t refCount = ShmHeader::DecrementRefCount(fManager.GetAddressFromHandle(fHandle, fSegmentId));
if (refCount == 1) { if (refCount == 1) {
fManager.Deallocate(fMeta.fHandle, fMeta.fSegmentId); fManager.Deallocate(fHandle, fSegmentId);
} }
} else { // unmanaged region } else { // unmanaged region
if (fMeta.fShared >= 0) { if (fShared >= 0) {
// make sure segment is initialized in this transport fRegionPtr = fManager.GetRegionFromCache(fRegionId);
fManager.GetSegment(fMeta.fSegmentId); if (!fRegionPtr) {
// release unmanaged region block if ref count is one throw TransportError(tools::ToString("Cannot get unmanaged region with id ", fRegionId));
uint16_t refCount = ShmHeader::DecrementRefCount(fManager.GetAddressFromHandle(fMeta.fShared, fMeta.fSegmentId)); }
if (fRegionPtr->fRcSegmentSize > 0) {
uint16_t refCount = fRegionPtr->GetRefCountAddressFromHandle(fShared)->Decrement();
if (refCount == 1) { if (refCount == 1) {
fManager.Deallocate(fMeta.fShared, fMeta.fSegmentId); fRegionPtr->RemoveRefCount(*(fRegionPtr->GetRefCountAddressFromHandle(fShared)));
ReleaseUnmanagedRegionBlock(); ReleaseUnmanagedRegionBlock();
} }
} else { // if RefCount segment size is 0, get the ref count from the managed segment
// make sure segment is initialized in this transport
fManager.GetSegment(fSegmentId);
// release unmanaged region block if ref count is one
uint16_t refCount = ShmHeader::DecrementRefCount(fManager.GetAddressFromHandle(fShared, fSegmentId));
if (refCount == 1) {
fManager.Deallocate(fShared, fSegmentId);
ReleaseUnmanagedRegionBlock();
}
}
} else { } else {
ReleaseUnmanagedRegionBlock(); ReleaseUnmanagedRegionBlock();
} }
} }
} }
fMeta.fHandle = -1; fHandle = -1;
fLocalPtr = nullptr; fLocalPtr = nullptr;
fMeta.fSize = 0; fSize = 0;
} }
void ReleaseUnmanagedRegionBlock() void ReleaseUnmanagedRegionBlock()
{ {
if (!fRegionPtr) { if (!fRegionPtr) {
fRegionPtr = fManager.GetRegionFromCache(fMeta.fRegionId); fRegionPtr = fManager.GetRegionFromCache(fRegionId);
} }
if (fRegionPtr) { if (fRegionPtr) {
fRegionPtr->ReleaseBlock({fMeta.fHandle, fMeta.fSize, fMeta.fHint}); fRegionPtr->ReleaseBlock({fHandle, fSize, fHint});
} else { } else {
LOG(warn) << "region ack queue for id " << fMeta.fRegionId << " no longer exist. Not sending ack"; LOG(warn) << "region ack queue for id " << fRegionId << " no longer exist. Not sending ack";
} }
} }
@ -379,7 +416,6 @@ class Message final : public fair::mq::Message
{ {
try { try {
Deallocate(); Deallocate();
fAlignment = 0;
fManager.DecrementMsgCounter(); fManager.DecrementMsgCounter();
} catch (SharedMemoryError& sme) { } catch (SharedMemoryError& sme) {
LOG(error) << "error closing message: " << sme.what(); LOG(error) << "error closing message: " << sme.what();

View File

@ -30,6 +30,7 @@
#include <ctime> #include <ctime>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <variant>
#include <poll.h> #include <poll.h>
@ -74,7 +75,7 @@ Monitor::Monitor(string shmId, bool selfDestruct, bool interactive, bool viewOnl
{ {
if (!fViewOnly) { if (!fViewOnly) {
try { try {
bipc::named_mutex monitorStatus(bipc::create_only, string("fmq_" + fShmId + "_ms").c_str()); bipc::named_mutex monitorStatus(bipc::create_only, MakeShmName(fShmId, "ms").c_str());
} catch (bie&) { } catch (bie&) {
if (fInteractive) { if (fInteractive) {
LOG(error) << "fairmq-shmmonitor for shm id " << fShmId << " is already running. Try `fairmq-shmmonitor --cleanup --shmid " << fShmId << "`, or run in view-only mode (-v)"; LOG(error) << "fairmq-shmmonitor for shm id " << fShmId << " is already running. Try `fairmq-shmmonitor --cleanup --shmid " << fShmId << "`, or run in view-only mode (-v)";
@ -132,7 +133,7 @@ void Monitor::Watch()
using namespace boost::interprocess; using namespace boost::interprocess;
try { try {
managed_shared_memory managementSegment(open_read_only, std::string("fmq_" + fShmId + "_mng").c_str()); managed_shared_memory managementSegment(open_read_only, MakeShmName(fShmId, "mng").c_str());
fSeenOnce = true; fSeenOnce = true;
@ -180,11 +181,11 @@ bool Monitor::PrintShm(const ShmId& shmId)
using namespace boost::interprocess; using namespace boost::interprocess;
try { try {
managed_shared_memory managementSegment(open_read_only, std::string("fmq_" + shmId.shmId + "_mng").c_str()); managed_shared_memory managementSegment(open_read_only, MakeShmName(shmId.shmId, "mng").c_str());
VoidAlloc allocInstance(managementSegment.get_segment_manager()); VoidAlloc allocInstance(managementSegment.get_segment_manager());
Uint16SegmentInfoHashMap* shmSegments = managementSegment.find<Uint16SegmentInfoHashMap>(unique_instance).first; Uint16SegmentInfoHashMap* shmSegments = managementSegment.find<Uint16SegmentInfoHashMap>(unique_instance).first;
std::unordered_map<uint16_t, boost::variant<RBTreeBestFitSegment, SimpleSeqFitSegment>> segments; std::unordered_map<uint16_t, std::variant<RBTreeBestFitSegment, SimpleSeqFitSegment>> segments;
Uint16RegionInfoHashMap* shmRegions = managementSegment.find<Uint16RegionInfoHashMap>(unique_instance).first; Uint16RegionInfoHashMap* shmRegions = managementSegment.find<Uint16RegionInfoHashMap>(unique_instance).first;
@ -199,9 +200,9 @@ bool Monitor::PrintShm(const ShmId& shmId)
for (const auto& s : *shmSegments) { for (const auto& s : *shmSegments) {
if (s.second.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) { if (s.second.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) {
segments.emplace(s.first, RBTreeBestFitSegment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + to_string(s.first)).c_str())); segments.emplace(s.first, RBTreeBestFitSegment(open_read_only, MakeShmName(shmId.shmId, "m", s.first).c_str()));
} else { } else {
segments.emplace(s.first, SimpleSeqFitSegment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + to_string(s.first)).c_str())); segments.emplace(s.first, SimpleSeqFitSegment(open_read_only, MakeShmName(shmId.shmId, "m", s.first).c_str()));
} }
} }
@ -234,8 +235,8 @@ bool Monitor::PrintShm(const ShmId& shmId)
<< ", managed segments:\n"; << ", managed segments:\n";
for (const auto& s : segments) { for (const auto& s : segments) {
size_t free = boost::apply_visitor(SegmentFreeMemory(), s.second); size_t free = std::visit([](auto& seg){ return seg.get_free_memory(); }, s.second);
size_t total = boost::apply_visitor(SegmentSize(), s.second); size_t total = std::visit([](auto& seg){ return seg.get_size(); }, s.second);
size_t used = total - free; size_t used = total - free;
std::string msgCount; std::string msgCount;
@ -267,12 +268,21 @@ bool Monitor::PrintShm(const ShmId& shmId)
if (shmRegions && !shmRegions->empty()) { if (shmRegions && !shmRegions->empty()) {
ss << "\n unmanaged regions:"; ss << "\n unmanaged regions:";
for (const auto& r : *shmRegions) { for (const auto& [id, info] : *shmRegions) {
ss << "\n [" << r.first << "]: " << (r.second.fDestroyed ? "destroyed" : "alive"); ss << "\n [" << id << "]: " << (info.fDestroyed ? "destroyed" : "alive");
ss << ", size: " << r.second.fSize; ss << ", size: " << info.fSize;
try {
managed_shared_memory rcCountSegment(open_read_only, MakeShmName(shmId.shmId, "rrc", id).c_str());
auto size = rcCountSegment.get_size();
auto free = rcCountSegment.get_free_memory();
ss << ", rcCountSegment size: " << size << ", free: " << free << ", used: " << size - free;
} catch (bie&) {
ss << ", rcCountSegment: not found";
}
// try { // try {
// boost::interprocess::message_queue q(open_only, std::string("fmq_" + std::string(shmId) + "_rgq_" + to_string(r.first)).c_str()); // boost::interprocess::message_queue q(open_only, std::string("fmq_" + std::string(shmId) + "_rgq_" + to_string(id)).c_str());
// ss << ", ack queue: " << q.get_num_msg() << " messages"; // ss << ", ack queue: " << q.get_num_msg() << " messages";
// } catch (bie&) { // } catch (bie&) {
// ss << ", ack queue: not found"; // ss << ", ack queue: not found";
@ -325,7 +335,7 @@ void Monitor::CheckHeartbeats()
while (!fTerminating) { while (!fTerminating) {
std::this_thread::sleep_for(std::chrono::milliseconds(200)); std::this_thread::sleep_for(std::chrono::milliseconds(200));
try { try {
managed_shared_memory managementSegment(open_read_only, std::string("fmq_" + fShmId + "_mng").c_str()); managed_shared_memory managementSegment(open_read_only, MakeShmName(fShmId, "mng").c_str());
Heartbeat* hb = managementSegment.find<Heartbeat>(unique_instance).first; Heartbeat* hb = managementSegment.find<Heartbeat>(unique_instance).first;
if (hb) { if (hb) {
@ -407,7 +417,7 @@ void Monitor::Interactive()
void Monitor::PrintDebugInfo(const ShmId& shmId __attribute__((unused))) void Monitor::PrintDebugInfo(const ShmId& shmId __attribute__((unused)))
{ {
#ifdef FAIRMQ_DEBUG_MODE #ifdef FAIRMQ_DEBUG_MODE
string managementSegmentName("fmq_" + shmId.shmId + "_mng"); string managementSegmentName = MakeShmName(shmId.shmId, "mng");
try { try {
bipc::managed_shared_memory managementSegment(bipc::open_only, managementSegmentName.c_str()); bipc::managed_shared_memory managementSegment(bipc::open_only, managementSegmentName.c_str());
bipc::interprocess_mutex* mtx(managementSegment.find_or_construct<bipc::interprocess_mutex>(bipc::unique_instance)()); bipc::interprocess_mutex* mtx(managementSegment.find_or_construct<bipc::interprocess_mutex>(bipc::unique_instance)());
@ -459,7 +469,7 @@ unordered_map<uint16_t, std::vector<BufferDebugInfo>> Monitor::GetDebugInfo(cons
unordered_map<uint16_t, std::vector<BufferDebugInfo>> result; unordered_map<uint16_t, std::vector<BufferDebugInfo>> result;
#ifdef FAIRMQ_DEBUG_MODE #ifdef FAIRMQ_DEBUG_MODE
string managementSegmentName("fmq_" + shmId.shmId + "_mng"); string managementSegmentName = MakeShmName(shmId.shmId, "mng");
try { try {
bipc::managed_shared_memory managementSegment(bipc::open_only, managementSegmentName.c_str()); bipc::managed_shared_memory managementSegment(bipc::open_only, managementSegmentName.c_str());
bipc::interprocess_mutex* mtx(managementSegment.find_or_construct<bipc::interprocess_mutex>(bipc::unique_instance)()); bipc::interprocess_mutex* mtx(managementSegment.find_or_construct<bipc::interprocess_mutex>(bipc::unique_instance)());
@ -499,7 +509,7 @@ unsigned long Monitor::GetFreeMemory(const ShmId& shmId, uint16_t segmentId)
{ {
using namespace boost::interprocess; using namespace boost::interprocess;
try { try {
bipc::managed_shared_memory managementSegment(bipc::open_only, std::string("fmq_" + shmId.shmId + "_mng").c_str()); bipc::managed_shared_memory managementSegment(bipc::open_only, MakeShmName(shmId.shmId, "mng").c_str());
boost::interprocess::interprocess_mutex* mtx(managementSegment.find_or_construct<bipc::interprocess_mutex>(bipc::unique_instance)()); boost::interprocess::interprocess_mutex* mtx(managementSegment.find_or_construct<bipc::interprocess_mutex>(bipc::unique_instance)());
boost::interprocess::scoped_lock<bipc::interprocess_mutex> lock(*mtx); boost::interprocess::scoped_lock<bipc::interprocess_mutex> lock(*mtx);
@ -513,10 +523,10 @@ unsigned long Monitor::GetFreeMemory(const ShmId& shmId, uint16_t segmentId)
auto it = shmSegments->find(segmentId); auto it = shmSegments->find(segmentId);
if (it != shmSegments->end()) { if (it != shmSegments->end()) {
if (it->second.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) { 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()); RBTreeBestFitSegment segment(open_read_only, MakeShmName(shmId.shmId, "m", segmentId).c_str());
return segment.get_free_memory(); return segment.get_free_memory();
} else { } else {
SimpleSeqFitSegment segment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + std::to_string(segmentId)).c_str()); SimpleSeqFitSegment segment(open_read_only, MakeShmName(shmId.shmId, "m", segmentId).c_str());
return segment.get_free_memory(); return segment.get_free_memory();
} }
} else { } else {
@ -538,7 +548,7 @@ bool Monitor::SegmentIsPresent(const ShmId& shmId, uint16_t segmentId)
{ {
using namespace boost::interprocess; using namespace boost::interprocess;
try { try {
bipc::managed_shared_memory managementSegment(bipc::open_read_only, std::string("fmq_" + shmId.shmId + "_mng").c_str()); bipc::managed_shared_memory managementSegment(bipc::open_read_only, MakeShmName(shmId.shmId, "mng").c_str());
Uint16SegmentInfoHashMap* shmSegments = managementSegment.find<Uint16SegmentInfoHashMap>(unique_instance).first; Uint16SegmentInfoHashMap* shmSegments = managementSegment.find<Uint16SegmentInfoHashMap>(unique_instance).first;
if (!shmSegments) { if (!shmSegments) {
@ -550,9 +560,9 @@ bool Monitor::SegmentIsPresent(const ShmId& shmId, uint16_t segmentId)
if (it != shmSegments->end()) { if (it != shmSegments->end()) {
try { try {
if (it->second.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) { 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()); RBTreeBestFitSegment segment(open_read_only, MakeShmName(shmId.shmId, "m", segmentId).c_str());
} else { } else {
SimpleSeqFitSegment segment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + std::to_string(segmentId)).c_str()); SimpleSeqFitSegment segment(open_read_only, MakeShmName(shmId.shmId, "m", segmentId).c_str());
} }
} catch (bie&) { } catch (bie&) {
LOG(error) << "Could not find segment with id '" << segmentId << "' for shmId '" << shmId.shmId << "'"; LOG(error) << "Could not find segment with id '" << segmentId << "' for shmId '" << shmId.shmId << "'";
@ -579,7 +589,7 @@ bool Monitor::RegionIsPresent(const ShmId& shmId, uint16_t regionId)
{ {
using namespace boost::interprocess; using namespace boost::interprocess;
try { try {
bipc::managed_shared_memory managementSegment(bipc::open_read_only, std::string("fmq_" + shmId.shmId + "_mng").c_str()); bipc::managed_shared_memory managementSegment(bipc::open_read_only, MakeShmName(shmId.shmId, "mng").c_str());
Uint16RegionInfoHashMap* shmRegions = managementSegment.find<Uint16RegionInfoHashMap>(bipc::unique_instance).first; Uint16RegionInfoHashMap* shmRegions = managementSegment.find<Uint16RegionInfoHashMap>(bipc::unique_instance).first;
if (!shmRegions) { if (!shmRegions) {
@ -587,7 +597,7 @@ bool Monitor::RegionIsPresent(const ShmId& shmId, uint16_t regionId)
return false; return false;
} }
std::string regionFileName("fmq_" + shmId.shmId + "_rg_" + to_string(regionId)); std::string regionFileName(MakeShmName(shmId.shmId, "rg", regionId));
auto it = shmRegions->find(regionId); auto it = shmRegions->find(regionId);
if (it != shmRegions->end()) { if (it != shmRegions->end()) {
@ -655,7 +665,7 @@ std::vector<std::pair<std::string, bool>> Monitor::Cleanup(const ShmId& shmIdT,
LOG(info) << "Cleaning up for shared memory id '" << shmId << "'..."; LOG(info) << "Cleaning up for shared memory id '" << shmId << "'...";
} }
string managementSegmentName("fmq_" + shmId + "_mng"); string managementSegmentName = MakeShmName(shmId, "mng");
try { try {
bipc::managed_shared_memory managementSegment(bipc::open_read_only, managementSegmentName.c_str()); bipc::managed_shared_memory managementSegment(bipc::open_read_only, managementSegmentName.c_str());
@ -673,11 +683,12 @@ std::vector<std::pair<std::string, bool>> Monitor::Cleanup(const ShmId& shmIdT,
LOG(info) << "Found UnmanagedRegion with id: " << id << ", path: '" << path << "', flags: " << flags << ", fDestroyed: " << info.fDestroyed << "."; LOG(info) << "Found UnmanagedRegion with id: " << id << ", path: '" << path << "', flags: " << flags << ", fDestroyed: " << info.fDestroyed << ".";
} }
if (!path.empty()) { if (!path.empty()) {
result.emplace_back(Remove<bipc::file_mapping>(path + "fmq_" + shmId + "_rg_" + to_string(id), verbose)); result.emplace_back(Remove<bipc::file_mapping>(path + MakeShmName(shmId, "rg", id), verbose));
} else { } else {
result.emplace_back(Remove<bipc::shared_memory_object>("fmq_" + shmId + "_rg_" + to_string(id), verbose)); result.emplace_back(Remove<bipc::shared_memory_object>(MakeShmName(shmId, "rg", id), verbose));
} }
result.emplace_back(Remove<bipc::message_queue>("fmq_" + shmId + "_rgq_" + to_string(id), verbose)); result.emplace_back(Remove<bipc::message_queue>(MakeShmName(shmId, "rgq", id), verbose));
result.emplace_back(Remove<bipc::shared_memory_object>(MakeShmName(shmId, "rrc", id), verbose));
} }
} }
@ -687,7 +698,7 @@ std::vector<std::pair<std::string, bool>> Monitor::Cleanup(const ShmId& shmIdT,
LOG(info) << "Found " << shmSegments->size() << " managed segments..."; LOG(info) << "Found " << shmSegments->size() << " managed segments...";
} }
for (const auto& segment : *shmSegments) { for (const auto& segment : *shmSegments) {
result.emplace_back(Remove<bipc::shared_memory_object>("fmq_" + shmId + "_m_" + to_string(segment.first), verbose)); result.emplace_back(Remove<bipc::shared_memory_object>(MakeShmName(shmId, "m", segment.first), verbose));
} }
} else { } else {
if (verbose) { if (verbose) {
@ -717,7 +728,7 @@ std::vector<std::pair<std::string, bool>> Monitor::Cleanup(const SessionId& sess
std::vector<std::pair<std::string, bool>> Monitor::CleanupFull(const ShmId& shmId, bool verbose /* = true */) std::vector<std::pair<std::string, bool>> Monitor::CleanupFull(const ShmId& shmId, bool verbose /* = true */)
{ {
auto result = Cleanup(shmId, verbose); auto result = Cleanup(shmId, verbose);
result.emplace_back(Remove<bipc::named_mutex>("fmq_" + shmId.shmId + "_ms", verbose)); result.emplace_back(Remove<bipc::named_mutex>(MakeShmName(shmId.shmId, "ms"), verbose));
return result; return result;
} }
@ -737,7 +748,7 @@ void Monitor::ResetContent(const ShmId& shmIdT, bool verbose /* = true */)
cout << "Resetting segments content for shared memory id '" << shmId << "'..." << endl; cout << "Resetting segments content for shared memory id '" << shmId << "'..." << endl;
} }
string managementSegmentName("fmq_" + shmId + "_mng"); string managementSegmentName = MakeShmName(shmId, "mng");
try { try {
using namespace boost::interprocess; using namespace boost::interprocess;
managed_shared_memory managementSegment(open_only, managementSegmentName.c_str()); managed_shared_memory managementSegment(open_only, managementSegmentName.c_str());
@ -745,18 +756,18 @@ void Monitor::ResetContent(const ShmId& shmIdT, bool verbose /* = true */)
Uint16SegmentInfoHashMap* segmentInfos = managementSegment.find<Uint16SegmentInfoHashMap>(unique_instance).first; Uint16SegmentInfoHashMap* segmentInfos = managementSegment.find<Uint16SegmentInfoHashMap>(unique_instance).first;
if (segmentInfos) { if (segmentInfos) {
cout << "Found info for " << segmentInfos->size() << " managed segments" << endl; cout << "Found info for " << segmentInfos->size() << " managed segments" << endl;
for (const auto& s : *segmentInfos) { for (const auto& [id, info] : *segmentInfos) {
if (verbose) { if (verbose) {
cout << "Resetting content of segment '" << "fmq_" << shmId << "_m_" << s.first << "'..." << endl; cout << "Resetting content of segment '" << MakeShmName(shmId, "m", id) << "'..." << endl;
} }
try { try {
if (s.second.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) { if (info.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) {
RBTreeBestFitSegment segment(open_only, std::string("fmq_" + shmId + "_m_" + to_string(s.first)).c_str()); RBTreeBestFitSegment segment(open_only, MakeShmName(shmId, "m", id).c_str());
void* ptr = segment.get_segment_manager(); void* ptr = segment.get_segment_manager();
size_t size = segment.get_segment_manager()->get_size(); size_t size = segment.get_segment_manager()->get_size();
new(ptr) segment_manager<char, rbtree_best_fit<mutex_family, offset_ptr<void>>, null_index>(size); new(ptr) segment_manager<char, rbtree_best_fit<mutex_family, offset_ptr<void>>, null_index>(size);
} else { } else {
SimpleSeqFitSegment segment(open_only, std::string("fmq_" + shmId + "_m_" + to_string(s.first)).c_str()); SimpleSeqFitSegment segment(open_only, MakeShmName(shmId, "m", id).c_str());
void* ptr = segment.get_segment_manager(); void* ptr = segment.get_segment_manager();
size_t size = segment.get_segment_manager()->get_size(); size_t size = segment.get_segment_manager()->get_size();
new(ptr) segment_manager<char, simple_seq_fit<mutex_family, offset_ptr<void>>, null_index>(size); new(ptr) segment_manager<char, simple_seq_fit<mutex_family, offset_ptr<void>>, null_index>(size);
@ -766,7 +777,7 @@ void Monitor::ResetContent(const ShmId& shmIdT, bool verbose /* = true */)
} }
} catch (bie& e) { } catch (bie& e) {
if (verbose) { if (verbose) {
cout << "Error resetting content of segment '" << std::string("fmq_" + shmId + "_m_" + to_string(s.first)) << "': " << e.what() << endl; cout << "Error resetting content of segment '" << MakeShmName(shmId, "m", id) << "': " << e.what() << endl;
} }
} }
} }
@ -778,7 +789,8 @@ void Monitor::ResetContent(const ShmId& shmIdT, bool verbose /* = true */)
if (shmRegions) { if (shmRegions) {
for (const auto& region : *shmRegions) { for (const auto& region : *shmRegions) {
uint16_t id = region.first; uint16_t id = region.first;
Remove<bipc::message_queue>("fmq_" + shmId + "_rgq_" + to_string(id), verbose); Remove<bipc::message_queue>(MakeShmName(shmId, "rgq", id), verbose);
Remove<bipc::shared_memory_object>(MakeShmName(shmId, "rrc", id), verbose);
} }
} }
} catch (bie& e) { } catch (bie& e) {
@ -807,7 +819,7 @@ void Monitor::ResetContent(const ShmId& shmIdT, const std::vector<SegmentConfig>
using namespace boost::interprocess; using namespace boost::interprocess;
std::string shmId = shmIdT.shmId; std::string shmId = shmIdT.shmId;
std::string managementSegmentName("fmq_" + shmId + "_mng"); std::string managementSegmentName = MakeShmName(shmId, "mng");
// delete management segment // delete management segment
cout << "deleting management segment" << endl; cout << "deleting management segment" << endl;
Remove<bipc::shared_memory_object>(managementSegmentName, verbose); Remove<bipc::shared_memory_object>(managementSegmentName, verbose);
@ -855,7 +867,7 @@ Monitor::~Monitor()
Cleanup(ShmId{fShmId}); Cleanup(ShmId{fShmId});
} }
if (!fViewOnly) { if (!fViewOnly) {
RemoveMutex("fmq_" + fShmId + "_ms"); RemoveMutex(MakeShmName(fShmId, "ms"));
} }
} }

View File

@ -16,6 +16,7 @@ FairMQ Shared Memory currently uses the following names to register shared memor
| `fmq_<shmId>_mng` | management segment (management data) | one of the devices | devices | | `fmq_<shmId>_mng` | management segment (management data) | one of the devices | devices |
| `fmq_<shmId>_rg_<index>` | unmanaged region(s) | one of the devices | devices with unmanaged regions | | `fmq_<shmId>_rg_<index>` | unmanaged region(s) | one of the devices | devices with unmanaged regions |
| `fmq_<shmId>_rgq_<index>` | unmanaged region queue(s) | one of the devices | devices with unmanaged regions | | `fmq_<shmId>_rgq_<index>` | unmanaged region queue(s) | one of the devices | devices with unmanaged regions |
| `fmq_<shmId>_rrc_<index>` | unmanaged region reference count pool(s) | one of the devices | devices with unmanaged regions |
| `fmq_<shmId>_ms` | shmmonitor status | shmmonitor | devices, shmmonitor | | `fmq_<shmId>_ms` | shmmonitor status | shmmonitor | devices, shmmonitor |
The shmId is generated out of session id and user id. The shmId is generated out of session id and user id.

View File

@ -10,11 +10,11 @@
#include <fairmq/shmem/Common.h> #include <fairmq/shmem/Common.h>
#include <fairmq/shmem/Monitor.h> #include <fairmq/shmem/Monitor.h>
#include <fairmq/Transports.h>
#include <boost/variant.hpp>
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <variant>
namespace fair::mq::shmem namespace fair::mq::shmem
{ {
@ -29,27 +29,23 @@ struct Segment
friend class Monitor; friend class Monitor;
Segment(const std::string& shmId, uint16_t id, size_t size, SimpleSeqFit) Segment(const std::string& shmId, uint16_t id, size_t size, SimpleSeqFit)
: fSegment(SimpleSeqFitSegment(boost::interprocess::open_or_create, : fSegment(SimpleSeqFitSegment(boost::interprocess::open_or_create, MakeShmName(shmId, "m", id).c_str(), size))
std::string("fmq_" + shmId + "_m_" + std::to_string(id)).c_str(),
size))
{ {
Register(shmId, id, AllocationAlgorithm::simple_seq_fit); Register(shmId, id, AllocationAlgorithm::simple_seq_fit);
} }
Segment(const std::string& shmId, uint16_t id, size_t size, RBTreeBestFit) Segment(const std::string& shmId, uint16_t id, size_t size, RBTreeBestFit)
: fSegment(RBTreeBestFitSegment(boost::interprocess::open_or_create, : fSegment(RBTreeBestFitSegment(boost::interprocess::open_or_create, MakeShmName(shmId, "m", id).c_str(), size))
std::string("fmq_" + shmId + "_m_" + std::to_string(id)).c_str(),
size))
{ {
Register(shmId, id, AllocationAlgorithm::rbtree_best_fit); Register(shmId, id, AllocationAlgorithm::rbtree_best_fit);
} }
size_t GetSize() const { return boost::apply_visitor(SegmentSize(), fSegment); } size_t GetSize() const { return std::visit([](auto& s){ return s.get_size(); }, fSegment); }
void* GetData() { return boost::apply_visitor(SegmentAddress(), fSegment); } void* GetData() { return std::visit([](auto& s){ return s.get_address(); }, fSegment); }
size_t GetFreeMemory() const { return boost::apply_visitor(SegmentFreeMemory(), fSegment); } size_t GetFreeMemory() const { return std::visit([](auto& s){ return s.get_free_memory(); }, fSegment); }
void Zero() { boost::apply_visitor(SegmentMemoryZeroer(), fSegment); } void Zero() { std::visit([](auto& s){ return s.zero_free_memory(); }, fSegment); }
void Lock() void Lock()
{ {
if (mlock(GetData(), GetSize()) == -1) { if (mlock(GetData(), GetSize()) == -1) {
@ -59,16 +55,16 @@ struct Segment
static void Remove(const std::string& shmId, uint16_t id) static void Remove(const std::string& shmId, uint16_t id)
{ {
Monitor::RemoveObject("fmq_" + shmId + "_m_" + std::to_string(id)); Monitor::RemoveObject(MakeShmName(shmId, "m", id));
} }
private: private:
boost::variant<RBTreeBestFitSegment, SimpleSeqFitSegment> fSegment; std::variant<RBTreeBestFitSegment, SimpleSeqFitSegment> fSegment;
static void Register(const std::string& shmId, uint16_t id, AllocationAlgorithm allocAlgo) static void Register(const std::string& shmId, uint16_t id, AllocationAlgorithm allocAlgo)
{ {
using namespace boost::interprocess; using namespace boost::interprocess;
managed_shared_memory mngSegment(open_or_create, std::string("fmq_" + shmId + "_mng").c_str(), kManagementSegmentSize); managed_shared_memory mngSegment(open_or_create, MakeShmName(shmId, "mng").c_str(), kManagementSegmentSize);
VoidAlloc alloc(mngSegment.get_segment_manager()); VoidAlloc alloc(mngSegment.get_segment_manager());
Uint16SegmentInfoHashMap* shmSegments = mngSegment.find_or_construct<Uint16SegmentInfoHashMap>(unique_instance)(alloc); Uint16SegmentInfoHashMap* shmSegments = mngSegment.find_or_construct<Uint16SegmentInfoHashMap>(unique_instance)(alloc);

View File

@ -129,9 +129,11 @@ class Socket final : public fair::mq::Socket
} }
int elapsed = 0; int elapsed = 0;
MetaHeader meta{ shmMsg->fSize, shmMsg->fHint, shmMsg->fHandle, shmMsg->fShared, shmMsg->fRegionId, shmMsg->fSegmentId, shmMsg->fManaged };
// meta msg format: | MetaHeader | padded to fMetadataMsgSize | // meta msg format: | MetaHeader | padded to fMetadataMsgSize |
zmq::ZMsg zmqMsg(std::max(fMetadataMsgSize, sizeof(MetaHeader))); zmq::ZMsg zmqMsg(std::max(fMetadataMsgSize, sizeof(MetaHeader)));
std::memcpy(zmqMsg.Data(), &(shmMsg->fMeta), sizeof(MetaHeader)); std::memcpy(zmqMsg.Data(), &meta, sizeof(MetaHeader));
while (true) { while (true) {
int nbytes = zmq_msg_send(zmqMsg.Msg(), fSocket, flags); int nbytes = zmq_msg_send(zmqMsg.Msg(), fSocket, flags);
@ -167,7 +169,8 @@ class Socket final : public fair::mq::Socket
while (true) { while (true) {
Message* shmMsg = static_cast<Message*>(msg.get()); Message* shmMsg = static_cast<Message*>(msg.get());
int nbytes = zmq_recv(fSocket, &(shmMsg->fMeta), sizeof(MetaHeader), flags); MetaHeader meta;
int nbytes = zmq_recv(fSocket, &meta, sizeof(MetaHeader), flags);
if (nbytes > 0) { if (nbytes > 0) {
// check for number of received messages. must be 1 // check for number of received messages. must be 1
if (static_cast<std::size_t>(nbytes) < sizeof(MetaHeader)) { if (static_cast<std::size_t>(nbytes) < sizeof(MetaHeader)) {
@ -177,6 +180,8 @@ class Socket final : public fair::mq::Socket
"Expected minimum size of ", sizeof(MetaHeader), " bytes, received ", nbytes)); "Expected minimum size of ", sizeof(MetaHeader), " bytes, received ", nbytes));
} }
shmMsg->SetMeta(meta);
size_t size = shmMsg->GetSize(); size_t size = shmMsg->GetSize();
fBytesRx += size; fBytesRx += size;
++fMessagesRx; ++fMessagesRx;
@ -195,7 +200,7 @@ class Socket final : public fair::mq::Socket
} }
} }
int64_t Send(std::vector<MessagePtr>& msgVec, int timeout = -1) override int64_t Send(Parts::container& msgVec, int timeout = -1) override
{ {
int flags = 0; int flags = 0;
if (timeout == 0) { if (timeout == 0) {
@ -218,7 +223,8 @@ class Socket final : public fair::mq::Socket
} }
assertm(dynamic_cast<shmem::Message*>(msgPtr), "given mq::Message is a shmem::Message"); // NOLINT 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) auto shmMsg = static_cast<shmem::Message*>(msgPtr); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
std::memcpy(metas++, &(shmMsg->fMeta), sizeof(MetaHeader)); MetaHeader meta{ shmMsg->fSize, shmMsg->fHint, shmMsg->fHandle, shmMsg->fShared, shmMsg->fRegionId, shmMsg->fSegmentId, shmMsg->fManaged };
std::memcpy(metas++, &meta, sizeof(MetaHeader));
} }
while (true) { while (true) {
@ -230,7 +236,7 @@ class Socket final : public fair::mq::Socket
for (auto& msg : msgVec) { for (auto& msg : msgVec) {
Message* shmMsg = static_cast<Message*>(msg.get()); Message* shmMsg = static_cast<Message*>(msg.get());
shmMsg->fQueued = true; shmMsg->fQueued = true;
totalSize += shmMsg->fMeta.fSize; totalSize += shmMsg->fSize;
} }
// store statistics on how many messages have been sent // store statistics on how many messages have been sent
@ -254,7 +260,7 @@ class Socket final : public fair::mq::Socket
return static_cast<int>(TransferCode::error); return static_cast<int>(TransferCode::error);
} }
int64_t Receive(std::vector<MessagePtr>& msgVec, int timeout = -1) override int64_t Receive(Parts::container& msgVec, int timeout = -1) override
{ {
int flags = 0; int flags = 0;
if (timeout == 0) { if (timeout == 0) {

View File

@ -13,6 +13,7 @@
#include <fairmq/shmem/Monitor.h> #include <fairmq/shmem/Monitor.h>
#include <fairmq/tools/Strings.h> #include <fairmq/tools/Strings.h>
#include <fairmq/UnmanagedRegion.h> #include <fairmq/UnmanagedRegion.h>
#include <fairmq/Transports.h>
#include <fairlogger/Logger.h> #include <fairlogger/Logger.h>
@ -59,11 +60,13 @@ struct UnmanagedRegion
, fRemoveOnDestruction(cfg.removeOnDestruction) , fRemoveOnDestruction(cfg.removeOnDestruction)
, fLinger(cfg.linger) , fLinger(cfg.linger)
, fStopAcks(false) , fStopAcks(false)
, fName("fmq_" + shmId + "_rg_" + std::to_string(cfg.id.value())) , fName(MakeShmName(shmId, "rg", cfg.id.value()))
, fQueueName("fmq_" + shmId + "_rgq_" + std::to_string(cfg.id.value())) , fQueueName(MakeShmName(shmId, "rgq", cfg.id.value()))
, fRefCountSegmentName(MakeShmName(shmId, "rrc", cfg.id.value()))
, fShmemObject() , fShmemObject()
, fFile(nullptr) , fFile(nullptr)
, fFileMapping() , fFileMapping()
, fRcSegmentSize(cfg.rcSegmentSize)
, fQueue(nullptr) , fQueue(nullptr)
, fCallback(nullptr) , fCallback(nullptr)
, fBulkCallback(nullptr) , fBulkCallback(nullptr)
@ -145,11 +148,13 @@ struct UnmanagedRegion
LOG(debug) << "Successfully zeroed free memory of region " << id << "."; LOG(debug) << "Successfully zeroed free memory of region " << id << ".";
} }
InitializeRefCountSegment(fRcSegmentSize);
if (fControlling && created) { if (fControlling && created) {
Register(shmId, cfg); Register(shmId, cfg);
} }
LOG(debug) << (created ? "Created" : "Opened") << " unmanaged shared memory region: " << fName << " (" << (fControlling ? "controller" : "viewer") << ")"; LOG(debug) << (created ? "Created" : "Opened") << " unmanaged shared memory region: " << fName << " (" << (fControlling ? "controller" : "viewer") << "), refCount segment size: " << fRcSegmentSize;
} }
UnmanagedRegion() = delete; UnmanagedRegion() = delete;
@ -186,6 +191,19 @@ struct UnmanagedRegion
bool RemoveOnDestruction() { return fRemoveOnDestruction; } bool RemoveOnDestruction() { return fRemoveOnDestruction; }
RefCount& MakeRefCount(uint16_t initialCount = 1)
{
RefCount* refCount = fRefCountPool->allocate_one().get();
new (refCount) RefCount(initialCount);
return *refCount;
}
void RemoveRefCount(RefCount& refCount)
{
refCount.~RefCount();
fRefCountPool->deallocate_one(&refCount);
}
~UnmanagedRegion() ~UnmanagedRegion()
{ {
LOG(debug) << "~UnmanagedRegion(): " << fName << " (" << (fControlling ? "controller" : "viewer") << ")"; LOG(debug) << "~UnmanagedRegion(): " << fName << " (" << (fControlling ? "controller" : "viewer") << ")";
@ -208,6 +226,11 @@ struct UnmanagedRegion
if (Monitor::RemoveFileMapping(fName.c_str())) { if (Monitor::RemoveFileMapping(fName.c_str())) {
LOG(trace) << "File mapping '" << fName << "' destroyed."; LOG(trace) << "File mapping '" << fName << "' destroyed.";
} }
if (fRefCountSegment) {
if (Monitor::RemoveObject(fRefCountSegmentName)) {
LOG(trace) << "Ref Count Segment '" << fRefCountSegmentName << "' destroyed.";
}
}
} else { } else {
LOG(debug) << "Skipping removal of " << fName << " unmanaged region, because RegionConfig::removeOnDestruction is false"; LOG(debug) << "Skipping removal of " << fName << " unmanaged region, because RegionConfig::removeOnDestruction is false";
} }
@ -235,6 +258,7 @@ struct UnmanagedRegion
std::atomic<bool> fStopAcks; std::atomic<bool> fStopAcks;
std::string fName; std::string fName;
std::string fQueueName; std::string fQueueName;
std::string fRefCountSegmentName;
boost::interprocess::shared_memory_object fShmemObject; boost::interprocess::shared_memory_object fShmemObject;
FILE* fFile; FILE* fFile;
boost::interprocess::file_mapping fFileMapping; boost::interprocess::file_mapping fFileMapping;
@ -244,7 +268,10 @@ struct UnmanagedRegion
std::condition_variable fBlockSendCV; std::condition_variable fBlockSendCV;
std::vector<RegionBlock> fBlocksToFree; std::vector<RegionBlock> fBlocksToFree;
const std::size_t fAckBunchSize = 256; const std::size_t fAckBunchSize = 256;
uint64_t fRcSegmentSize;
std::unique_ptr<boost::interprocess::message_queue> fQueue; std::unique_ptr<boost::interprocess::message_queue> fQueue;
std::unique_ptr<boost::interprocess::managed_shared_memory> fRefCountSegment;
std::unique_ptr<RefCountPool> fRefCountPool;
std::thread fAcksReceiver; std::thread fAcksReceiver;
std::thread fAcksSender; std::thread fAcksSender;
@ -262,7 +289,7 @@ struct UnmanagedRegion
{ {
using namespace boost::interprocess; using namespace boost::interprocess;
LOG(debug) << "Registering unmanaged shared memory region with id " << cfg.id.value(); 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); managed_shared_memory mngSegment(open_or_create, MakeShmName(shmId, "mng").c_str(), kManagementSegmentSize);
VoidAlloc alloc(mngSegment.get_segment_manager()); VoidAlloc alloc(mngSegment.get_segment_manager());
Uint16RegionInfoHashMap* shmRegions = mngSegment.find_or_construct<Uint16RegionInfoHashMap>(unique_instance)(alloc); Uint16RegionInfoHashMap* shmRegions = mngSegment.find_or_construct<Uint16RegionInfoHashMap>(unique_instance)(alloc);
@ -275,7 +302,7 @@ struct UnmanagedRegion
throw TransportError(tools::ToString("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."));
} }
shmRegions->emplace(cfg.id.value(), RegionInfo(cfg.path.c_str(), cfg.creationFlags, cfg.userFlags, cfg.size, alloc)); shmRegions->emplace(cfg.id.value(), RegionInfo(cfg.path.c_str(), cfg.creationFlags, cfg.userFlags, cfg.size, cfg.rcSegmentSize, alloc));
(eventCounter->fCount)++; (eventCounter->fCount)++;
} }
@ -294,6 +321,29 @@ struct UnmanagedRegion
} }
} }
void InitializeRefCountSegment(uint64_t size)
{
using namespace boost::interprocess;
if (!fRefCountSegment && size > 0) {
fRefCountSegment = std::make_unique<managed_shared_memory>(open_or_create, fRefCountSegmentName.c_str(), size);
LOG(trace) << "shmem: initialized ref count segment: " << fRefCountSegmentName;
fRefCountPool = std::make_unique<RefCountPool>(fRefCountSegment->get_segment_manager());
}
}
RefCount* GetRefCountAddressFromHandle(const boost::interprocess::managed_shared_memory::handle_t handle)
{
if (fRefCountPool) {
return reinterpret_cast<RefCount*>(fRefCountSegment->get_address_from_handle(handle));
}
return nullptr;
};
boost::interprocess::managed_shared_memory::handle_t HandleFromAddress(const void* ptr)
{
return fRefCountSegment->get_handle_from_address(ptr);
}
void StartAckSender() void StartAckSender()
{ {
if (!fAcksSender.joinable()) { if (!fAcksSender.joinable()) {

View File

@ -1,5 +1,5 @@
/******************************************************************************** /********************************************************************************
* Copyright (C) 2017-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Copyright (C) 2017-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* * * *
* This software is distributed under the terms of the * * This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, * * GNU Lesser General Public Licence (LGPL) version 3, *
@ -8,12 +8,12 @@
#include <fairlogger/Logger.h> #include <fairlogger/Logger.h>
#include <fairmq/tools/Network.h> #include <fairmq/tools/Network.h>
#include <fairmq/tools/Strings.h>
#ifndef _GNU_SOURCE #ifndef _GNU_SOURCE
#define _GNU_SOURCE // To get defns of NI_MAXSERV and NI_MAXHOST #define _GNU_SOURCE // To get defns of NI_MAXSERV and NI_MAXHOST
#endif #endif
#include <algorithm>
#include <array> #include <array>
#include <boost/algorithm/string.hpp> // trim #include <boost/algorithm/string.hpp> // trim
#include <boost/asio.hpp> #include <boost/asio.hpp>
@ -158,33 +158,22 @@ string getDefaultRouteNetworkInterface()
} }
string getIpFromHostname(const string& hostname) string getIpFromHostname(const string& hostname)
{ try {
boost::asio::io_context ioc; boost::asio::io_context ioc;
boost::asio::ip::tcp::resolver resolver(ioc);
using namespace boost::asio::ip; auto const result = resolver.resolve(boost::asio::ip::tcp::v4(), hostname, "");
try {
tcp::resolver resolver(ioc);
tcp::resolver::query query(hostname, "");
tcp::resolver::iterator end;
auto it = find_if(static_cast<basic_resolver_iterator<tcp>>(resolver.resolve(query)),
end,
[](const tcp::endpoint& ep) { return ep.address().is_v4(); });
if (it != end) {
stringstream ss;
ss << static_cast<tcp::endpoint>(*it).address();
return ss.str();
}
if (result.empty()) {
LOG(warn) << "could not find ipv4 address for hostname '" << hostname << "'"; LOG(warn) << "could not find ipv4 address for hostname '" << hostname << "'";
return "";
} catch (exception& e) {
LOG(error) << "could not resolve hostname '" << hostname << "', reason: " << e.what();
return ""; return "";
} }
return ToString(result.begin()->endpoint().address());
}
catch (std::exception const& ex)
{
LOG(error) << "could not resolve hostname '" << hostname << "', reason: " << ex.what();
return "";
} }
} // namespace fair::mq::tools } // namespace fair::mq::tools

View File

@ -1,5 +1,5 @@
/******************************************************************************** /********************************************************************************
* Copyright (C) 2017-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Copyright (C) 2017-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* * * *
* This software is distributed under the terms of the * * This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, * * GNU Lesser General Public Licence (LGPL) version 3, *
@ -29,7 +29,7 @@ class LinePrinter
public: public:
LinePrinter(stringstream& out, string prefix) LinePrinter(stringstream& out, string prefix)
: fOut(out) : fOut(out)
, fPrefix(move(prefix)) , fPrefix(std::move(prefix))
{} {}
// prints line with prefix on both cout (thread-safe) and output stream // prints line with prefix on both cout (thread-safe) and output stream
@ -64,22 +64,22 @@ execute_result execute(const string& cmd, const string& prefix, const string& in
p.Print(cmd); p.Print(cmd);
ba::io_service ios; ba::io_context ioc;
// containers for std_in // containers for std_in
ba::const_buffer inputBuffer(ba::buffer(input)); ba::const_buffer inputBuffer(ba::buffer(input));
bp::async_pipe inputPipe(ios); bp::async_pipe inputPipe(ioc);
// containers for std_out // containers for std_out
ba::streambuf outputBuffer; ba::streambuf outputBuffer;
bp::async_pipe outputPipe(ios); bp::async_pipe outputPipe(ioc);
// containers for std_err // containers for std_err
ba::streambuf errorBuffer; ba::streambuf errorBuffer;
bp::async_pipe errorPipe(ios); bp::async_pipe errorPipe(ioc);
const string delimiter = "\n"; const string delimiter = "\n";
ba::steady_timer inputTimer(ios); ba::steady_timer inputTimer(ioc);
inputTimer.expires_after(std::chrono::milliseconds(1000)); // NOLINT inputTimer.expires_after(std::chrono::milliseconds(1000)); // NOLINT
ba::steady_timer signalTimer(ios); ba::steady_timer signalTimer(ioc);
signalTimer.expires_after(std::chrono::milliseconds(2000)); // NOLINT signalTimer.expires_after(std::chrono::milliseconds(2000)); // NOLINT
// child process // child process
@ -154,7 +154,7 @@ execute_result execute(const string& cmd, const string& prefix, const string& in
}; };
ba::async_read_until(errorPipe, errorBuffer, delimiter, onStdErr); ba::async_read_until(errorPipe, errorBuffer, delimiter, onStdErr);
ios.run(); ioc.run();
c.wait(); c.wait();
result.exit_code = c.exit_code(); result.exit_code = c.exit_code();

View File

@ -210,7 +210,7 @@ class Message final : public fair::mq::Message
// destroyed. Used size is applied only once in ApplyUsedSize, which is called by the socket // destroyed. Used size is applied only once in ApplyUsedSize, which is called by the socket
// before sending. This function just updates the desired size until the actual "resizing" // before sending. This function just updates the desired size until the actual "resizing"
// happens. // happens.
bool SetUsedSize(size_t size) override bool SetUsedSize(size_t size, Alignment /* alignment */ = Alignment{0}) override
{ {
if (size == GetSize()) { if (size == GetSize()) {
// nothing to do // nothing to do

View File

@ -154,7 +154,7 @@ class Socket final : public fair::mq::Socket
} }
} }
int64_t Send(std::vector<std::unique_ptr<fair::mq::Message>>& msgVec, int timeout = -1) override int64_t Send(Parts::container& msgVec, int timeout = -1) override
{ {
int flags = 0; int flags = 0;
if (timeout == 0) { if (timeout == 0) {
@ -206,7 +206,7 @@ class Socket final : public fair::mq::Socket
} }
} }
int64_t Receive(std::vector<std::unique_ptr<fair::mq::Message>>& msgVec, int timeout = -1) override int64_t Receive(Parts::container& msgVec, int timeout = -1) override
{ {
int flags = 0; int flags = 0;
if (timeout == 0) { if (timeout == 0) {
@ -225,7 +225,7 @@ class Socket final : public fair::mq::Socket
int nbytes = zmq_msg_recv(static_cast<Message*>(part.get())->GetMessage(), fSocket, flags); int nbytes = zmq_msg_recv(static_cast<Message*>(part.get())->GetMessage(), fSocket, flags);
if (nbytes >= 0) { if (nbytes >= 0) {
static_cast<Message*>(part.get())->Realign(); static_cast<Message*>(part.get())->Realign();
msgVec.push_back(move(part)); msgVec.push_back(std::move(part));
totalSize += nbytes; totalSize += nbytes;
} else if (zmq_errno() == EAGAIN || zmq_errno() == EINTR) { } else if (zmq_errno() == EAGAIN || zmq_errno() == EINTR) {
if (fCtx.Interrupted()) { if (fCtx.Interrupted()) {

View File

@ -253,7 +253,7 @@ add_testsuite(Tools
LINKS FairMQ LINKS FairMQ
INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}
TIMEOUT 20 TIMEOUT 5
${environment} ${environment}
) )

View File

@ -16,6 +16,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <cstring> #include <cstring>
#include <string>
#include <vector> #include <vector>
namespace namespace
@ -101,7 +102,7 @@ TEST(MemoryResources, allocator)
size_t session{tools::UuidHash()}; size_t session{tools::UuidHash()};
ProgOptions config; ProgOptions config;
config.SetProperty<string>("session", to_string(session)); config.SetProperty<std::string>("session", to_string(session));
FactoryType factoryZMQ = TransportFactory::CreateTransportFactory("zeromq", fair::mq::tools::Uuid(), &config); FactoryType factoryZMQ = TransportFactory::CreateTransportFactory("zeromq", fair::mq::tools::Uuid(), &config);
@ -129,7 +130,7 @@ TEST(MemoryResources, getMessage)
size_t session{tools::UuidHash()}; size_t session{tools::UuidHash()};
ProgOptions config; ProgOptions config;
config.SetProperty<string>("session", to_string(session)); config.SetProperty<std::string>("session", to_string(session));
config.SetProperty<bool>("shm-monitor", true); config.SetProperty<bool>("shm-monitor", true);
FactoryType factoryZMQ = TransportFactory::CreateTransportFactory("zeromq", fair::mq::tools::Uuid(), &config); FactoryType factoryZMQ = TransportFactory::CreateTransportFactory("zeromq", fair::mq::tools::Uuid(), &config);

View File

@ -50,7 +50,7 @@ auto RunPushPullWithMsgResize(string const & transport, string const & _address,
Channel push{"Push", "push", factory}; Channel push{"Push", "push", factory};
Channel pull{"Pull", "pull", factory}; Channel pull{"Pull", "pull", factory};
auto const address(tools::ToString(_address, "_", transport)); auto const address(tools::ToString(_address, "_", transport, "_", config.GetProperty<string>("session")));
push.Bind(address); push.Bind(address);
pull.Connect(address); pull.Connect(address);
@ -153,7 +153,7 @@ auto RunPushPullWithAlignment(string const& transport, string const& _address, b
Channel push{"Push", "push", factory}; Channel push{"Push", "push", factory};
Channel pull{"Pull", "pull", factory}; Channel pull{"Pull", "pull", factory};
auto const address(tools::ToString(_address, "_", transport)); auto const address(tools::ToString(_address, "_", transport, "_", config.GetProperty<string>("session")));
push.Bind(address); push.Bind(address);
pull.Connect(address); pull.Connect(address);
@ -211,7 +211,7 @@ auto EmptyMessage(string const& transport, string const& _address, bool expanded
Channel push{"Push", "push", factory}; Channel push{"Push", "push", factory};
Channel pull{"Pull", "pull", factory}; Channel pull{"Pull", "pull", factory};
auto const address(tools::ToString(_address, "_", transport)); auto const address(tools::ToString(_address, "_", transport, "_", config.GetProperty<string>("session")));
push.Bind(address); push.Bind(address);
pull.Connect(address); pull.Connect(address);
@ -287,8 +287,10 @@ auto ZeroCopy(bool expandedShmMetadata = false) -> void
// The "zero copy" property of the Copy() method is an implementation detail and is not guaranteed. // The "zero copy" property of the Copy() method is an implementation detail and is not guaranteed.
// Currently it holds true for the shmem (across devices) and for zeromq (within same device) transports. // Currently it holds true for the shmem (across devices) and for zeromq (within same device) transports.
auto ZeroCopyFromUnmanaged(string const& address, bool expandedShmMetadata = false) -> void auto ZeroCopyFromUnmanaged(string const& address, bool expandedShmMetadata, uint64_t rcSegmentSize) -> void
{ {
fair::Logger::SetConsoleSeverity(fair::Severity::debug);
ProgOptions config1; ProgOptions config1;
ProgOptions config2; ProgOptions config2;
string session(tools::Uuid()); string session(tools::Uuid());
@ -311,18 +313,20 @@ auto ZeroCopyFromUnmanaged(string const& address, bool expandedShmMetadata = fal
const size_t msgSize{100}; const size_t msgSize{100};
const size_t regionSize{1000000}; const size_t regionSize{1000000};
RegionConfig cfg;
cfg.rcSegmentSize = rcSegmentSize;
tools::Semaphore blocker; tools::Semaphore blocker;
auto region = factory1->CreateUnmanagedRegion(regionSize, [&blocker](void*, size_t, void*) { auto region = factory1->CreateUnmanagedRegion(regionSize, [&blocker](void*, size_t, void*) {
blocker.Signal(); blocker.Signal();
}); }, cfg);
{ {
Channel push("Push", "push", factory1); Channel push("Push", "push", factory1);
Channel pull("Pull", "pull", factory2); Channel pull("Pull", "pull", factory2);
push.Bind(address); push.Bind(address + "_" + session);
pull.Connect(address); pull.Connect(address + "_" + session);
const size_t offset = 100; const size_t offset = 100;
auto msg1(push.NewMessage(region, static_cast<char*>(region->GetData()), msgSize, nullptr)); auto msg1(push.NewMessage(region, static_cast<char*>(region->GetData()), msgSize, nullptr));
@ -461,12 +465,22 @@ TEST(ZeroCopy, shmem_expanded_metadata) // NOLINT
TEST(ZeroCopyFromUnmanaged, shmem) // NOLINT TEST(ZeroCopyFromUnmanaged, shmem) // NOLINT
{ {
ZeroCopyFromUnmanaged("ipc://test_zerocopy_unmanaged"); ZeroCopyFromUnmanaged("ipc://test_zerocopy_unmanaged", false, 10000000);
} }
TEST(ZeroCopyFromUnmanaged, shmem_expanded_metadata) // NOLINT TEST(ZeroCopyFromUnmanaged, shmem_expanded_metadata) // NOLINT
{ {
ZeroCopyFromUnmanaged("ipc://test_zerocopy_unmanaged", true); ZeroCopyFromUnmanaged("ipc://test_zerocopy_unmanaged_expanded", true, 10000000);
}
TEST(ZeroCopyFromUnmanaged, shmem_no_rc_segment) // NOLINT
{
ZeroCopyFromUnmanaged("ipc://test_zerocopy_unmanaged_no_rc_segment", false, 0);
}
TEST(ZeroCopyFromUnmanaged, shmem_expanded_metadata_no_rc_segment) // NOLINT
{
ZeroCopyFromUnmanaged("ipc://test_zerocopy_unmanaged_expanded_no_rc_segment", true, 0);
} }
} // namespace } // namespace

View File

@ -1,5 +1,5 @@
/******************************************************************************** /********************************************************************************
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Copyright (C) 2017-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* * * *
* This software is distributed under the terms of the * * This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, * * GNU Lesser General Public Licence (LGPL) version 3, *
@ -7,8 +7,12 @@
********************************************************************************/ ********************************************************************************/
#include "Fixture.h" #include "Fixture.h"
#include <array>
#include <condition_variable> #include <condition_variable>
#include <fairmq/Tools.h>
#include <memory>
#include <mutex> #include <mutex>
#include <string>
namespace namespace
{ {
@ -142,4 +146,27 @@ TEST_F(PluginServices, ControlStateTransitionConversions)
EXPECT_NO_THROW(mServices.ToStr(DeviceStateTransition::ErrorFound)); EXPECT_NO_THROW(mServices.ToStr(DeviceStateTransition::ErrorFound));
} }
TEST_F(PluginServices, SubscriptionThreadSafety)
{
// obviously not a perfect test, but I could segfault fmq reliably with it (without the fix)
constexpr auto attempts = 1000;
constexpr auto subscribers = 5;
std::array<std::unique_ptr<std::thread>, subscribers> threads;
auto id = 0;
for (auto& thread : threads) {
thread = std::make_unique<std::thread>([&](){
auto const subscriber = fair::mq::tools::ToString("subscriber_", id);
for (auto i = 0; i < attempts; ++i) {
mServices.SubscribeToDeviceStateChange(subscriber, [](DeviceState){});
mServices.UnsubscribeFromDeviceStateChange(subscriber);
}
});
++id;
}
for (auto& thread : threads) { thread->join(); }
}
} /* namespace */ } /* namespace */

View File

@ -35,6 +35,9 @@ auto RunSingleThreadedMultipart(string transport, string address1, string addres
config.SetProperty<size_t>("shm-metadata-msg-size", 2048); config.SetProperty<size_t>("shm-metadata-msg-size", 2048);
} }
address1 += "_" + config.GetProperty<string>("session");
address2 += "_" + config.GetProperty<string>("session");
auto factory = TransportFactory::CreateTransportFactory(transport, tools::Uuid(), &config); auto factory = TransportFactory::CreateTransportFactory(transport, tools::Uuid(), &config);
Channel push1("Push1", "push", factory); Channel push1("Push1", "push", factory);
@ -118,6 +121,8 @@ auto RunMultiThreadedMultipart(string transport, string address1, bool expandedS
config.SetProperty<size_t>("shm-metadata-msg-size", 2048); config.SetProperty<size_t>("shm-metadata-msg-size", 2048);
} }
address1 += "_" + config.GetProperty<string>("session");
auto factory = TransportFactory::CreateTransportFactory(transport, tools::Uuid(), &config); auto factory = TransportFactory::CreateTransportFactory(transport, tools::Uuid(), &config);
Channel push1("Push1", "push", factory); Channel push1("Push1", "push", factory);
@ -210,7 +215,7 @@ TEST(PushPull, Multipart_MultiThreaded_ipc_shmem) // NOLINT
TEST(PushPull, Multipart_MultiThreaded_ipc_shmem_expanded_metadata) // NOLINT TEST(PushPull, Multipart_MultiThreaded_ipc_shmem_expanded_metadata) // NOLINT
{ {
RunMultiThreadedMultipart("shmem", "ipc://test_Multipart_MultiThreaded_ipc_shmem_1", true); RunMultiThreadedMultipart("shmem", "ipc://test_Multipart_MultiThreaded_ipc_shmem__expanded_metadata_1", true);
} }
} // namespace } // namespace

View File

@ -1,28 +1,37 @@
/******************************************************************************** /********************************************************************************
* Copyright (C) 2018 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Copyright (C) 2018-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* * * *
* This software is distributed under the terms of the * * This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, * * GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" * * copied verbatim in the file "LICENSE" *
********************************************************************************/ ********************************************************************************/
#include <gtest/gtest.h>
#include <fairmq/tools/Network.h> #include <fairmq/tools/Network.h>
#include <gtest/gtest.h>
#include <string> #include <string>
namespace namespace
{ {
using namespace std; TEST(Tools, NetworkDefaultIP)
using namespace fair::mq;
TEST(Tools, Network)
{ {
string interface = fair::mq::tools::getDefaultRouteNetworkInterface(); auto const interface = fair::mq::tools::getDefaultRouteNetworkInterface();
EXPECT_NE(interface, ""); EXPECT_NE(interface, "");
string interfaceIP = fair::mq::tools::getInterfaceIP(interface); auto const interfaceIP = fair::mq::tools::getInterfaceIP(interface);
EXPECT_NE(interfaceIP, ""); EXPECT_NE(interfaceIP, "");
} }
TEST(Tools, NetworkIPv4Localhost)
{
auto const ip = fair::mq::tools::getIpFromHostname("localhost");
EXPECT_FALSE(ip.empty());
EXPECT_EQ(ip, "127.0.0.1");
}
TEST(Tools, NetworkInvalidHostname)
{
auto const ip = fair::mq::tools::getIpFromHostname("non.existent.domain.invalid");
EXPECT_TRUE(ip.empty());
}
} /* namespace */ } /* namespace */