Compare commits

..

5 Commits

Author SHA1 Message Date
Alexey Rybalchenko
d322d97d4a f 2025-08-26 11:59:20 +02:00
Alexey Rybalchenko
4176376b21 f 2025-08-26 11:56:49 +02:00
Alexey Rybalchenko
75b1208af0 f 2025-08-26 11:12:28 +02:00
Alexey Rybalchenko
e18140e110 f 2025-08-26 11:11:30 +02:00
Alexey Rybalchenko
6f9b72a27f Add test workflow 2025-08-26 11:02:11 +02:00
63 changed files with 460 additions and 992 deletions

View File

@@ -1,3 +1,3 @@
---
Checks: '-*,misc-unused-alias-decls,misc-unused-parameters,modernize-deprecated-headers,modernize-raw-string-literal,modernize-redundant-void-arg,modernize-use-bool-literals,modernize-use-default-member-init,modernize-use-emplace,modernize-use-equals-delete,modernize-use-noexcept,modernize-use-nullptr,modernize-use-override,modernize-use-using,performance-faster-string-find,performance-for-range-copy,performance-unnecessary-copy-initialization,readability-avoid-const-params-in-decls,readability-braces-around-statements,readability-container-size-empty,readability-delete-null-pointer,readability-redundant-member-init,readability-redundant-string-init,readability-static-accessed-through-instance,readability-string-compare'
Checks: 'cppcoreguidelines-*,misc-unused-alias-decls,misc-unused-parameters,modernize-pass-by-value,modernize-deprecated-headers,modernize-raw-string-literal,modernize-redundant-void-arg,modernize-use-bool-literals,modernize-use-default-member-init,modernize-use-emplace,modernize-use-equals-default,modernize-use-equals-delete,modernize-use-noexcept,modernize-use-nullptr,modernize-use-override,modernize-use-using,performance-faster-string-find,performance-for-range-copy,performance-unnecessary-copy-initialization,readability-avoid-const-params-in-decls,readability-braces-around-statements,readability-container-size-empty,readability-delete-null-pointer,readability-redundant-member-init,readability-redundant-string-init,readability-static-accessed-through-instance,readability-string-compare'
HeaderFilterRegex: '/(fairmq/)'

View File

@@ -1,78 +0,0 @@
name: Install dependencies
description: Setup spack and install dependencies
inputs:
gcc:
description: 'GCC version to use'
required: true
env:
description: 'Spack environment name (latest, boost187, tsan)'
default: 'latest'
fresh:
description: 'Use fresh concretization'
default: 'false'
push-gcc:
description: 'Push the freshly-built gcc node to the buildcache (buildcache workflow only)'
default: 'false'
push-token:
description: 'Token with packages:write, required when push-gcc is true'
default: ''
runs:
using: composite
# Composite action step names are not shown in the UI (https://github.com/actions/runner/issues/1877),
# so we use ::group:: to make them visible.
steps:
- name: Setup spack
uses: spack/setup-spack@v3
with:
color: true
buildcache: true
- name: Find system compiler
shell: spack-bash {0}
run: |
echo "::group::Find system compiler"
spack compiler find
echo "::endgroup::"
- name: Add FairMQ buildcache mirror
shell: spack-bash {0}
run: |
echo "::group::Add FairMQ buildcache mirror"
spack mirror add --unsigned --type binary \
ghcr-buildcache oci://ghcr.io/fairrootgroup/fairmq-spack-buildcache
echo "::endgroup::"
- name: Install GCC
shell: spack-bash {0}
env:
GITHUB_TOKEN: ${{ inputs.push-token }}
run: |
echo "::group::Install GCC"
spack install ${{ inputs.fresh == 'true' && '--fresh' || '' }} gcc@${{ inputs.gcc }} target=x86_64_v3
gcc_hash=$(spack find --json gcc@${{ inputs.gcc }} target=x86_64_v3 | jq -r 'sort_by(.version | split(".") | map(tonumber)) | last | .hash')
if [ "${{ inputs.push-gcc }}" = "true" ]; then
# Push the gcc node now, BEFORE `spack compiler find` registers it as an
# external -- externals are excluded from `buildcache push`. This is what
# lets CI pull gcc from the cache (~1 min) instead of building it (~1 h).
# --allow-missing: when gcc itself was pulled from the cache, its
# build-time dependencies are not installed locally and would
# otherwise fail the push.
spack mirror set --oci-username ${{ github.actor }} --oci-password-variable GITHUB_TOKEN ghcr-buildcache
spack buildcache push --unsigned --allow-missing ghcr-buildcache /$gcc_hash
fi
spack compiler find "$(spack location -i /$gcc_hash)"
echo "::endgroup::"
- name: Install dependencies
shell: spack-bash {0}
run: |
echo "::group::Install dependencies"
spack repo add "$GITHUB_WORKSPACE/test/ci/spack_repo/fairmq_ci"
spack env create fairmq test/ci/spack-${{ inputs.env }}.yaml
spack -e fairmq add gcc@${{ inputs.gcc }}
spack -e fairmq config add "packages:all:require:'%gcc@${{ inputs.gcc }}'"
spack -e fairmq install --fail-fast ${{ inputs.fresh == 'true' && '--fresh' || '' }}
spack env activate --sh fairmq | grep '^export ' | sed 's/^export //;s/;$//' >> $GITHUB_ENV
echo "::endgroup::"

View File

@@ -1,82 +0,0 @@
name: Spack Buildcache
on:
workflow_dispatch:
schedule:
- cron: '0 3 * * 0' # Weekly on Sunday at 3am UTC
push:
branches: [dev, master]
paths:
- 'test/ci/spack-*.yaml'
- 'test/ci/spack_repo/**'
- '.github/workflows/buildcache.yml'
- '.github/actions/setup-deps/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
if: github.repository == 'FairRootGroup/FairMQ'
name: ${{ matrix.env }}-gcc-${{ matrix.gcc }}
runs-on: ubuntu-24.04
permissions:
packages: write
strategy:
fail-fast: false
matrix:
gcc: ['12', '13', '14', '15']
env: ['latest']
include:
- gcc: '15'
env: 'boost187'
- gcc: '15'
env: 'tsan'
steps:
- uses: actions/checkout@v6
- name: Setup spack environment
uses: ./.github/actions/setup-deps
with:
gcc: ${{ matrix.gcc }}
env: ${{ matrix.env }}
fresh: 'true'
push-gcc: 'true'
push-token: ${{ secrets.GITHUB_TOKEN }}
- name: Push to buildcache
if: ${{ !cancelled() }}
shell: spack-bash {0}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
spack -e fairmq mirror set --oci-username ${{ github.actor }} --oci-password-variable GITHUB_TOKEN ghcr-buildcache
# --allow-missing: build-time dependencies of specs that were
# satisfied from the buildcache are not installed locally and
# would otherwise fail the whole push.
spack -e fairmq buildcache push --unsigned --allow-missing ghcr-buildcache
update-index:
if: github.repository == 'FairRootGroup/FairMQ' && !cancelled()
needs: build
runs-on: ubuntu-24.04
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
permissions:
packages: write
steps:
- uses: actions/checkout@v6
- name: Setup spack
uses: spack/setup-spack@v3
with:
color: true
- name: Update buildcache index
shell: spack-bash {0}
run: |
spack env create fairmq test/ci/spack-latest.yaml
spack -e fairmq mirror set --oci-username ${{ github.actor }} --oci-password-variable GITHUB_TOKEN ghcr-buildcache
spack -e fairmq buildcache update-index ghcr-buildcache

View File

@@ -22,7 +22,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Try updating metadata
run: python meta_update.py
- name: Check for Updates

View File

@@ -1,209 +0,0 @@
name: CI
on:
push:
branches: [master, dev]
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
if: github.repository == 'FairRootGroup/FairMQ'
name: ${{ matrix.env }}-gcc-${{ matrix.gcc }}
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
gcc: ['12', '13', '14', '15']
env: ['latest']
include:
- gcc: '15'
env: 'boost187'
steps:
- uses: actions/checkout@v6
with:
submodules: true
fetch-tags: true
fetch-depth: 0
- name: Setup spack environment
uses: ./.github/actions/setup-deps
with:
gcc: ${{ matrix.gcc }}
env: ${{ matrix.env }}
- name: ccache
uses: hendrikmuhs/ccache-action@d62db5f07c26379fc4b4e0916f098a92573c3b03 # v1.2.23
with:
key: ${{ github.job }}-${{ matrix.env }}-gcc${{ matrix.gcc }}
max-size: 500M
- name: Configure and Build
uses: threeal/cmake-action@725d1314ccf9ea922805d7e3f9d9bcbca892b406 # v2.1.0
with:
generator: Ninja
options: |
CMAKE_BUILD_TYPE=RelWithDebInfo
CMAKE_INSTALL_PREFIX=${{ github.workspace }}/install
BUILD_TESTING=ON
CMAKE_C_COMPILER_LAUNCHER=ccache
CMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Test
run: |
# Region/segment tests mlock() shared memory; raise the locked-memory
# limit so it does not hit RLIMIT_MEMLOCK on the runner.
sudo prlimit --pid $$ --memlock=unlimited:unlimited
ulimit -l
ctest --test-dir build --output-on-failure --no-tests=error
- name: Install
run: cmake --install build
sanitizers:
if: github.repository == 'FairRootGroup/FairMQ'
name: ${{ matrix.sanitizer.name }}
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
sanitizer:
- name: asan+lsan+ubsan
gcc: '14'
env: latest
options: |
ENABLE_SANITIZER_ADDRESS=ON
ENABLE_SANITIZER_LEAK=ON
ENABLE_SANITIZER_UNDEFINED_BEHAVIOR=ON
cxx-flags: -O1 -fno-omit-frame-pointer
- name: tsan
gcc: '15'
env: tsan
options: ENABLE_SANITIZER_THREAD=ON
cxx-flags: -fno-omit-frame-pointer
steps:
- uses: actions/checkout@v6
with:
submodules: true
fetch-tags: true
fetch-depth: 0
- name: Setup spack environment
uses: ./.github/actions/setup-deps
with:
gcc: ${{ matrix.sanitizer.gcc }}
env: ${{ matrix.sanitizer.env }}
- name: ccache
uses: hendrikmuhs/ccache-action@d62db5f07c26379fc4b4e0916f098a92573c3b03 # v1.2.23
with:
key: ${{ github.job }}-${{ matrix.sanitizer.name }}
max-size: 500M
- name: Locate instrumented libstdc++
if: matrix.sanitizer.name == 'tsan'
shell: spack-bash {0}
# The test processes must load the tsan-instrumented libstdc++
# instead of the compiler's own (same soname, LD_LIBRARY_PATH beats
# the RUNPATH). Set per test via ctest, not at the job level: like
# any shared library built with gcc -fsanitize=thread it has
# unresolved __tsan_* symbols, so loading it into uninstrumented
# tools (cmake, ctest, ninja) would break them.
run: |
prefix=$(spack -e fairmq location -i libstdcxx-tsan)
echo "test_library_path=$prefix/lib" >> $GITHUB_ENV
- name: Configure and Build
uses: threeal/cmake-action@725d1314ccf9ea922805d7e3f9d9bcbca892b406 # v2.1.0
with:
generator: Ninja
cxx-flags: ${{ matrix.sanitizer.cxx-flags }}
options: |
CMAKE_BUILD_TYPE=Debug
BUILD_TESTING=ON
CMAKE_C_COMPILER_LAUNCHER=ccache
CMAKE_CXX_COMPILER_LAUNCHER=ccache
FAIRMQ_TEST_LD_LIBRARY_PATH=${{ env.test_library_path }}
${{ matrix.sanitizer.options }}
- name: Verify tsan instrumentation wiring
if: matrix.sanitizer.name == 'tsan'
shell: spack-bash {0}
run: |
set -x
# the test environment must resolve libstdc++ to the instrumented copy
LD_LIBRARY_PATH=$test_library_path ldd build/test/testsuite_Channel \
| grep 'libstdc++' | tee /dev/stderr | grep -q libstdcxx-tsan
# libzmq must be instrumented
nm -D --undefined-only "$(spack -e fairmq location -i libzmq)/lib/libzmq.so" \
| grep -q __tsan_
# the instrumented libstdc++ must match the compiler release exactly,
# or binaries may reference GLIBCXX versions the runtime lacks
# (--color=never: the CI config forces SPACK_COLOR=always, which
# would wrap the version in ANSI escapes)
test "$(spack --color=never -e fairmq find --format '{version}' libstdcxx-tsan)" \
= "$(g++ -dumpfullversion)"
# the LD_LIBRARY_PATH prepend must reach the registered tests
# (via a file: grep -q quits on first match, and SIGPIPE on the
# large json output would fail the step under pipefail)
ctest --test-dir build --show-only=json-v1 > ctest-show-only.json
grep -q libstdcxx-tsan ctest-show-only.json
- name: Test
run: |
# Region/segment tests mlock() shared memory; raise the locked-memory
# limit so it does not hit RLIMIT_MEMLOCK on the runner.
sudo prlimit --pid $$ --memlock=unlimited:unlimited
ulimit -l
ctest --test-dir build --output-on-failure --no-tests=error
static-analysis:
if: github.repository == 'FairRootGroup/FairMQ'
name: static-analysis
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
with:
submodules: true
fetch-tags: true
fetch-depth: 0
- name: Setup spack environment
uses: ./.github/actions/setup-deps
with:
gcc: '14'
- name: ccache
uses: hendrikmuhs/ccache-action@d62db5f07c26379fc4b4e0916f098a92573c3b03 # v1.2.23
with:
key: ${{ github.job }}
max-size: 500M
- name: Configure
run: |
cmake -S . -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_TESTING=ON \
-DRUN_STATIC_ANALYSIS=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: |
set -o pipefail
cmake --build build 2>&1 | tee build.log
- name: Check for warnings
run: |
test -f build.log || { echo "::error::build.log was not produced"; exit 1; }
if grep -q "warning:" build.log; then
echo "::error::Static analysis found warnings"
grep "warning:" build.log
exit 1
fi

View File

@@ -16,6 +16,6 @@ jobs:
container:
image: gitlab-registry.in2p3.fr/escape2020/wp3/eossr:v1.0
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: validate codemeta
run: eossr-metadata-validator codemeta.json

70
.github/workflows/test-macos-runner.yml vendored Normal file
View File

@@ -0,0 +1,70 @@
name: Test macOS Self-Hosted Runner
on:
workflow_dispatch:
push:
branches: [ dev, master ]
pull_request:
branches: [ dev, master ]
jobs:
test-runner:
runs-on: [self-hosted, macOS-15]
timeout-minutes: 120
steps:
- name: Setup environment
run: |
echo "Setting up PATH for Homebrew..."
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
echo "PATH=$PATH" >> $GITHUB_ENV
- name: Checkout code
uses: actions/checkout@v4
- name: System information
run: |
echo "Runner information:"
uname -a
sw_vers
echo "CPU info:"
sysctl -n machdep.cpu.brand_string
echo "Memory info:"
system_profiler SPHardwareDataType | grep "Memory:"
echo "Disk space:"
df -h
- name: Check development tools
run: |
echo "Xcode tools version:"
xcode-select -p
clang --version
echo "CMake version:"
cmake --version || echo "CMake not installed"
echo "Git version:"
git --version
echo "Available SDKs:"
xcodebuild -showsdks || echo "Xcode not fully installed"
- name: Test basic compilation
run: |
echo "Testing basic C++ compilation:"
cat > test.cpp << 'EOF'
#include <iostream>
int main() {
std::cout << "Hello from macOS 15 UTM runner!" << std::endl;
return 0;
}
EOF
clang++ -o test_cpp test.cpp
./test_cpp
- name: Check FairMQ dependencies
run: |
echo "Checking potential FairMQ build dependencies:"
brew --version || echo "Homebrew not installed"
pkg-config --version || echo "pkg-config not available"
echo "Looking for common HEP libraries..."
find /usr/local /opt -name "*root*" -type d 2>/dev/null | head -5 || echo "No ROOT installation found"

View File

@@ -46,13 +46,6 @@ include(FairMQDependencies)
# Targets ######################################################################
if(FAIRMQ_TEST_LD_LIBRARY_PATH AND CMAKE_VERSION VERSION_LESS 3.22)
# The per-test injection relies on ctest's ENVIRONMENT_MODIFICATION, which
# older CMake silently drops -- the tests would run against the default
# runtime while looking green.
message(FATAL_ERROR "FAIRMQ_TEST_LD_LIBRARY_PATH requires CMake >= 3.22")
endif()
if(BUILD_FAIRMQ)
add_subdirectory(fairmq)
endif()

93
FairMQTest.cmake Normal file
View File

@@ -0,0 +1,93 @@
################################################################################
# Copyright (C) 2021-2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH #
# #
# This software is distributed under the terms of the #
# GNU Lesser General Public Licence (LGPL) version 3, #
# copied verbatim in the file "LICENSE" #
################################################################################
cmake_host_system_information(RESULT fqdn QUERY FQDN)
set(CTEST_SOURCE_DIRECTORY .)
set(CTEST_BINARY_DIRECTORY build)
set(CTEST_CMAKE_GENERATOR "Ninja")
set(CTEST_USE_LAUNCHERS ON)
set(CTEST_CONFIGURATION_TYPE "RelWithDebInfo")
set(CTEST_CUSTOM_MAXIMUM_PASSED_TEST_OUTPUT_SIZE 102400)
if(NOT NCPUS)
if(ENV{SLURM_CPUS_PER_TASK})
set(NCPUS $ENV{SLURM_CPUS_PER_TASK})
else()
include(ProcessorCount)
ProcessorCount(NCPUS)
if(NCPUS EQUAL 0)
set(NCPUS 1)
endif()
endif()
endif()
if ("$ENV{CTEST_SITE}" STREQUAL "")
set(CTEST_SITE "${fqdn}")
else()
set(CTEST_SITE $ENV{CTEST_SITE})
endif()
if ("$ENV{LABEL}" STREQUAL "")
set(CTEST_BUILD_NAME "build")
else()
set(CTEST_BUILD_NAME $ENV{LABEL})
endif()
ctest_start(Continuous)
list(APPEND options "-DDISABLE_COLOR=ON" "-DBUILD_EXAMPLES=ON" "-DBUILD_TESTING=ON")
if(RUN_STATIC_ANALYSIS)
list(APPEND options "-DRUN_STATIC_ANALYSIS=ON")
endif()
if(CMAKE_BUILD_TYPE)
set(CTEST_CONFIGURATION_TYPE ${CMAKE_BUILD_TYPE})
endif()
if(ENABLE_SANITIZER_ADDRESS)
list(APPEND options "-DENABLE_SANITIZER_ADDRESS=ON")
endif()
if(ENABLE_SANITIZER_LEAK)
list(APPEND options "-DENABLE_SANITIZER_LEAK=ON")
endif()
if(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR)
list(APPEND options "-DENABLE_SANITIZER_UNDEFINED_BEHAVIOR=ON")
endif()
if(ENABLE_SANITIZER_MEMORY)
list(APPEND options "-DENABLE_SANITIZER_MEMORY=ON")
endif()
if(ENABLE_SANITIZER_THREAD)
list(APPEND options "-DENABLE_SANITIZER_THREAD=ON")
endif()
if(CMAKE_CXX_COMPILER)
list(APPEND options "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}")
endif()
if(CMAKE_CXX_FLAGS)
list(APPEND options "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}")
endif()
list(REMOVE_DUPLICATES options)
list(JOIN options ";" optionsstr)
ctest_configure(OPTIONS "${optionsstr}")
ctest_submit()
ctest_build(FLAGS "-j${NCPUS}")
ctest_submit()
if(NOT RUN_STATIC_ANALYSIS)
ctest_test(BUILD "${CTEST_BINARY_DIRECTORY}"
PARALLEL_LEVEL ${NCPUS}
SCHEDULE_RANDOM ON
RETURN_VALUE _ctest_test_ret_val)
ctest_submit()
endif()
if(_ctest_test_ret_val)
Message(FATAL_ERROR "Some tests failed.")
endif()

118
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,118 @@
#!groovy
def jobMatrix(String type, List specs) {
def nodes = [:]
for (spec in specs) {
def job = ""
def selector = "slurm"
def os = ""
def ver = ""
if (type == 'build') {
job = "${spec.os}-${spec.ver}-${spec.arch}-${spec.compiler}"
if (spec.os =~ /^macos/) {
selector = "${spec.os}-${spec.ver}-${spec.arch}"
}
os = spec.os
ver = spec.ver
} else { // == 'check'
job = "${spec.name}"
os = 'fedora'
ver = '36'
}
def label = "${job}"
def extra = spec.extra
nodes[label] = {
node(selector) {
githubNotify(context: "${label}", description: 'Building ...', status: 'PENDING')
try {
deleteDir()
checkout scm
def jobscript = 'job.sh'
def ctestcmd = "ctest ${extra} -S FairMQTest.cmake -V --output-on-failure"
sh "echo \"set -e\" >> ${jobscript}"
sh "echo \"export LABEL=\\\"\${JOB_BASE_NAME} ${label}\\\"\" >> ${jobscript}"
if (selector =~ /^macos/) {
sh """\
echo \"${ctestcmd}\" >> ${jobscript}
"""
sh "cat ${jobscript}"
sh "bash ${jobscript}"
} else { // selector == "slurm"
def imageurl = "oras://ghcr.io/fairrootgroup/fairmq-dev/${os}-${ver}-sif:latest"
def execopts = "--ipc --uts --pid -B/shared"
def containercmd = "singularity exec ${execopts} ${imageurl} bash -l -c \\\"${ctestcmd} ${extra}\\\""
sh """\
echo \"echo \\\"*** Job started at .......: \\\$(date -R)\\\"\" >> ${jobscript}
echo \"echo \\\"*** Job ID ...............: \\\${SLURM_JOB_ID}\\\"\" >> ${jobscript}
echo \"echo \\\"*** Compute node .........: \\\$(hostname -f)\\\"\" >> ${jobscript}
echo \"unset http_proxy\" >> ${jobscript}
echo \"unset HTTP_PROXY\" >> ${jobscript}
echo \"${containercmd}\" >> ${jobscript}
"""
sh "cat ${jobscript}"
sh "test/ci/slurm-submit.sh \"FairMQ \${JOB_BASE_NAME} ${label}\" ${jobscript}"
if (job == "static-analyzers") {
recordIssues(enabledForFailure: true,
tools: [gcc(pattern: 'build/Testing/Temporary/*.log')],
filters: [excludeFile('extern/*'), excludeFile('usr/*')],
skipBlames: true,
skipPublishingChecks: true)
}
}
deleteDir()
githubNotify(context: "${label}", description: 'Success', status: 'SUCCESS')
} catch (e) {
deleteDir()
githubNotify(context: "${label}", description: 'Error', status: 'ERROR')
throw e
}
}
}
}
return nodes
}
pipeline{
agent none
stages {
stage("CI") {
steps{
script {
def builds = jobMatrix('build', [
[os: 'ubuntu', ver: '20.04', arch: 'x86_64', compiler: 'gcc-9'],
[os: 'ubuntu', ver: '22.04', arch: 'x86_64', compiler: 'gcc-11'],
[os: 'ubuntu', ver: '24.04', arch: 'x86_64', compiler: 'gcc-13'],
[os: 'fedora', ver: '33', arch: 'x86_64', compiler: 'gcc-10'],
[os: 'fedora', ver: '34', arch: 'x86_64', compiler: 'gcc-11'],
[os: 'fedora', ver: '35', arch: 'x86_64', compiler: 'gcc-11'],
[os: 'fedora', ver: '36', arch: 'x86_64', compiler: 'gcc-12'],
[os: 'fedora', ver: '37', arch: 'x86_64', compiler: 'gcc-12'],
[os: 'fedora', ver: '38', arch: 'x86_64', compiler: 'gcc-13'],
[os: 'fedora', ver: '39', arch: 'x86_64', compiler: 'gcc-13'],
[os: 'fedora', ver: '40', arch: 'x86_64', compiler: 'gcc-14'],
[os: 'macos', ver: '14', arch: 'x86_64', compiler: 'apple-clang-16'],
[os: 'macos', ver: '15', arch: 'x86_64', compiler: 'apple-clang-16'],
[os: 'macos', ver: '15', arch: 'arm64', compiler: 'apple-clang-16'],
])
def all_debug = "-DCMAKE_BUILD_TYPE=Debug"
def checks = jobMatrix('check', [
[name: 'static-analyzers', extra: "${all_debug} -DRUN_STATIC_ANALYSIS=ON"],
[name: '{address,leak,ub}-sanitizers',
extra: "${all_debug} -DENABLE_SANITIZER_ADDRESS=ON -DENABLE_SANITIZER_LEAK=ON -DENABLE_SANITIZER_UNDEFINED_BEHAVIOUR=ON -DCMAKE_CXX_FLAGS='-O1 -fno-omit-frame-pointer'"],
[name: 'thread-sanitizer', extra: "${all_debug} -DENABLE_SANITIZER_THREAD=ON -DCMAKE_CXX_COMPILER=clang++"],
])
parallel(builds + checks)
}
}
}
}
}

View File

@@ -68,7 +68,6 @@ function(build_bundled package bundle)
exec(${CMAKE_COMMAND} -S ${${package}_SOURCE_DIR} -B ${${package}_BINARY_DIR} -G ${CMAKE_GENERATOR}
-DCMAKE_INSTALL_PREFIX=${${package}_INSTALL_DIR} -DBUILD_GMOCK=OFF
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
)
exec(${CMAKE_COMMAND} --build ${${package}_BINARY_DIR})
exec(${CMAKE_COMMAND} --build ${${package}_BINARY_DIR} --target install)

View File

@@ -23,18 +23,6 @@ if(BUILD_FAIRMQ OR BUILD_TIDY_TOOL)
find_package2(PUBLIC Boost REQUIRED VERSION 1.66
COMPONENTS container program_options filesystem date_time regex
)
# Check Boost.Process compatibility
# Boost 1.88 has broken Boost.Process v2 without v1 compatibility headers
# Boost 1.89+ provides <boost/process/v1.hpp> for the old API
if(Boost_VERSION VERSION_EQUAL "1.88.0")
message(FATAL_ERROR "Boost version 1.88 is not supported due to Boost.Process API changes. "
"Please use Boost < 1.88 or >= 1.89")
endif()
if(Boost_VERSION VERSION_GREATER_EQUAL "1.89")
set(FAIRMQ_BOOST_PROCESS_V1_HEADER ON CACHE INTERNAL "Use boost/process/v1.hpp for Boost >= 1.89")
endif()
endif()
if(BUILD_FAIRMQ)

View File

@@ -54,8 +54,8 @@ endif()
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/${PROJECT_INSTALL_LIBDIR}" isSystemDir)
if("${isSystemDir}" STREQUAL "-1")
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,--enable-new-dtags")
string(APPEND CMAKE_SHARED_LINKER_FLAGS " -Wl,--enable-new-dtags")
list(APPEND CMAKE_EXE_LINKER_FLAGS "-Wl,--enable-new-dtags")
list(APPEND CMAKE_SHARED_LINKER_FLAGS "-Wl,--enable-new-dtags")
list(PREPEND CMAKE_INSTALL_RPATH "$ORIGIN/../${PROJECT_INSTALL_LIBDIR}")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
list(PREPEND CMAKE_INSTALL_RPATH "@loader_path/../${PROJECT_INSTALL_LIBDIR}")

View File

@@ -12,22 +12,6 @@ set(test_script_prefix "test-ex")
set(testsuite "Example")
set(transports "zeromq" "shmem")
# Environment for every example test (directory scope, so the subdirectories
# see it too)
set(env_mods)
if(ENABLE_SANITIZER_LEAK AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.22)
get_filename_component(lsan_supps "${CMAKE_SOURCE_DIR}/test/leak_sanitizer_suppressions.txt" ABSOLUTE)
list(APPEND env_mods "LSAN_OPTIONS=set:suppressions=${lsan_supps}")
endif()
# Run the example tests against an alternative runtime library directory as
# well (see test/CMakeLists.txt). The test scripts only launch instrumented
# binaries (bash itself does not link libstdc++). Note: unlike the
# testsuites, the example binaries get no locale-cache warmup -- their
# main() comes from the installed public header fairmq/runDevice.h.
if(FAIRMQ_TEST_LD_LIBRARY_PATH)
list(APPEND env_mods "LD_LIBRARY_PATH=path_list_prepend:${FAIRMQ_TEST_LD_LIBRARY_PATH}")
endif()
function(add_example)
cmake_parse_arguments(PARSE_ARGV 0 ARG
"CONFIG;NO_TRANSPORT;NO_TEST"
@@ -45,6 +29,11 @@ function(add_example)
message(FATAL_ERROR "NAME arg is required")
endif()
if(ENABLE_SANITIZER_LEAK AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.22)
get_filename_component(lsan_supps "${CMAKE_SOURCE_DIR}/test/leak_sanitizer_suppressions.txt" ABSOLUTE)
set(lsan_options "LSAN_OPTIONS=set:suppressions=${lsan_supps}")
endif()
if(ARG_DEVICE)
set(exe_targets)
foreach(device IN LISTS ARG_DEVICE)
@@ -89,8 +78,8 @@ function(add_example)
set(test "${testsuite}.${name}.${transport}")
add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${test_script} ${transport})
set_tests_properties(${test} PROPERTIES TIMEOUT "30")
if(env_mods)
set_tests_properties(${test} PROPERTIES ENVIRONMENT_MODIFICATION "${env_mods}")
if(lsan_options)
set_tests_properties(${test} PROPERTIES ENVIRONMENT_MODIFICATION ${lsan_options})
endif()
else()
foreach(transport IN LISTS transports)
@@ -99,16 +88,16 @@ function(add_example)
set(test "${testsuite}.${name}.${variant}.${transport}")
add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${test_script} ${transport} ${variant})
set_tests_properties(${test} PROPERTIES TIMEOUT "30")
if(env_mods)
set_tests_properties(${test} PROPERTIES ENVIRONMENT_MODIFICATION "${env_mods}")
if(lsan_options)
set_tests_properties(${test} PROPERTIES ENVIRONMENT_MODIFICATION ${lsan_options})
endif()
endforeach()
else()
set(test "${testsuite}.${name}.${transport}")
add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${test_script} ${transport})
set_tests_properties(${test} PROPERTIES TIMEOUT "30")
if(env_mods)
set_tests_properties(${test} PROPERTIES ENVIRONMENT_MODIFICATION "${env_mods}")
if(lsan_options)
set_tests_properties(${test} PROPERTIES ENVIRONMENT_MODIFICATION ${lsan_options})
endif()
endif()
endforeach()

View File

@@ -15,8 +15,6 @@ set_target_properties(${exe} PROPERTIES ENABLE_EXPORTS ON)
set(test "${testsuite}.${name}")
add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${exe})
set_tests_properties(${test} PROPERTIES TIMEOUT 30)
# (the previous `if(lsan_options)` here could never fire -- that variable
# only ever existed in the add_example() function scope)
if(env_mods)
set_tests_properties(${test} PROPERTIES ENVIRONMENT_MODIFICATION "${env_mods}")
if(lsan_options)
set_tests_properties(${test} PROPERTIES ENVIRONMENT_MODIFICATION ${lsan_options})
endif()

View File

@@ -176,9 +176,6 @@ if(BUILD_FAIRMQ)
FAIRMQ_HAS_STD_FILESYSTEM=${FAIRMQ_HAS_STD_FILESYSTEM}
FAIRMQ_HAS_STD_PMR=${FAIRMQ_HAS_STD_PMR}
)
if(FAIRMQ_BOOST_PROCESS_V1_HEADER)
target_compile_definitions(${target} PRIVATE FAIRMQ_BOOST_PROCESS_V1_HEADER)
endif()
if(DEFINED FAIRMQ_CHANNEL_DEFAULT_AUTOBIND)
# translate CMake boolean (TRUE, FALSE, 0, 1, OFF, ON) into C++ boolean literal (true, false)
if(FAIRMQ_CHANNEL_DEFAULT_AUTOBIND)

View File

@@ -181,8 +181,7 @@ try {
// validate channel name
smatch m;
static regex const invalidName(R"([^a-zA-Z0-9\-_\[\]#])");
if (regex_search(fName, m, invalidName)) {
if (regex_search(fName, m, regex("[^a-zA-Z0-9\\-_\\[\\]#]"))) {
ss << "INVALID";
LOG(debug) << ss.str();
LOG(error) << "channel name contains illegal character: '" << m.str(0) << "', allowed characters are: a-z, A-Z, 0-9, -, _, [, ], #";

View File

@@ -513,8 +513,7 @@ void Device::HandleMultipleTransportInput()
fMultitransportProceed = true;
for (const auto& i : fMultitransportInputs) {
threads.emplace_back(
&Device::PollForTransport, this, fTransports.at(i.first).get(), i.second);
threads.emplace_back(thread(&Device::PollForTransport, this, fTransports.at(i.first).get(), i.second));
}
for (thread& t : threads) {

View File

@@ -66,13 +66,10 @@ bool DeviceRunner::HandleGeneralOptions(const fair::mq::ProgOptions& config, boo
if (printLogo) {
LOG(info) << endl
<< " ______ _ _______ _________ " << endl
<< " / ____/___ _(_)_______ |/ /_ __ \\ version "
<< FAIRMQ_GIT_VERSION << endl
<< " / /_ / __ `/ / ___/__ /|_/ /_ / / / build " << FAIRMQ_BUILD_TYPE
<< endl
<< " / ____/___ _(_)_______ |/ /_ __ \\ version " << FAIRMQ_GIT_VERSION << endl
<< " / /_ / __ `/ / ___/__ /|_/ /_ / / / build " << FAIRMQ_BUILD_TYPE << endl
<< " / __/ / /_/ / / / _ / / / / /_/ / " << FAIRMQ_REPO_URL << endl
<< R"( /_/ \__,_/_/_/ /_/ /_/ \___\_\ )" << FAIRMQ_LICENSE
<< " © " << FAIRMQ_COPYRIGHT << endl;
<< " /_/ \\__,_/_/_/ /_/ /_/ \\___\\_\\ " << FAIRMQ_LICENSE << " © " << FAIRMQ_COPYRIGHT << endl;
}
}

View File

@@ -87,7 +87,7 @@ struct Parts
const_iterator end() const noexcept { return fParts.end(); }
const_iterator cend() const noexcept { return fParts.cend(); }
container fParts;
container fParts{};
};
} // namespace fair::mq

View File

@@ -32,9 +32,12 @@ const std::string fair::mq::PluginManager::fgkLibPrefixAlt = "FairMQPlugin_";
std::vector<boost::dll::shared_library> fair::mq::PluginManager::fgDLLKeepAlive =
std::vector<boost::dll::shared_library>();
fair::mq::PluginManager::PluginManager() {}
fair::mq::PluginManager::PluginManager()
: fPluginServices()
{}
fair::mq::PluginManager::PluginManager(const vector<string>& args)
: fPluginServices()
{
// Parse command line options
auto options = ProgramOptions();

View File

@@ -17,9 +17,7 @@ auto PluginServices::ChangeDeviceState(const string& controller, const DeviceSta
{
lock_guard<mutex> lock{fDeviceControllerMutex};
if (!fDeviceController) {
fDeviceController = controller;
}
if (!fDeviceController) fDeviceController = controller;
if (fDeviceController == controller) {
return fDevice.ChangeState(next);

View File

@@ -189,7 +189,7 @@ vector<string> ProgOptions::GetPropertyKeys() const
vector<string> keys;
for (const auto& it : fVarMap) {
keys.emplace_back(it.first.c_str());
keys.push_back(it.first.c_str());
}
return keys;

View File

@@ -79,7 +79,7 @@ Properties SuboptParser(const vector<string>& channelConfig, const string& devic
ptree channelsArray;
for (const auto& token : channelConfig) {
for (auto token : channelConfig) {
string channelName;
ptree channelProperties;

View File

@@ -135,7 +135,7 @@ struct RegionConfig
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 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
uint32_t linger = 100; /// delay in ms before region destruction to collect outstanding events
};

View File

@@ -180,6 +180,7 @@ struct RegionInfo
, fUserFlags(userFlags)
, fSize(size)
, fRCSegmentSize(rcSegmentSize)
, fDestroyed(false)
{}
Str fPath;
@@ -187,7 +188,7 @@ struct RegionInfo
uint64_t fUserFlags;
uint64_t fSize;
uint64_t fRCSegmentSize;
bool fDestroyed{false};
bool fDestroyed;
};
using Uint16RegionInfoPairAlloc = boost::interprocess::allocator<std::pair<const uint16_t, RegionInfo>, SegmentManager>;
@@ -339,13 +340,4 @@ struct SegmentBufferShrink
} // namespace fair::mq::shmem
namespace fair::mq { class TransportFactory; }
namespace fair::mq::shmem {
// Resolve a MetaHeader (received over a side channel) to the local data pointer.
// The caller is responsible for ensuring the backing buffer remains alive for the
// duration of access; FairMQ provides no refcount protection for this path.
char* GetDataAddressFromHandle(fair::mq::TransportFactory& factory, const MetaHeader& meta);
} // namespace fair::mq::shmem
#endif /* FAIR_MQ_SHMEM_COMMON_H_ */

View File

@@ -7,18 +7,11 @@
********************************************************************************/
#include "Manager.h"
#include "TransportFactory.h"
// Needed to compile-firewall the <boost/process/async.hpp> header because it
// interferes with the <asio/buffer.hpp> header. So, let's factor
// the whole dependency to Boost.Process out of the header.
#ifdef FAIRMQ_BOOST_PROCESS_V1_HEADER
#include <boost/process/v1.hpp>
namespace bp = boost::process::v1;
#else
#include <boost/process.hpp>
namespace bp = boost::process;
#endif
#include <fairlogger/Logger.h>
namespace fair::mq::shmem {
@@ -35,7 +28,7 @@ bool Manager::SpawnShmMonitor(const std::string& id)
path.emplace(path.begin(), env.at(fairmq_path_key).to_string());
}
auto exe(bp::search_path(shmmonitor_exe_name, path));
auto exe(boost::process::search_path(shmmonitor_exe_name, path));
if (exe.empty()) {
LOG(warn) << "could not find " << shmmonitor_exe_name << " in \"$" << fairmq_path_key
<< ":$PATH\"";
@@ -46,18 +39,10 @@ bool Manager::SpawnShmMonitor(const std::string& id)
bool verbose(env.count(shmmonitor_verbose_key)
&& env.at(shmmonitor_verbose_key).to_string() == "true");
bp::spawn(
boost::process::spawn(
exe, "-x", "-m", "--shmid", id, "-d", "-t", "2000", (verbose ? "--verbose" : ""), env);
return true;
}
char* GetDataAddressFromHandle(fair::mq::TransportFactory& factory, const MetaHeader& meta)
{
if (factory.GetType() != fair::mq::Transport::SHM) {
throw SharedMemoryError("GetDataAddressFromHandle called on a non-shmem transport");
}
return static_cast<TransportFactory&>(factory).GetDataAddressFromHandle(meta);
}
} // namespace fair::mq::shmem

View File

@@ -390,10 +390,8 @@ class Manager
}
auto* lRegion = GetRegion(id);
if (lRegion) {
fTlRegionCache.fRegionsTLCache.emplace_back(lRegion, id, fShmId64);
fTlRegionCache.fRegionsTLCache.emplace_back(std::make_tuple(lRegion, id, fShmId64));
fTlRegionCache.fRegionsTLCacheGen = fRegionsGen;
}
return lRegion;
}
@@ -778,30 +776,6 @@ class Manager
auto GetMetadataMsgSize() const noexcept { return fMetadataMsgSize; }
// Resolve a MetaHeader (received over a side channel) to the local data pointer.
// The caller is responsible for ensuring the backing buffer remains alive for the
// duration of access; FairMQ provides no refcount protection for this path.
char* GetDataAddressFromHandle(const MetaHeader& meta)
{
if (meta.fManaged) {
if (meta.fSize == 0) {
return nullptr;
}
GetSegment(meta.fSegmentId);
auto it = fSegments.find(meta.fSegmentId);
if (it == fSegments.end()) {
throw SharedMemoryError(tools::ToString("GetDataAddressFromHandle: cannot open segment with id ", meta.fSegmentId));
}
return ShmHeader::UserPtr(GetAddressFromHandle(meta.fHandle, meta.fSegmentId));
} else {
UnmanagedRegion* region = GetRegionFromCache(meta.fRegionId);
if (!region) {
throw SharedMemoryError(tools::ToString("GetDataAddressFromHandle: cannot get unmanaged region with id ", meta.fRegionId));
}
return reinterpret_cast<char*>(region->GetData()) + meta.fHandle;
}
}
~Manager()
{
fRegionsGen += 1; // signal TL cache invalidation

View File

@@ -77,12 +77,7 @@ class Message final : public fair::mq::Message
fManager.IncrementMsgCounter();
}
Message(Manager& manager,
UnmanagedRegionPtr& region,
void* data,
const size_t size,
void* hint = nullptr,
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)
, fManager(manager)
, fLocalPtr(static_cast<char*>(data))
@@ -167,11 +162,6 @@ class Message final : public fair::mq::Message
}
}
MetaHeader GetMeta() const
{
return {fSize, fHint, fHandle, fShared, fRegionId, fSegmentId, fManaged};
}
void* GetData() const override
{
if (!fLocalPtr) {

View File

@@ -59,18 +59,12 @@ void signalHandler(int signal)
gSignalStatus = signal;
}
Monitor::Monitor(string shmId,
bool selfDestruct,
bool interactive,
bool viewOnly,
unsigned int timeoutInMS,
unsigned int intervalInMS,
bool monitor,
bool cleanOnExit)
Monitor::Monitor(string shmId, bool selfDestruct, bool interactive, bool viewOnly, unsigned int timeoutInMS, unsigned int intervalInMS, bool monitor, bool cleanOnExit)
: fSelfDestruct(selfDestruct)
, fInteractive(interactive)
, fViewOnly(viewOnly)
, fMonitor(monitor)
, fSeenOnce(false)
, fCleanOnExit(cleanOnExit)
, fTimeoutInMS(timeoutInMS)
, fIntervalInMS(intervalInMS)

View File

@@ -170,7 +170,7 @@ class Monitor
bool fInteractive; // running in interactive mode
bool fViewOnly; // view only mode
bool fMonitor;
bool fSeenOnce{false}; // true is segment has been opened successfully at least once
bool fSeenOnce; // true is segment has been opened successfully at least once
bool fCleanOnExit;
unsigned int fTimeoutInMS;
unsigned int fIntervalInMS;

View File

@@ -71,7 +71,7 @@ class Poller final : public fair::mq::Poller
try {
int offset = 0;
// calculate offsets and the total size of the poll item set
for (const std::string& channel : channelList) {
for (std::string channel : channelList) {
fOffsetMap[channel] = offset;
offset += channelsMap.at(channel).size();
fNumItems += channelsMap.at(channel).size();
@@ -80,7 +80,7 @@ class Poller final : public fair::mq::Poller
fItems = new zmq_pollitem_t[fNumItems];
int index = 0;
for (const std::string& channel : channelList) {
for (std::string channel : channelList) {
for (unsigned int i = 0; i < channelsMap.at(channel).size(); ++i) {
index = fOffsetMap[channel] + i;

View File

@@ -39,19 +39,18 @@ namespace fair::mq::shmem
class Socket final : public fair::mq::Socket
{
public:
Socket(Manager& manager,
const std::string& type,
const std::string& name,
const std::string& id,
void* context,
fair::mq::TransportFactory* fac = nullptr)
Socket(Manager& manager, const std::string& type, const std::string& name, const std::string& id, void* context, fair::mq::TransportFactory* fac = nullptr)
: fair::mq::Socket(fac)
, fManager(manager)
, fId(id + "." + name + "." + type)
, fSocket(nullptr)
, fMonitorSocket(nullptr)
, fBytesTx(0)
, fBytesRx(0)
, fMessagesTx(0)
, fMessagesRx(0)
, fTimeout(100)
, fConnectedPeersCount(0)
, fMetadataMsgSize(manager.GetMetadataMsgSize())
{
assert(context);
@@ -457,15 +456,15 @@ class Socket final : public fair::mq::Socket
private:
Manager& fManager;
std::string fId;
void* fSocket{nullptr};
void* fMonitorSocket{nullptr};
void* fSocket;
void* fMonitorSocket;
std::atomic<unsigned long> fBytesTx;
std::atomic<unsigned long> fBytesRx;
std::atomic<unsigned long> fMessagesTx;
std::atomic<unsigned long> fMessagesRx;
int fTimeout{100};
mutable unsigned long fConnectedPeersCount{0};
int fTimeout;
mutable unsigned long fConnectedPeersCount;
std::size_t fMetadataMsgSize;
};

View File

@@ -118,10 +118,7 @@ class TransportFactory final : public fair::mq::TransportFactory
return std::make_unique<Message>(*fManager, data, size, ffn, hint, this);
}
MessagePtr CreateMessage(UnmanagedRegionPtr& region,
void* data,
size_t size,
void* hint = nullptr) override
MessagePtr CreateMessage(UnmanagedRegionPtr& region, void* data, size_t size, void* hint = 0) override
{
return std::make_unique<Message>(*fManager, region, data, size, hint, this);
}
@@ -201,8 +198,6 @@ class TransportFactory final : public fair::mq::TransportFactory
void Resume() override { fManager->Resume(); }
void Reset() override { fManager->Reset(); }
char* GetDataAddressFromHandle(const MetaHeader& meta) { return fManager->GetDataAddressFromHandle(meta); }
~TransportFactory() override
{
LOG(debug) << "Destroying Shared Memory transport...";

View File

@@ -63,7 +63,9 @@ struct UnmanagedRegion
, fName(MakeShmName(shmId, "rg", cfg.id.value()))
, fQueueName(MakeShmName(shmId, "rgq", cfg.id.value()))
, fRefCountSegmentName(MakeShmName(shmId, "rrc", cfg.id.value()))
, fShmemObject()
, fFile(nullptr)
, fFileMapping()
, fRcSegmentSize(cfg.rcSegmentSize)
, fQueue(nullptr)
, fCallback(nullptr)
@@ -100,7 +102,7 @@ struct UnmanagedRegion
}
fFileMapping = file_mapping(fName.c_str(), read_write);
LOG(debug) << "UnmanagedRegion(): initialized file: " << fName;
fRegion = mapped_region(fFileMapping, read_write, 0, size, nullptr, cfg.creationFlags);
fRegion = mapped_region(fFileMapping, read_write, 0, size, 0, cfg.creationFlags);
} else {
try {
// if opening fails, create
@@ -124,7 +126,7 @@ struct UnmanagedRegion
}
try {
fRegion = mapped_region(fShmemObject, read_write, 0, 0, nullptr, cfg.creationFlags);
fRegion = mapped_region(fShmemObject, read_write, 0, 0, 0, cfg.creationFlags);
if (size != 0 && size != fRegion.get_size()) {
LOG(error) << "Created/opened region size (" << fRegion.get_size() << ") does not match configured size (" << size << ")";
throw TransportError(tools::ToString("Created/opened region size (", fRegion.get_size(), ") does not match configured size (", size, ")"));

View File

@@ -37,6 +37,8 @@ class UnmanagedRegionImpl final : public fair::mq::UnmanagedRegion
fair::mq::TransportFactory* factory)
: fair::mq::UnmanagedRegion(factory)
, fManager(manager)
, fRegion(nullptr)
, fRegionId(0)
{
auto [regionPtr, regionId] = fManager.CreateRegion(size, callback, bulkCallback, std::move(cfg));
fRegion = regionPtr;
@@ -60,8 +62,8 @@ class UnmanagedRegionImpl final : public fair::mq::UnmanagedRegion
private:
Manager& fManager;
shmem::UnmanagedRegion* fRegion{nullptr};
uint16_t fRegionId{0};
shmem::UnmanagedRegion* fRegion;
uint16_t fRegionId;
};
} // namespace fair::mq::shmem

View File

@@ -99,7 +99,7 @@ string getDefaultRouteNetworkInterface()
string interfaceName;
#ifdef __APPLE__ // MacOS
unique_ptr<FILE, int (*)(FILE*)> file(
unique_ptr<FILE, decltype(pclose)*> file(
popen("route -n get default | grep interface | cut -d \":\" -f 2", "r"), pclose);
#else // Linux
ifstream is("/proc/net/route");
@@ -128,7 +128,7 @@ string getDefaultRouteNetworkInterface()
LOG(debug) << "could not get network interface of the default route from /proc/net/route, "
"going to try via 'ip route'";
unique_ptr<FILE, int (*)(FILE*)> file(
unique_ptr<FILE, decltype(pclose)*> file(
popen("ip route | grep default | cut -d \" \" -f 5 | head -n 1", "r"), pclose);
#endif

View File

@@ -10,26 +10,17 @@
#include <fairmq/tools/Strings.h>
#include <boost/asio.hpp>
#ifdef FAIRMQ_BOOST_PROCESS_V1_HEADER
#include <boost/process/v1.hpp>
#else
#include <boost/process.hpp>
#endif
#include <chrono>
#include <csignal> // kill, signals
#include <iostream>
#include <mutex>
#include <sstream>
#include <stdexcept>
#include <thread>
#include <utility>
using namespace std;
#ifdef FAIRMQ_BOOST_PROCESS_V1_HEADER
namespace bp = boost::process::v1;
#else
namespace bp = boost::process;
#endif
namespace ba = boost::asio;
namespace bs = boost::system;
@@ -41,18 +32,10 @@ class LinePrinter
, fPrefix(std::move(prefix))
{}
// prints line with prefix on both cout and output stream
// prints line with prefix on both cout (thread-safe) and output stream
void Print(const string& line)
{
// Serialize: the standard allows concurrent insertion on std::cout,
// but libstdc++ maintains the formatted-output state (e.g.
// ios_base::width) with plain reads and writes that constitute data
// races; the lock also keeps whole lines from interleaving.
static mutex sCoutMutex;
{
lock_guard<mutex> lock(sCoutMutex);
cout << fair::mq::tools::ToString(fPrefix, line, "\n") << flush;
}
fOut << fPrefix << line << endl;
}

View File

@@ -37,6 +37,7 @@ class Context
Context(int numIoThreads)
: fZmqCtx(zmq_ctx_new())
, fInterrupted(false)
, fRegionCounter(1)
{
if (!fZmqCtx) {
throw ContextError(tools::ToString("failed creating context, reason: ", zmq_strerror(errno)));
@@ -179,7 +180,7 @@ class Context
mutable std::mutex fMtx;
std::atomic<bool> fInterrupted;
uint16_t fRegionCounter{1};
uint16_t fRegionCounter;
std::condition_variable fRegionEventsCV;
std::vector<RegionInfo> fRegionInfos;
std::queue<RegionInfo> fRegionEvents;

View File

@@ -107,11 +107,7 @@ class Message final : public fair::mq::Message
}
}
Message(UnmanagedRegionPtr& region,
void* data,
const size_t size,
void* hint = nullptr,
fair::mq::TransportFactory* factory = nullptr)
Message(UnmanagedRegionPtr& region, void* data, const size_t size, void* hint = 0, fair::mq::TransportFactory* factory = nullptr)
: fair::mq::Message(factory)
, fMsg(std::make_unique<zmq_msg_t>())
{

View File

@@ -86,7 +86,7 @@ class Poller final : public fair::mq::Poller
fItems = new zmq_pollitem_t[fNumItems];
int index = 0;
for (const std::string& channel : channelList) {
for (std::string channel : channelList) {
for (unsigned int i = 0; i < channelsMap.at(channel).size(); ++i) {
index = fOffsetMap[channel] + i;

View File

@@ -41,7 +41,8 @@ class Socket final : public fair::mq::Socket
, fBytesRx(0)
, fMessagesTx(0)
, fMessagesRx(0)
, fTimeout(100)
, fConnectedPeersCount(0)
{
if (fSocket == nullptr) {
LOG(error) << "Failed creating socket " << fId << ", reason: " << zmq_strerror(errno);
@@ -404,8 +405,8 @@ class Socket final : public fair::mq::Socket
std::atomic<unsigned long> fMessagesTx;
std::atomic<unsigned long> fMessagesRx;
int fTimeout{100};
mutable unsigned long fConnectedPeersCount{0};
int fTimeout;
mutable unsigned long fConnectedPeersCount;
};
} // namespace fair::mq::zmq

View File

@@ -73,10 +73,7 @@ class TransportFactory final : public fair::mq::TransportFactory
return std::make_unique<Message>(data, size, ffn, hint, this);
}
MessagePtr CreateMessage(UnmanagedRegionPtr& region,
void* data,
size_t size,
void* hint = nullptr) override
MessagePtr CreateMessage(UnmanagedRegionPtr& region, void* data, size_t size, void* hint = 0) override
{
return std::make_unique<Message>(region, data, size, hint, this);
}

View File

@@ -16,33 +16,18 @@ if(FairLogger_VERSION VERSION_LESS 1.9.0 AND FairLogger_VERSION VERSION_GREATER_
LIST(APPEND definitions FAIR_MIN_SEVERITY=trace)
endif()
if(FAIRMQ_BOOST_PROCESS_V1_HEADER)
LIST(APPEND definitions FAIRMQ_BOOST_PROCESS_V1_HEADER)
endif()
if(definitions)
set(definitions DEFINITIONS ${definitions})
endif()
set(test_environment)
if(ENABLE_SANITIZER_LEAK)
get_filename_component(lsan_supps "${CMAKE_CURRENT_SOURCE_DIR}/leak_sanitizer_suppressions.txt" ABSOLUTE)
list(APPEND test_environment "LSAN_OPTIONS=set:suppressions=${lsan_supps}")
endif()
# Run the tests against an alternative runtime library directory, e.g. a
# tsan-instrumented libstdc++. Set per test (not at the job level), because
# such a library must only be loaded into instrumented executables.
if(FAIRMQ_TEST_LD_LIBRARY_PATH)
list(APPEND test_environment "LD_LIBRARY_PATH=path_list_prepend:${FAIRMQ_TEST_LD_LIBRARY_PATH}")
endif()
if(test_environment)
set(environment ENVIRONMENT ${test_environment})
set(environment ENVIRONMENT "LSAN_OPTIONS=set:suppressions=${lsan_supps}")
endif()
add_testhelper(runTestDevice
SOURCES
helper/runTestDevice.cxx
helper/LocaleWarmup.h
helper/devices/TestPairLeft.h
helper/devices/TestPairRight.h
helper/devices/TestPollIn.h
@@ -149,7 +134,6 @@ add_testsuite(Device
${CMAKE_CURRENT_SOURCE_DIR}/device
${CMAKE_CURRENT_BINARY_DIR}
TIMEOUT 20
${definitions}
${environment}
)

View File

@@ -0,0 +1,19 @@
ARG VERSION=latest
FROM fedora:${VERSION}
ARG VERSION=latest
LABEL org.opencontainers.image.source "https://github.com/FairRootGroup/FairMQ"
LABEL org.opencontainers.image.description "FairMQ development environment"
RUN dnf -y update
# https://git.gsi.de/SDE/packages/builder
RUN dnf -y install https://alfa-ci.gsi.de/packages/rpm/fedora-$VERSION-x86_64/fairsoft-release-dev.rpm
RUN dnf -y install clang cli11-devel ninja-build 'dnf-command(builddep)' libasan liblsan libtsan libubsan clang-tools-extra
RUN dnf -y builddep fairmq
RUN dnf -y clean all
# buildah build --build-arg "VERSION=36" -t "ghcr.io/fairrootgroup/fairmq-dev/fedora-36:latest" -f test/ci/Containerfile.fedora .
# echo $GH_PAT | buildah login -u dennisklein --password-stdin ghcr.io
# buildah push ghcr.io/fairrootgroup/fairmq-dev/fedora-36:latest
# apptainer pull docker://ghcr.io/fairrootgroup/fairmq-dev/fedora-36:latest
# echo $GH_PAT | apptainer remote login -u dennisklein --password-stdin oras://ghcr.io
# apptainer push ./fedora-36_latest.sif oras://ghcr.io/fairrootgroup/fairmq-dev/fedora-36-sif:latest

View File

@@ -0,0 +1,36 @@
ARG VERSION=latest
FROM ubuntu:${VERSION}
ARG VERSION=latest
LABEL org.opencontainers.image.source "https://github.com/FairRootGroup/FairMQ"
LABEL org.opencontainers.image.description "FairMQ development environment"
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
RUN apt-get update
RUN apt-get -y upgrade
RUN apt-get -y install ca-certificates patch cmake git libboost-dev libboost-log-dev libboost-system-dev libboost-regex-dev libboost-filesystem-dev libboost-container-dev libboost-thread-dev libboost-date-time-dev libboost-program-options-dev g++ libfmt-dev ninja-build wget libczmq-dev libxml2-utils libfabric-dev libfabric-bin pkg-config
RUN apt-get -y clean
RUN cd /tmp
RUN git clone -b v1.19.2 --recurse-submodules https://github.com/FairRootGroup/asio
RUN cmake -GNinja -S asio -B asio_build -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release
RUN cmake --build asio_build --target install
RUN rm -rf asio asio_build
RUN git clone -b v1.0.0 https://github.com/FairRootGroup/FairCMakeModules
RUN cmake -GNinja -S FairCMakeModules -B FairCMakeModules_build -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release
RUN cmake --build FairCMakeModules_build --target install
RUN rm -rf FairCMakeModules FairCMakeModules_build
RUN git clone -b v1.11.0 https://github.com/FairRootGroup/FairLogger
RUN cmake -GNinja -S FairLogger -B FairLogger_build -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DUSE_EXTERNAL_FMT=ON
RUN cmake --build FairLogger_build --target install
RUN rm -rf FairLogger FairLogger_build
# buildah build --build-arg "VERSION=22.04" -t "ghcr.io/fairrootgroup/fairmq-dev/ubuntu-22.04:latest" -f test/ci/Containerfile.ubuntu .
# echo $GH_PAT | buildah login -u dennisklein --password-stdin ghcr.io
# buildah push ghcr.io/fairrootgroup/fairmq-dev/ubuntu-22.04:latest
# apptainer pull docker://ghcr.io/fairrootgroup/fairmq-dev/ubuntu-22.04:latest
# echo $GH_PAT | apptainer remote login -u dennisklein --password-stdin oras://ghcr.io
# apptainer push ./ubuntu-22.04_latest.sif oras://ghcr.io/fairrootgroup/fairmq-dev/ubuntu-22.04-sif:latest

42
test/ci/slurm-submit.sh Executable file
View File

@@ -0,0 +1,42 @@
#! /bin/bash
label="$1"
jobsh="$2"
if [ -z "$ALFACI_SLURM_CPUS" ]
then
ALFACI_SLURM_CPUS=20
fi
CPUS_PER_JOB=$(($ALFACI_SLURM_CPUS / 2))
if [ -z "$ALFACI_SLURM_EXTRA_OPTS" ]
then
ALFACI_SLURM_EXTRA_OPTS="--hint=compute_bound"
fi
if [ -z "$ALFACI_SLURM_TIMEOUT" ]
then
ALFACI_SLURM_TIMEOUT=30
fi
if [ -z "$ALFACI_SLURM_QUEUE" ]
then
ALFACI_SLURM_QUEUE=main
fi
echo "*** Slurm request options :"
echo "*** Working directory ..: $PWD"
echo "*** Queue ..............: $ALFACI_SLURM_QUEUE"
echo "*** CPUs ...............: $ALFACI_SLURM_CPUS"
echo "*** Wall Time ..........: $ALFACI_SLURM_TIMEOUT min"
echo "*** Job Name ...........: ${label}"
echo "*** Extra Options ......: ${ALFACI_SLURM_EXTRA_OPTS}"
echo "*** Submitting job at ....: $(date -R)"
(
set -x
srun -p $ALFACI_SLURM_QUEUE -c $CPUS_PER_JOB -n 1 \
-t $ALFACI_SLURM_TIMEOUT \
--job-name="${label}" \
${ALFACI_SLURM_EXTRA_OPTS} \
bash "${jobsh}"
)
retval=$?
echo "*** Exit Code ............: $retval"
exit "$retval"

View File

@@ -1,21 +0,0 @@
spack:
specs:
- "fairlogger@2.2.0 ^fmt@:11"
- "boost@1.87 +container +program_options +filesystem +date_time +regex"
- "libzmq@4.1.4:"
- ninja
- "cmake@3.15:"
concretizer:
targets:
granularity: generic
packages:
all:
require:
- target=x86_64_v3
mirrors:
ghcr-buildcache:
url: oci://ghcr.io/fairrootgroup/fairmq-spack-buildcache
signed: false

View File

@@ -1,21 +0,0 @@
spack:
specs:
- "fairlogger@2.2.0 ^fmt@:11"
- "boost@1.66: +container +program_options +filesystem +date_time +regex"
- "libzmq@4.1.4:"
- ninja
- "cmake@3.15:"
concretizer:
targets:
granularity: generic
packages:
all:
require:
- target=x86_64_v3
mirrors:
ghcr-buildcache:
url: oci://ghcr.io/fairrootgroup/fairmq-spack-buildcache
signed: false

View File

@@ -1,47 +0,0 @@
spack:
specs:
# fairlogger and boost are left uninstrumented on purpose: no tsan report
# has ever implicated them. Should that change, instrument them with
# per-node flags like libzmq below.
- "fairlogger@2.2.0 ^fmt@:11"
- "boost@1.66: +container +program_options +filesystem +date_time +regex"
# libzmq (and the libsodium it links) are built with ThreadSanitizer
# instrumentation so tsan can observe the happens-before edges that
# libzmq's lock-free queues establish between user and I/O threads.
# Flags are set per node on purpose: the propagating '==' operator could
# reach the gcc node (the compiler is a dependency since spack 1.0) and
# trigger a full compiler rebuild.
- "libzmq@4.1.4: cflags='-g -fno-omit-frame-pointer -fsanitize=thread'
cxxflags='-g -fno-omit-frame-pointer -fsanitize=thread'
ldflags='-fsanitize=thread'
^libsodium cflags='-g -fno-omit-frame-pointer -fsanitize=thread'
ldflags='-fsanitize=thread'"
# ThreadSanitizer-instrumented libstdc++ (from test/ci/spack_repo), a
# drop-in runtime replacement injected per test via LD_LIBRARY_PATH (see
# FAIRMQ_TEST_LD_LIBRARY_PATH in test/CMakeLists.txt).
# Keep the major version in lockstep with the gcc used by the tsan job.
- "libstdcxx-tsan@15"
- ninja
# the per-test env injection uses ctest's ENVIRONMENT_MODIFICATION,
# which needs cmake >= 3.22
- "cmake@3.22:"
concretizer:
targets:
granularity: generic
reuse:
# always re-concretize libstdcxx-tsan from the recipe in
# test/ci/spack_repo: spec reuse would keep resurrecting previously
# built (and cached) variants after the recipe changed; unchanged
# recipes still hit the buildcache because the hash is identical
exclude: ["libstdcxx-tsan"]
packages:
all:
require:
- target=x86_64_v3
mirrors:
ghcr-buildcache:
url: oci://ghcr.io/fairrootgroup/fairmq-spack-buildcache
signed: false

View File

@@ -1,90 +0,0 @@
import os
import shutil
from spack_repo.builtin.build_systems.generic import Package
from spack.package import *
class LibstdcxxTsan(Package):
"""ThreadSanitizer-instrumented build of GCC's libstdc++.
GCC ships no supported way to produce a tsan-instrumented libstdc++ (and
spack's gcc package offers no hook into the target-library flags), so this
package builds only the libstdc++-v3 subtree from the GCC release tarball
matching the toolchain, with -fsanitize=thread. The result is a drop-in
runtime replacement for the compiler's own libstdc++ (same soname and
symbol versions) that lets tsan observe the synchronization inside the
C++ standard library (e.g. std::locale/std::regex lazy initialization).
Select it via LD_LIBRARY_PATH in the environment of the instrumented
executables only. Like any shared library built with gcc
-fsanitize=thread it carries unresolved __tsan_* symbols satisfied by the
executable's libtsan, so loading it into an uninstrumented executable
fails.
Recipe modeled on https://iree.dev/developers/debugging/sanitizers/.
"""
homepage = "https://gcc.gnu.org/onlinedocs/libstdc++/"
url = "https://ftp.gnu.org/gnu/gcc/gcc-15.2.0/gcc-15.2.0.tar.xz"
# Keep in lockstep with the gcc version used by the tsan CI job: a
# libstdc++ older than the compiler may lack GLIBCXX symbol versions
# that binaries built by that compiler reference. Checksums match the
# version() directives in spack's gcc package.
version("15.2.0", sha256="438fd996826b0c82485a29da03a72d71d6e3541a83ec702df4271f6fe025d24e")
version("14.3.0", sha256="e0dc77297625631ac8e50fa92fffefe899a4eb702592da5c32ef04e2293aca3a")
depends_on("c", type="build")
depends_on("cxx", type="build")
depends_on("gmake", type="build")
def install(self, spec, prefix):
# gthr-default.h only materializes in a configured libgcc build dir;
# on POSIX targets it is a verbatim copy of gthr-posix.h. Providing it
# up front spares building libgcc (and the in-tree compiler).
gthr_include = join_path(self.stage.source_path, "spack-gthr")
mkdirp(gthr_include)
copy(
join_path(self.stage.source_path, "libgcc", "gthr-posix.h"),
join_path(gthr_include, "gthr-default.h"),
)
flags = f"-I{gthr_include} -O2 -g -fno-omit-frame-pointer -fsanitize=thread"
build_dir = join_path(self.stage.source_path, "spack-build")
mkdirp(build_dir)
with working_dir(build_dir):
configure = Executable(
join_path(self.stage.source_path, "libstdc++-v3", "configure")
)
configure(
f"--prefix={prefix}",
f"--libdir={prefix.lib}",
"--disable-multilib",
"--disable-libstdcxx-pch",
"--enable-libstdcxx-threads=yes",
"--with-default-libstdcxx-abi=new",
f"CFLAGS={flags}",
f"CXXFLAGS={flags}",
"LDFLAGS=-fsanitize=thread",
)
make()
make("install")
# The runtime libraries land in the multilib os dir (lib64 on
# x86_64), --libdir notwithstanding (--with-toolexeclibdir only
# applies to cross builds); normalize to lib/ for the consumer.
if os.path.isdir(prefix.lib64):
mkdirp(prefix.lib)
for entry in os.listdir(prefix.lib64):
shutil.move(join_path(prefix.lib64, entry), join_path(prefix.lib, entry))
os.rmdir(prefix.lib64)
# Only the runtime library is consumed -- compilation uses the
# compiler's own (identical) headers. Drop the rest to keep the
# buildcache entry small.
for directory in ("include", "share"):
path = join_path(prefix, directory)
if os.path.isdir(path):
shutil.rmtree(path)

View File

@@ -1,3 +0,0 @@
repo:
namespace: fairmq_ci
api: v2.0

View File

@@ -9,11 +9,7 @@
#include "runner.h"
#include <gtest/gtest.h>
#ifdef FAIRMQ_BOOST_PROCESS_V1_HEADER
#include <boost/process/v1.hpp>
#else
#include <boost/process.hpp>
#endif
#include <fairmq/tools/Process.h>
#include <fairmq/tools/Unique.h>
#include <fairmq/Device.h>

View File

@@ -9,11 +9,7 @@
#include "runner.h"
#include <gtest/gtest.h>
#ifdef FAIRMQ_BOOST_PROCESS_V1_HEADER
#include <boost/process/v1.hpp>
#else
#include <boost/process.hpp>
#endif
#include <fairmq/tools/Process.h>
#include <fairmq/tools/Unique.h>

View File

@@ -9,11 +9,7 @@
#include "runner.h"
#include <gtest/gtest.h>
#ifdef FAIRMQ_BOOST_PROCESS_V1_HEADER
#include <boost/process/v1.hpp>
#else
#include <boost/process.hpp>
#endif
#include <fairmq/tools/Unique.h>
#include <fairmq/tools/Process.h>

View File

@@ -29,17 +29,13 @@ TEST(EventManager, Basics)
std::function<void(typename TestEvent::KeyType, int)> callback{
[&](TestEvent::KeyType key, int newValue){
++call_counter;
if (key == "test") {
value = newValue;
}
if (key == "test") value = newValue;
}
};
std::function<void(typename TestEvent::KeyType, string)> callback2{
[&](TestEvent::KeyType key, string newValue){
++call_counter2;
if (key == "test") {
value2 = newValue;
}
if (key == "test") value2 = newValue;
}
};

View File

@@ -1,63 +0,0 @@
/********************************************************************************
* Copyright (C) 2026 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_TEST_LOCALEWARMUP_H
#define FAIR_MQ_TEST_LOCALEWARMUP_H
#include <locale>
#include <sstream>
namespace fair::mq::test {
/// libstdc++'s std::ctype<char> caches narrow()/widen() results per character
/// in plain char arrays of the global classic-locale facet -- by design with
/// unsynchronized writes, emitted from header-inlined code (locale_facets.h).
/// Whenever two threads exercise an uncached character concurrently (e.g. by
/// compiling a std::regex), ThreadSanitizer rightfully reports the write as a
/// data race. The stores are real and unsynchronized, so instrumenting
/// libstdc++ cannot help; instead, fill the caches before any thread exists,
/// which turns every later access into a pure read.
inline void WarmUpLocaleCaches()
{
auto const& ct = std::use_facet<std::ctype<char>>(std::locale::classic());
for (int c = 1; c < 256; ++c) {
auto const ch = static_cast<char>(c);
ct.narrow(ch, '\0');
ct.widen(ch);
}
// Only the range overload runs _M_narrow_init(), which sets the
// all-cached flag (_M_narrow_ok); the single-char loop above fills the
// cache without it. (widen() needs no equivalent: the single-char
// overload already runs _M_widen_init().)
char from[256];
char to[256];
for (int c = 0; c < 256; ++c) {
from[c] = static_cast<char>(c);
}
ct.narrow(from, from + 256, '?', to);
// num_put/num_get caches used by stream insertion/extraction
std::ostringstream os;
os << 4711 << ' ' << 3.14;
std::istringstream is("42 3.14");
int i = 0;
double d = 0.;
is >> i >> d;
}
/// For binaries that do not own main(): define a namespace-scope instance to
/// run the warm-up during static initialization, before main().
struct LocaleWarmup
{
LocaleWarmup() { WarmUpLocaleCaches(); }
};
} // namespace fair::mq::test
#endif /* FAIR_MQ_TEST_LOCALEWARMUP_H */

View File

@@ -6,7 +6,6 @@
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include "LocaleWarmup.h"
#include "devices/TestPairLeft.h"
#include "devices/TestPairRight.h"
#include "devices/TestPollIn.h"
@@ -31,10 +30,6 @@
namespace bpo = boost::program_options;
namespace {
[[maybe_unused]] fair::mq::test::LocaleWarmup const gLocaleWarmup{};
}
auto addCustomOptions(bpo::options_description& options) -> void
{
options.add_options()

View File

@@ -1,2 +1 @@
leak:zmq_msg_init_size
leak:zmq::msg_t::init_size

View File

@@ -453,46 +453,6 @@ TEST(EmptyMessage, shmem_expanded_metadata) // NOLINT
EmptyMessage("shmem", "ipc://test_empty_message", true);
}
// GetMeta() + GetDataAddressFromHandle() round-trip: simulates a consumer device that
// receives message metadata over a side channel and resolves it to a local pointer.
// Uses a second factory on the same session to exercise the open_only segment attach path.
auto SideChannel(bool expandedShmMetadata = false) -> void
{
const string session = tools::Uuid();
ProgOptions producerConfig;
producerConfig.SetProperty<string>("session", session);
producerConfig.SetProperty<size_t>("shm-segment-size", 100000000);
producerConfig.SetProperty<bool>("shm-monitor", true);
if (expandedShmMetadata) {
producerConfig.SetProperty<size_t>("shm-metadata-msg-size", 2048);
}
auto producerFactory(TransportFactory::CreateTransportFactory("shmem", tools::Uuid(), &producerConfig));
ProgOptions consumerConfig;
consumerConfig.SetProperty<string>("session", session);
consumerConfig.SetProperty<size_t>("shm-segment-size", 100000000);
consumerConfig.SetProperty<bool>("shm-monitor", true);
auto consumerFactory(TransportFactory::CreateTransportFactory("shmem", tools::Uuid(), &consumerConfig));
const size_t size = 2;
MessagePtr msg(producerFactory->CreateMessage(size));
memcpy(msg->GetData(), "AB", size);
// producer side: extract metadata to send over the side channel
const auto meta = static_cast<const shmem::Message&>(*msg).GetMeta();
EXPECT_EQ(meta.fSize, size);
EXPECT_TRUE(meta.fManaged);
// consumer side: resolve via a different factory — exercises open_only segment attach.
// The virtual address differs between factory mappings; only the content must match.
char* ptr = shmem::GetDataAddressFromHandle(*consumerFactory, meta);
ASSERT_NE(ptr, nullptr);
EXPECT_EQ(ptr[0], 'A');
EXPECT_EQ(ptr[1], 'B');
}
TEST(ZeroCopy, shmem) // NOLINT
{
ZeroCopy();
@@ -503,93 +463,6 @@ TEST(ZeroCopy, shmem_expanded_metadata) // NOLINT
ZeroCopy(true);
}
// Uses a second factory on the same session to exercise the remote region lookup path.
auto SideChannelUnmanaged() -> void
{
const string session = tools::Uuid();
ProgOptions producerConfig;
producerConfig.SetProperty<string>("session", session);
producerConfig.SetProperty<size_t>("shm-segment-size", 100000000);
producerConfig.SetProperty<bool>("shm-monitor", true);
auto producerFactory(TransportFactory::CreateTransportFactory("shmem", tools::Uuid(), &producerConfig));
ProgOptions consumerConfig;
consumerConfig.SetProperty<string>("session", session);
consumerConfig.SetProperty<size_t>("shm-segment-size", 100000000);
consumerConfig.SetProperty<bool>("shm-monitor", true);
auto consumerFactory(TransportFactory::CreateTransportFactory("shmem", tools::Uuid(), &consumerConfig));
const size_t regionSize = 1000000;
tools::Semaphore blocker;
auto region = producerFactory->CreateUnmanagedRegion(regionSize, [&blocker](void*, size_t, void*) {
blocker.Signal();
});
const size_t size = 2;
auto msg(producerFactory->CreateMessage(region, static_cast<char*>(region->GetData()), size, nullptr));
memcpy(msg->GetData(), "AB", size);
const auto meta = static_cast<const shmem::Message&>(*msg).GetMeta();
EXPECT_EQ(meta.fSize, size);
EXPECT_FALSE(meta.fManaged);
// consumer resolves via its own factory — exercises remote region lookup.
// The virtual address differs between factory mappings; only the content must match.
char* ptr = shmem::GetDataAddressFromHandle(*consumerFactory, meta);
ASSERT_NE(ptr, nullptr);
EXPECT_EQ(ptr[0], 'A');
EXPECT_EQ(ptr[1], 'B');
msg.reset();
blocker.Wait();
}
auto SideChannelErrors() -> void
{
ProgOptions config;
config.SetProperty<string>("session", tools::Uuid());
config.SetProperty<size_t>("shm-segment-size", 100000000);
config.SetProperty<bool>("shm-monitor", true);
auto shmFactory(TransportFactory::CreateTransportFactory("shmem", tools::Uuid(), &config));
auto zmqFactory(TransportFactory::CreateTransportFactory("zeromq", tools::Uuid(), &config));
// non-shmem factory must throw
shmem::MetaHeader meta{};
meta.fManaged = true;
EXPECT_THROW(shmem::GetDataAddressFromHandle(*zmqFactory, meta), shmem::SharedMemoryError);
// bad segment id must throw
meta.fSize = 1;
meta.fSegmentId = 999;
EXPECT_THROW(shmem::GetDataAddressFromHandle(*shmFactory, meta), shmem::SharedMemoryError);
// zero-size managed message must return nullptr (mirrors Message::GetData())
meta.fSize = 0;
EXPECT_EQ(shmem::GetDataAddressFromHandle(*shmFactory, meta), nullptr);
}
TEST(SideChannel, shmem) // NOLINT
{
SideChannel();
}
TEST(SideChannel, shmem_expanded_metadata) // NOLINT
{
SideChannel(true);
}
TEST(SideChannel, shmem_unmanaged) // NOLINT
{
SideChannelUnmanaged();
}
TEST(SideChannel, errors) // NOLINT
{
SideChannelErrors();
}
TEST(ZeroCopyFromUnmanaged, shmem) // NOLINT
{
ZeroCopyFromUnmanaged("ipc://test_zerocopy_unmanaged", false, 10000000);

View File

@@ -156,7 +156,7 @@ TEST_F(PluginServices, SubscriptionThreadSafety)
std::array<std::unique_ptr<std::thread>, subscribers> threads;
auto id = 0;
for (auto& thread : threads) {
thread = std::make_unique<std::thread>([&, id](){
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){});

View File

@@ -10,7 +10,6 @@
#include <TestEnvironment.h>
#include <gtest/gtest.h>
#include <helper/LocaleWarmup.h>
#include <string>
@@ -25,7 +24,6 @@ string runTestDevice = "@RUN_TEST_DEVICE@";
int main(int argc, char** argv)
{
fair::mq::test::WarmUpLocaleCaches();
::testing::InitGoogleTest(&argc, argv);
::testing::FLAGS_gtest_death_test_style = "threadsafe";
setenv("FAIRMQ_PATH", FAIRMQ_TEST_ENVIRONMENT, 0);