Compare commits

..

No commits in common. "master" and "v1.7.0" have entirely different histories.

59 changed files with 610 additions and 1181 deletions

View File

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

View File

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

View File

@ -1,29 +0,0 @@
# 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

@ -1,21 +0,0 @@
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,7 +1,6 @@
{ {
"creators": [ "creators": [
{ {
"orcid": "0000-0002-8071-4497",
"name": "Al-Turany, Mohammad" "name": "Al-Turany, Mohammad"
}, },
{ {

View File

@ -1,5 +1,5 @@
################################################################################ ################################################################################
# Copyright (C) 2018-2024 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # # Copyright (C) 2018-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, #
@ -8,7 +8,8 @@
# Project ###################################################################### # Project ######################################################################
cmake_minimum_required(VERSION 3.15...3.30 FATAL_ERROR) cmake_minimum_required(VERSION 3.15 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 = "--ipc --uts --pid -B/shared" def execopts = "--net --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,18 +87,14 @@ 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: 'fedora', ver: '39', arch: 'x86_64', compiler: 'gcc-13'], [os: 'macos', ver: '12', arch: 'x86_64', compiler: 'apple-clang-14'],
[os: 'fedora', ver: '40', arch: 'x86_64', compiler: 'gcc-14'], [os: 'macos', ver: '13', arch: 'arm64', compiler: 'apple-clang-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 [-DBUILD_TESTING=ON] cmake -S fairmq_source -B fairmq_build -GNinja -DCMAKE_BUILD_TYPE=Release
cmake --build fairmq_build cmake --build fairmq_build
[ctest --test-dir fairmq_build --output-on-failure --schedule-random -j<ncpus>] # needs -DBUILD_TESTING=ON ctest --test-dir fairmq_build --output-on-failure --schedule-random -j<ncpus>
cmake --install fairmq_build --prefix $(pwd)/fairmq_install cmake --install fairmq_build --prefix $(pwd)/fairmq_install
``` ```
@ -56,24 +56,6 @@ 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-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # # Copyright (C) 2018-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, #
@ -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 "Dec 26 2024 @7d76a23") set(GTest_VERSION "Apr 8 2022 @a1cc8c55")
set(GTest_PREFIX "<bundled>") set(GTest_PREFIX "<bundled>")
endif() endif()
endif() endif()

View File

@ -6,23 +6,12 @@
"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}" @ONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${script_file}.in" "${CMAKE_CURRENT_BINARY_DIR}/${script_file}")
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" @ONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${script_file}.in" "${CMAKE_CURRENT_BINARY_DIR}/${script_file}_install")
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,6 +7,5 @@
################################################################################ ################################################################################
add_example(NAME region add_example(NAME region
DEVICE sampler processor sink keep-alive DEVICE sampler sink keep-alive
SCRIPT region region-advanced region-advanced-external
) )

View File

@ -1,95 +0,0 @@
#!/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

@ -1,80 +0,0 @@
#!/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,8 +2,16 @@
export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@ export FAIRMQ_PATH=@FAIRMQ_BIN_DIR@
transport=${1:-shmem} transport="shmem"
msgSize=${2:-1000000} msgSize="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,11 +95,10 @@ 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, cfg)); auto ret = regions.emplace(id, make_unique<fair::mq::shmem::UnmanagedRegion>(shmId, id, size));
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

@ -1,80 +0,0 @@
/********************************************************************************
* 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,7 +8,6 @@
#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>
@ -24,11 +23,8 @@ 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(fChanName, 0).Transport()->SubscribeToRegionEvents([](fair::mq::RegionInfo info) { GetChannel("data", 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
@ -46,9 +42,8 @@ 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(
fChanName, // region is created using the transport of this channel... "data", // 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
@ -64,33 +59,22 @@ struct Sampler : fair::mq::Device
void Run() override void Run() override
{ {
fair::mq::tools::RateLimiter rateLimiter(fSamplingRate);
while (!NewStatePending()) { while (!NewStatePending()) {
fair::mq::Parts parts; fair::mq::MessagePtr msg(NewMessageFor("data", // channel
// make 64 parts 0, // sub-channel
for (int i = 0; i < 64; ++i) { fRegion, // region
parts.AddPart(NewMessageFor( fRegion->GetData(), // ptr within region
fChanName, // channel fMsgSize, // offset from ptr
0, // sub-channel nullptr // hint
fRegion, // region ));
fRegion->GetData(), // ptr within region
fMsgSize, // offset from ptr
nullptr // hint
));
}
std::lock_guard<std::mutex> lock(fMtx); std::lock_guard<std::mutex> lock(fMtx);
fNumUnackedMsgs += parts.Size(); ++fNumUnackedMsgs;
if (Send(parts, fChanName, 0) > 0) { if (Send(msg, "data", 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();
}
} }
} }
@ -115,7 +99,7 @@ struct Sampler : fair::mq::Device
void ResetTask() override void ResetTask() override
{ {
fRegion.reset(); fRegion.reset();
GetChannel(fChanName, 0).Transport()->UnsubscribeFromRegionEvents(); GetChannel("data", 0).Transport()->UnsubscribeFromRegionEvents();
} }
private: private:
@ -124,24 +108,18 @@ 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,8 +22,7 @@ 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");
fChanName = fConfig->GetProperty<std::string>("chan-name"); GetChannel("data", 0).Transport()->SubscribeToRegionEvents([](RegionInfo info) {
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
@ -33,11 +32,15 @@ struct Sink : Device
void Run() override void Run() override
{ {
Channel& dataIn = GetChannel(fChanName, 0); Channel& dataInChannel = GetChannel("data", 0);
while (!NewStatePending()) { while (!NewStatePending()) {
fair::mq::Parts parts; auto msg(dataInChannel.Transport()->CreateMessage());
dataIn.Receive(parts); dataInChannel.Receive(msg);
// 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.";
@ -48,22 +51,22 @@ struct Sink : Device
void ResetTask() override void ResetTask() override
{ {
GetChannel(fChanName, 0).Transport()->UnsubscribeFromRegionEvents(); GetChannel("data", 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()(
("chan-name", bpo::value<std::string>()->default_value("data"), "name of the input channel") "max-iterations",
("max-iterations", bpo::value<uint64_t>()->default_value(0), "Maximum number of iterations of Run/ConditionalRun/OnData (0 - infinite)"); 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<Sink>(); } unique_ptr<Device> getDevice(ProgOptions& /*config*/) { return make_unique<Sink>(); }

2
extern/googletest vendored

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

View File

@ -1,5 +1,5 @@
################################################################################ ################################################################################
# Copyright (C) 2012-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # # Copyright (C) 2012-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, #
@ -63,21 +63,14 @@ 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
@ -102,6 +95,12 @@ 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
@ -119,7 +118,6 @@ 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,7 +12,6 @@
#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>
@ -384,10 +383,4 @@ 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/TransportEnum.h> #include <fairmq/Transports.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; std::string GetTransportName() const { return TransportName(fTransportType); }
/// Get channel transport type /// Get channel transport type
/// @return Returns channel transport type /// @return Returns channel transport type
mq::Transport GetTransportType() const; mq::Transport GetTransportType() const { return fTransportType; }
/// 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); void UpdateTransport(const std::string& transport) { fTransportType = TransportType(transport); Invalidate(); }
/// 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(Parts::container & msgVec) void CheckSendCompatibility(std::vector<MessagePtr>& 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(Parts::container& msgVec) void CheckReceiveCompatibility(std::vector<MessagePtr>& msgVec)
{ {
for (auto& msg : msgVec) { for (auto& msg : msgVec) {
if (fTransportType != msg->GetType()) { if (fTransportType != msg->GetType()) {

View File

@ -9,7 +9,6 @@
// 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
@ -177,6 +176,7 @@ 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,6 +208,8 @@ 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++;
} }
} }
@ -286,7 +288,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 << " on " << (*itr)->fAddress << ")"; LOG(error) << "failed to attach channel " << (*itr)->fName << " (" << (*itr)->fMethod << ")";
++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/TransportEnum.h> #include <fairmq/Transports.h>
#include <fairmq/UnmanagedRegion.h> #include <fairmq/UnmanagedRegion.h>
// logger // logger

View File

@ -1,20 +0,0 @@
/********************************************************************************
* 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-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Copyright (C) 2014-2017 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,8 +57,27 @@ 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
@ -100,58 +119,21 @@ 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};
if (fSignals.find(key) == fSignals.end()) {
// 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
auto signal = std::make_shared<Signal<E, Args...>>();
fSignals.insert(std::make_pair(key, signal));
}
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}; std::lock_guard<std::mutex> lock{fMutex};
if (fConnections.find(connectionsKey) != fConnections.end()) { if (fSignals.find(key) == fSignals.end())
fConnections.at(connectionsKey).disconnect(); {
fConnections.erase(connectionsKey); // 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
auto signal = std::make_shared<Signal<E, Args...>>();
fSignals.insert(std::make_pair(key, signal));
} }
fConnections.insert({connectionsKey, connection});
return boost::any_cast<std::shared_ptr<Signal<E, Args...>>>(fSignals.at(key));
} }
} }; /* class EventManager */
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 <memory_resource> #include <boost/container/pmr/memory_resource.hpp>
#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 = std::pmr; namespace pmr = boost::container::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/TransportEnum.h> #include <fairmq/Transports.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, Alignment alignment = Alignment{0}) = 0; virtual bool SetUsedSize(size_t size) = 0;
virtual Transport GetType() const = 0; virtual Transport GetType() const = 0;
TransportFactory* GetTransport() { return fTransport; } TransportFactory* GetTransport() { return fTransport; }
@ -76,11 +76,6 @@ 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,6 +448,3 @@ 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,7 +129,17 @@ 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
@ -265,20 +275,5 @@ 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-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Copyright (C) 2014-2018 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,6 +29,7 @@ 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(Parts::container& msgVec, int timeout = -1) = 0; virtual int64_t Send(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 Receive(std::vector<std::unique_ptr<Message>>& 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,7 +177,6 @@ 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;
@ -311,17 +310,12 @@ try {
void StateMachine::SubscribeToStateChange(const string& key, function<void(const State)> callback) void StateMachine::SubscribeToStateChange(const string& key, function<void(const State)> callback)
{ {
// Check if the key has a integer value as prefix, if yes, decode it. static_pointer_cast<FairMQFSM>(fFsm)->fStateChangeSignalsMap.insert({key, static_pointer_cast<FairMQFSM>(fFsm)->fStateChangeSignal.connect(callback)});
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);
@ -361,15 +355,12 @@ 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)
{ {
auto fsm = static_pointer_cast<FairMQFSM>(fFsm); static_pointer_cast<FairMQFSM>(fFsm)->fNewTransitionSignalsMap.insert({key, static_pointer_cast<FairMQFSM>(fFsm)->fNewTransitionSignal.connect(callback)});
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);

View File

@ -1,22 +0,0 @@
/********************************************************************************
* 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/TransportEnum.h> #include <fairmq/Transports.h>
#include <fairmq/UnmanagedRegion.h> #include <fairmq/UnmanagedRegion.h>
#include <memory> // shared_ptr #include <memory> // shared_ptr
#include <stdexcept> #include <stdexcept>

View File

@ -10,7 +10,6 @@
#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>
@ -19,6 +18,13 @@
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/TransportEnum.h> #include <fairmq/Transports.h>
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstdint> // uint32_t #include <cstdint> // uint32_t
@ -134,7 +134,6 @@ 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-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Copyright (C) 2018-2022 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-2025 GSI" #define FAIRMQ_COPYRIGHT "2012-2023 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,17 +65,13 @@ Control::Control(const string& name, Plugin::Version version, const string& main
}); });
try { try {
auto control = GetProperty<string>("control"); TakeDeviceControl();
if (control != "none") { auto control = GetProperty<string>("control");
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);
@ -146,7 +142,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), 'none', 'gui'") ("control", po::value<string>()->default_value("dynamic"), "Control mode, 'static' or 'dynamic' (aliases for dynamic are external and interactive)")
("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;
} }
@ -275,11 +271,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) {
@ -408,7 +404,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) {
@ -425,7 +421,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,8 +13,6 @@
#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>
@ -23,12 +21,10 @@
#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 <variant> #include <boost/variant.hpp>
#include <sys/types.h> #include <sys/types.h>
#include <fairmq/tools/Strings.h>
namespace fair::mq::shmem namespace fair::mq::shmem
{ {
@ -45,36 +41,6 @@ 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>;
@ -82,90 +48,6 @@ 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,
@ -174,12 +56,19 @@ enum class AllocationAlgorithm : int
struct RegionInfo struct RegionInfo
{ {
RegionInfo(const char* path, int flags, uint64_t userFlags, uint64_t size, uint64_t rcSegmentSize, const VoidAlloc& alloc) RegionInfo(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)
{} {}
@ -187,7 +76,6 @@ 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;
}; };
@ -255,6 +143,17 @@ 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
{ {
@ -320,7 +219,73 @@ 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)
@ -338,6 +303,16 @@ struct SegmentBufferShrink
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,6 +24,7 @@
#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>
@ -41,7 +42,6 @@
#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,6 +51,79 @@
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:
@ -58,7 +131,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, MakeShmName(fShmId, "mng").c_str(), kManagementSegmentSize) , fManagementSegment(boost::interprocess::open_or_create, std::string("fmq_" + 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)
@ -158,7 +231,7 @@ class Manager
bool createdSegment = false; bool createdSegment = false;
try { try {
std::string segmentName = MakeShmName(fShmId, "m", fSegmentId); std::string segmentName("fmq_" + fShmId + "_m_" + std::to_string(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
@ -193,8 +266,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: " << std::visit([](auto& s) { return s.get_size(); }, fSegments.at(fSegmentId)) << " bytes." << ". Size: " << boost::apply_visitor(SegmentSize(), fSegments.at(fSegmentId)) << " bytes."
<< " Available: " << std::visit([](auto& s) { return s.get_free_memory(); }, fSegments.at(fSegmentId)) << " bytes." << " Available: " << boost::apply_visitor(SegmentFreeMemory(), 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();
@ -232,16 +305,14 @@ 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...";
std::visit([](auto& s) { return s.zero_free_memory(); }, fSegments.at(id)); boost::apply_visitor(SegmentMemoryZeroer(), 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( if (mlock(boost::apply_visitor(SegmentAddress(), fSegments.at(id)), boost::apply_visitor(SegmentSize(), fSegments.at(id))) == -1) {
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)));
} }
@ -256,7 +327,7 @@ class Manager
{ {
using namespace boost::interprocess; using namespace boost::interprocess;
try { try {
named_mutex monitorStatus(open_only, MakeShmName(id, "ms").c_str()); named_mutex monitorStatus(open_only, std::string("fmq_" + 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...";
@ -265,7 +336,7 @@ class Manager
int numTries = 0; int numTries = 0;
do { do {
try { try {
named_mutex monitorStatus(open_only, MakeShmName(id, "ms").c_str()); named_mutex monitorStatus(open_only, std::string("fmq_" + 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&) {
@ -339,12 +410,6 @@ 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();
} }
@ -401,32 +466,31 @@ 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 {
RegionConfig cfg;
// get region info
{
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> shmLock(*fShmMtx);
RegionInfo regionInfo = fShmRegions->at(id);
cfg.id = id;
cfg.creationFlags = regionInfo.fCreationFlags;
cfg.path = regionInfo.fPath.c_str();
}
// LOG(debug) << "Located remote region with id '" << id << "', path: '" << cfg.path << "', flags: '" << cfg.creationFlags << "'";
try { auto r = fRegions.emplace(id, std::make_unique<UnmanagedRegion>(fShmId, 0, false, std::move(cfg)));
RegionConfig cfg; r.first->second->InitializeQueues();
// get region info r.first->second->StartAckSender();
{ return r.first->second.get();
boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> shmLock(*fShmMtx); } catch (std::out_of_range& oor) {
RegionInfo regionInfo = fShmRegions->at(id); LOG(error) << "Could not get remote region with id '" << id << "'. Does the region creator run with the same session id?";
cfg.id = id; LOG(error) << oor.what();
cfg.creationFlags = regionInfo.fCreationFlags; return nullptr;
cfg.rcSegmentSize = regionInfo.fRCSegmentSize; } catch (boost::interprocess::interprocess_exception& e) {
cfg.path = regionInfo.fPath.c_str(); LOG(error) << "Could not get remote region for id '" << id << "': " << e.what();
return nullptr;
} }
// LOG(debug) << "Located remote region with id '" << id << "', path: '" << cfg.path << "', flags: '" << cfg.creationFlags << "'";
auto r = fRegions.emplace(id, std::make_unique<UnmanagedRegion>(fShmId, 0, false, std::move(cfg)));
r.first->second->InitializeQueues();
r.first->second->StartAckSender();
return r.first->second.get();
} catch (std::out_of_range& oor) {
LOG(error) << "Could not get remote region with id '" << id << "'. Does the region creator run with the same session id?";
LOG(error) << oor.what();
return nullptr;
} catch (boost::interprocess::interprocess_exception& e) {
LOG(error) << "Could not get remote region for id '" << id << "': " << e.what();
return nullptr;
} }
} }
@ -465,8 +529,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 = std::visit([](auto& s) { return s.get_address(); }, fSegments.at(segmentId)); info.ptr = boost::apply_visitor(SegmentAddress(), fSegments.at(segmentId));
info.size = std::visit([](auto& s) { return s.get_size(); }, fSegments.at(segmentId)); info.size = boost::apply_visitor(SegmentSize(), 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;
@ -485,7 +549,6 @@ 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 {
@ -647,9 +710,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, MakeShmName(fShmId, "m", id).c_str())); fSegments.emplace(id, RBTreeBestFitSegment(open_only, std::string("fmq_" + fShmId + "_m_" + std::to_string(id)).c_str()));
} else { } else {
fSegments.emplace(id, SimpleSeqFitSegment(open_only, MakeShmName(fShmId, "m", id).c_str())); fSegments.emplace(id, SimpleSeqFitSegment(open_only, std::string("fmq_" + fShmId + "_m_" + std::to_string(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();
@ -661,11 +724,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 std::visit([ptr](auto& s) { return s.get_handle_from_address(ptr); }, fSegments.at(segmentId)); return boost::apply_visitor(SegmentHandleFromAddress(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 std::visit([handle](auto& s) { return reinterpret_cast<char*>(s.get_address_from_handle(handle)); }, fSegments.at(segmentId)); return boost::apply_visitor(SegmentAddressFromHandle(handle), fSegments.at(segmentId));
} }
char* Allocate(size_t size, size_t alignment = 0) char* Allocate(size_t size, size_t alignment = 0)
@ -678,32 +741,24 @@ class Manager
while (!ptr) { while (!ptr) {
try { try {
size_t segmentSize = std::visit([](auto& s) { return s.get_size(); }, fSegments.at(fSegmentId)); size_t segmentSize = boost::apply_visitor(SegmentSize(), 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 = std::visit([fullSize](auto& s) { return reinterpret_cast<char*>(s.allocate(fullSize)); }, fSegments.at(fSegmentId)); ptr = boost::apply_visitor(SegmentAllocate{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, 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))));
", 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, 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");
", 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, 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))));
", 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;
} }
@ -737,12 +792,12 @@ class Manager
} }
#endif #endif
ShmHeader::Destruct(ptr); ShmHeader::Destruct(ptr);
std::visit([ptr](auto& s) { s.deallocate(ptr); }, fSegments.at(segmentId)); boost::apply_visitor(SegmentDeallocate(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 std::visit(SegmentBufferShrink(newSize, localPtr), fSegments.at(segmentId)); return boost::apply_visitor(SegmentBufferShrink(newSize, localPtr), fSegments.at(segmentId));
} }
uint16_t GetSegmentId() const { return fSegmentId; } uint16_t GetSegmentId() const { return fSegmentId; }
@ -790,7 +845,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, std::variant<RBTreeBestFitSegment, SimpleSeqFitSegment>> fSegments; // TODO: refactor to use Segment class std::unordered_map<uint16_t, boost::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,7 +14,6 @@
#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>
@ -37,34 +36,60 @@ 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)
: Message(manager, Alignment{0}, factory)
{}
Message(Manager& manager, Alignment /* alignment */, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory) : fair::mq::Message(factory)
, fManager(manager) , fManager(manager)
, fSegmentId(fManager.GetSegmentId()) , 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)
: fair::mq::Message(factory)
, fManager(manager)
, fQueued(false)
, 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)
: Message(manager, size, Alignment{0}, factory) : fair::mq::Message(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)
, fSegmentId(fManager.GetSegmentId()) , fQueued(false)
, fMeta{0, 0, -1, -1, 0, fManager.GetSegmentId(), true}
, fAlignment(alignment.alignment)
, fRegionPtr(nullptr)
, fLocalPtr(nullptr)
{ {
InitializeChunk(size, alignment.alignment); InitializeChunk(size, fAlignment);
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)
, fSegmentId(fManager.GetSegmentId()) , fQueued(false)
, 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);
@ -80,12 +105,10 @@ 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() << ")";
@ -94,7 +117,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()) {
fHandle = (boost::interprocess::managed_shared_memory::handle_t)(reinterpret_cast<const char*>(data) - reinterpret_cast<const char*>(region->GetData())); fMeta.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");
@ -105,13 +128,10 @@ 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)
, fSize(hdr.fSize) , fQueued(false)
, fHint(hdr.fHint) , fMeta{hdr}
, fHandle(hdr.fHandle) , fRegionPtr(nullptr)
, fShared(hdr.fShared) , fLocalPtr(nullptr)
, fRegionId(hdr.fRegionId)
, fSegmentId(hdr.fSegmentId)
, fManaged(hdr.fManaged)
{ {
fManager.IncrementMsgCounter(); fManager.IncrementMsgCounter();
} }
@ -127,10 +147,11 @@ 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
@ -144,7 +165,8 @@ class Message final : public fair::mq::Message
{ {
CloseMessage(); CloseMessage();
fQueued = false; fQueued = false;
InitializeChunk(size, alignment.alignment); fAlignment = 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
@ -165,17 +187,17 @@ class Message final : public fair::mq::Message
void* GetData() const override void* GetData() const override
{ {
if (!fLocalPtr) { if (!fLocalPtr) {
if (fManaged) { if (fMeta.fManaged) {
if (fSize > 0) { if (fMeta.fSize > 0) {
fManager.GetSegment(fSegmentId); fManager.GetSegment(fMeta.fSegmentId);
fLocalPtr = ShmHeader::UserPtr(fManager.GetAddressFromHandle(fHandle, fSegmentId)); fLocalPtr = ShmHeader::UserPtr(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId));
} else { } else {
fLocalPtr = nullptr; fLocalPtr = nullptr;
} }
} else { } else {
fRegionPtr = fManager.GetRegionFromCache(fRegionId); fRegionPtr = fManager.GetRegionFromCache(fMeta.fRegionId);
if (fRegionPtr) { if (fRegionPtr) {
fLocalPtr = reinterpret_cast<char*>(fRegionPtr->GetData()) + fHandle; fLocalPtr = reinterpret_cast<char*>(fRegionPtr->GetData()) + fMeta.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;
@ -186,41 +208,37 @@ class Message final : public fair::mq::Message
return static_cast<void*>(fLocalPtr); return static_cast<void*>(fLocalPtr);
} }
size_t GetSize() const override { return fSize; } size_t GetSize() const override { return fMeta.fSize; }
bool SetUsedSize(size_t newSize, Alignment alignment = Alignment{0}) override bool SetUsedSize(size_t newSize) override
{ {
if (newSize == fSize) { if (newSize == fMeta.fSize) {
return true; return true;
} else if (newSize == 0) { } else if (newSize == 0) {
Deallocate(); Deallocate();
return true; return true;
} else if (newSize <= fSize) { } else if (newSize <= fMeta.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, fSegmentId); char* ptr = fManager.ShrinkInPlace(userOffset + newSize, oldPtr, fMeta.fSegmentId);
fLocalPtr = ShmHeader::UserPtr(ptr); fLocalPtr = ShmHeader::UserPtr(ptr);
fSize = newSize; fMeta.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 (fSize - newSize >= 1000000) { if (fMeta.fSize - newSize >= 1000000) {
if (alignment.alignment == 0) { char* ptr = fManager.Allocate(newSize, fAlignment);
// 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(fHandle, fSegmentId); fManager.Deallocate(fMeta.fHandle, fMeta.fSegmentId);
fLocalPtr = userPtr; fLocalPtr = userPtr;
fHandle = fManager.GetHandleFromAddress(ptr, fSegmentId); fMeta.fHandle = fManager.GetHandleFromAddress(ptr, fMeta.fSegmentId);
} }
fSize = newSize; fMeta.fSize = newSize;
return true; return true;
} }
} catch (boost::interprocess::interprocess_exception& e) { } catch (boost::interprocess::interprocess_exception& e) {
@ -237,178 +255,123 @@ class Message final : public fair::mq::Message
uint16_t GetRefCount() const uint16_t GetRefCount() const
{ {
if (fHandle < 0) { if (fMeta.fHandle < 0) {
return 1; return 1;
} }
if (fManaged) { // managed segment if (fMeta.fManaged) { // managed segment
fManager.GetSegment(fSegmentId); fManager.GetSegment(fMeta.fSegmentId);
return ShmHeader::RefCount(fManager.GetAddressFromHandle(fHandle, fSegmentId)); return ShmHeader::RefCount(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId));
} } else { // unmanaged region
if (fShared < 0) { // UR msg is not yet shared if (fMeta.fShared < 0) { // UR msg is not yet shared
return 1; return 1;
} } else {
fRegionPtr = fManager.GetRegionFromCache(fRegionId); fManager.GetSegment(fMeta.fSegmentId);
if (!fRegionPtr) { return ShmHeader::RefCount(fManager.GetAddressFromHandle(fMeta.fShared, fMeta.fSegmentId));
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 the other message is not initialized, close this one too and return if (otherMsg.fMeta.fHandle < 0) {
if (otherMsg.fHandle < 0) { // if the other message is not initialized, close this one too and return
CloseMessage(); CloseMessage();
return; return;
} }
// if this msg is already initialized, close it first if (fMeta.fHandle >= 0) {
if (fHandle >= 0) { // if this msg is already initialized, close it first
CloseMessage(); CloseMessage();
} }
// increment ref count if (otherMsg.fMeta.fManaged) { // managed segment
if (otherMsg.fManaged) { // msg in managed segment fMeta = otherMsg.fMeta;
fManager.GetSegment(otherMsg.fSegmentId); fManager.GetSegment(fMeta.fSegmentId);
ShmHeader::IncrementRefCount(fManager.GetAddressFromHandle(otherMsg.fHandle, otherMsg.fSegmentId)); ShmHeader::IncrementRefCount(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId));
} else { // msg in unmanaged region } else { // unmanaged region
fRegionPtr = fManager.GetRegionFromCache(otherMsg.fRegionId); if (otherMsg.fMeta.fShared < 0) { // if UR msg is not yet shared
if (!fRegionPtr) { // TODO: minimize the size to 0 and don't create extra space for user buffer alignment
throw TransportError(tools::ToString("Cannot get unmanaged region with id ", otherMsg.fRegionId)); char* ptr = fManager.Allocate(2, 0);
} // point the fShared in the unmanaged region message to the refCount holder
if (fRegionPtr->fRcSegmentSize > 0) { otherMsg.fMeta.fShared = fManager.GetHandleFromAddress(ptr, fMeta.fSegmentId);
if (otherMsg.fShared < 0) { // the message needs to be able to locate in which segment the refCount is stored
// UR msg not yet shared, create the reference counting object with count 2 otherMsg.fMeta.fSegmentId = fMeta.fSegmentId;
try { // point this message to the same content as the unmanaged region message
otherMsg.fShared = fRegionPtr->HandleFromAddress(&(fRegionPtr->MakeRefCount(2))); fMeta = otherMsg.fMeta;
} catch (boost::interprocess::bad_alloc& ba) { // increment the refCount
throw RefCountBadAlloc(tools::ToString("Insufficient space in the reference count segment ", otherMsg.fRegionId, ", original exception: bad_alloc: ", ba.what())); ShmHeader::IncrementRefCount(ptr);
} } else { // if the UR msg is already shared
} else { fMeta = otherMsg.fMeta;
fRegionPtr->GetRefCountAddressFromHandle(otherMsg.fShared)->Increment(); fManager.GetSegment(fMeta.fSegmentId);
} ShmHeader::IncrementRefCount(fManager.GetAddressFromHandle(fMeta.fShared, fMeta.fSegmentId));
} 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);
// point the fShared in the unmanaged region message to the refCount holder
otherMsg.fShared = fManager.GetHandleFromAddress(ptr, fSegmentId);
// the message needs to be able to locate in which segment the refCount is stored
otherMsg.fSegmentId = fSegmentId;
ShmHeader::IncrementRefCount(ptr);
} else { // if the UR msg is already shared
fManager.GetSegment(otherMsg.fSegmentId);
ShmHeader::IncrementRefCount(fManager.GetAddressFromHandle(otherMsg.fShared, otherMsg.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;
mutable UnmanagedRegion* fRegionPtr = nullptr; bool fQueued;
mutable char* fLocalPtr = nullptr; MetaHeader fMeta;
size_t fSize = 0; // size of the shm buffer size_t fAlignment;
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 UnmanagedRegion* fRegionPtr;
boost::interprocess::managed_shared_memory::handle_t fHandle = -1; // handle to shm buffer, convertible to shm buffer ptr mutable char* fLocalPtr;
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) {
fSize = 0; fMeta.fSize = 0;
return fLocalPtr; return fLocalPtr;
} }
char* ptr = fManager.Allocate(size, alignment); char* ptr = fManager.Allocate(size, alignment);
fHandle = fManager.GetHandleFromAddress(ptr, fSegmentId); fMeta.fHandle = fManager.GetHandleFromAddress(ptr, fMeta.fSegmentId);
fSize = size; fMeta.fSize = size;
fLocalPtr = ShmHeader::UserPtr(ptr); fLocalPtr = ShmHeader::UserPtr(ptr);
return fLocalPtr; return fLocalPtr;
} }
void Deallocate() void Deallocate()
{ {
if (fHandle >= 0 && !fQueued) { if (fMeta.fHandle >= 0 && !fQueued) {
if (fManaged) { // managed segment if (fMeta.fManaged) { // managed segment
fManager.GetSegment(fSegmentId); fManager.GetSegment(fMeta.fSegmentId);
uint16_t refCount = ShmHeader::DecrementRefCount(fManager.GetAddressFromHandle(fHandle, fSegmentId)); uint16_t refCount = ShmHeader::DecrementRefCount(fManager.GetAddressFromHandle(fMeta.fHandle, fMeta.fSegmentId));
if (refCount == 1) { if (refCount == 1) {
fManager.Deallocate(fHandle, fSegmentId); fManager.Deallocate(fMeta.fHandle, fMeta.fSegmentId);
} }
} else { // unmanaged region } else { // unmanaged region
if (fShared >= 0) { if (fMeta.fShared >= 0) {
fRegionPtr = fManager.GetRegionFromCache(fRegionId); // make sure segment is initialized in this transport
if (!fRegionPtr) { fManager.GetSegment(fMeta.fSegmentId);
throw TransportError(tools::ToString("Cannot get unmanaged region with id ", fRegionId)); // release unmanaged region block if ref count is one
} uint16_t refCount = ShmHeader::DecrementRefCount(fManager.GetAddressFromHandle(fMeta.fShared, fMeta.fSegmentId));
if (fRegionPtr->fRcSegmentSize > 0) { if (refCount == 1) {
uint16_t refCount = fRegionPtr->GetRefCountAddressFromHandle(fShared)->Decrement(); fManager.Deallocate(fMeta.fShared, fMeta.fSegmentId);
if (refCount == 1) { ReleaseUnmanagedRegionBlock();
fRegionPtr->RemoveRefCount(*(fRegionPtr->GetRefCountAddressFromHandle(fShared)));
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();
} }
} }
} }
fHandle = -1; fMeta.fHandle = -1;
fLocalPtr = nullptr; fLocalPtr = nullptr;
fSize = 0; fMeta.fSize = 0;
} }
void ReleaseUnmanagedRegionBlock() void ReleaseUnmanagedRegionBlock()
{ {
if (!fRegionPtr) { if (!fRegionPtr) {
fRegionPtr = fManager.GetRegionFromCache(fRegionId); fRegionPtr = fManager.GetRegionFromCache(fMeta.fRegionId);
} }
if (fRegionPtr) { if (fRegionPtr) {
fRegionPtr->ReleaseBlock({fHandle, fSize, fHint}); fRegionPtr->ReleaseBlock({fMeta.fHandle, fMeta.fSize, fMeta.fHint});
} else { } else {
LOG(warn) << "region ack queue for id " << fRegionId << " no longer exist. Not sending ack"; LOG(warn) << "region ack queue for id " << fMeta.fRegionId << " no longer exist. Not sending ack";
} }
} }
@ -416,6 +379,7 @@ 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,7 +30,6 @@
#include <ctime> #include <ctime>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <variant>
#include <poll.h> #include <poll.h>
@ -75,7 +74,7 @@ Monitor::Monitor(string shmId, bool selfDestruct, bool interactive, bool viewOnl
{ {
if (!fViewOnly) { if (!fViewOnly) {
try { try {
bipc::named_mutex monitorStatus(bipc::create_only, MakeShmName(fShmId, "ms").c_str()); bipc::named_mutex monitorStatus(bipc::create_only, string("fmq_" + 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)";
@ -133,7 +132,7 @@ void Monitor::Watch()
using namespace boost::interprocess; using namespace boost::interprocess;
try { try {
managed_shared_memory managementSegment(open_read_only, MakeShmName(fShmId, "mng").c_str()); managed_shared_memory managementSegment(open_read_only, std::string("fmq_" + fShmId + "_mng").c_str());
fSeenOnce = true; fSeenOnce = true;
@ -181,11 +180,11 @@ bool Monitor::PrintShm(const ShmId& shmId)
using namespace boost::interprocess; using namespace boost::interprocess;
try { try {
managed_shared_memory managementSegment(open_read_only, MakeShmName(shmId.shmId, "mng").c_str()); managed_shared_memory managementSegment(open_read_only, std::string("fmq_" + 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, std::variant<RBTreeBestFitSegment, SimpleSeqFitSegment>> segments; std::unordered_map<uint16_t, boost::variant<RBTreeBestFitSegment, SimpleSeqFitSegment>> segments;
Uint16RegionInfoHashMap* shmRegions = managementSegment.find<Uint16RegionInfoHashMap>(unique_instance).first; Uint16RegionInfoHashMap* shmRegions = managementSegment.find<Uint16RegionInfoHashMap>(unique_instance).first;
@ -200,9 +199,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, MakeShmName(shmId.shmId, "m", s.first).c_str())); segments.emplace(s.first, RBTreeBestFitSegment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + to_string(s.first)).c_str()));
} else { } else {
segments.emplace(s.first, SimpleSeqFitSegment(open_read_only, MakeShmName(shmId.shmId, "m", s.first).c_str())); segments.emplace(s.first, SimpleSeqFitSegment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + to_string(s.first)).c_str()));
} }
} }
@ -235,8 +234,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 = std::visit([](auto& seg){ return seg.get_free_memory(); }, s.second); size_t free = boost::apply_visitor(SegmentFreeMemory(), s.second);
size_t total = std::visit([](auto& seg){ return seg.get_size(); }, s.second); size_t total = boost::apply_visitor(SegmentSize(), s.second);
size_t used = total - free; size_t used = total - free;
std::string msgCount; std::string msgCount;
@ -268,21 +267,12 @@ 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& [id, info] : *shmRegions) { for (const auto& r : *shmRegions) {
ss << "\n [" << id << "]: " << (info.fDestroyed ? "destroyed" : "alive"); ss << "\n [" << r.first << "]: " << (r.second.fDestroyed ? "destroyed" : "alive");
ss << ", size: " << info.fSize; ss << ", size: " << r.second.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(id)).c_str()); // boost::interprocess::message_queue q(open_only, std::string("fmq_" + std::string(shmId) + "_rgq_" + to_string(r.first)).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";
@ -335,7 +325,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, MakeShmName(fShmId, "mng").c_str()); managed_shared_memory managementSegment(open_read_only, std::string("fmq_" + fShmId + "_mng").c_str());
Heartbeat* hb = managementSegment.find<Heartbeat>(unique_instance).first; Heartbeat* hb = managementSegment.find<Heartbeat>(unique_instance).first;
if (hb) { if (hb) {
@ -417,7 +407,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 = MakeShmName(shmId.shmId, "mng"); string managementSegmentName("fmq_" + 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)());
@ -469,7 +459,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 = MakeShmName(shmId.shmId, "mng"); string managementSegmentName("fmq_" + 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)());
@ -509,7 +499,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, MakeShmName(shmId.shmId, "mng").c_str()); bipc::managed_shared_memory managementSegment(bipc::open_only, std::string("fmq_" + 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);
@ -523,10 +513,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, MakeShmName(shmId.shmId, "m", segmentId).c_str()); RBTreeBestFitSegment segment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + std::to_string(segmentId)).c_str());
return segment.get_free_memory(); return segment.get_free_memory();
} else { } else {
SimpleSeqFitSegment segment(open_read_only, MakeShmName(shmId.shmId, "m", segmentId).c_str()); SimpleSeqFitSegment segment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + std::to_string(segmentId)).c_str());
return segment.get_free_memory(); return segment.get_free_memory();
} }
} else { } else {
@ -548,7 +538,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, MakeShmName(shmId.shmId, "mng").c_str()); bipc::managed_shared_memory managementSegment(bipc::open_read_only, std::string("fmq_" + shmId.shmId + "_mng").c_str());
Uint16SegmentInfoHashMap* shmSegments = managementSegment.find<Uint16SegmentInfoHashMap>(unique_instance).first; Uint16SegmentInfoHashMap* shmSegments = managementSegment.find<Uint16SegmentInfoHashMap>(unique_instance).first;
if (!shmSegments) { if (!shmSegments) {
@ -560,9 +550,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, MakeShmName(shmId.shmId, "m", segmentId).c_str()); RBTreeBestFitSegment segment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + std::to_string(segmentId)).c_str());
} else { } else {
SimpleSeqFitSegment segment(open_read_only, MakeShmName(shmId.shmId, "m", segmentId).c_str()); SimpleSeqFitSegment segment(open_read_only, std::string("fmq_" + shmId.shmId + "_m_" + std::to_string(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 << "'";
@ -589,7 +579,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, MakeShmName(shmId.shmId, "mng").c_str()); bipc::managed_shared_memory managementSegment(bipc::open_read_only, std::string("fmq_" + shmId.shmId + "_mng").c_str());
Uint16RegionInfoHashMap* shmRegions = managementSegment.find<Uint16RegionInfoHashMap>(bipc::unique_instance).first; Uint16RegionInfoHashMap* shmRegions = managementSegment.find<Uint16RegionInfoHashMap>(bipc::unique_instance).first;
if (!shmRegions) { if (!shmRegions) {
@ -597,7 +587,7 @@ bool Monitor::RegionIsPresent(const ShmId& shmId, uint16_t regionId)
return false; return false;
} }
std::string regionFileName(MakeShmName(shmId.shmId, "rg", regionId)); std::string regionFileName("fmq_" + shmId.shmId + "_rg_" + to_string(regionId));
auto it = shmRegions->find(regionId); auto it = shmRegions->find(regionId);
if (it != shmRegions->end()) { if (it != shmRegions->end()) {
@ -665,7 +655,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 = MakeShmName(shmId, "mng"); string managementSegmentName("fmq_" + 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());
@ -683,12 +673,11 @@ 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 + MakeShmName(shmId, "rg", id), verbose)); result.emplace_back(Remove<bipc::file_mapping>(path + "fmq_" + shmId + "_rg_" + to_string(id), verbose));
} else { } else {
result.emplace_back(Remove<bipc::shared_memory_object>(MakeShmName(shmId, "rg", id), verbose)); result.emplace_back(Remove<bipc::shared_memory_object>("fmq_" + shmId + "_rg_" + to_string(id), verbose));
} }
result.emplace_back(Remove<bipc::message_queue>(MakeShmName(shmId, "rgq", id), verbose)); result.emplace_back(Remove<bipc::message_queue>("fmq_" + shmId + "_rgq_" + to_string(id), verbose));
result.emplace_back(Remove<bipc::shared_memory_object>(MakeShmName(shmId, "rrc", id), verbose));
} }
} }
@ -698,7 +687,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>(MakeShmName(shmId, "m", segment.first), verbose)); result.emplace_back(Remove<bipc::shared_memory_object>("fmq_" + shmId + "_m_" + to_string(segment.first), verbose));
} }
} else { } else {
if (verbose) { if (verbose) {
@ -728,7 +717,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>(MakeShmName(shmId.shmId, "ms"), verbose)); result.emplace_back(Remove<bipc::named_mutex>("fmq_" + shmId.shmId + "_ms", verbose));
return result; return result;
} }
@ -748,7 +737,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 = MakeShmName(shmId, "mng"); string managementSegmentName("fmq_" + 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());
@ -756,18 +745,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& [id, info] : *segmentInfos) { for (const auto& s : *segmentInfos) {
if (verbose) { if (verbose) {
cout << "Resetting content of segment '" << MakeShmName(shmId, "m", id) << "'..." << endl; cout << "Resetting content of segment '" << "fmq_" << shmId << "_m_" << s.first << "'..." << endl;
} }
try { try {
if (info.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) { if (s.second.fAllocationAlgorithm == AllocationAlgorithm::rbtree_best_fit) {
RBTreeBestFitSegment segment(open_only, MakeShmName(shmId, "m", id).c_str()); RBTreeBestFitSegment segment(open_only, std::string("fmq_" + shmId + "_m_" + to_string(s.first)).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, MakeShmName(shmId, "m", id).c_str()); SimpleSeqFitSegment segment(open_only, std::string("fmq_" + shmId + "_m_" + to_string(s.first)).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);
@ -777,7 +766,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 '" << MakeShmName(shmId, "m", id) << "': " << e.what() << endl; cout << "Error resetting content of segment '" << std::string("fmq_" + shmId + "_m_" + to_string(s.first)) << "': " << e.what() << endl;
} }
} }
} }
@ -789,8 +778,7 @@ 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>(MakeShmName(shmId, "rgq", id), verbose); Remove<bipc::message_queue>("fmq_" + shmId + "_rgq_" + to_string(id), verbose);
Remove<bipc::shared_memory_object>(MakeShmName(shmId, "rrc", id), verbose);
} }
} }
} catch (bie& e) { } catch (bie& e) {
@ -819,7 +807,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 = MakeShmName(shmId, "mng"); std::string managementSegmentName("fmq_" + 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);
@ -867,7 +855,7 @@ Monitor::~Monitor()
Cleanup(ShmId{fShmId}); Cleanup(ShmId{fShmId});
} }
if (!fViewOnly) { if (!fViewOnly) {
RemoveMutex(MakeShmName(fShmId, "ms")); RemoveMutex("fmq_" + fShmId + "_ms");
} }
} }

View File

@ -16,7 +16,6 @@ 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,23 +29,27 @@ 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, MakeShmName(shmId, "m", id).c_str(), size)) : fSegment(SimpleSeqFitSegment(boost::interprocess::open_or_create,
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, MakeShmName(shmId, "m", id).c_str(), size)) : fSegment(RBTreeBestFitSegment(boost::interprocess::open_or_create,
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 std::visit([](auto& s){ return s.get_size(); }, fSegment); } size_t GetSize() const { return boost::apply_visitor(SegmentSize(), fSegment); }
void* GetData() { return std::visit([](auto& s){ return s.get_address(); }, fSegment); } void* GetData() { return boost::apply_visitor(SegmentAddress(), fSegment); }
size_t GetFreeMemory() const { return std::visit([](auto& s){ return s.get_free_memory(); }, fSegment); } size_t GetFreeMemory() const { return boost::apply_visitor(SegmentFreeMemory(), fSegment); }
void Zero() { std::visit([](auto& s){ return s.zero_free_memory(); }, fSegment); } void Zero() { boost::apply_visitor(SegmentMemoryZeroer(), fSegment); }
void Lock() void Lock()
{ {
if (mlock(GetData(), GetSize()) == -1) { if (mlock(GetData(), GetSize()) == -1) {
@ -55,16 +59,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(MakeShmName(shmId, "m", id)); Monitor::RemoveObject("fmq_" + shmId + "_m_" + std::to_string(id));
} }
private: private:
std::variant<RBTreeBestFitSegment, SimpleSeqFitSegment> fSegment; boost::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, MakeShmName(shmId, "mng").c_str(), kManagementSegmentSize); managed_shared_memory mngSegment(open_or_create, std::string("fmq_" + 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,11 +129,9 @@ 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(), &meta, sizeof(MetaHeader)); std::memcpy(zmqMsg.Data(), &(shmMsg->fMeta), sizeof(MetaHeader));
while (true) { while (true) {
int nbytes = zmq_msg_send(zmqMsg.Msg(), fSocket, flags); int nbytes = zmq_msg_send(zmqMsg.Msg(), fSocket, flags);
@ -169,8 +167,7 @@ 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());
MetaHeader meta; int nbytes = zmq_recv(fSocket, &(shmMsg->fMeta), sizeof(MetaHeader), flags);
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)) {
@ -180,8 +177,6 @@ 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;
@ -200,7 +195,7 @@ class Socket final : public fair::mq::Socket
} }
} }
int64_t Send(Parts::container& msgVec, int timeout = -1) override int64_t Send(std::vector<MessagePtr>& msgVec, int timeout = -1) override
{ {
int flags = 0; int flags = 0;
if (timeout == 0) { if (timeout == 0) {
@ -223,8 +218,7 @@ 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)
MetaHeader meta{ shmMsg->fSize, shmMsg->fHint, shmMsg->fHandle, shmMsg->fShared, shmMsg->fRegionId, shmMsg->fSegmentId, shmMsg->fManaged }; std::memcpy(metas++, &(shmMsg->fMeta), sizeof(MetaHeader));
std::memcpy(metas++, &meta, sizeof(MetaHeader));
} }
while (true) { while (true) {
@ -236,7 +230,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->fSize; totalSize += shmMsg->fMeta.fSize;
} }
// store statistics on how many messages have been sent // store statistics on how many messages have been sent
@ -260,7 +254,7 @@ class Socket final : public fair::mq::Socket
return static_cast<int>(TransferCode::error); return static_cast<int>(TransferCode::error);
} }
int64_t Receive(Parts::container& msgVec, int timeout = -1) override int64_t Receive(std::vector<MessagePtr>& msgVec, int timeout = -1) override
{ {
int flags = 0; int flags = 0;
if (timeout == 0) { if (timeout == 0) {

View File

@ -13,7 +13,6 @@
#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>
@ -60,13 +59,11 @@ struct UnmanagedRegion
, fRemoveOnDestruction(cfg.removeOnDestruction) , fRemoveOnDestruction(cfg.removeOnDestruction)
, fLinger(cfg.linger) , fLinger(cfg.linger)
, fStopAcks(false) , fStopAcks(false)
, fName(MakeShmName(shmId, "rg", cfg.id.value())) , fName("fmq_" + shmId + "_rg_" + std::to_string(cfg.id.value()))
, fQueueName(MakeShmName(shmId, "rgq", cfg.id.value())) , fQueueName("fmq_" + shmId + "_rgq_" + std::to_string(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)
@ -148,13 +145,11 @@ 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") << "), refCount segment size: " << fRcSegmentSize; LOG(debug) << (created ? "Created" : "Opened") << " unmanaged shared memory region: " << fName << " (" << (fControlling ? "controller" : "viewer") << ")";
} }
UnmanagedRegion() = delete; UnmanagedRegion() = delete;
@ -191,19 +186,6 @@ 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") << ")";
@ -226,11 +208,6 @@ 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";
} }
@ -258,7 +235,6 @@ 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;
@ -268,10 +244,7 @@ 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;
@ -289,7 +262,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, MakeShmName(shmId, "mng").c_str(), kManagementSegmentSize); managed_shared_memory mngSegment(open_or_create, std::string("fmq_" + 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);
@ -302,7 +275,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, cfg.rcSegmentSize, alloc)); shmRegions->emplace(cfg.id.value(), RegionInfo(cfg.path.c_str(), cfg.creationFlags, cfg.userFlags, cfg.size, alloc));
(eventCounter->fCount)++; (eventCounter->fCount)++;
} }
@ -321,29 +294,6 @@ 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-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Copyright (C) 2017-2021 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,22 +158,33 @@ 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);
auto const result = resolver.resolve(boost::asio::ip::tcp::v4(), hostname, ""); using namespace boost::asio::ip;
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-2025 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, *
@ -29,7 +29,7 @@ class LinePrinter
public: public:
LinePrinter(stringstream& out, string prefix) LinePrinter(stringstream& out, string prefix)
: fOut(out) : fOut(out)
, fPrefix(std::move(prefix)) , fPrefix(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_context ioc; ba::io_service ios;
// 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(ioc); bp::async_pipe inputPipe(ios);
// containers for std_out // containers for std_out
ba::streambuf outputBuffer; ba::streambuf outputBuffer;
bp::async_pipe outputPipe(ioc); bp::async_pipe outputPipe(ios);
// containers for std_err // containers for std_err
ba::streambuf errorBuffer; ba::streambuf errorBuffer;
bp::async_pipe errorPipe(ioc); bp::async_pipe errorPipe(ios);
const string delimiter = "\n"; const string delimiter = "\n";
ba::steady_timer inputTimer(ioc); ba::steady_timer inputTimer(ios);
inputTimer.expires_after(std::chrono::milliseconds(1000)); // NOLINT inputTimer.expires_after(std::chrono::milliseconds(1000)); // NOLINT
ba::steady_timer signalTimer(ioc); ba::steady_timer signalTimer(ios);
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);
ioc.run(); ios.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, Alignment /* alignment */ = Alignment{0}) override bool SetUsedSize(size_t size) 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(Parts::container& msgVec, int timeout = -1) override int64_t Send(std::vector<std::unique_ptr<fair::mq::Message>>& 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(Parts::container& msgVec, int timeout = -1) override int64_t Receive(std::vector<std::unique_ptr<fair::mq::Message>>& 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(std::move(part)); msgVec.push_back(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 5 TIMEOUT 20
${environment} ${environment}
) )

View File

@ -27,7 +27,7 @@ class TestDevice : public Device
{ {
public: public:
TestDevice(const string& transport) TestDevice(const string& transport)
: fDeviceThread(&Device::RunStateMachine, this) : fDeviceThread(&Device::RunStateMachine, this)
{ {
SetTransport(transport); SetTransport(transport);
test::Control(*this, test::Cycle::ToRun); test::Control(*this, test::Cycle::ToRun);

View File

@ -16,7 +16,6 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <cstring> #include <cstring>
#include <string>
#include <vector> #include <vector>
namespace namespace
@ -102,7 +101,7 @@ TEST(MemoryResources, allocator)
size_t session{tools::UuidHash()}; size_t session{tools::UuidHash()};
ProgOptions config; ProgOptions config;
config.SetProperty<std::string>("session", to_string(session)); config.SetProperty<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);
@ -130,7 +129,7 @@ TEST(MemoryResources, getMessage)
size_t session{tools::UuidHash()}; size_t session{tools::UuidHash()};
ProgOptions config; ProgOptions config;
config.SetProperty<std::string>("session", to_string(session)); config.SetProperty<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, "_", config.GetProperty<string>("session"))); auto const address(tools::ToString(_address, "_", transport));
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, "_", config.GetProperty<string>("session"))); auto const address(tools::ToString(_address, "_", transport));
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, "_", config.GetProperty<string>("session"))); auto const address(tools::ToString(_address, "_", transport));
push.Bind(address); push.Bind(address);
pull.Connect(address); pull.Connect(address);
@ -287,10 +287,8 @@ 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, uint64_t rcSegmentSize) -> void auto ZeroCopyFromUnmanaged(string const& address, bool expandedShmMetadata = false) -> void
{ {
fair::Logger::SetConsoleSeverity(fair::Severity::debug);
ProgOptions config1; ProgOptions config1;
ProgOptions config2; ProgOptions config2;
string session(tools::Uuid()); string session(tools::Uuid());
@ -313,20 +311,18 @@ auto ZeroCopyFromUnmanaged(string const& address, bool expandedShmMetadata, uint
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 + "_" + session); push.Bind(address);
pull.Connect(address + "_" + session); pull.Connect(address);
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));
@ -465,22 +461,12 @@ TEST(ZeroCopy, shmem_expanded_metadata) // NOLINT
TEST(ZeroCopyFromUnmanaged, shmem) // NOLINT TEST(ZeroCopyFromUnmanaged, shmem) // NOLINT
{ {
ZeroCopyFromUnmanaged("ipc://test_zerocopy_unmanaged", false, 10000000); ZeroCopyFromUnmanaged("ipc://test_zerocopy_unmanaged");
} }
TEST(ZeroCopyFromUnmanaged, shmem_expanded_metadata) // NOLINT TEST(ZeroCopyFromUnmanaged, shmem_expanded_metadata) // NOLINT
{ {
ZeroCopyFromUnmanaged("ipc://test_zerocopy_unmanaged_expanded", true, 10000000); ZeroCopyFromUnmanaged("ipc://test_zerocopy_unmanaged", true);
}
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-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Copyright (C) 2017 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,12 +7,8 @@
********************************************************************************/ ********************************************************************************/
#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
{ {
@ -146,27 +142,4 @@ 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,9 +35,6 @@ 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);
@ -121,8 +118,6 @@ 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);
@ -215,7 +210,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__expanded_metadata_1", true); RunMultiThreadedMultipart("shmem", "ipc://test_Multipart_MultiThreaded_ipc_shmem_1", true);
} }
} // namespace } // namespace

View File

@ -1,37 +1,28 @@
/******************************************************************************** /********************************************************************************
* Copyright (C) 2018-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * Copyright (C) 2018 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 <fairmq/tools/Network.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <fairmq/tools/Network.h>
#include <string> #include <string>
namespace namespace
{ {
TEST(Tools, NetworkDefaultIP) using namespace std;
using namespace fair::mq;
TEST(Tools, Network)
{ {
auto const interface = fair::mq::tools::getDefaultRouteNetworkInterface(); string interface = fair::mq::tools::getDefaultRouteNetworkInterface();
EXPECT_NE(interface, ""); EXPECT_NE(interface, "");
auto const interfaceIP = fair::mq::tools::getInterfaceIP(interface); string 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 */