├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .pre-commit-config.yaml ├── CMakeLists.txt ├── _clang-format ├── build.jam ├── doc ├── Jamfile.v2 └── lockfree.qbk ├── examples ├── Jamfile.v2 ├── queue.cpp ├── spsc_queue.cpp └── stack.cpp ├── include └── boost │ └── lockfree │ ├── detail │ ├── atomic.hpp │ ├── copy_payload.hpp │ ├── freelist.hpp │ ├── parameter.hpp │ ├── prefix.hpp │ ├── tagged_ptr.hpp │ ├── tagged_ptr_dcas.hpp │ ├── tagged_ptr_ptrcompression.hpp │ └── uses_optional.hpp │ ├── lockfree_forward.hpp │ ├── policies.hpp │ ├── queue.hpp │ ├── spsc_queue.hpp │ ├── spsc_value.hpp │ └── stack.hpp ├── index.html ├── meta └── libraries.json └── test ├── CMakeLists.txt ├── Jamfile.v2 ├── cmake_install_test ├── CMakeLists.txt └── main.cpp ├── cmake_subdir_test └── CMakeLists.txt ├── destructor_test.cpp ├── freelist_test.cpp ├── queue_bounded_stress_test.cpp ├── queue_fixedsize_stress_test.cpp ├── queue_interprocess_test.cpp ├── queue_test.cpp ├── queue_unbounded_stress_test.cpp ├── spsc_queue_stress_test.cpp ├── spsc_queue_test.cpp ├── spsc_value_stress_test.cpp ├── spsc_value_test.cpp ├── stack_bounded_stress_test.cpp ├── stack_fixedsize_stress_test.cpp ├── stack_interprocess_test.cpp ├── stack_test.cpp ├── stack_unbounded_stress_test.cpp ├── tagged_ptr_test.cpp ├── test_common.hpp └── test_helpers.hpp /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto !eol svneol=native#text/plain 2 | *.gitattributes text svneol=native#text/plain 3 | 4 | # Scriptish formats 5 | *.bat text svneol=native#text/plain 6 | *.bsh text svneol=native#text/x-beanshell 7 | *.cgi text svneol=native#text/plain 8 | *.cmd text svneol=native#text/plain 9 | *.js text svneol=native#text/javascript 10 | *.php text svneol=native#text/x-php 11 | *.pl text svneol=native#text/x-perl 12 | *.pm text svneol=native#text/x-perl 13 | *.py text svneol=native#text/x-python 14 | *.sh eol=lf svneol=LF#text/x-sh 15 | configure eol=lf svneol=LF#text/x-sh 16 | 17 | # Image formats 18 | *.bmp binary svneol=unset#image/bmp 19 | *.gif binary svneol=unset#image/gif 20 | *.ico binary svneol=unset#image/ico 21 | *.jpeg binary svneol=unset#image/jpeg 22 | *.jpg binary svneol=unset#image/jpeg 23 | *.png binary svneol=unset#image/png 24 | *.tif binary svneol=unset#image/tiff 25 | *.tiff binary svneol=unset#image/tiff 26 | *.svg text svneol=native#image/svg%2Bxml 27 | 28 | # Data formats 29 | *.pdf binary svneol=unset#application/pdf 30 | *.avi binary svneol=unset#video/avi 31 | *.doc binary svneol=unset#application/msword 32 | *.dsp text svneol=crlf#text/plain 33 | *.dsw text svneol=crlf#text/plain 34 | *.eps binary svneol=unset#application/postscript 35 | *.gz binary svneol=unset#application/gzip 36 | *.mov binary svneol=unset#video/quicktime 37 | *.mp3 binary svneol=unset#audio/mpeg 38 | *.ppt binary svneol=unset#application/vnd.ms-powerpoint 39 | *.ps binary svneol=unset#application/postscript 40 | *.psd binary svneol=unset#application/photoshop 41 | *.rdf binary svneol=unset#text/rdf 42 | *.rss text svneol=unset#text/xml 43 | *.rtf binary svneol=unset#text/rtf 44 | *.sln text svneol=native#text/plain 45 | *.swf binary svneol=unset#application/x-shockwave-flash 46 | *.tgz binary svneol=unset#application/gzip 47 | *.vcproj text svneol=native#text/xml 48 | *.vcxproj text svneol=native#text/xml 49 | *.vsprops text svneol=native#text/xml 50 | *.wav binary svneol=unset#audio/wav 51 | *.xls binary svneol=unset#application/vnd.ms-excel 52 | *.zip binary svneol=unset#application/zip 53 | 54 | # Text formats 55 | .htaccess text svneol=native#text/plain 56 | *.bbk text svneol=native#text/xml 57 | *.cmake text svneol=native#text/plain 58 | *.css text svneol=native#text/css 59 | *.dtd text svneol=native#text/xml 60 | *.htm text svneol=native#text/html 61 | *.html text svneol=native#text/html 62 | *.ini text svneol=native#text/plain 63 | *.log text svneol=native#text/plain 64 | *.mak text svneol=native#text/plain 65 | *.qbk text svneol=native#text/plain 66 | *.rst text svneol=native#text/plain 67 | *.sql text svneol=native#text/x-sql 68 | *.txt text svneol=native#text/plain 69 | *.xhtml text svneol=native#text/xhtml%2Bxml 70 | *.xml text svneol=native#text/xml 71 | *.xsd text svneol=native#text/xml 72 | *.xsl text svneol=native#text/xml 73 | *.xslt text svneol=native#text/xml 74 | *.xul text svneol=native#text/xul 75 | *.yml text svneol=native#text/plain 76 | boost-no-inspect text svneol=native#text/plain 77 | CHANGES text svneol=native#text/plain 78 | COPYING text svneol=native#text/plain 79 | INSTALL text svneol=native#text/plain 80 | Jamfile text svneol=native#text/plain 81 | Jamroot text svneol=native#text/plain 82 | Jamfile.v2 text svneol=native#text/plain 83 | Jamrules text svneol=native#text/plain 84 | Makefile* text svneol=native#text/plain 85 | README text svneol=native#text/plain 86 | TODO text svneol=native#text/plain 87 | 88 | # Code formats 89 | *.c text svneol=native#text/plain 90 | *.cpp text svneol=native#text/plain 91 | *.h text svneol=native#text/plain 92 | *.hpp text svneol=native#text/plain 93 | *.ipp text svneol=native#text/plain 94 | *.tpp text svneol=native#text/plain 95 | *.jam text svneol=native#text/plain 96 | *.java text svneol=native#text/plain 97 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Actions CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | - develop 9 | - githubactions* 10 | - fix/** 11 | - pr/** 12 | 13 | jobs: 14 | runner-selection: 15 | runs-on: ubuntu-latest 16 | # runs-on: ${{ github.repository_owner == 'boostorg' && fromJSON('[ "self-hosted", "linux", "x64", "ubuntu-latest-aws" ]') || 'ubuntu-latest' }} 17 | outputs: 18 | labelmatrix: ${{ steps.aws_hosted_runners.outputs.labelmatrix }} 19 | steps: 20 | - name: AWS Hosted Runners 21 | id: aws_hosted_runners 22 | uses: cppalliance/aws-hosted-runners@v1.0.0 23 | 24 | posix: 25 | if: true 26 | defaults: 27 | run: 28 | shell: bash 29 | 30 | strategy: 31 | fail-fast: false 32 | matrix: 33 | include: 34 | - toolset: gcc-10 35 | cxxstd: "14,17,2a" 36 | os: ubuntu-22.04 37 | install: g++-10 38 | supported: true 39 | - toolset: gcc-11 40 | cxxstd: "14,17,20" 41 | os: ubuntu-22.04 42 | install: g++-11 43 | supported: true 44 | - toolset: gcc-12 45 | cxxstd: "14,17,20" 46 | os: ubuntu-22.04 47 | install: g++-12 48 | supported: true 49 | - toolset: gcc-13 50 | cxxstd: "14,17,20" 51 | os: ubuntu-22.04 52 | install: g++-13 53 | supported: true 54 | - toolset: gcc-14 55 | cxxstd: "14,17,20" 56 | os: ubuntu-24.04 57 | install: g++-14 58 | supported: true 59 | - toolset: clang 60 | install: clang-13 61 | compiler: clang++-13 62 | cxxstd: "14,17,20" 63 | os: ubuntu-22.04 64 | supported: true 65 | - toolset: clang 66 | install: clang-14 67 | compiler: clang++-14 68 | cxxstd: "14,17,20" 69 | os: ubuntu-22.04 70 | supported: true 71 | - toolset: clang 72 | install: clang-15 73 | compiler: clang++-15 74 | cxxstd: "14,17,20" 75 | os: ubuntu-22.04 76 | supported: true 77 | - toolset: clang 78 | install: clang-16 79 | compiler: clang++-16 80 | cxxstd: "14,17,20" 81 | os: ubuntu-24.04 82 | supported: true 83 | - toolset: clang 84 | compiler: clang++-17 85 | cxxstd: "14,17,20,2b" 86 | os: ubuntu-24.04 87 | install: clang-17 88 | supported: true 89 | - toolset: clang 90 | compiler: clang++-18 91 | cxxstd: "14,17,20,2b" 92 | os: ubuntu-24.04 93 | install: clang-18 94 | supported: true 95 | 96 | # macos 97 | - description: macos-13 98 | toolset: clang 99 | cxxstd: "14,17,20" 100 | os: macos-13 101 | supported: true 102 | - description: macos-14 103 | toolset: clang 104 | cxxstd: "14,17,2a" 105 | os: macos-14 106 | supported: true 107 | - description: macos-15 108 | toolset: clang 109 | cxxstd: "14,17,20" 110 | os: macos-15 111 | supported: true 112 | 113 | 114 | 115 | needs: [runner-selection] 116 | # runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.os] }} 117 | runs-on: ${{matrix.os}} 118 | container: ${{ matrix.container }} 119 | env: {B2_USE_CCACHE: 1} 120 | 121 | steps: 122 | - name: Check if running in container 123 | if: matrix.container != '' 124 | run: echo "GHA_CONTAINER=${{ matrix.container }}" >> $GITHUB_ENV 125 | - name: If running in container, upgrade packages 126 | if: matrix.container != '' 127 | run: | 128 | apt-get -o Acquire::Retries=3 update && DEBIAN_FRONTEND=noninteractive apt-get -y install tzdata && apt-get -o Acquire::Retries=3 install -y sudo software-properties-common wget curl apt-transport-https make apt-file sudo unzip libssl-dev build-essential autotools-dev autoconf automake g++ libc++-helpers python ruby cpio gcc-multilib g++-multilib pkgconf python3 ccache libpython-dev locales 129 | sudo apt-add-repository ppa:git-core/ppa 130 | sudo apt-get -o Acquire::Retries=3 update && apt-get -o Acquire::Retries=3 -y install git 131 | python_version=$(python3 -c 'import sys; print("{0.major}.{0.minor}".format(sys.version_info))') 132 | if [[ ${python_version} =~ ^3\.[0-5]$ ]]; then 133 | true 134 | else 135 | apt-get install -y python3-distutils 136 | fi 137 | sudo wget https://bootstrap.pypa.io/pip/$python_version/get-pip.py 138 | sudo python3 get-pip.py 139 | sudo /usr/local/bin/pip install cmake 140 | - uses: actions/checkout@v3 141 | 142 | - name: Cache ccache 143 | uses: actions/cache@v4 144 | if: env.B2_USE_CCACHE 145 | with: 146 | path: ~/.ccache 147 | key: ${{matrix.os}}-${{matrix.container}}-${{matrix.compiler}}-${{github.sha}} 148 | restore-keys: ${{matrix.os}}-${{matrix.container}}-${{matrix.compiler}}- 149 | 150 | - name: Install packages 151 | if: matrix.install 152 | run: | 153 | for i in {1..3}; do sudo -E apt-add-repository -y "ppa:ubuntu-toolchain-r/test" && break || sleep 2; done 154 | sudo apt-get update 155 | sudo apt-get install -y ${{matrix.install}} 156 | 157 | - name: Setup Boost 158 | shell: bash 159 | run: | 160 | if [[ $(uname) =~ [Ll]inux ]]; then 161 | echo Installing locales for ${TRAVIS_OS_NAME} ... 162 | sudo /usr/sbin/locale-gen fr_FR 163 | sudo /usr/sbin/locale-gen en_GB 164 | sudo locale -a 165 | echo ...done with locales 166 | fi 167 | echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY 168 | LIBRARY=${GITHUB_REPOSITORY#*/} 169 | echo LIBRARY: $LIBRARY 170 | echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV 171 | echo GITHUB_BASE_REF: $GITHUB_BASE_REF 172 | echo GITHUB_REF: $GITHUB_REF 173 | REF=${GITHUB_BASE_REF:-$GITHUB_REF} 174 | REF=${REFrefs/heads/} 175 | echo REF: $REF 176 | BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true 177 | echo BOOST_BRANCH: $BOOST_BRANCH 178 | cd .. 179 | git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root 180 | cd boost-root 181 | cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY 182 | git submodule update --init tools/boostdep 183 | python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY 184 | ./bootstrap.sh 185 | ./b2 -d0 headers 186 | 187 | - name: Run tests 188 | shell: bash 189 | run: | 190 | cd ../boost-root 191 | if [[ "${{matrix.supported}}" != "true" ]] ; then 192 | echo "This configuration is not supported because ${{matrix.supported}}" 193 | exit 0 194 | fi 195 | toolsets=$(sed 's/,/ /g' <<<"${{matrix.toolset}}") 196 | standards=$(sed 's/,/ /g' <<<"${{matrix.cxxstd}}") 197 | variants="debug release" 198 | err=0 199 | cd libs/$LIBRARY 200 | for toolset in ${toolsets} ; do 201 | for standard in ${standards} ; do 202 | for variant in ${variants} ; do 203 | if [[ err -ne 0 ]] ; then 204 | echo "skipping: ${toolset} ${standard} ${variant}" 205 | else 206 | echo "running: ${toolset} ${standard} ${variant}" 207 | ../../b2 -j3 test toolset=${toolset} cxxstd=${standard} variant=${variant} 208 | err=$? 209 | fi 210 | done 211 | done 212 | done 213 | [[ $err -ne 0 ]] && false || true 214 | 215 | windows: 216 | if: true 217 | strategy: 218 | fail-fast: false 219 | matrix: 220 | include: 221 | - toolset: msvc-14.0 222 | cxxstd: "14,latest" 223 | addrmd: 32,64 224 | os: windows-2019 225 | supported: false # requires _ENABLE_ATOMIC_ALIGNMENT_FIX 226 | - toolset: msvc-14.2 227 | cxxstd: "14,17,20,latest" 228 | addrmd: 32,64 229 | os: windows-2019 230 | supported: true 231 | - toolset: msvc-14.3 232 | cxxstd: "14,17,20,latest" 233 | addrmd: 32,64 234 | os: windows-2022 235 | supported: true 236 | - toolset: clang-win 237 | cxxstd: "14,17,20,latest" 238 | addrmd: 32,64 239 | os: windows-2022 240 | supported: true 241 | - toolset: gcc 242 | cxxstd: "14,17,2a" 243 | addrmd: 64 244 | os: windows-2019 245 | supported: true 246 | 247 | needs: [runner-selection] 248 | # runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.os] }} 249 | runs-on: ${{matrix.os}} 250 | 251 | steps: 252 | - uses: actions/checkout@v3 253 | 254 | - name: Setup Boost 255 | shell: cmd 256 | run: | 257 | echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY% 258 | for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi 259 | echo LIBRARY: %LIBRARY% 260 | echo LIBRARY=%LIBRARY%>>%GITHUB_ENV% 261 | echo GITHUB_BASE_REF: %GITHUB_BASE_REF% 262 | echo GITHUB_REF: %GITHUB_REF% 263 | if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF% 264 | set BOOST_BRANCH=develop 265 | for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master 266 | echo BOOST_BRANCH: %BOOST_BRANCH% 267 | cd .. 268 | git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root 269 | cd boost-root 270 | xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\ 271 | git submodule update --init tools/boostdep 272 | python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY% 273 | copy libs\%LIBRARY%\tools\user-config.jam %USERPROFILE%\user-config.jam 274 | cmd /c bootstrap 275 | b2 -d0 headers 276 | 277 | - name: Run tests 278 | shell: bash 279 | run: | 280 | echo "current directory: $(pwd)" 281 | cd ../boost-root 282 | if [[ "${{matrix.supported}}" != "true" ]] ; then 283 | echo "This configuration is not supported because ${{matrix.supported}}" 284 | exit 0 285 | fi 286 | toolsets=$(sed 's/,/ /g' <<<"${{matrix.toolset}}") 287 | standards=$(sed 's/,/ /g' <<<"${{matrix.cxxstd}}") 288 | address_models=$(sed 's/,/ /g' <<<"${{matrix.addrmd}}") 289 | variants="debug release" 290 | cd libs/$LIBRARY 291 | for toolset in ${toolsets} ; do 292 | for standard in ${standards} ; do 293 | for address_model in ${address_models} ; do 294 | for variant in ${variants} ; do 295 | echo "running: ${toolset} ${standard} ${address_model} ${variant}" 296 | ../../b2 -j3 ${{matrix.cxxflags}} test toolset=${toolset} cxxstd=${standard} address-model=${address_model} variant=${variant} || exit $? 297 | done 298 | done 299 | done 300 | done 301 | 302 | 303 | posix-cmake-subdir: 304 | strategy: 305 | fail-fast: false 306 | matrix: 307 | include: 308 | - os: ubuntu-22.04 309 | - os: ubuntu-24.04 310 | - os: macos-13 311 | - os: macos-14 312 | - os: macos-15 313 | 314 | runs-on: ${{matrix.os}} 315 | 316 | steps: 317 | - uses: actions/checkout@v3 318 | 319 | - name: Install packages 320 | if: matrix.install 321 | run: sudo apt-get -y install ${{matrix.install}} 322 | 323 | - name: Setup Boost 324 | run: | 325 | echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY 326 | LIBRARY=${GITHUB_REPOSITORY#*/} 327 | echo LIBRARY: $LIBRARY 328 | echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV 329 | echo GITHUB_BASE_REF: $GITHUB_BASE_REF 330 | echo GITHUB_REF: $GITHUB_REF 331 | REF=${GITHUB_BASE_REF:-$GITHUB_REF} 332 | REF=${REF#refs/heads/} 333 | echo REF: $REF 334 | BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true 335 | echo BOOST_BRANCH: $BOOST_BRANCH 336 | cd .. 337 | git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root 338 | cd boost-root 339 | cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY 340 | git submodule update --init tools/boostdep 341 | python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY 342 | 343 | - name: Use library with add_subdirectory 344 | run: | 345 | cd ../boost-root/libs/$LIBRARY/test/cmake_subdir_test 346 | mkdir __build__ && cd __build__ 347 | cmake .. 348 | cmake --build . 349 | ctest --output-on-failure --no-tests=error 350 | 351 | posix-cmake-install: 352 | strategy: 353 | fail-fast: false 354 | matrix: 355 | include: 356 | - os: ubuntu-22.04 357 | - os: ubuntu-24.04 358 | - os: macos-13 359 | - os: macos-14 360 | - os: macos-15 361 | 362 | runs-on: ${{matrix.os}} 363 | 364 | steps: 365 | - uses: actions/checkout@v3 366 | 367 | - name: Install packages 368 | if: matrix.install 369 | run: sudo apt-get -y install ${{matrix.install}} 370 | 371 | - name: Setup Boost 372 | run: | 373 | echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY 374 | LIBRARY=${GITHUB_REPOSITORY#*/} 375 | echo LIBRARY: $LIBRARY 376 | echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV 377 | echo GITHUB_BASE_REF: $GITHUB_BASE_REF 378 | echo GITHUB_REF: $GITHUB_REF 379 | REF=${GITHUB_BASE_REF:-$GITHUB_REF} 380 | REF=${REF#refs/heads/} 381 | echo REF: $REF 382 | BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true 383 | echo BOOST_BRANCH: $BOOST_BRANCH 384 | cd .. 385 | git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root 386 | cd boost-root 387 | cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY 388 | git submodule update --init tools/boostdep 389 | python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY 390 | 391 | - name: Configure 392 | run: | 393 | cd ../boost-root 394 | mkdir __build__ && cd __build__ 395 | cmake -DBOOST_INCLUDE_LIBRARIES=$LIBRARY -DCMAKE_INSTALL_PREFIX=~/.local .. 396 | 397 | - name: Install 398 | run: | 399 | cd ../boost-root/__build__ 400 | cmake --build . --target install 401 | 402 | - name: Use the installed library 403 | run: | 404 | cd ../boost-root/libs/$LIBRARY/test/cmake_install_test && mkdir __build__ && cd __build__ 405 | cmake -DCMAKE_INSTALL_PREFIX=~/.local .. 406 | cmake --build . 407 | ctest --output-on-failure --no-tests=error 408 | 409 | posix-cmake-test: 410 | strategy: 411 | fail-fast: false 412 | matrix: 413 | include: 414 | - os: ubuntu-22.04 415 | - os: ubuntu-24.04 416 | - os: macos-13 417 | - os: macos-14 418 | - os: macos-15 419 | 420 | runs-on: ${{matrix.os}} 421 | 422 | steps: 423 | - uses: actions/checkout@v3 424 | 425 | - name: Install packages 426 | if: matrix.install 427 | run: sudo apt-get -y install ${{matrix.install}} 428 | 429 | - name: Setup Boost 430 | run: | 431 | echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY 432 | LIBRARY=${GITHUB_REPOSITORY#*/} 433 | echo LIBRARY: $LIBRARY 434 | echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV 435 | echo GITHUB_BASE_REF: $GITHUB_BASE_REF 436 | echo GITHUB_REF: $GITHUB_REF 437 | REF=${GITHUB_BASE_REF:-$GITHUB_REF} 438 | REF=${REF#refs/heads/} 439 | echo REF: $REF 440 | BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true 441 | echo BOOST_BRANCH: $BOOST_BRANCH 442 | cd .. 443 | git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root 444 | cd boost-root 445 | cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY 446 | git submodule update --init tools/boostdep 447 | python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY 448 | 449 | - name: Configure 450 | run: | 451 | cd ../boost-root 452 | mkdir __build__ && cd __build__ 453 | cmake -DBOOST_INCLUDE_LIBRARIES=$LIBRARY -DBUILD_TESTING=ON .. 454 | 455 | - name: Build tests 456 | run: | 457 | cd ../boost-root/__build__ 458 | cmake --build . --target tests 459 | 460 | - name: Run tests 461 | run: | 462 | cd ../boost-root/__build__ 463 | ctest --output-on-failure --no-tests=error 464 | 465 | windows-cmake-subdir: 466 | strategy: 467 | fail-fast: false 468 | matrix: 469 | include: 470 | - os: windows-2019 471 | - os: windows-2022 472 | 473 | runs-on: ${{matrix.os}} 474 | 475 | steps: 476 | - uses: actions/checkout@v3 477 | 478 | - name: Setup Boost 479 | shell: cmd 480 | run: | 481 | echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY% 482 | for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi 483 | echo LIBRARY: %LIBRARY% 484 | echo LIBRARY=%LIBRARY%>>%GITHUB_ENV% 485 | echo GITHUB_BASE_REF: %GITHUB_BASE_REF% 486 | echo GITHUB_REF: %GITHUB_REF% 487 | if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF% 488 | set BOOST_BRANCH=develop 489 | for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master 490 | echo BOOST_BRANCH: %BOOST_BRANCH% 491 | cd .. 492 | git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root 493 | cd boost-root 494 | xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\ 495 | git submodule update --init tools/boostdep 496 | python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY% 497 | 498 | - name: Use library with add_subdirectory (Debug) 499 | shell: cmd 500 | run: | 501 | cd ../boost-root/libs/%LIBRARY%/test/cmake_subdir_test 502 | mkdir __build__ && cd __build__ 503 | cmake .. 504 | cmake --build . --config Debug 505 | ctest --output-on-failure --no-tests=error -C Debug 506 | 507 | - name: Use library with add_subdirectory (Release) 508 | shell: cmd 509 | run: | 510 | cd ../boost-root/libs/%LIBRARY%/test/cmake_subdir_test/__build__ 511 | cmake --build . --config Release 512 | ctest --output-on-failure --no-tests=error -C Release 513 | 514 | windows-cmake-install: 515 | strategy: 516 | fail-fast: false 517 | matrix: 518 | include: 519 | - os: windows-2019 520 | - os: windows-2022 521 | 522 | runs-on: ${{matrix.os}} 523 | 524 | steps: 525 | - uses: actions/checkout@v3 526 | 527 | - name: Setup Boost 528 | shell: cmd 529 | run: | 530 | echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY% 531 | for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi 532 | echo LIBRARY: %LIBRARY% 533 | echo LIBRARY=%LIBRARY%>>%GITHUB_ENV% 534 | echo GITHUB_BASE_REF: %GITHUB_BASE_REF% 535 | echo GITHUB_REF: %GITHUB_REF% 536 | if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF% 537 | set BOOST_BRANCH=develop 538 | for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master 539 | echo BOOST_BRANCH: %BOOST_BRANCH% 540 | cd .. 541 | git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root 542 | cd boost-root 543 | xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\ 544 | git submodule update --init tools/boostdep 545 | python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY% 546 | 547 | - name: Configure 548 | shell: cmd 549 | run: | 550 | cd ../boost-root 551 | mkdir __build__ && cd __build__ 552 | cmake -DBOOST_INCLUDE_LIBRARIES=%LIBRARY% -DCMAKE_INSTALL_PREFIX=C:/cmake-prefix .. 553 | 554 | - name: Install (Debug) 555 | shell: cmd 556 | run: | 557 | cd ../boost-root/__build__ 558 | cmake --build . --target install --config Debug 559 | 560 | - name: Install (Release) 561 | shell: cmd 562 | run: | 563 | cd ../boost-root/__build__ 564 | cmake --build . --target install --config Release 565 | 566 | - name: Use the installed library (Debug) 567 | shell: cmd 568 | run: | 569 | cd ../boost-root/libs/%LIBRARY%/test/cmake_install_test && mkdir __build__ && cd __build__ 570 | cmake -DCMAKE_INSTALL_PREFIX=C:/cmake-prefix .. 571 | cmake --build . --config Debug 572 | ctest --output-on-failure --no-tests=error -C Debug 573 | 574 | - name: Use the installed library (Release) 575 | shell: cmd 576 | run: | 577 | cd ../boost-root/libs/%LIBRARY%/test/cmake_install_test/__build__ 578 | cmake --build . --config Release 579 | ctest --output-on-failure --no-tests=error -C Release 580 | 581 | windows-cmake-test: 582 | strategy: 583 | fail-fast: false 584 | matrix: 585 | include: 586 | - os: windows-2019 587 | - os: windows-2022 588 | 589 | runs-on: ${{matrix.os}} 590 | 591 | steps: 592 | - uses: actions/checkout@v3 593 | 594 | - name: Setup Boost 595 | shell: cmd 596 | run: | 597 | echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY% 598 | for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi 599 | echo LIBRARY: %LIBRARY% 600 | echo LIBRARY=%LIBRARY%>>%GITHUB_ENV% 601 | echo GITHUB_BASE_REF: %GITHUB_BASE_REF% 602 | echo GITHUB_REF: %GITHUB_REF% 603 | if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF% 604 | set BOOST_BRANCH=develop 605 | for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master 606 | echo BOOST_BRANCH: %BOOST_BRANCH% 607 | cd .. 608 | git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root 609 | cd boost-root 610 | xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\ 611 | git submodule update --init tools/boostdep 612 | python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY% 613 | 614 | - name: Configure 615 | shell: cmd 616 | run: | 617 | cd ../boost-root 618 | mkdir __build__ && cd __build__ 619 | cmake -DBOOST_INCLUDE_LIBRARIES=%LIBRARY% -DBUILD_TESTING=ON .. 620 | 621 | - name: Build tests (Debug) 622 | shell: cmd 623 | run: | 624 | cd ../boost-root/__build__ 625 | cmake --build . --target tests --config Debug 626 | 627 | - name: Run tests (Debug) 628 | shell: cmd 629 | run: | 630 | cd ../boost-root/__build__ 631 | ctest --output-on-failure --no-tests=error -C Debug 632 | 633 | - name: Build tests (Release) 634 | shell: cmd 635 | run: | 636 | cd ../boost-root/__build__ 637 | cmake --build . --target tests --config Release 638 | 639 | - name: Run tests (Release) 640 | shell: cmd 641 | run: | 642 | cd ../boost-root/__build__ 643 | ctest --output-on-failure --no-tests=error -C Release 644 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.5.0 4 | hooks: 5 | - id: check-yaml 6 | - id: end-of-file-fixer 7 | - id: trailing-whitespace 8 | - id: check-shebang-scripts-are-executable 9 | - id: mixed-line-ending 10 | args: [--fix=auto] 11 | 12 | 13 | - repo: https://github.com/pre-commit/mirrors-clang-format 14 | rev: v20.1.0 15 | hooks: 16 | - id: clang-format 17 | types_or: [c++, c, cuda] 18 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Generated by `boostdep --cmake lockfree` 2 | # Copyright 2020 Peter Dimov 3 | # Distributed under the Boost Software License, Version 1.0. 4 | # https://www.boost.org/LICENSE_1_0.txt 5 | 6 | cmake_minimum_required(VERSION 3.5...3.16) 7 | 8 | project(boost_lockfree VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) 9 | 10 | option(BOOST_LOCKFREE_BUILD_TESTS "Build boost::lockfree tests" OFF) 11 | option(BOOST_LOCKFREE_TESTS_STRESSTEST "Build boost::lockfree with more excessive stress tests" OFF) 12 | option(BOOST_LOCKFREE_USE_FILE_SET "Use FILE_SET for boost::lockfree" OFF) 13 | 14 | if (NOT DEFINED CMAKE_CXX_STANDARD) 15 | set(CMAKE_CXX_STANDARD 14) 16 | endif() 17 | 18 | add_library(boost_lockfree INTERFACE) 19 | add_library(Boost::lockfree ALIAS boost_lockfree) 20 | 21 | target_include_directories(boost_lockfree INTERFACE include) 22 | 23 | if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.23 AND BOOST_LOCKFREE_USE_FILE_SET) 24 | set(Headers 25 | include/boost/lockfree/spsc_queue.hpp 26 | include/boost/lockfree/spsc_value.hpp 27 | include/boost/lockfree/policies.hpp 28 | include/boost/lockfree/queue.hpp 29 | include/boost/lockfree/lockfree_forward.hpp 30 | include/boost/lockfree/detail/atomic.hpp 31 | include/boost/lockfree/detail/copy_payload.hpp 32 | include/boost/lockfree/detail/freelist.hpp 33 | include/boost/lockfree/detail/parameter.hpp 34 | include/boost/lockfree/detail/prefix.hpp 35 | include/boost/lockfree/detail/tagged_ptr.hpp 36 | include/boost/lockfree/detail/tagged_ptr_dcas.hpp 37 | include/boost/lockfree/detail/tagged_ptr_ptrcompression.hpp 38 | include/boost/lockfree/detail/uses_optional.hpp 39 | include/boost/lockfree/stack.hpp 40 | ) 41 | 42 | target_sources(boost_lockfree PUBLIC FILE_SET HEADERS FILES ${Headers} ) 43 | endif() 44 | 45 | target_compile_features(boost_lockfree INTERFACE cxx_std_14) 46 | set_target_properties( boost_lockfree PROPERTIES CXX_STANDARD_REQUIRED 14) 47 | 48 | target_link_libraries(boost_lockfree 49 | INTERFACE 50 | Boost::align 51 | Boost::assert 52 | Boost::atomic 53 | Boost::config 54 | Boost::core 55 | Boost::parameter 56 | Boost::predef 57 | Boost::static_assert 58 | Boost::utility 59 | ) 60 | 61 | 62 | if (CMAKE_SYSTEM_NAME STREQUAL "Linux") 63 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 64 | target_link_libraries(boost_lockfree 65 | INTERFACE atomic 66 | ) 67 | endif() 68 | endif() 69 | 70 | 71 | 72 | if( BOOST_LOCKFREE_BUILD_TESTS OR BUILD_TESTING ) 73 | add_subdirectory(test) 74 | endif() 75 | -------------------------------------------------------------------------------- /_clang-format: -------------------------------------------------------------------------------- 1 | AccessModifierOffset: -4 2 | AlignAfterOpenBracket: Align 3 | 4 | # crashes: https://github.com/llvm/llvm-project/issues/55493 5 | # AlignArrayOfStructures: Left 6 | 7 | AlignConsecutiveAssignments: AcrossComments 8 | AlignConsecutiveBitFields: AcrossComments 9 | AlignConsecutiveDeclarations: AcrossComments 10 | AlignConsecutiveMacros: AcrossComments 11 | AlignConsecutiveShortCaseStatements: 12 | Enabled: true 13 | AcrossEmptyLines: true 14 | AcrossComments: true 15 | AlignCaseColons: false 16 | AlignEscapedNewlines: Left 17 | AlignOperands: true 18 | AlignTrailingComments: 19 | Kind: Always 20 | OverEmptyLines: 1 21 | 22 | AllowAllArgumentsOnNextLine: true 23 | AllowAllParametersOfDeclarationOnNextLine: true 24 | 25 | AllowShortBlocksOnASingleLine: Empty 26 | AllowShortCaseLabelsOnASingleLine: true 27 | AllowShortEnumsOnASingleLine: false 28 | AllowShortFunctionsOnASingleLine: None 29 | AllowShortIfStatementsOnASingleLine: false 30 | AllowShortLambdasOnASingleLine: Empty 31 | AllowShortLoopsOnASingleLine: false 32 | 33 | AlwaysBreakAfterReturnType: None 34 | AlwaysBreakBeforeMultilineStrings: false 35 | AlwaysBreakTemplateDeclarations: true 36 | BinPackArguments: false 37 | BinPackParameters: false 38 | BitFieldColonSpacing: Both 39 | BraceWrapping: 40 | AfterCaseLabel: false 41 | AfterClass: true 42 | AfterControlStatement: Never 43 | AfterEnum: true 44 | AfterFunction: true 45 | AfterNamespace: false 46 | AfterObjCDeclaration: true 47 | AfterStruct: true 48 | AfterUnion: true 49 | AfterExternBlock: false 50 | BeforeCatch: false 51 | BeforeElse: false 52 | BeforeLambdaBody: false 53 | BeforeWhile: false 54 | IndentBraces: false 55 | SplitEmptyFunction: false 56 | SplitEmptyRecord: false 57 | SplitEmptyNamespace: false 58 | BreakAfterAttributes: Leave 59 | BreakBeforeInlineASMColon: Always 60 | BreakBeforeBinaryOperators: All 61 | BreakBeforeBraces: Custom 62 | BreakBeforeTernaryOperators: true 63 | BreakConstructorInitializers: AfterColon 64 | BreakInheritanceList: AfterColon 65 | BreakStringLiterals: true 66 | ColumnLimit: 120 67 | CommentPragmas: '^!' 68 | CompactNamespaces: true 69 | ConstructorInitializerIndentWidth: 4 70 | ContinuationIndentWidth: 4 71 | Cpp11BracedListStyle: true 72 | DerivePointerAlignment: false 73 | DisableFormat: false 74 | EmptyLineAfterAccessModifier: Never 75 | EmptyLineBeforeAccessModifier: LogicalBlock 76 | ExperimentalAutoDetectBinPacking: false 77 | FixNamespaceComments: true 78 | ForEachMacros: [ foreach, BOOST_FOREACH ] 79 | IncludeBlocks: Preserve 80 | IndentAccessModifiers: false 81 | IndentCaseBlocks: false 82 | IndentCaseLabels: false 83 | IndentExternBlock: NoIndent 84 | IndentGotoLabels: false 85 | IndentPPDirectives: AfterHash 86 | # IndentRequiresClause: true 87 | 88 | IndentWidth: 4 89 | IndentWrappedFunctionNames: false 90 | InsertNewlineAtEOF: True 91 | InsertTrailingCommas: Wrapped 92 | # IntegerLiteralSeparator: 93 | # Binary: 0 94 | # Decimal: 3 95 | # Hex: -1 96 | KeepEmptyLinesAtTheStartOfBlocks: false 97 | KeepEmptyLinesAtEOF: false 98 | LambdaBodyIndentation: OuterScope 99 | MaxEmptyLinesToKeep: 2 100 | NamespaceIndentation: None 101 | NamespaceMacros: [ MSGPACK_API_VERSION_NAMESPACE ] 102 | ObjCSpaceAfterProperty: false 103 | ObjCSpaceBeforeProtocolList: true 104 | PPIndentWidth: 4 105 | PackConstructorInitializers: Never 106 | PenaltyBreakAssignment: 10 107 | PenaltyBreakBeforeFirstCallParameter: 100 108 | PenaltyBreakComment: 300 109 | PenaltyBreakFirstLessLess: 1200 110 | PenaltyBreakString: 10 111 | PenaltyBreakOpenParenthesis: 100 112 | PenaltyExcessCharacter: 20 113 | PenaltyReturnTypeOnItsOwnLine: 50 114 | PointerAlignment: Left 115 | ReferenceAlignment: Left 116 | ReflowComments: true 117 | RemoveParentheses: Leave 118 | 119 | # NOTE: results in broken code 120 | # RemoveSemicolon: true 121 | # RequiresClausePosition: OwnLine 122 | ShortNamespaceLines: 0 123 | SortIncludes: CaseSensitive 124 | SortUsingDeclarations: true 125 | SpaceAfterCStyleCast: false 126 | SpaceAfterLogicalNot: false 127 | SpaceAfterTemplateKeyword: true 128 | SpaceBeforeAssignmentOperators: true 129 | SpaceBeforeCpp11BracedList: true 130 | SpaceBeforeCtorInitializerColon: true 131 | SpaceBeforeInheritanceColon: true 132 | SpaceBeforeParens: ControlStatements 133 | SpaceBeforeRangeBasedForLoopColon: true 134 | SpaceBeforeSquareBrackets: false 135 | SpaceInEmptyBlock: false 136 | SpaceInEmptyParentheses: false 137 | SpacesBeforeTrailingComments: 1 138 | SpacesInAngles: Always 139 | SpacesInCStyleCastParentheses: false 140 | SpacesInConditionalStatement: false 141 | SpacesInContainerLiterals: true 142 | SpacesInParentheses: true 143 | SpacesInSquareBrackets: true 144 | Standard: c++20 145 | TabWidth: 8 146 | UseTab: Never 147 | WhitespaceSensitiveMacros: ['BOOST_PP_STRINGIZE'] 148 | -------------------------------------------------------------------------------- /build.jam: -------------------------------------------------------------------------------- 1 | # Copyright René Ferdinand Rivera Morell 2023-2024 2 | # Distributed under the Boost Software License, Version 1.0. 3 | # (See accompanying file LICENSE_1_0.txt or copy at 4 | # http://www.boost.org/LICENSE_1_0.txt) 5 | 6 | require-b2 5.2 ; 7 | 8 | constant boost_dependencies : 9 | /boost/align//boost_align 10 | /boost/assert//boost_assert 11 | /boost/atomic//boost_atomic 12 | /boost/config//boost_config 13 | /boost/core//boost_core 14 | /boost/parameter//boost_parameter 15 | /boost/predef//boost_predef 16 | /boost/static_assert//boost_static_assert 17 | /boost/type_traits//boost_type_traits ; 18 | 19 | project /boost/lockfree 20 | : common-requirements 21 | include 22 | ; 23 | 24 | explicit 25 | [ alias boost_lockfree : : : : $(boost_dependencies) ] 26 | [ alias all : boost_lockfree examples test ] 27 | ; 28 | 29 | call-if : boost-library lockfree 30 | ; 31 | -------------------------------------------------------------------------------- /doc/Jamfile.v2: -------------------------------------------------------------------------------- 1 | # Copyright 2010 Tim Blechmann 2 | # Distributed under the Boost Software License, Version 1.0. (See accompanying 3 | # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | using quickbook ; 6 | using boostbook ; 7 | using doxygen ; 8 | using xsltproc ; 9 | 10 | import set ; 11 | import doxygen ; 12 | import xsltproc ; 13 | import notfile ; 14 | import path ; 15 | 16 | doxygen autodoc 17 | : 18 | [ glob ../include/boost/lockfree/*.hpp ] 19 | : 20 | #EXTRACT_ALL=YES 21 | "PREDEFINED=\"BOOST_DOXYGEN_INVOKED\" \\ 22 | \"BOOST_DEDUCED_TYPENAME=typename\" \\ 23 | \"BOOST_HAS_RVALUE_REFS\" \\ 24 | " 25 | HIDE_UNDOC_MEMBERS=YES 26 | HIDE_UNDOC_CLASSES=YES 27 | INLINE_INHERITED_MEMB=YES 28 | EXTRACT_PRIVATE=NO 29 | ENABLE_PREPROCESSING=YES 30 | MACRO_EXPANSION=YES 31 | EXPAND_ONLY_PREDEF=YES 32 | SEARCH_INCLUDES=YES 33 | INCLUDE_PATH=$(BOOST_ROOT) 34 | EXAMPLE_PATH=$(BOOST_ROOT)/libs/lockfree/examples 35 | BRIEF_MEMBER_DESC=YES 36 | REPEAT_BRIEF=YES 37 | MULTILINE_CPP_IS_BRIEF=YES 38 | ; 39 | 40 | xml lockfree : lockfree.qbk : ; 41 | 42 | boostbook standalone 43 | : lockfree 44 | : html.stylesheet=../boostbook.css 45 | boost.root=../../../.. 46 | boost.libraries=../../../libraries.htm 47 | toc.max.depth=2 48 | toc.section.depth=2 49 | autodoc 50 | pdf:boost.url.prefix=http://www.boost.org/doc/libs/release/libs/lockfree/doc/html 51 | ; 52 | 53 | install css : [ glob $(BOOST_ROOT)/doc/src/*.css ] 54 | : html ; 55 | install images : [ glob $(BOOST_ROOT)/doc/src/images/*.png ] 56 | : html/images ; 57 | explicit css ; 58 | explicit images ; 59 | 60 | ############################################################################### 61 | alias boostdoc 62 | : lockfree 63 | : 64 | : autodoc 65 | : ; 66 | explicit boostdoc ; 67 | alias boostrelease ; 68 | explicit boostrelease ; 69 | -------------------------------------------------------------------------------- /doc/lockfree.qbk: -------------------------------------------------------------------------------- 1 | [library Boost.Lockfree 2 | [quickbook 1.4] 3 | [authors [Blechmann, Tim]] 4 | [copyright 2008-2011 Tim Blechmann] 5 | [category algorithms] 6 | [purpose 7 | lockfree concurrent data structures 8 | ] 9 | [id lockfree] 10 | [dirname lockfree] 11 | [license 12 | Distributed under the Boost Software License, Version 1.0. 13 | (See accompanying file LICENSE_1_0.txt or copy at 14 | [@http://www.boost.org/LICENSE_1_0.txt]) 15 | ] 16 | ] 17 | 18 | [c++] 19 | 20 | 21 | [/ Images ] 22 | 23 | [def _note_ [$images/note.png]] 24 | [def _alert_ [$images/caution.png]] 25 | [def _detail_ [$images/note.png]] 26 | [def _tip_ [$images/tip.png]] 27 | 28 | [/ Links ] 29 | 30 | [def _lockfree_ [^boost.lockfree]] 31 | 32 | [section Introduction & Motivation] 33 | 34 | [h2 Introduction & Terminology] 35 | 36 | The term *non-blocking* denotes concurrent data structures, which do not use traditional synchronization primitives like 37 | guards to ensure thread-safety. Maurice Herlihy and Nir Shavit (compare [@http://books.google.com/books?id=pFSwuqtJgxYC 38 | "The Art of Multiprocessor Programming"]) distinguish between 3 types of non-blocking data structures, each having different 39 | properties: 40 | 41 | * data structures are *wait-free*, if every concurrent operation is guaranteed to be finished in a finite number of 42 | steps. It is therefore possible to give worst-case guarantees for the number of operations. 43 | 44 | * data structures are *lock-free*, if some concurrent operations are guaranteed to be finished in a finite number of 45 | steps. While it is in theory possible that some operations never make any progress, it is very unlikely to happen in 46 | practical applications. 47 | 48 | * data structures are *obstruction-free*, if a concurrent operation is guaranteed to be finished in a finite number of 49 | steps, unless another concurrent operation interferes. 50 | 51 | 52 | Some data structures can only be implemented in a lock-free manner, if they are used under certain restrictions. The 53 | relevant aspects for the implementation of _lockfree_ are the number of producer and consumer threads. *Single-producer* 54 | (*sp*) or *multiple producer* (*mp*) means that only a single thread or multiple concurrent threads are allowed to add 55 | data to a data structure. *Single-consumer* (*sc*) or *Multiple-consumer* (*mc*) denote the equivalent for the removal 56 | of data from the data structure. 57 | 58 | 59 | [h2 Properties of Non-Blocking Data Structures] 60 | 61 | Non-blocking data structures do not rely on locks and mutexes to ensure thread-safety. The synchronization is done completely in 62 | user-space without any direct interaction with the operating system [footnote Spinlocks do not 63 | directly interact with the operating system either. However it is possible that the owning thread is preempted by the 64 | operating system, which violates the lock-free property.]. This implies that they are not prone to issues like priority 65 | inversion (a low-priority thread needs to wait for a high-priority thread). 66 | 67 | Instead of relying on guards, non-blocking data structures require *atomic operations* (specific CPU instructions executed 68 | without interruption). This means that any thread either sees the state before or after the operation, but no 69 | intermediate state can be observed. Not all hardware supports the same set of atomic instructions. If it is not 70 | available in hardware, it can be emulated in software using guards. However this has the obvious drawback of losing the 71 | lock-free property. 72 | 73 | 74 | [h2 Performance of Non-Blocking Data Structures] 75 | 76 | When discussing the performance of non-blocking data structures, one has to distinguish between *amortized* and 77 | *worst-case* costs. The definition of 'lock-free' and 'wait-free' only mention the upper bound of an operation. Therefore 78 | lock-free data structures are not necessarily the best choice for every use case. In order to maximise the throughput of an 79 | application one should consider high-performance concurrent data structures [footnote 80 | [@http://threadingbuildingblocks.org/ Intel's Thread Building Blocks library] provides many efficient concurrent data structures, 81 | which are not necessarily lock-free.]. 82 | 83 | Lock-free data structures will be a better choice in order to optimize the latency of a system or to avoid priority inversion, 84 | which may be necessary in real-time applications. In general we advise to consider if lock-free data structures are necessary or if 85 | concurrent data structures are sufficient. In any case we advice to perform benchmarks with different data structures for a 86 | specific workload. 87 | 88 | 89 | [h2 Sources of Blocking Behavior] 90 | 91 | Apart from locks and mutexes (which we are not using in _lockfree_ anyway), there are three other aspects, that could violate 92 | lock-freedom: 93 | 94 | [variablelist 95 | [[Atomic Operations] 96 | [Some architectures do not provide the necessary atomic operations in natively in hardware. If this is not 97 | the case, they are emulated in software using spinlocks, which by itself is blocking. 98 | ] 99 | ] 100 | 101 | [[Memory Allocations] 102 | [Allocating memory from the operating system is not lock-free. This makes it impossible to implement true 103 | dynamically-sized non-blocking data structures. The node-based data structures of _lockfree_ use a memory pool to allocate the 104 | internal nodes. If this memory pool is exhausted, memory for new nodes has to be allocated from the operating system. However 105 | all data structures of _lockfree_ can be configured to avoid memory allocations (instead the specific calls will fail). 106 | This is especially useful for real-time systems that require lock-free memory allocations. 107 | ] 108 | ] 109 | 110 | [[Exception Handling] 111 | [The C++ exception handling does not give any guarantees about its real-time behavior. We therefore do 112 | not encourage the use of exceptions and exception handling in lock-free code.] 113 | ] 114 | ] 115 | 116 | [h2 Data Structures] 117 | 118 | _lockfree_ implements four lock-free data structures: 119 | 120 | [variablelist 121 | [[[classref boost::lockfree::queue]] 122 | [a lock-free multi-producer/multi-consumer queue] 123 | ] 124 | 125 | [[[classref boost::lockfree::stack]] 126 | [a lock-free multi-producer/multi-consumer stack] 127 | ] 128 | 129 | [[[classref boost::lockfree::spsc_queue]] 130 | [a wait-free single-producer/single-consumer queue (commonly known as ringbuffer)] 131 | ] 132 | 133 | [[[classref boost::lockfree::spsc_value]] 134 | [a wait-free single-producer/single-consumer value (commonly known as triple buffer)] 135 | ] 136 | ] 137 | 138 | [h3 Data Structure Configuration] 139 | 140 | The data structures can be configured with [@boost:/libs/parameter/doc/html/index.html Boost.Parameter]-style templates: 141 | 142 | [variablelist 143 | [[[classref boost::lockfree::fixed_sized]] 144 | [Configures the data structure as *fixed sized*. The internal nodes are stored inside an array and they are addressed by 145 | array indexing. This limits the possible size of the queue to the number of elements that can be addressed by the index 146 | type (usually 2**16-2), but on platforms that lack double-width compare-and-exchange instructions, this is the best way 147 | to achieve lock-freedom. 148 | ] 149 | ] 150 | 151 | [[[classref boost::lockfree::capacity]] 152 | [Sets the *capacity* of a data structure at compile-time. This implies that a data structure is fixed-sized. 153 | ] 154 | ] 155 | 156 | [[[classref boost::lockfree::allocator]] 157 | [Defines the allocator.] 158 | ] 159 | 160 | [[[classref boost::lockfree::allow_multiple_reads]] 161 | [Configures the [classref boost::lockfree::spsc_value] to allow the content to be read multiple times.] 162 | ] 163 | ] 164 | 165 | 166 | [endsect] 167 | 168 | [section Examples] 169 | 170 | [h2 Queue] 171 | 172 | The [classref boost::lockfree::queue] class implements a multi-writer/multi-reader queue. The 173 | following example shows how integer values are produced and consumed by 4 threads each: 174 | 175 | [import ../examples/queue.cpp] 176 | [queue_example] 177 | 178 | The program output is: 179 | 180 | [pre 181 | produced 40000000 objects. 182 | consumed 40000000 objects. 183 | ] 184 | 185 | 186 | [h2 Stack] 187 | 188 | The [classref boost::lockfree::stack] class implements a multi-writer/multi-reader stack. The 189 | following example shows how integer values are produced and consumed by 4 threads each: 190 | 191 | [import ../examples/stack.cpp] 192 | [stack_example] 193 | 194 | 195 | The program output is: 196 | 197 | [pre 198 | produced 4000000 objects. 199 | consumed 4000000 objects. 200 | ] 201 | 202 | [h2 Waitfree Single-Producer/Single-Consumer Queue] 203 | 204 | The [classref boost::lockfree::spsc_queue boost::lockfree::spsc_queue] class implements a wait-free single-producer/single-consumer queue. The 205 | following example shows how integer values are produced and consumed by 2 separate threads: 206 | 207 | [import ../examples/spsc_queue.cpp] 208 | [spsc_queue_example] 209 | 210 | 211 | The program output is: 212 | 213 | [pre 214 | produced 10000000 objects. 215 | consumed 10000000 objects. 216 | ] 217 | 218 | [endsect] 219 | 220 | 221 | [section Rationale] 222 | 223 | [section Memory Management] 224 | 225 | The lock-free [classref boost::lockfree::queue] and [classref boost::lockfree::stack] classes are node-based data structures, 226 | based on a linked list. Memory management of lock-free data structures is a non-trivial problem, because we need to avoid that 227 | one thread frees an internal node, while another thread still uses it. _lockfree_ uses a simple approach not returning any memory 228 | to the operating system. Instead they maintain a *free-list* in order to reuse them later. This is done for two reasons: 229 | first, depending on the implementation of the memory allocator freeing the memory may block (so the implementation would not 230 | be lock-free anymore), and second, most memory reclamation algorithms are patented. 231 | 232 | [endsect] 233 | 234 | [section ABA Prevention] 235 | 236 | The ABA problem is a common problem when implementing lock-free data structures. The problem occurs when updating an atomic 237 | variable using a =compare_exchange= operation: if the value A was read, thread 1 changes it to say C and tries to update 238 | the variable, it uses =compare_exchange= to write C, only if the current value is A. This might be a problem if in the meanwhile 239 | thread 2 changes the value from A to B and back to A, because thread 1 does not observe the change of the state. The common way to 240 | avoid the ABA problem is to associate a version counter with the value and change both atomically. 241 | 242 | _lockfree_ uses a =tagged_ptr= helper class which associates a pointer with an integer tag. This usually requires a double-width 243 | =compare_exchange=, which is not available on all platforms. IA32 did not provide the =cmpxchg8b= opcode before the pentium 244 | processor and it is also lacking on many RISC architectures like PPC. Early X86-64 processors also did not provide a =cmpxchg16b= 245 | instruction. 246 | On 64bit platforms one can work around this issue, because often not the full 64bit address space is used. On X86_64 for example, 247 | only 48bit are used for the address, so we can use the remaining 16bit for the ABA prevention tag. For details please consult the 248 | implementation of the =boost::lockfree::detail::tagged_ptr= class. 249 | 250 | For lock-free operations on 32bit platforms without double-width =compare_exchange=, we support a third approach: by using a 251 | fixed-sized array to store the internal nodes we can avoid the use of 32bit pointers, but instead 16bit indices into the array 252 | are sufficient. However this is only possible for fixed-sized data structures, that have an upper bound of internal nodes. 253 | 254 | [endsect] 255 | 256 | [section Interprocess Support] 257 | 258 | The _lockfree_ data structures have basic support for [@boost:/libs/interprocess/index.html Boost.Interprocess]. The only 259 | problem is the blocking emulation of lock-free atomics, which in the current implementation is not guaranteed to be interprocess-safe. 260 | 261 | [endsect] 262 | 263 | [endsect] 264 | 265 | [xinclude autodoc.xml] 266 | 267 | [section Appendices] 268 | 269 | [section Supported Platforms & Compilers] 270 | 271 | _lockfree_ requires a c++14 compliant compiler. Users of MSVC are strongly recommended to use 2017 or higher. For 2015 272 | =_ENABLE_ATOMIC_ALIGNMENT_FIX= needs to be defined. 273 | 274 | [endsect] 275 | 276 | [section References] 277 | 278 | # [@http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.37.3574 Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms by Michael Scott and Maged Michael], 279 | In Symposium on Principles of Distributed Computing, pages 267–275, 1996. 280 | # [@http://books.google.com/books?id=pFSwuqtJgxYC M. Herlihy & Nir Shavit. The Art of Multiprocessor Programming], Morgan Kaufmann Publishers, 2008 281 | 282 | [endsect] 283 | 284 | [endsect] 285 | -------------------------------------------------------------------------------- /examples/Jamfile.v2: -------------------------------------------------------------------------------- 1 | # (C) Copyright 2009: Tim Blechmann 2 | # Distributed under the Boost Software License, Version 1.0. 3 | # (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | project 6 | : requirements 7 | /boost/lockfree//boost_lockfree 8 | /boost/thread//boost_thread 9 | /boost/atomic//boost_atomic 10 | ; 11 | 12 | exe queue : queue.cpp ; 13 | exe stack : stack.cpp ; 14 | exe spsc_queue : spsc_queue.cpp ; 15 | -------------------------------------------------------------------------------- /examples/queue.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2009 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | //[queue_example 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | boost::atomic_int producer_count( 0 ); 15 | boost::atomic_int consumer_count( 0 ); 16 | 17 | boost::lockfree::queue< int > queue( 128 ); 18 | 19 | const int iterations = 10000000; 20 | const int producer_thread_count = 4; 21 | const int consumer_thread_count = 4; 22 | 23 | void producer( void ) 24 | { 25 | for ( int i = 0; i != iterations; ++i ) { 26 | int value = ++producer_count; 27 | while ( !queue.push( value ) ) 28 | ; 29 | } 30 | } 31 | 32 | boost::atomic< bool > done( false ); 33 | void consumer( void ) 34 | { 35 | int value; 36 | while ( !done ) { 37 | while ( queue.pop( value ) ) 38 | ++consumer_count; 39 | } 40 | 41 | while ( queue.pop( value ) ) 42 | ++consumer_count; 43 | } 44 | 45 | int main( int argc, char* argv[] ) 46 | { 47 | using namespace std; 48 | cout << "boost::lockfree::queue is "; 49 | if ( !queue.is_lock_free() ) 50 | cout << "not "; 51 | cout << "lockfree" << endl; 52 | 53 | boost::thread_group producer_threads, consumer_threads; 54 | 55 | for ( int i = 0; i != producer_thread_count; ++i ) 56 | producer_threads.create_thread( producer ); 57 | 58 | for ( int i = 0; i != consumer_thread_count; ++i ) 59 | consumer_threads.create_thread( consumer ); 60 | 61 | producer_threads.join_all(); 62 | done = true; 63 | 64 | consumer_threads.join_all(); 65 | 66 | cout << "produced " << producer_count << " objects." << endl; 67 | cout << "consumed " << consumer_count << " objects." << endl; 68 | } 69 | //] 70 | -------------------------------------------------------------------------------- /examples/spsc_queue.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2009 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | //[spsc_queue_example 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | int producer_count = 0; 15 | boost::atomic_int consumer_count( 0 ); 16 | 17 | boost::lockfree::spsc_queue< int, boost::lockfree::capacity< 1024 > > spsc_queue; 18 | 19 | const int iterations = 10000000; 20 | 21 | void producer( void ) 22 | { 23 | for ( int i = 0; i != iterations; ++i ) { 24 | int value = ++producer_count; 25 | while ( !spsc_queue.push( value ) ) 26 | ; 27 | } 28 | } 29 | 30 | boost::atomic< bool > done( false ); 31 | 32 | void consumer( void ) 33 | { 34 | int value; 35 | while ( !done ) { 36 | while ( spsc_queue.pop( value ) ) 37 | ++consumer_count; 38 | } 39 | 40 | while ( spsc_queue.pop( value ) ) 41 | ++consumer_count; 42 | } 43 | 44 | int main( int argc, char* argv[] ) 45 | { 46 | using namespace std; 47 | cout << "boost::lockfree::queue is "; 48 | if ( !spsc_queue.is_lock_free() ) 49 | cout << "not "; 50 | cout << "lockfree" << endl; 51 | 52 | boost::thread producer_thread( producer ); 53 | boost::thread consumer_thread( consumer ); 54 | 55 | producer_thread.join(); 56 | done = true; 57 | consumer_thread.join(); 58 | 59 | cout << "produced " << producer_count << " objects." << endl; 60 | cout << "consumed " << consumer_count << " objects." << endl; 61 | } 62 | //] 63 | -------------------------------------------------------------------------------- /examples/stack.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2009 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | //[stack_example 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | boost::atomic_int producer_count( 0 ); 15 | boost::atomic_int consumer_count( 0 ); 16 | 17 | boost::lockfree::stack< int > stack( 128 ); 18 | 19 | const int iterations = 1000000; 20 | const int producer_thread_count = 4; 21 | const int consumer_thread_count = 4; 22 | 23 | void producer( void ) 24 | { 25 | for ( int i = 0; i != iterations; ++i ) { 26 | int value = ++producer_count; 27 | while ( !stack.push( value ) ) 28 | ; 29 | } 30 | } 31 | 32 | boost::atomic< bool > done( false ); 33 | 34 | void consumer( void ) 35 | { 36 | int value; 37 | while ( !done ) { 38 | while ( stack.pop( value ) ) 39 | ++consumer_count; 40 | } 41 | 42 | while ( stack.pop( value ) ) 43 | ++consumer_count; 44 | } 45 | 46 | int main( int argc, char* argv[] ) 47 | { 48 | using namespace std; 49 | cout << "boost::lockfree::stack is "; 50 | if ( !stack.is_lock_free() ) 51 | cout << "not "; 52 | cout << "lockfree" << endl; 53 | 54 | boost::thread_group producer_threads, consumer_threads; 55 | 56 | for ( int i = 0; i != producer_thread_count; ++i ) 57 | producer_threads.create_thread( producer ); 58 | 59 | for ( int i = 0; i != consumer_thread_count; ++i ) 60 | consumer_threads.create_thread( consumer ); 61 | 62 | producer_threads.join_all(); 63 | done = true; 64 | 65 | consumer_threads.join_all(); 66 | 67 | cout << "produced " << producer_count << " objects." << endl; 68 | cout << "consumed " << consumer_count << " objects." << endl; 69 | } 70 | //] 71 | -------------------------------------------------------------------------------- /include/boost/lockfree/detail/atomic.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011-2013, 2016 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #ifndef BOOST_LOCKFREE_DETAIL_ATOMIC_HPP 8 | #define BOOST_LOCKFREE_DETAIL_ATOMIC_HPP 9 | 10 | #if defined( BOOST_LOCKFREE_FORCE_BOOST_ATOMIC ) 11 | # include 12 | #else 13 | # include 14 | #endif 15 | 16 | namespace boost { namespace lockfree { 17 | namespace detail { 18 | 19 | #if defined( BOOST_LOCKFREE_FORCE_BOOST_ATOMIC ) 20 | using boost::atomic; 21 | using boost::memory_order_acquire; 22 | using boost::memory_order_consume; 23 | using boost::memory_order_relaxed; 24 | using boost::memory_order_release; 25 | #else 26 | using std::atomic; 27 | using std::memory_order_acquire; 28 | using std::memory_order_consume; 29 | using std::memory_order_relaxed; 30 | using std::memory_order_release; 31 | #endif 32 | 33 | } // namespace detail 34 | using detail::atomic; 35 | using detail::memory_order_acquire; 36 | using detail::memory_order_consume; 37 | using detail::memory_order_relaxed; 38 | using detail::memory_order_release; 39 | 40 | }} // namespace boost::lockfree 41 | 42 | #endif /* BOOST_LOCKFREE_DETAIL_ATOMIC_HPP */ 43 | -------------------------------------------------------------------------------- /include/boost/lockfree/detail/copy_payload.hpp: -------------------------------------------------------------------------------- 1 | // boost lockfree: copy_payload helper 2 | // 3 | // Copyright (C) 2011, 2016 Tim Blechmann 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See 6 | // accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifndef BOOST_LOCKFREE_DETAIL_COPY_PAYLOAD_HPP_INCLUDED 10 | #define BOOST_LOCKFREE_DETAIL_COPY_PAYLOAD_HPP_INCLUDED 11 | 12 | #include 13 | 14 | #if defined( _MSC_VER ) 15 | # pragma warning( push ) 16 | # pragma warning( disable : 4512 ) // assignment operator could not be generated 17 | #endif 18 | 19 | namespace boost { namespace lockfree { namespace detail { 20 | 21 | struct copy_convertible 22 | { 23 | template < typename T, typename U > 24 | static void copy( T& t, U& u ) 25 | { 26 | u = t; 27 | } 28 | }; 29 | 30 | struct copy_constructible_and_copyable 31 | { 32 | template < typename T, typename U > 33 | static void copy( T& t, U& u ) 34 | { 35 | u = U( t ); 36 | } 37 | }; 38 | 39 | template < typename T, typename U > 40 | void copy_payload( T& t, U& u ) 41 | { 42 | static constexpr bool is_convertible = std::is_convertible< T, U >::value; 43 | typedef std::conditional_t< is_convertible, copy_convertible, copy_constructible_and_copyable > copy_type; 44 | copy_type::copy( t, u ); 45 | } 46 | 47 | }}} // namespace boost::lockfree::detail 48 | 49 | #if defined( _MSC_VER ) 50 | # pragma warning( pop ) 51 | #endif 52 | 53 | #endif /* BOOST_LOCKFREE_DETAIL_COPY_PAYLOAD_HPP_INCLUDED */ 54 | -------------------------------------------------------------------------------- /include/boost/lockfree/detail/freelist.hpp: -------------------------------------------------------------------------------- 1 | // lock-free freelist 2 | // 3 | // Copyright (C) 2008-2016 Tim Blechmann 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See 6 | // accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifndef BOOST_LOCKFREE_FREELIST_HPP_INCLUDED 10 | #define BOOST_LOCKFREE_FREELIST_HPP_INCLUDED 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #if defined( _MSC_VER ) 30 | # pragma warning( push ) 31 | # pragma warning( disable : 4100 ) // unreferenced formal parameter 32 | # pragma warning( disable : 4127 ) // conditional expression is constant 33 | #endif 34 | 35 | namespace boost { namespace lockfree { namespace detail { 36 | 37 | //---------------------------------------------------------------------------------------------------------------------- 38 | 39 | template < typename T, typename Alloc = std::allocator< T > > 40 | class alignas( cacheline_bytes ) freelist_stack : Alloc 41 | { 42 | struct freelist_node 43 | { 44 | tagged_ptr< freelist_node > next; 45 | }; 46 | 47 | typedef tagged_ptr< freelist_node > tagged_node_ptr; 48 | 49 | public: 50 | typedef T* index_t; 51 | typedef tagged_ptr< T > tagged_node_handle; 52 | 53 | template < typename Allocator > 54 | freelist_stack( Allocator const& alloc, std::size_t n = 0 ) : 55 | Alloc( alloc ), 56 | pool_( tagged_node_ptr( NULL ) ) 57 | { 58 | for ( std::size_t i = 0; i != n; ++i ) { 59 | T* node = Alloc::allocate( 1 ); 60 | std::memset( (void*)node, 0, sizeof( T ) ); 61 | #ifdef BOOST_LOCKFREE_FREELIST_INIT_RUNS_DTOR 62 | destruct< false >( node ); 63 | #else 64 | deallocate< false >( node ); 65 | #endif 66 | } 67 | } 68 | 69 | template < bool ThreadSafe > 70 | void reserve( std::size_t count ) 71 | { 72 | for ( std::size_t i = 0; i != count; ++i ) { 73 | T* node = Alloc::allocate( 1 ); 74 | std::memset( (void*)node, 0, sizeof( T ) ); 75 | deallocate< ThreadSafe >( node ); 76 | } 77 | } 78 | 79 | template < bool ThreadSafe, bool Bounded > 80 | T* construct( void ) 81 | { 82 | T* node = allocate< ThreadSafe, Bounded >(); 83 | if ( node ) 84 | new ( node ) T(); 85 | return node; 86 | } 87 | 88 | template < bool ThreadSafe, bool Bounded, typename ArgumentType > 89 | T* construct( const ArgumentType& arg ) 90 | { 91 | T* node = allocate< ThreadSafe, Bounded >(); 92 | if ( node ) 93 | new ( node ) T( arg ); 94 | return node; 95 | } 96 | 97 | template < bool ThreadSafe, bool Bounded, typename ArgumentType > 98 | T* construct( ArgumentType&& arg ) 99 | { 100 | T* node = allocate< ThreadSafe, Bounded >(); 101 | if ( node ) 102 | new ( node ) T( std::forward< ArgumentType >( arg ) ); 103 | return node; 104 | } 105 | 106 | template < bool ThreadSafe, bool Bounded, typename ArgumentType1, typename ArgumentType2 > 107 | T* construct( ArgumentType1&& arg1, ArgumentType2&& arg2 ) 108 | { 109 | T* node = allocate< ThreadSafe, Bounded >(); 110 | if ( node ) 111 | new ( node ) T( arg1, arg2 ); 112 | return node; 113 | } 114 | 115 | template < bool ThreadSafe > 116 | void destruct( tagged_node_handle const& tagged_ptr ) 117 | { 118 | T* n = tagged_ptr.get_ptr(); 119 | n->~T(); 120 | deallocate< ThreadSafe >( n ); 121 | } 122 | 123 | template < bool ThreadSafe > 124 | void destruct( T* n ) 125 | { 126 | n->~T(); 127 | deallocate< ThreadSafe >( n ); 128 | } 129 | 130 | ~freelist_stack( void ) 131 | { 132 | tagged_node_ptr current = pool_.load(); 133 | 134 | while ( current ) { 135 | freelist_node* current_ptr = current.get_ptr(); 136 | if ( current_ptr ) 137 | current = current_ptr->next; 138 | Alloc::deallocate( (T*)current_ptr, 1 ); 139 | } 140 | } 141 | 142 | bool is_lock_free( void ) const 143 | { 144 | return pool_.is_lock_free(); 145 | } 146 | 147 | T* get_handle( T* pointer ) const 148 | { 149 | return pointer; 150 | } 151 | 152 | T* get_handle( tagged_node_handle const& handle ) const 153 | { 154 | return get_pointer( handle ); 155 | } 156 | 157 | T* get_pointer( tagged_node_handle const& tptr ) const 158 | { 159 | return tptr.get_ptr(); 160 | } 161 | 162 | T* get_pointer( T* pointer ) const 163 | { 164 | return pointer; 165 | } 166 | 167 | T* null_handle( void ) const 168 | { 169 | return NULL; 170 | } 171 | 172 | protected: // allow use from subclasses 173 | template < bool ThreadSafe, bool Bounded > 174 | T* allocate( void ) 175 | { 176 | if ( ThreadSafe ) 177 | return allocate_impl< Bounded >(); 178 | else 179 | return allocate_impl_unsafe< Bounded >(); 180 | } 181 | 182 | private: 183 | template < bool Bounded > 184 | T* allocate_impl( void ) 185 | { 186 | tagged_node_ptr old_pool = pool_.load( memory_order_consume ); 187 | 188 | for ( ;; ) { 189 | if ( !old_pool.get_ptr() ) { 190 | if ( !Bounded ) { 191 | T* ptr = Alloc::allocate( 1 ); 192 | std::memset( (void*)ptr, 0, sizeof( T ) ); 193 | return ptr; 194 | } else 195 | return 0; 196 | } 197 | 198 | freelist_node* new_pool_ptr = old_pool->next.get_ptr(); 199 | tagged_node_ptr new_pool( new_pool_ptr, old_pool.get_next_tag() ); 200 | 201 | if ( pool_.compare_exchange_weak( old_pool, new_pool ) ) { 202 | void* ptr = old_pool.get_ptr(); 203 | return reinterpret_cast< T* >( ptr ); 204 | } 205 | } 206 | } 207 | 208 | template < bool Bounded > 209 | T* allocate_impl_unsafe( void ) 210 | { 211 | tagged_node_ptr old_pool = pool_.load( memory_order_relaxed ); 212 | 213 | if ( !old_pool.get_ptr() ) { 214 | if ( !Bounded ) { 215 | T* ptr = Alloc::allocate( 1 ); 216 | std::memset( (void*)ptr, 0, sizeof( T ) ); 217 | return ptr; 218 | } else 219 | return 0; 220 | } 221 | 222 | freelist_node* new_pool_ptr = old_pool->next.get_ptr(); 223 | tagged_node_ptr new_pool( new_pool_ptr, old_pool.get_next_tag() ); 224 | 225 | pool_.store( new_pool, memory_order_relaxed ); 226 | void* ptr = old_pool.get_ptr(); 227 | return reinterpret_cast< T* >( ptr ); 228 | } 229 | 230 | protected: 231 | template < bool ThreadSafe > 232 | void deallocate( T* n ) 233 | { 234 | if ( ThreadSafe ) 235 | deallocate_impl( n ); 236 | else 237 | deallocate_impl_unsafe( n ); 238 | } 239 | 240 | private: 241 | void deallocate_impl( T* n ) 242 | { 243 | void* node = n; 244 | tagged_node_ptr old_pool = pool_.load( memory_order_consume ); 245 | freelist_node* new_pool_ptr = reinterpret_cast< freelist_node* >( node ); 246 | 247 | for ( ;; ) { 248 | tagged_node_ptr new_pool( new_pool_ptr, old_pool.get_tag() ); 249 | new_pool->next.set_ptr( old_pool.get_ptr() ); 250 | 251 | if ( pool_.compare_exchange_weak( old_pool, new_pool ) ) 252 | return; 253 | } 254 | } 255 | 256 | void deallocate_impl_unsafe( T* n ) 257 | { 258 | void* node = n; 259 | tagged_node_ptr old_pool = pool_.load( memory_order_relaxed ); 260 | freelist_node* new_pool_ptr = reinterpret_cast< freelist_node* >( node ); 261 | 262 | tagged_node_ptr new_pool( new_pool_ptr, old_pool.get_tag() ); 263 | new_pool->next.set_ptr( old_pool.get_ptr() ); 264 | 265 | pool_.store( new_pool, memory_order_relaxed ); 266 | } 267 | 268 | atomic< tagged_node_ptr > pool_; 269 | }; 270 | 271 | class tagged_index 272 | { 273 | public: 274 | typedef std::uint16_t tag_t; 275 | typedef std::uint16_t index_t; 276 | 277 | /** uninitialized constructor */ 278 | tagged_index( void ) noexcept //: index(0), tag(0) 279 | {} 280 | 281 | /** copy constructor */ 282 | tagged_index( tagged_index const& rhs ) = default; 283 | 284 | explicit tagged_index( index_t i, tag_t t = 0 ) : 285 | index( i ), 286 | tag( t ) 287 | {} 288 | 289 | /** index access */ 290 | /* @{ */ 291 | index_t get_index() const 292 | { 293 | return index; 294 | } 295 | 296 | void set_index( index_t i ) 297 | { 298 | index = i; 299 | } 300 | /* @} */ 301 | 302 | /** tag access */ 303 | /* @{ */ 304 | tag_t get_tag() const 305 | { 306 | return tag; 307 | } 308 | 309 | tag_t get_next_tag() const 310 | { 311 | tag_t next = ( get_tag() + 1u ) & ( std::numeric_limits< tag_t >::max )(); 312 | return next; 313 | } 314 | 315 | void set_tag( tag_t t ) 316 | { 317 | tag = t; 318 | } 319 | /* @} */ 320 | 321 | bool operator==( tagged_index const& rhs ) const 322 | { 323 | return ( index == rhs.index ) && ( tag == rhs.tag ); 324 | } 325 | 326 | bool operator!=( tagged_index const& rhs ) const 327 | { 328 | return !operator==( rhs ); 329 | } 330 | 331 | protected: 332 | index_t index; 333 | tag_t tag; 334 | }; 335 | 336 | //---------------------------------------------------------------------------------------------------------------------- 337 | 338 | template < typename T, std::size_t size > 339 | struct alignas( cacheline_bytes ) compiletime_sized_freelist_storage 340 | { 341 | // array-based freelists only support a 16bit address space. 342 | BOOST_STATIC_ASSERT( size < 65536 ); 343 | 344 | std::array< char, size * sizeof( T ) + 64 > data; 345 | 346 | // unused ... only for API purposes 347 | template < typename Allocator > 348 | compiletime_sized_freelist_storage( Allocator const& /* alloc */, std::size_t /* count */ ) 349 | { 350 | data.fill( 0 ); 351 | } 352 | 353 | T* nodes( void ) const 354 | { 355 | char* data_pointer = const_cast< char* >( data.data() ); 356 | return reinterpret_cast< T* >( boost::alignment::align_up( data_pointer, cacheline_bytes ) ); 357 | } 358 | 359 | std::size_t node_count( void ) const 360 | { 361 | return size; 362 | } 363 | }; 364 | 365 | //---------------------------------------------------------------------------------------------------------------------- 366 | 367 | template < typename T, typename Alloc = std::allocator< T > > 368 | struct runtime_sized_freelist_storage : boost::alignment::aligned_allocator_adaptor< Alloc, cacheline_bytes > 369 | { 370 | typedef boost::alignment::aligned_allocator_adaptor< Alloc, cacheline_bytes > allocator_type; 371 | T* nodes_; 372 | std::size_t node_count_; 373 | 374 | template < typename Allocator > 375 | runtime_sized_freelist_storage( Allocator const& alloc, std::size_t count ) : 376 | allocator_type( alloc ), 377 | node_count_( count ) 378 | { 379 | if ( count > 65535 ) 380 | boost::throw_exception( std::runtime_error( "boost.lockfree: freelist size is limited to a maximum of " 381 | "65535 objects" ) ); 382 | nodes_ = allocator_type::allocate( count ); 383 | std::memset( (void*)nodes_, 0, sizeof( T ) * count ); 384 | } 385 | 386 | ~runtime_sized_freelist_storage( void ) 387 | { 388 | allocator_type::deallocate( nodes_, node_count_ ); 389 | } 390 | 391 | T* nodes( void ) const 392 | { 393 | return nodes_; 394 | } 395 | 396 | std::size_t node_count( void ) const 397 | { 398 | return node_count_; 399 | } 400 | }; 401 | 402 | //---------------------------------------------------------------------------------------------------------------------- 403 | 404 | template < typename T, typename NodeStorage = runtime_sized_freelist_storage< T > > 405 | class fixed_size_freelist : NodeStorage 406 | { 407 | struct freelist_node 408 | { 409 | tagged_index next; 410 | }; 411 | 412 | void initialize( void ) 413 | { 414 | T* nodes = NodeStorage::nodes(); 415 | for ( std::size_t i = 0; i != NodeStorage::node_count(); ++i ) { 416 | tagged_index* next_index = reinterpret_cast< tagged_index* >( nodes + i ); 417 | next_index->set_index( null_handle() ); 418 | 419 | #ifdef BOOST_LOCKFREE_FREELIST_INIT_RUNS_DTOR 420 | destruct< false >( nodes + i ); 421 | #else 422 | deallocate< false >( static_cast< index_t >( i ) ); 423 | #endif 424 | } 425 | } 426 | 427 | public: 428 | typedef tagged_index tagged_node_handle; 429 | typedef tagged_index::index_t index_t; 430 | 431 | template < typename Allocator > 432 | fixed_size_freelist( Allocator const& alloc, std::size_t count ) : 433 | NodeStorage( alloc, count ), 434 | pool_( tagged_index( static_cast< index_t >( count ), 0 ) ) 435 | { 436 | initialize(); 437 | } 438 | 439 | fixed_size_freelist( void ) : 440 | pool_( tagged_index( NodeStorage::node_count(), 0 ) ) 441 | { 442 | initialize(); 443 | } 444 | 445 | template < bool ThreadSafe, bool Bounded > 446 | T* construct( void ) 447 | { 448 | index_t node_index = allocate< ThreadSafe >(); 449 | if ( node_index == null_handle() ) 450 | return NULL; 451 | 452 | T* node = NodeStorage::nodes() + node_index; 453 | new ( node ) T(); 454 | return node; 455 | } 456 | 457 | template < bool ThreadSafe, bool Bounded, typename ArgumentType > 458 | T* construct( const ArgumentType& arg ) 459 | { 460 | index_t node_index = allocate< ThreadSafe >(); 461 | if ( node_index == null_handle() ) 462 | return NULL; 463 | 464 | T* node = NodeStorage::nodes() + node_index; 465 | new ( node ) T( arg ); 466 | return node; 467 | } 468 | 469 | template < bool ThreadSafe, bool Bounded, typename ArgumentType > 470 | T* construct( ArgumentType&& arg ) 471 | { 472 | index_t node_index = allocate< ThreadSafe >(); 473 | if ( node_index == null_handle() ) 474 | return NULL; 475 | 476 | T* node = NodeStorage::nodes() + node_index; 477 | new ( node ) T( std::forward< ArgumentType >( arg ) ); 478 | return node; 479 | } 480 | 481 | template < bool ThreadSafe, bool Bounded, typename ArgumentType1, typename ArgumentType2 > 482 | T* construct( const ArgumentType1& arg1, const ArgumentType2& arg2 ) 483 | { 484 | index_t node_index = allocate< ThreadSafe >(); 485 | if ( node_index == null_handle() ) 486 | return NULL; 487 | 488 | T* node = NodeStorage::nodes() + node_index; 489 | new ( node ) T( arg1, arg2 ); 490 | return node; 491 | } 492 | 493 | template < bool ThreadSafe > 494 | void destruct( tagged_node_handle tagged_index ) 495 | { 496 | index_t index = tagged_index.get_index(); 497 | T* n = NodeStorage::nodes() + index; 498 | (void)n; // silence msvc warning 499 | n->~T(); 500 | deallocate< ThreadSafe >( index ); 501 | } 502 | 503 | template < bool ThreadSafe > 504 | void destruct( T* n ) 505 | { 506 | n->~T(); 507 | deallocate< ThreadSafe >( static_cast< index_t >( n - NodeStorage::nodes() ) ); 508 | } 509 | 510 | bool is_lock_free( void ) const 511 | { 512 | return pool_.is_lock_free(); 513 | } 514 | 515 | index_t null_handle( void ) const 516 | { 517 | return static_cast< index_t >( NodeStorage::node_count() ); 518 | } 519 | 520 | index_t get_handle( T* pointer ) const 521 | { 522 | if ( pointer == NULL ) 523 | return null_handle(); 524 | else 525 | return static_cast< index_t >( pointer - NodeStorage::nodes() ); 526 | } 527 | 528 | index_t get_handle( tagged_node_handle const& handle ) const 529 | { 530 | return handle.get_index(); 531 | } 532 | 533 | T* get_pointer( tagged_node_handle const& tptr ) const 534 | { 535 | return get_pointer( tptr.get_index() ); 536 | } 537 | 538 | T* get_pointer( index_t index ) const 539 | { 540 | if ( index == null_handle() ) 541 | return 0; 542 | else 543 | return NodeStorage::nodes() + index; 544 | } 545 | 546 | T* get_pointer( T* ptr ) const 547 | { 548 | return ptr; 549 | } 550 | 551 | protected: // allow use from subclasses 552 | template < bool ThreadSafe > 553 | index_t allocate( void ) 554 | { 555 | if ( ThreadSafe ) 556 | return allocate_impl(); 557 | else 558 | return allocate_impl_unsafe(); 559 | } 560 | 561 | private: 562 | index_t allocate_impl( void ) 563 | { 564 | tagged_index old_pool = pool_.load( memory_order_consume ); 565 | 566 | for ( ;; ) { 567 | index_t index = old_pool.get_index(); 568 | if ( index == null_handle() ) 569 | return index; 570 | 571 | T* old_node = NodeStorage::nodes() + index; 572 | tagged_index* next_index = reinterpret_cast< tagged_index* >( old_node ); 573 | 574 | tagged_index new_pool( next_index->get_index(), old_pool.get_next_tag() ); 575 | 576 | if ( pool_.compare_exchange_weak( old_pool, new_pool ) ) 577 | return old_pool.get_index(); 578 | } 579 | } 580 | 581 | index_t allocate_impl_unsafe( void ) 582 | { 583 | tagged_index old_pool = pool_.load( memory_order_consume ); 584 | 585 | index_t index = old_pool.get_index(); 586 | if ( index == null_handle() ) 587 | return index; 588 | 589 | T* old_node = NodeStorage::nodes() + index; 590 | tagged_index* next_index = reinterpret_cast< tagged_index* >( old_node ); 591 | 592 | tagged_index new_pool( next_index->get_index(), old_pool.get_next_tag() ); 593 | 594 | pool_.store( new_pool, memory_order_relaxed ); 595 | return old_pool.get_index(); 596 | } 597 | 598 | template < bool ThreadSafe > 599 | void deallocate( index_t index ) 600 | { 601 | if ( ThreadSafe ) 602 | deallocate_impl( index ); 603 | else 604 | deallocate_impl_unsafe( index ); 605 | } 606 | 607 | void deallocate_impl( index_t index ) 608 | { 609 | freelist_node* new_pool_node = reinterpret_cast< freelist_node* >( NodeStorage::nodes() + index ); 610 | tagged_index old_pool = pool_.load( memory_order_consume ); 611 | 612 | for ( ;; ) { 613 | tagged_index new_pool( index, old_pool.get_tag() ); 614 | new_pool_node->next.set_index( old_pool.get_index() ); 615 | 616 | if ( pool_.compare_exchange_weak( old_pool, new_pool ) ) 617 | return; 618 | } 619 | } 620 | 621 | void deallocate_impl_unsafe( index_t index ) 622 | { 623 | freelist_node* new_pool_node = reinterpret_cast< freelist_node* >( NodeStorage::nodes() + index ); 624 | tagged_index old_pool = pool_.load( memory_order_consume ); 625 | 626 | tagged_index new_pool( index, old_pool.get_tag() ); 627 | new_pool_node->next.set_index( old_pool.get_index() ); 628 | 629 | pool_.store( new_pool ); 630 | } 631 | 632 | atomic< tagged_index > pool_; 633 | }; 634 | 635 | //---------------------------------------------------------------------------------------------------------------------- 636 | 637 | template < typename T, typename Alloc, bool IsCompileTimeSized, bool IsFixedSize, std::size_t Capacity > 638 | struct select_freelist 639 | { 640 | typedef std::conditional_t< IsCompileTimeSized, 641 | compiletime_sized_freelist_storage< T, Capacity >, 642 | runtime_sized_freelist_storage< T, Alloc > > 643 | fixed_sized_storage_type; 644 | 645 | typedef std::conditional_t< IsCompileTimeSized || IsFixedSize, 646 | fixed_size_freelist< T, fixed_sized_storage_type >, 647 | freelist_stack< T, Alloc > > 648 | type; 649 | }; 650 | 651 | template < typename T, typename Alloc, bool IsCompileTimeSized, bool IsFixedSize, std::size_t Capacity > 652 | using select_freelist_t = typename select_freelist< T, Alloc, IsCompileTimeSized, IsFixedSize, Capacity >::type; 653 | 654 | //---------------------------------------------------------------------------------------------------------------------- 655 | 656 | template < typename T, bool IsNodeBased > 657 | struct select_tagged_handle 658 | { 659 | typedef std::conditional_t< IsNodeBased, tagged_ptr< T >, tagged_index > tagged_handle_type; 660 | typedef std::conditional_t< IsNodeBased, T*, typename tagged_index::index_t > handle_type; 661 | }; 662 | 663 | //---------------------------------------------------------------------------------------------------------------------- 664 | 665 | }}} // namespace boost::lockfree::detail 666 | 667 | #if defined( _MSC_VER ) 668 | # pragma warning( pop ) 669 | #endif 670 | 671 | 672 | #endif /* BOOST_LOCKFREE_FREELIST_HPP_INCLUDED */ 673 | -------------------------------------------------------------------------------- /include/boost/lockfree/detail/parameter.hpp: -------------------------------------------------------------------------------- 1 | // boost lockfree 2 | // 3 | // Copyright (C) 2011, 2016 Tim Blechmann 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See 6 | // accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifndef BOOST_LOCKFREE_DETAIL_PARAMETER_HPP 10 | #define BOOST_LOCKFREE_DETAIL_PARAMETER_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | namespace boost { namespace lockfree { namespace detail { 21 | 22 | //---------------------------------------------------------------------------------------------------------------------- 23 | 24 | template < typename bound_args, typename tag_type, typename default_ > 25 | using extract_arg_or_default_t = typename parameter::binding< bound_args, tag_type, default_ >::type; 26 | 27 | 28 | template < typename BoundArgs, typename TypeTag, typename IntegralType, IntegralType default_ = IntegralType {} > 29 | struct extract_integral_arg_or_default_t 30 | { 31 | static constexpr IntegralType value 32 | = extract_arg_or_default_t< BoundArgs, TypeTag, std::integral_constant< IntegralType, default_ > >::value; 33 | }; 34 | 35 | 36 | struct no_such_parameter_t 37 | {}; 38 | 39 | template < typename bound_args, typename tag_type > 40 | using has_no_arg_t 41 | = std::is_same< extract_arg_or_default_t< bound_args, tag_type, no_such_parameter_t >, no_such_parameter_t >; 42 | 43 | //---------------------------------------------------------------------------------------------------------------------- 44 | 45 | template < typename bound_args > 46 | struct extract_capacity 47 | { 48 | using capacity_t = extract_arg_or_default_t< bound_args, tag::capacity, std::integral_constant< size_t, 0 > >; 49 | using has_no_capacity_t = has_no_arg_t< bound_args, tag::capacity >; 50 | static constexpr std::size_t capacity = capacity_t::value; 51 | static constexpr bool has_capacity = !has_no_capacity_t::value; 52 | }; 53 | 54 | template < typename bound_args > 55 | using extract_capacity_t = typename extract_capacity< bound_args >::type; 56 | 57 | //---------------------------------------------------------------------------------------------------------------------- 58 | 59 | template < typename bound_args, typename T > 60 | struct extract_allocator 61 | { 62 | using default_allocator = boost::alignment::aligned_allocator< T, cacheline_bytes >; 63 | using allocator_t = extract_arg_or_default_t< bound_args, tag::allocator, default_allocator >; 64 | 65 | using has_no_allocator_t = has_no_arg_t< bound_args, tag::allocator >; 66 | static constexpr bool has_allocator = !has_no_allocator_t::value; 67 | 68 | typedef typename boost::allocator_rebind< allocator_t, T >::type type; 69 | }; 70 | 71 | template < typename bound_args, typename T > 72 | using extract_allocator_t = typename extract_allocator< bound_args, T >::type; 73 | 74 | //---------------------------------------------------------------------------------------------------------------------- 75 | 76 | template < typename bound_args, bool default_ = false > 77 | using extract_fixed_sized = extract_integral_arg_or_default_t< bound_args, tag::fixed_sized, bool, default_ >; 78 | 79 | //---------------------------------------------------------------------------------------------------------------------- 80 | 81 | template < typename bound_args, bool default_ = false > 82 | using extract_allow_multiple_reads 83 | = extract_integral_arg_or_default_t< bound_args, tag::allow_multiple_reads, bool, default_ >; 84 | 85 | //---------------------------------------------------------------------------------------------------------------------- 86 | 87 | }}} // namespace boost::lockfree::detail 88 | 89 | #endif /* BOOST_LOCKFREE_DETAIL_PARAMETER_HPP */ 90 | -------------------------------------------------------------------------------- /include/boost/lockfree/detail/prefix.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2009, 2016 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #ifndef BOOST_LOCKFREE_PREFIX_HPP_INCLUDED 8 | #define BOOST_LOCKFREE_PREFIX_HPP_INCLUDED 9 | 10 | #include 11 | 12 | /* this file defines the following macros: 13 | BOOST_LOCKFREE_PTR_COMPRESSION: use tag/pointer compression to utilize parts 14 | of the virtual address space as tag (at least 16bit) 15 | */ 16 | 17 | namespace boost { namespace lockfree { namespace detail { 18 | 19 | #ifdef __cpp_inline_variables 20 | # define inline_constexpr inline 21 | #else 22 | # define inline_constexpr 23 | #endif 24 | 25 | #if BOOST_ARCH_SYS390 26 | inline_constexpr constexpr size_t cacheline_bytes = 256; 27 | #elif BOOST_ARCH_PPC 28 | inline_constexpr constexpr size_t cacheline_bytes = 128; 29 | #elif BOOST_ARCH_ARM && ( BOOST_OS_MACOS || BOOST_OS_IOS ) 30 | // technically this is for apple's the M chips, but the A chip are probably similar 31 | inline_constexpr constexpr size_t cacheline_bytes = 128; 32 | #else 33 | inline_constexpr constexpr size_t cacheline_bytes = 64; 34 | #endif 35 | 36 | }}} // namespace boost::lockfree::detail 37 | 38 | #if BOOST_ARCH_X86_64 || ( ( BOOST_ARCH_ARM >= BOOST_VERSION_NUMBER( 8, 0, 0 ) ) && !BOOST_PLAT_ANDROID ) 39 | # define BOOST_LOCKFREE_PTR_COMPRESSION 1 40 | #endif 41 | 42 | #undef inline_constexpr 43 | 44 | #endif /* BOOST_LOCKFREE_PREFIX_HPP_INCLUDED */ 45 | -------------------------------------------------------------------------------- /include/boost/lockfree/detail/tagged_ptr.hpp: -------------------------------------------------------------------------------- 1 | // tagged pointer, for aba prevention 2 | // 3 | // Copyright (C) 2008, 2016 Tim Blechmann 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See 6 | // accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifndef BOOST_LOCKFREE_TAGGED_PTR_HPP_INCLUDED 10 | #define BOOST_LOCKFREE_TAGGED_PTR_HPP_INCLUDED 11 | 12 | #include 13 | #include 14 | 15 | #ifndef BOOST_LOCKFREE_PTR_COMPRESSION 16 | # include 17 | #else 18 | # include 19 | #endif 20 | 21 | #endif /* BOOST_LOCKFREE_TAGGED_PTR_HPP_INCLUDED */ 22 | -------------------------------------------------------------------------------- /include/boost/lockfree/detail/tagged_ptr_dcas.hpp: -------------------------------------------------------------------------------- 1 | // tagged pointer, for aba prevention 2 | // 3 | // Copyright (C) 2008, 2016 Tim Blechmann 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See 6 | // accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifndef BOOST_LOCKFREE_TAGGED_PTR_DCAS_HPP_INCLUDED 10 | #define BOOST_LOCKFREE_TAGGED_PTR_DCAS_HPP_INCLUDED 11 | 12 | #include /* for std::size_t */ 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | namespace boost { namespace lockfree { namespace detail { 19 | 20 | 21 | template < class T > 22 | class alignas( 2 * sizeof( void* ) ) tagged_ptr 23 | { 24 | public: 25 | typedef std::size_t tag_t; 26 | 27 | /** uninitialized constructor */ 28 | tagged_ptr( void ) noexcept //: ptr(0), tag(0) 29 | {} 30 | 31 | tagged_ptr( tagged_ptr const& p ) = default; 32 | 33 | explicit tagged_ptr( T* p, tag_t t = 0 ) : 34 | ptr( p ), 35 | tag( t ) 36 | {} 37 | 38 | /** unsafe set operation */ 39 | /* @{ */ 40 | tagged_ptr& operator=( tagged_ptr const& p ) = default; 41 | 42 | void set( T* p, tag_t t ) 43 | { 44 | ptr = p; 45 | tag = t; 46 | } 47 | /* @} */ 48 | 49 | /** comparing semantics */ 50 | /* @{ */ 51 | bool operator==( volatile tagged_ptr const& p ) const 52 | { 53 | return ( ptr == p.ptr ) && ( tag == p.tag ); 54 | } 55 | 56 | bool operator!=( volatile tagged_ptr const& p ) const 57 | { 58 | return !operator==( p ); 59 | } 60 | /* @} */ 61 | 62 | /** pointer access */ 63 | /* @{ */ 64 | T* get_ptr( void ) const 65 | { 66 | return ptr; 67 | } 68 | 69 | void set_ptr( T* p ) 70 | { 71 | ptr = p; 72 | } 73 | /* @} */ 74 | 75 | /** tag access */ 76 | /* @{ */ 77 | tag_t get_tag() const 78 | { 79 | return tag; 80 | } 81 | 82 | tag_t get_next_tag() const 83 | { 84 | tag_t next = ( get_tag() + 1 ) & ( std::numeric_limits< tag_t >::max )(); 85 | return next; 86 | } 87 | 88 | void set_tag( tag_t t ) 89 | { 90 | tag = t; 91 | } 92 | /* @} */ 93 | 94 | /** smart pointer support */ 95 | /* @{ */ 96 | T& operator*() const 97 | { 98 | return *ptr; 99 | } 100 | 101 | T* operator->() const 102 | { 103 | return ptr; 104 | } 105 | 106 | operator bool( void ) const 107 | { 108 | return ptr != 0; 109 | } 110 | /* @} */ 111 | 112 | protected: 113 | T* ptr; 114 | tag_t tag; 115 | }; 116 | 117 | }}} // namespace boost::lockfree::detail 118 | 119 | #endif /* BOOST_LOCKFREE_TAGGED_PTR_DCAS_HPP_INCLUDED */ 120 | -------------------------------------------------------------------------------- /include/boost/lockfree/detail/tagged_ptr_ptrcompression.hpp: -------------------------------------------------------------------------------- 1 | // tagged pointer, for aba prevention 2 | // 3 | // Copyright (C) 2008, 2009, 2016 Tim Blechmann, based on code by Cory Nelson 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See 6 | // accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifndef BOOST_LOCKFREE_TAGGED_PTR_PTRCOMPRESSION_HPP_INCLUDED 10 | #define BOOST_LOCKFREE_TAGGED_PTR_PTRCOMPRESSION_HPP_INCLUDED 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace boost { namespace lockfree { namespace detail { 18 | 19 | #ifdef BOOST_LOCKFREE_PTR_COMPRESSION 20 | 21 | template < class T > 22 | class tagged_ptr 23 | { 24 | typedef std::uint64_t compressed_ptr_t; 25 | 26 | public: 27 | typedef std::uint16_t tag_t; 28 | 29 | private: 30 | union cast_unit 31 | { 32 | compressed_ptr_t value; 33 | tag_t tag[ 4 ]; 34 | }; 35 | 36 | static constexpr int tag_index = 3; 37 | static constexpr compressed_ptr_t ptr_mask = 0xffffffffffffUL; //(1L<<48L)-1; 38 | 39 | static T* extract_ptr( volatile compressed_ptr_t const& i ) 40 | { 41 | return (T*)( i & ptr_mask ); 42 | } 43 | 44 | static tag_t extract_tag( volatile compressed_ptr_t const& i ) 45 | { 46 | cast_unit cu; 47 | cu.value = i; 48 | return cu.tag[ tag_index ]; 49 | } 50 | 51 | static compressed_ptr_t pack_ptr( T* ptr, tag_t tag ) 52 | { 53 | cast_unit ret; 54 | ret.value = compressed_ptr_t( ptr ); 55 | ret.tag[ tag_index ] = tag; 56 | return ret.value; 57 | } 58 | 59 | public: 60 | /** uninitialized constructor */ 61 | tagged_ptr( void ) noexcept //: ptr(0), tag(0) 62 | {} 63 | 64 | /** copy constructor */ 65 | tagged_ptr( tagged_ptr const& p ) = default; 66 | 67 | explicit tagged_ptr( T* p, tag_t t = 0 ) : 68 | ptr( pack_ptr( p, t ) ) 69 | {} 70 | 71 | /** unsafe set operation */ 72 | /* @{ */ 73 | tagged_ptr& operator=( tagged_ptr const& p ) = default; 74 | 75 | void set( T* p, tag_t t ) 76 | { 77 | ptr = pack_ptr( p, t ); 78 | } 79 | /* @} */ 80 | 81 | /** comparing semantics */ 82 | /* @{ */ 83 | bool operator==( volatile tagged_ptr const& p ) volatile const 84 | { 85 | return ( ptr == p.ptr ); 86 | } 87 | 88 | bool operator!=( volatile tagged_ptr const& p ) volatile const 89 | { 90 | return !operator==( p ); 91 | } 92 | /* @} */ 93 | 94 | /** pointer access */ 95 | /* @{ */ 96 | T* get_ptr() const 97 | { 98 | return extract_ptr( ptr ); 99 | } 100 | 101 | void set_ptr( T* p ) 102 | { 103 | tag_t tag = get_tag(); 104 | ptr = pack_ptr( p, tag ); 105 | } 106 | /* @} */ 107 | 108 | /** tag access */ 109 | /* @{ */ 110 | tag_t get_tag() const 111 | { 112 | return extract_tag( ptr ); 113 | } 114 | 115 | tag_t get_next_tag() const 116 | { 117 | tag_t next = ( get_tag() + 1u ) & ( std::numeric_limits< tag_t >::max )(); 118 | return next; 119 | } 120 | 121 | void set_tag( tag_t t ) 122 | { 123 | T* p = get_ptr(); 124 | ptr = pack_ptr( p, t ); 125 | } 126 | /* @} */ 127 | 128 | /** smart pointer support */ 129 | /* @{ */ 130 | T& operator*() const 131 | { 132 | return *get_ptr(); 133 | } 134 | 135 | T* operator->() const 136 | { 137 | return get_ptr(); 138 | } 139 | 140 | operator bool( void ) const 141 | { 142 | return get_ptr() != 0; 143 | } 144 | /* @} */ 145 | 146 | protected: 147 | compressed_ptr_t ptr; 148 | }; 149 | #else 150 | # error unsupported platform 151 | #endif 152 | 153 | }}} // namespace boost::lockfree::detail 154 | 155 | #endif /* BOOST_LOCKFREE_TAGGED_PTR_PTRCOMPRESSION_HPP_INCLUDED */ 156 | -------------------------------------------------------------------------------- /include/boost/lockfree/detail/uses_optional.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #ifndef BOOST_LOCKFREE_DETAIL_USES_OPTIONAL_HPP 8 | #define BOOST_LOCKFREE_DETAIL_USES_OPTIONAL_HPP 9 | 10 | #include 11 | 12 | #ifndef BOOST_NO_CXX17_HDR_OPTIONAL 13 | 14 | # include 15 | 16 | namespace boost { namespace lockfree { 17 | 18 | struct uses_optional_t 19 | {}; 20 | 21 | # ifdef BOOST_NO_CXX17_INLINE_VARIABLES 22 | static 23 | # else 24 | inline 25 | # endif 26 | constexpr uses_optional_t uses_optional; 27 | 28 | }} // namespace boost::lockfree 29 | 30 | #endif 31 | 32 | #endif /* BOOST_LOCKFREE_DETAIL_USES_OPTIONAL_HPP */ 33 | -------------------------------------------------------------------------------- /include/boost/lockfree/lockfree_forward.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2008-2016 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | 8 | #ifndef BOOST_LOCKFREE_FORWARD_HPP_INCLUDED 9 | #define BOOST_LOCKFREE_FORWARD_HPP_INCLUDED 10 | 11 | #include 12 | #ifdef BOOST_HAS_PRAGMA_ONCE 13 | # pragma once 14 | #endif 15 | 16 | 17 | #ifndef BOOST_DOXYGEN_INVOKED 18 | 19 | # include 20 | # if !defined( BOOST_NO_CXX20_HDR_CONCEPTS ) 21 | # include 22 | # endif 23 | 24 | namespace boost { namespace lockfree { 25 | 26 | // policies 27 | template < bool IsFixedSized > 28 | struct fixed_sized; 29 | 30 | template < size_t Size > 31 | struct capacity; 32 | 33 | template < class Alloc > 34 | struct allocator; 35 | 36 | 37 | // data structures 38 | 39 | template < typename T, typename... Options > 40 | # if !defined( BOOST_NO_CXX20_HDR_CONCEPTS ) 41 | requires( std::is_copy_assignable_v< T >, 42 | std::is_trivially_copy_assignable_v< T >, 43 | std::is_trivially_destructible_v< T > ) 44 | # endif 45 | class queue; 46 | 47 | template < typename T, typename... Options > 48 | # if !defined( BOOST_NO_CXX20_HDR_CONCEPTS ) 49 | requires( std::is_copy_assignable_v< T > || std::is_move_assignable_v< T > ) 50 | # endif 51 | class stack; 52 | 53 | template < typename T, typename... Options > 54 | # if !defined( BOOST_NO_CXX20_HDR_CONCEPTS ) 55 | requires( std::is_default_constructible_v< T >, std::is_move_assignable_v< T > || std::is_copy_assignable_v< T > ) 56 | # endif 57 | class spsc_queue; 58 | 59 | template < typename T, typename... Options > 60 | struct spsc_value; 61 | 62 | }} // namespace boost::lockfree 63 | 64 | #endif // BOOST_DOXYGEN_INVOKED 65 | #endif // BOOST_LOCKFREE_FORWARD_HPP_INCLUDED 66 | -------------------------------------------------------------------------------- /include/boost/lockfree/policies.hpp: -------------------------------------------------------------------------------- 1 | // boost lockfree 2 | // 3 | // Copyright (C) 2011 Tim Blechmann 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See 6 | // accompanying file LICENSE_1_0.txt or copy at 7 | // http://www.boost.org/LICENSE_1_0.txt) 8 | 9 | #ifndef BOOST_LOCKFREE_POLICIES_HPP_INCLUDED 10 | #define BOOST_LOCKFREE_POLICIES_HPP_INCLUDED 11 | 12 | #include 13 | #ifdef BOOST_HAS_PRAGMA_ONCE 14 | # pragma once 15 | #endif 16 | 17 | #include 18 | 19 | #include 20 | 21 | namespace boost { namespace lockfree { 22 | 23 | #ifndef BOOST_DOXYGEN_INVOKED 24 | namespace tag { 25 | struct allocator; 26 | struct fixed_sized; 27 | struct capacity; 28 | struct allow_multiple_reads; 29 | } // namespace tag 30 | 31 | template < bool IsFixedSized > 32 | struct fixed_sized : boost::parameter::template_keyword< tag::fixed_sized, std::integral_constant< bool, IsFixedSized > > 33 | {}; 34 | 35 | template < size_t Size > 36 | struct capacity : boost::parameter::template_keyword< tag::capacity, std::integral_constant< size_t, Size > > 37 | {}; 38 | 39 | template < class Alloc > 40 | struct allocator : boost::parameter::template_keyword< tag::allocator, Alloc > 41 | {}; 42 | 43 | template < bool AllowMultipleReads > 44 | struct allow_multiple_reads : 45 | boost::parameter::template_keyword< tag::allow_multiple_reads, std::integral_constant< bool, AllowMultipleReads > > 46 | {}; 47 | 48 | #else 49 | 50 | /** Configures a data structure as \b fixed-sized. 51 | * 52 | * The internal nodes are stored inside an array and they are addressed by array indexing. This limits the possible 53 | * size of the queue to the number of elements that can be addressed by the index type (usually 2**16-2), but on 54 | * platforms that lack double-width compare-and-exchange instructions, this is the best way to achieve lock-freedom. 55 | * This implies that a data structure is bounded. 56 | * */ 57 | template < bool IsFixedSized > 58 | struct fixed_sized; 59 | 60 | /** Sets the \b capacity of a data structure at compile-time. 61 | * 62 | * This implies that a data structure is bounded and fixed-sized. 63 | * */ 64 | template < size_t Size > 65 | struct capacity; 66 | 67 | /** Defines the \b allocator type of a data structure. 68 | * */ 69 | template < class Alloc > 70 | struct allocator; 71 | 72 | /** Configures the spsc_value to consume the value multiple times 73 | * 74 | * Caveats: 75 | * * one cannot move the value out 76 | * */ 77 | template < bool AllowMultipleReads > 78 | struct allow_multiple_reads; 79 | 80 | #endif 81 | 82 | }} // namespace boost::lockfree 83 | 84 | #endif /* BOOST_LOCKFREE_POLICIES_HPP_INCLUDED */ 85 | -------------------------------------------------------------------------------- /include/boost/lockfree/queue.hpp: -------------------------------------------------------------------------------- 1 | // lock-free queue from 2 | // Michael, M. M. and Scott, M. L., 3 | // "simple, fast and practical non-blocking and blocking concurrent queue algorithms" 4 | // 5 | // Copyright (C) 2008-2013 Tim Blechmann 6 | // 7 | // Distributed under the Boost Software License, Version 1.0. (See 8 | // accompanying file LICENSE_1_0.txt or copy at 9 | // http://www.boost.org/LICENSE_1_0.txt) 10 | 11 | #ifndef BOOST_LOCKFREE_FIFO_HPP_INCLUDED 12 | #define BOOST_LOCKFREE_FIFO_HPP_INCLUDED 13 | 14 | #include 15 | #ifdef BOOST_HAS_PRAGMA_ONCE 16 | # pragma once 17 | #endif 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | 34 | #if defined( _MSC_VER ) 35 | # pragma warning( push ) 36 | # pragma warning( disable : 4324 ) // structure was padded due to __declspec(align()) 37 | #endif 38 | 39 | #if defined( BOOST_INTEL ) && ( BOOST_INTEL_CXX_VERSION > 1000 ) 40 | # pragma warning( push ) 41 | # pragma warning( disable : 488 ) // template parameter unused in declaring parameter types, 42 | // gets erronously triggered the queue constructor which 43 | // takes an allocator of another type and rebinds it 44 | #endif 45 | 46 | 47 | namespace boost { namespace lockfree { 48 | 49 | #ifndef BOOST_DOXYGEN_INVOKED 50 | namespace detail { 51 | 52 | typedef parameter::parameters< boost::parameter::optional< tag::allocator >, boost::parameter::optional< tag::capacity > > 53 | queue_signature; 54 | 55 | } /* namespace detail */ 56 | #endif 57 | 58 | 59 | /** The queue class provides a multi-writer/multi-reader queue, pushing and popping is lock-free, 60 | * construction/destruction has to be synchronized. It uses a freelist for memory management, 61 | * freed nodes are pushed to the freelist and not returned to the OS before the queue is destroyed. 62 | * 63 | * \b Policies: 64 | * - \ref boost::lockfree::fixed_sized, defaults to \c boost::lockfree::fixed_sized \n 65 | * Can be used to completely disable dynamic memory allocations during push in order to ensure lockfree behavior. \n 66 | * If the data structure is configured as fixed-sized, the internal nodes are stored inside an array and they are 67 | * addressed by array indexing. This limits the possible size of the queue to the number of elements that can be 68 | * addressed by the index type (usually 2**16-2), but on platforms that lack double-width compare-and-exchange 69 | * instructions, this is the best way to achieve lock-freedom. 70 | * 71 | * - \ref boost::lockfree::capacity, optional \n 72 | * If this template argument is passed to the options, the size of the queue is set at compile-time.\n 73 | * This option implies \c fixed_sized 74 | * 75 | * - \ref boost::lockfree::allocator, defaults to \c boost::lockfree::allocator> \n 76 | * Specifies the allocator that is used for the internal freelist 77 | * 78 | * \b Requirements: 79 | * - T must have a copy constructor 80 | * - T must have a trivial copy assignment operator 81 | * - T must have a trivial destructor 82 | * 83 | * */ 84 | template < typename T, typename... Options > 85 | #if !defined( BOOST_NO_CXX20_HDR_CONCEPTS ) 86 | requires( std::is_copy_assignable_v< T >, 87 | std::is_trivially_copy_assignable_v< T >, 88 | std::is_trivially_destructible_v< T > ) 89 | #endif 90 | class queue 91 | { 92 | private: 93 | #ifndef BOOST_DOXYGEN_INVOKED 94 | 95 | BOOST_STATIC_ASSERT( ( std::is_trivially_destructible< T >::value ) ); 96 | BOOST_STATIC_ASSERT( ( std::is_trivially_copy_assignable< T >::value ) ); 97 | 98 | typedef typename detail::queue_signature::bind< Options... >::type bound_args; 99 | 100 | static constexpr bool has_capacity = detail::extract_capacity< bound_args >::has_capacity; 101 | static constexpr size_t capacity 102 | = detail::extract_capacity< bound_args >::capacity + 1; // the queue uses one dummy node 103 | static constexpr bool fixed_sized = detail::extract_fixed_sized< bound_args >::value; 104 | static constexpr bool node_based = !( has_capacity || fixed_sized ); 105 | static constexpr bool compile_time_sized = has_capacity; 106 | 107 | struct alignas( detail::cacheline_bytes ) node 108 | { 109 | typedef typename detail::select_tagged_handle< node, node_based >::tagged_handle_type tagged_node_handle; 110 | typedef typename detail::select_tagged_handle< node, node_based >::handle_type handle_type; 111 | 112 | node( T const& v, handle_type null_handle ) : 113 | data( v ) 114 | { 115 | /* increment tag to avoid ABA problem */ 116 | tagged_node_handle old_next = next.load( memory_order_relaxed ); 117 | tagged_node_handle new_next( null_handle, old_next.get_next_tag() ); 118 | next.store( new_next, memory_order_release ); 119 | } 120 | 121 | node( handle_type null_handle ) : 122 | next( tagged_node_handle( null_handle, 0 ) ) 123 | {} 124 | 125 | node( void ) 126 | {} 127 | 128 | atomic< tagged_node_handle > next; 129 | T data; 130 | }; 131 | 132 | typedef detail::extract_allocator_t< bound_args, node > node_allocator; 133 | typedef detail::select_freelist_t< node, node_allocator, compile_time_sized, fixed_sized, capacity > pool_t; 134 | typedef typename pool_t::tagged_node_handle tagged_node_handle; 135 | typedef typename detail::select_tagged_handle< node, node_based >::handle_type handle_type; 136 | 137 | void initialize( void ) 138 | { 139 | node* n = pool.template construct< true, false >( pool.null_handle() ); 140 | tagged_node_handle dummy_node( pool.get_handle( n ), 0 ); 141 | head_.store( dummy_node, memory_order_relaxed ); 142 | tail_.store( dummy_node, memory_order_release ); 143 | } 144 | 145 | struct implementation_defined 146 | { 147 | typedef node_allocator allocator; 148 | typedef std::size_t size_type; 149 | }; 150 | 151 | #endif 152 | 153 | public: 154 | typedef T value_type; 155 | typedef typename implementation_defined::allocator allocator; 156 | typedef typename implementation_defined::size_type size_type; 157 | 158 | /** 159 | * \return true, if implementation is lock-free. 160 | * 161 | * \warning It only checks, if the queue head and tail nodes and the freelist can be modified in a lock-free manner. 162 | * On most platforms, the whole implementation is lock-free, if this is true. Using c++0x-style atomics, there 163 | * is no possibility to provide a completely accurate implementation, because one would need to test every internal 164 | * node, which is impossible if further nodes will be allocated from the operating system. 165 | * */ 166 | bool is_lock_free( void ) const 167 | { 168 | return head_.is_lock_free() && tail_.is_lock_free() && pool.is_lock_free(); 169 | } 170 | 171 | /** Construct a fixed-sized queue 172 | * 173 | * \pre Must specify a capacity<> argument 174 | * */ 175 | queue( void ) 176 | #if !defined( BOOST_NO_CXX20_HDR_CONCEPTS ) 177 | requires( has_capacity ) 178 | #endif 179 | : 180 | head_( tagged_node_handle( 0, 0 ) ), 181 | tail_( tagged_node_handle( 0, 0 ) ), 182 | pool( node_allocator(), capacity ) 183 | { 184 | // Don't use BOOST_STATIC_ASSERT() here since it will be evaluated when compiling 185 | // this function and this function may be compiled even when it isn't being used. 186 | BOOST_ASSERT( has_capacity ); 187 | initialize(); 188 | } 189 | 190 | /** Construct a fixed-sized queue with a custom allocator 191 | * 192 | * \pre Must specify a capacity<> argument 193 | * */ 194 | template < typename U, typename Enabler = std::enable_if< has_capacity > > 195 | explicit queue( typename boost::allocator_rebind< node_allocator, U >::type const& alloc ) : 196 | head_( tagged_node_handle( 0, 0 ) ), 197 | tail_( tagged_node_handle( 0, 0 ) ), 198 | pool( alloc, capacity ) 199 | { 200 | initialize(); 201 | } 202 | 203 | /** Construct a fixed-sized queue with a custom allocator 204 | * 205 | * \pre Must specify a capacity<> argument 206 | * */ 207 | template < typename Enabler = std::enable_if< has_capacity > > 208 | explicit queue( allocator const& alloc ) : 209 | head_( tagged_node_handle( 0, 0 ) ), 210 | tail_( tagged_node_handle( 0, 0 ) ), 211 | pool( alloc, capacity ) 212 | { 213 | initialize(); 214 | } 215 | 216 | /** Construct a variable-sized queue 217 | * 218 | * Allocate n nodes initially for the freelist 219 | * 220 | * \pre Must \b not specify a capacity<> argument 221 | * */ 222 | template < typename Enabler = std::enable_if< !has_capacity > > 223 | explicit queue( size_type n ) : 224 | head_( tagged_node_handle( 0, 0 ) ), 225 | tail_( tagged_node_handle( 0, 0 ) ), 226 | pool( node_allocator(), n + 1 ) 227 | { 228 | initialize(); 229 | } 230 | 231 | /** Construct a variable-sized queue with a custom allocator 232 | * 233 | * Allocate n nodes initially for the freelist 234 | * 235 | * \pre Must \b not specify a capacity<> argument 236 | * */ 237 | template < typename U, typename Enabler = std::enable_if< !has_capacity > > 238 | queue( size_type n, typename boost::allocator_rebind< node_allocator, U >::type const& alloc ) : 239 | head_( tagged_node_handle( 0, 0 ) ), 240 | tail_( tagged_node_handle( 0, 0 ) ), 241 | pool( alloc, n + 1 ) 242 | { 243 | initialize(); 244 | } 245 | 246 | /** Construct a variable-sized queue with a custom allocator 247 | * 248 | * Allocate n nodes initially for the freelist 249 | * 250 | * \pre Must \b not specify a capacity<> argument 251 | * */ 252 | template < typename Enabler = std::enable_if< !has_capacity > > 253 | queue( size_type n, allocator const& alloc ) : 254 | head_( tagged_node_handle( 0, 0 ) ), 255 | tail_( tagged_node_handle( 0, 0 ) ), 256 | pool( alloc, n + 1 ) 257 | { 258 | initialize(); 259 | } 260 | 261 | queue( const queue& ) = delete; 262 | queue& operator=( const queue& ) = delete; 263 | queue( queue&& ) = delete; 264 | queue& operator=( queue&& ) = delete; 265 | 266 | /** \copydoc boost::lockfree::stack::reserve 267 | * */ 268 | void reserve( size_type n ) 269 | { 270 | pool.template reserve< true >( n ); 271 | } 272 | 273 | /** \copydoc boost::lockfree::stack::reserve_unsafe 274 | * */ 275 | void reserve_unsafe( size_type n ) 276 | { 277 | pool.template reserve< false >( n ); 278 | } 279 | 280 | /** Destroys queue, free all nodes from freelist. 281 | * */ 282 | ~queue( void ) 283 | { 284 | consume_all( []( const T& ) {} ); 285 | 286 | pool.template destruct< false >( head_.load( memory_order_relaxed ) ); 287 | } 288 | 289 | /** Check if the queue is empty 290 | * 291 | * \return true, if the queue is empty, false otherwise 292 | * \note The result is only accurate, if no other thread modifies the queue. Therefore it is rarely practical to use 293 | * this value in program logic. 294 | * */ 295 | bool empty( void ) const 296 | { 297 | return pool.get_handle( head_.load() ) == pool.get_handle( tail_.load() ); 298 | } 299 | 300 | /** Pushes object t to the queue. 301 | * 302 | * \post object will be pushed to the queue, if internal node can be allocated 303 | * \returns true, if the push operation is successful. 304 | * 305 | * \note Thread-safe. If internal memory pool is exhausted and the memory pool is not fixed-sized, a new node will 306 | * be allocated from the OS. This may not be lock-free. 307 | * */ 308 | bool push( const T& t ) 309 | { 310 | return do_push< false >( t ); 311 | } 312 | 313 | /// \copydoc boost::lockfree::queue::push(const T & t) 314 | bool push( T&& t ) 315 | { 316 | return do_push< false >( std::forward< T >( t ) ); 317 | } 318 | 319 | /** Pushes object t to the queue. 320 | * 321 | * \post object will be pushed to the queue, if internal node can be allocated 322 | * \returns true, if the push operation is successful. 323 | * 324 | * \note Thread-safe and non-blocking. If internal memory pool is exhausted, operation will fail 325 | * \throws if memory allocator throws 326 | * */ 327 | bool bounded_push( const T& t ) 328 | { 329 | return do_push< true >( t ); 330 | } 331 | 332 | /// \copydoc boost::lockfree::queue::bounded_push(const T & t) 333 | bool bounded_push( T&& t ) 334 | { 335 | return do_push< true >( std::forward< T >( t ) ); 336 | } 337 | 338 | 339 | private: 340 | #ifndef BOOST_DOXYGEN_INVOKED 341 | template < bool Bounded > 342 | bool do_push( T&& t ) 343 | { 344 | node* n = pool.template construct< true, Bounded >( std::forward< T >( t ), pool.null_handle() ); 345 | return do_push_node( n ); 346 | } 347 | 348 | template < bool Bounded > 349 | bool do_push( T const& t ) 350 | { 351 | node* n = pool.template construct< true, Bounded >( t, pool.null_handle() ); 352 | return do_push_node( n ); 353 | } 354 | 355 | bool do_push_node( node* n ) 356 | { 357 | handle_type node_handle = pool.get_handle( n ); 358 | 359 | if ( n == NULL ) 360 | return false; 361 | 362 | for ( ;; ) { 363 | tagged_node_handle tail = tail_.load( memory_order_acquire ); 364 | node* tail_node = pool.get_pointer( tail ); 365 | tagged_node_handle next = tail_node->next.load( memory_order_acquire ); 366 | node* next_ptr = pool.get_pointer( next ); 367 | 368 | tagged_node_handle tail2 = tail_.load( memory_order_acquire ); 369 | if ( BOOST_LIKELY( tail == tail2 ) ) { 370 | if ( next_ptr == 0 ) { 371 | tagged_node_handle new_tail_next( node_handle, next.get_next_tag() ); 372 | if ( tail_node->next.compare_exchange_weak( next, new_tail_next ) ) { 373 | tagged_node_handle new_tail( node_handle, tail.get_next_tag() ); 374 | tail_.compare_exchange_strong( tail, new_tail ); 375 | return true; 376 | } 377 | } else { 378 | tagged_node_handle new_tail( pool.get_handle( next_ptr ), tail.get_next_tag() ); 379 | tail_.compare_exchange_strong( tail, new_tail ); 380 | } 381 | } 382 | } 383 | } 384 | 385 | #endif 386 | 387 | public: 388 | /** Pushes object t to the queue. 389 | * 390 | * \post object will be pushed to the queue, if internal node can be allocated 391 | * \returns true, if the push operation is successful. 392 | * 393 | * \note Not Thread-safe. If internal memory pool is exhausted and the memory pool is not fixed-sized, a new node 394 | * will be allocated from the OS. This may not be lock-free. \throws if memory allocator throws 395 | * */ 396 | bool unsynchronized_push( T&& t ) 397 | { 398 | node* n = pool.template construct< false, false >( std::forward< T >( t ), pool.null_handle() ); 399 | 400 | if ( n == NULL ) 401 | return false; 402 | 403 | for ( ;; ) { 404 | tagged_node_handle tail = tail_.load( memory_order_relaxed ); 405 | tagged_node_handle next = tail->next.load( memory_order_relaxed ); 406 | node* next_ptr = next.get_ptr(); 407 | 408 | if ( next_ptr == 0 ) { 409 | tail->next.store( tagged_node_handle( n, next.get_next_tag() ), memory_order_relaxed ); 410 | tail_.store( tagged_node_handle( n, tail.get_next_tag() ), memory_order_relaxed ); 411 | return true; 412 | } else 413 | tail_.store( tagged_node_handle( next_ptr, tail.get_next_tag() ), memory_order_relaxed ); 414 | } 415 | } 416 | 417 | /** Pops object from queue. 418 | * 419 | * \post if pop operation is successful, object will be copied to ret. 420 | * \returns true, if the pop operation is successful, false if queue was empty. 421 | * 422 | * \note Thread-safe and non-blocking. Might modify return argument even if operation fails. 423 | * */ 424 | bool pop( T& ret ) 425 | { 426 | return pop< T >( ret ); 427 | } 428 | 429 | /** Pops object from queue. 430 | * 431 | * \pre type U must be constructible by T and copyable, or T must be convertible to U 432 | * \post if pop operation is successful, object will be copied to ret. 433 | * \returns true, if the pop operation is successful, false if queue was empty. 434 | * 435 | * \note Thread-safe and non-blocking. Might modify return argument even if operation fails. 436 | * */ 437 | template < typename U > 438 | bool pop( U& ret ) 439 | { 440 | for ( ;; ) { 441 | tagged_node_handle head = head_.load( memory_order_acquire ); 442 | node* head_ptr = pool.get_pointer( head ); 443 | 444 | tagged_node_handle tail = tail_.load( memory_order_acquire ); 445 | tagged_node_handle next = head_ptr->next.load( memory_order_acquire ); 446 | node* next_ptr = pool.get_pointer( next ); 447 | 448 | tagged_node_handle head2 = head_.load( memory_order_acquire ); 449 | if ( BOOST_LIKELY( head == head2 ) ) { 450 | if ( pool.get_handle( head ) == pool.get_handle( tail ) ) { 451 | if ( next_ptr == 0 ) 452 | return false; 453 | 454 | tagged_node_handle new_tail( pool.get_handle( next ), tail.get_next_tag() ); 455 | tail_.compare_exchange_strong( tail, new_tail ); 456 | 457 | } else { 458 | if ( next_ptr == 0 ) 459 | /* this check is not part of the original algorithm as published by michael and scott 460 | * 461 | * however we reuse the tagged_ptr part for the freelist and clear the next part during node 462 | * allocation. we can observe a null-pointer here. 463 | * */ 464 | continue; 465 | detail::copy_payload( next_ptr->data, ret ); 466 | 467 | tagged_node_handle new_head( pool.get_handle( next ), head.get_next_tag() ); 468 | if ( head_.compare_exchange_weak( head, new_head ) ) { 469 | pool.template destruct< true >( head ); 470 | return true; 471 | } 472 | } 473 | } 474 | } 475 | } 476 | 477 | #if !defined( BOOST_NO_CXX17_HDR_OPTIONAL ) || defined( BOOST_DOXYGEN_INVOKED ) 478 | /** Pops object from queue, returning a std::optional<> 479 | * 480 | * \returns `std::optional` with value if successful, `std::nullopt` if queue is empty. 481 | * 482 | * \note Thread-safe and non-blocking 483 | * 484 | * */ 485 | std::optional< T > pop( uses_optional_t ) 486 | { 487 | T to_dequeue; 488 | if ( pop( to_dequeue ) ) 489 | return to_dequeue; 490 | else 491 | return std::nullopt; 492 | } 493 | 494 | /** Pops object from queue, returning a std::optional<> 495 | * 496 | * \pre type T must be convertible to U 497 | * \returns `std::optional` with value if successful, `std::nullopt` if queue is empty. 498 | * 499 | * \note Thread-safe and non-blocking 500 | * 501 | * */ 502 | template < typename U > 503 | std::optional< U > pop( uses_optional_t ) 504 | { 505 | U to_dequeue; 506 | if ( pop( to_dequeue ) ) 507 | return to_dequeue; 508 | else 509 | return std::nullopt; 510 | } 511 | #endif 512 | 513 | /** Pops object from queue. 514 | * 515 | * \post if pop operation is successful, object will be copied to ret. 516 | * \returns true, if the pop operation is successful, false if queue was empty. 517 | * 518 | * \note Not thread-safe, but non-blocking. Might modify return argument even if operation fails. 519 | * 520 | * */ 521 | bool unsynchronized_pop( T& ret ) 522 | { 523 | return unsynchronized_pop< T >( ret ); 524 | } 525 | 526 | /** Pops object from queue. 527 | * 528 | * \pre type U must be constructible by T and copyable, or T must be convertible to U 529 | * \post if pop operation is successful, object will be copied to ret. 530 | * 531 | * \returns true, if the pop operation is successful, false if queue was empty. 532 | * 533 | * \note Not thread-safe, but non-blocking. Might modify return argument even if operation fails. 534 | * 535 | * */ 536 | template < typename U > 537 | bool unsynchronized_pop( U& ret ) 538 | { 539 | for ( ;; ) { 540 | tagged_node_handle head = head_.load( memory_order_relaxed ); 541 | node* head_ptr = pool.get_pointer( head ); 542 | tagged_node_handle tail = tail_.load( memory_order_relaxed ); 543 | tagged_node_handle next = head_ptr->next.load( memory_order_relaxed ); 544 | node* next_ptr = pool.get_pointer( next ); 545 | 546 | if ( pool.get_handle( head ) == pool.get_handle( tail ) ) { 547 | if ( next_ptr == 0 ) 548 | return false; 549 | 550 | tagged_node_handle new_tail( pool.get_handle( next ), tail.get_next_tag() ); 551 | tail_.store( new_tail ); 552 | } else { 553 | if ( next_ptr == 0 ) 554 | /* this check is not part of the original algorithm as published by michael and scott 555 | * 556 | * however we reuse the tagged_ptr part for the freelist and clear the next part during node 557 | * allocation. we can observe a null-pointer here. 558 | * */ 559 | continue; 560 | detail::copy_payload( next_ptr->data, ret ); 561 | tagged_node_handle new_head( pool.get_handle( next ), head.get_next_tag() ); 562 | head_.store( new_head ); 563 | pool.template destruct< false >( head ); 564 | return true; 565 | } 566 | } 567 | } 568 | 569 | /** consumes one element via a functor 570 | * 571 | * pops one element from the queue and applies the functor on this object 572 | * 573 | * \returns true, if one element was consumed 574 | * 575 | * \note Thread-safe and non-blocking, if functor is thread-safe and non-blocking 576 | * */ 577 | template < typename Functor > 578 | bool consume_one( Functor&& f ) 579 | { 580 | T element; 581 | bool success = pop( element ); 582 | if ( success ) 583 | f( std::move( element ) ); 584 | 585 | return success; 586 | } 587 | 588 | /** consumes all elements via a functor 589 | * 590 | * sequentially pops all elements from the queue and applies the functor on each object 591 | * 592 | * \returns number of elements that are consumed 593 | * 594 | * \note Thread-safe and non-blocking, if functor is thread-safe and non-blocking 595 | * */ 596 | template < typename Functor > 597 | size_t consume_all( Functor&& f ) 598 | { 599 | size_t element_count = 0; 600 | while ( consume_one( f ) ) 601 | element_count += 1; 602 | 603 | return element_count; 604 | } 605 | 606 | private: 607 | #ifndef BOOST_DOXYGEN_INVOKED 608 | atomic< tagged_node_handle > head_; 609 | static constexpr int padding_size = detail::cacheline_bytes - sizeof( tagged_node_handle ); 610 | char padding1[ padding_size ]; 611 | atomic< tagged_node_handle > tail_; 612 | char padding2[ padding_size ]; 613 | 614 | pool_t pool; 615 | #endif 616 | }; 617 | 618 | }} // namespace boost::lockfree 619 | 620 | #if defined( BOOST_INTEL ) && ( BOOST_INTEL_CXX_VERSION > 1000 ) 621 | # pragma warning( pop ) 622 | #endif 623 | 624 | #if defined( _MSC_VER ) 625 | # pragma warning( pop ) 626 | #endif 627 | 628 | #endif /* BOOST_LOCKFREE_FIFO_HPP_INCLUDED */ 629 | -------------------------------------------------------------------------------- /include/boost/lockfree/spsc_value.hpp: -------------------------------------------------------------------------------- 1 | // lock-free single-producer/single-consumer value 2 | // implemented via a triple buffer 3 | // 4 | // Copyright (C) 2023-2024 Tim Blechmann 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. (See 7 | // accompanying file LICENSE_1_0.txt or copy at 8 | // http://www.boost.org/LICENSE_1_0.txt) 9 | 10 | #ifndef BOOST_LOCKFREE_SPSC_VALUE_HPP_INCLUDED 11 | #define BOOST_LOCKFREE_SPSC_VALUE_HPP_INCLUDED 12 | 13 | #include 14 | 15 | #ifdef BOOST_HAS_PRAGMA_ONCE 16 | # pragma once 17 | #endif 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | #ifndef BOOST_DOXYGEN_INVOKED 32 | 33 | # ifdef BOOST_NO_CXX17_IF_CONSTEXPR 34 | # define ifconstexpr 35 | # else 36 | # define ifconstexpr constexpr 37 | # endif 38 | 39 | #endif 40 | 41 | namespace boost { namespace lockfree { 42 | 43 | /** The spcs_value provides a single-writer/single-reader value, implemented by a triple buffer 44 | * 45 | * \b Policies: 46 | * - \ref boost::lockfree::allow_multiple_reads, defaults to 47 | * \ref boost::lockfree::allow_multiple_reads "boost::lockfree::allow_multiple_reads" \n 48 | * If multiple reads are allowed, a value written to the spsc_value can be read multiple times, but not moved out 49 | * of the instance. If multiple reads are not allowed, the class works as single-element queue that overwrites on 50 | * write 51 | * 52 | * */ 53 | template < typename T, typename... Options > 54 | struct spsc_value 55 | { 56 | #ifndef BOOST_DOXYGEN_INVOKED 57 | private: 58 | using spsc_value_signature = parameter::parameters< boost::parameter::optional< tag::allow_multiple_reads > >; 59 | using bound_args = typename spsc_value_signature::bind< Options... >::type; 60 | 61 | static const bool allow_multiple_reads = detail::extract_allow_multiple_reads< bound_args >::value; 62 | 63 | public: 64 | #endif 65 | 66 | /** Construct a \ref boost::lockfree::spsc_value "spsc_value" 67 | * 68 | * If configured with \ref boost::lockfree::allow_multiple_reads "boost::lockfree::allow_multiple_reads" it 69 | * is initialized to a default-constructed value 70 | * 71 | * */ 72 | explicit spsc_value() 73 | { 74 | if ifconstexpr ( allow_multiple_reads ) { 75 | // populate initial reader 76 | m_write_index = tagged_index { 77 | 1, 78 | }; 79 | m_available_index.store( 80 | tagged_index { 81 | 0, 82 | true, 83 | }, 84 | std::memory_order_relaxed ); 85 | m_buffer[ 0 ].value = {}; 86 | } 87 | } 88 | 89 | /** Construct a \ref boost::lockfree::spsc_value "spsc_value", initialized to a value 90 | * */ 91 | explicit spsc_value( T value ) : 92 | m_write_index { 93 | 1, 94 | }, 95 | m_available_index { 96 | tagged_index { 97 | 0, 98 | true, 99 | }, 100 | } 101 | { 102 | m_buffer[ 0 ].value = std::move( value ); 103 | } 104 | 105 | /** Writes `value` to the \ref boost::lockfree::spsc_value "spsc_value" 106 | * 107 | * \pre only one thread is allowed to write data to the \ref boost::lockfree::spsc_value "spsc_value" 108 | * \post object will be written to the \ref boost::lockfree::spsc_value "spsc_value" 109 | * 110 | * \note Thread-safe and wait-free 111 | * */ 112 | void write( T&& value ) 113 | { 114 | m_buffer[ m_write_index.index() ].value = std::forward< T >( value ); 115 | swap_write_buffer(); 116 | } 117 | 118 | /// \copydoc boost::lockfree::spsc_value::write(T&& value) 119 | void write( const T& value ) 120 | { 121 | m_buffer[ m_write_index.index() ].value = value; 122 | swap_write_buffer(); 123 | } 124 | 125 | /** Reads content of the \ref boost::lockfree::spsc_value "spsc_value" 126 | * 127 | * \pre only one thread is allowed to write data to the \ref boost::lockfree::spsc_value "spsc_value" 128 | * \post if read operation is successful, object will be copied to `ret`. 129 | * \returns `true`, if the read operation is successful, false if the \ref boost::lockfree::spsc_value "spsc_value" is 130 | * configured with \ref boost::lockfree::allow_multiple_reads 131 | * "boost::lockfree::allow_multiple_reads" and no value is available for reading 132 | * 133 | * \note Thread-safe and wait-free 134 | * */ 135 | bool read( T& ret ) 136 | { 137 | #ifndef BOOST_NO_CXX17_IF_CONSTEXPR 138 | bool read_index_updated = swap_read_buffer(); 139 | 140 | if constexpr ( allow_multiple_reads ) { 141 | ret = m_buffer[ m_read_index.index() ].value; 142 | } else { 143 | if ( !read_index_updated ) 144 | return false; 145 | ret = std::move( m_buffer[ m_read_index.index() ].value ); 146 | } 147 | 148 | return true; 149 | #else 150 | return read_helper( ret, std::integral_constant< bool, allow_multiple_reads > {} ); 151 | #endif 152 | } 153 | 154 | #if !defined( BOOST_NO_CXX17_HDR_OPTIONAL ) || defined( BOOST_DOXYGEN_INVOKED ) 155 | /** Reads content of the \ref boost::lockfree::spsc_value "spsc_value", returning an optional 156 | * 157 | * \pre only one thread is allowed to write data to the \ref boost::lockfree::spsc_value "spsc_value" 158 | * \returns `std::optional` with value if successful, `std::nullopt` if spsc_value is configured with \ref 159 | * boost::lockfree::allow_multiple_reads "boost::lockfree::allow_multiple_reads" and no value is 160 | * available for reading 161 | * 162 | * \note Thread-safe and wait-free 163 | * */ 164 | std::optional< T > read( uses_optional_t ) 165 | { 166 | T to_dequeue; 167 | if ( read( to_dequeue ) ) 168 | return to_dequeue; 169 | else 170 | return std::nullopt; 171 | } 172 | #endif 173 | 174 | /** consumes value via a functor 175 | * 176 | * reads element from the spsc_value and applies the functor on this object 177 | * 178 | * \returns `true`, if element was consumed 179 | * 180 | * \note Thread-safe and non-blocking, if functor is thread-safe and non-blocking 181 | * */ 182 | 183 | template < typename Functor > 184 | bool consume( Functor&& f ) 185 | { 186 | #ifndef BOOST_NO_CXX17_IF_CONSTEXPR 187 | bool read_index_updated = swap_read_buffer(); 188 | 189 | if constexpr ( allow_multiple_reads ) { 190 | f( m_buffer[ m_read_index.index() ].value ); 191 | } else { 192 | if ( !read_index_updated ) 193 | return false; 194 | f( std::move( m_buffer[ m_read_index.index() ].value ) ); 195 | } 196 | 197 | return true; 198 | #else 199 | return consume_helper( f, std::integral_constant< bool, allow_multiple_reads > {} ); 200 | #endif 201 | } 202 | 203 | private: 204 | #ifndef BOOST_DOXYGEN_INVOKED 205 | using allow_multiple_reads_true = std::true_type; 206 | using allow_multiple_reads_false = std::false_type; 207 | 208 | # ifdef BOOST_NO_CXX17_IF_CONSTEXPR 209 | template < typename Functor > 210 | bool consume_helper( Functor&& f, allow_multiple_reads_true = {} ) 211 | { 212 | swap_read_buffer(); 213 | f( m_buffer[ m_read_index.index() ].value ); 214 | return true; 215 | } 216 | 217 | template < typename Functor > 218 | bool consume_helper( Functor&& f, allow_multiple_reads_false = {} ) 219 | { 220 | bool read_index_updated = swap_read_buffer(); 221 | if ( !read_index_updated ) 222 | return false; 223 | f( std::move( m_buffer[ m_read_index.index() ].value ) ); 224 | return true; 225 | } 226 | 227 | template < typename TT > 228 | bool read_helper( TT& ret, allow_multiple_reads_true = {} ) 229 | { 230 | swap_read_buffer(); 231 | ret = m_buffer[ m_read_index.index() ].value; 232 | return true; 233 | } 234 | 235 | template < typename TT > 236 | bool read_helper( TT& ret, allow_multiple_reads_false = {} ) 237 | { 238 | bool read_index_updated = swap_read_buffer(); 239 | if ( !read_index_updated ) 240 | return false; 241 | ret = std::move( m_buffer[ m_read_index.index() ].value ); 242 | return true; 243 | } 244 | # endif 245 | 246 | void swap_write_buffer() 247 | { 248 | tagged_index old_avail_index = m_available_index.exchange( 249 | tagged_index { 250 | m_write_index.index(), 251 | true, 252 | }, 253 | std::memory_order_release ); 254 | m_write_index.set_tag_and_index( old_avail_index.index(), false ); 255 | } 256 | 257 | bool swap_read_buffer() 258 | { 259 | constexpr bool use_compare_exchange = false; // exchange is most likely faster 260 | 261 | if ifconstexpr ( use_compare_exchange ) { 262 | tagged_index new_avail_index = m_read_index; 263 | 264 | tagged_index current_avail_index_with_tag = tagged_index { 265 | m_available_index.load( std::memory_order_acquire ).index(), 266 | true, 267 | }; 268 | 269 | if ( m_available_index.compare_exchange_strong( current_avail_index_with_tag, 270 | new_avail_index, 271 | std::memory_order_acquire ) ) { 272 | m_read_index = tagged_index( current_avail_index_with_tag.index(), false ); 273 | return true; 274 | } else 275 | return false; 276 | } else { 277 | tagged_index new_avail_index = m_read_index; 278 | 279 | tagged_index current_avail_index = m_available_index.load( std::memory_order_acquire ); 280 | if ( !current_avail_index.is_consumable() ) 281 | return false; 282 | 283 | current_avail_index = m_available_index.exchange( new_avail_index, std::memory_order_acquire ); 284 | m_read_index = tagged_index { 285 | current_avail_index.index(), 286 | false, 287 | }; 288 | return true; 289 | } 290 | } 291 | 292 | struct tagged_index 293 | { 294 | tagged_index( uint8_t index, bool tag = false ) 295 | { 296 | set_tag_and_index( index, tag ); 297 | } 298 | 299 | uint8_t index() const 300 | { 301 | return byte & 0x07; 302 | } 303 | 304 | bool is_consumable() const 305 | { 306 | return byte & 0x08; 307 | } 308 | 309 | void set_tag_and_index( uint8_t index, bool tag ) 310 | { 311 | byte = index | ( tag ? 0x08 : 0x00 ); 312 | } 313 | 314 | uint8_t byte; 315 | }; 316 | 317 | static constexpr size_t cacheline_bytes = detail::cacheline_bytes; 318 | 319 | struct alignas( cacheline_bytes ) cache_aligned_value 320 | { 321 | T value; 322 | }; 323 | 324 | std::array< cache_aligned_value, 3 > m_buffer; 325 | 326 | alignas( cacheline_bytes ) tagged_index m_write_index { 0 }; 327 | alignas( cacheline_bytes ) detail::atomic< tagged_index > m_available_index { 1 }; 328 | alignas( cacheline_bytes ) tagged_index m_read_index { 2 }; 329 | #endif 330 | }; 331 | 332 | }} // namespace boost::lockfree 333 | 334 | #undef ifconstexpr 335 | 336 | #endif /* BOOST_LOCKFREE_SPSC_VALUE_HPP_INCLUDED */ 337 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Automatic redirection failed, please go to 7 | ../../doc/html/lockfree.html  
8 |

© Copyright Beman Dawes, 2001

9 |

Distributed under the Boost Software License, Version 1.0. (See accompanying 10 | file LICENSE_1_0.txt or copy 11 | at www.boost.org/LICENSE_1_0.txt)

12 | 13 | 14 | -------------------------------------------------------------------------------- /meta/libraries.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": "lockfree", 3 | "name": "Lockfree", 4 | "authors": [ 5 | "Tim Blechmann" 6 | ], 7 | "description": "Lockfree data structures.", 8 | "category": [ 9 | "Concurrent" 10 | ], 11 | "maintainers": [ 12 | "Tim Blechmann " 13 | ], 14 | "cxxstd": "14" 15 | } 16 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(NOT TARGET boost_lockfree_all_tests) 2 | add_custom_target(boost_lockfree_all_tests) 3 | endif() 4 | 5 | include(BoostTest) 6 | 7 | if (BOOST_LOCKFREE_BUILD_TESTS) 8 | set(BUILD_TESTING TRUE) 9 | endif() 10 | 11 | if (NOT BUILD_TESTING AND NOT TARGET tests) 12 | add_custom_target(tests) 13 | endif() 14 | 15 | add_library(boost_lockfree_test_common INTERFACE) 16 | 17 | if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.23) 18 | target_sources(boost_lockfree_test_common PUBLIC FILE_SET HEADERS FILES test_common.hpp test_helpers.hpp ) 19 | source_group( TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES test_common.hpp test_helpers.hpp) 20 | endif() 21 | 22 | set(Tests 23 | destructor_test 24 | freelist_test 25 | queue_bounded_stress_test 26 | queue_fixedsize_stress_test 27 | queue_interprocess_test 28 | queue_test 29 | queue_unbounded_stress_test 30 | spsc_queue_stress_test 31 | spsc_queue_test 32 | stack_bounded_stress_test 33 | stack_fixedsize_stress_test 34 | stack_interprocess_test 35 | stack_test 36 | stack_unbounded_stress_test 37 | tagged_ptr_test 38 | spsc_value_test 39 | spsc_value_stress_test 40 | ) 41 | 42 | foreach(Test ${Tests}) 43 | set (Libs Boost::lockfree 44 | Boost::unit_test_framework 45 | Boost::thread 46 | boost_lockfree_test_common) 47 | 48 | if (Test MATCHES ".*interprocess.*") 49 | list(APPEND Libs Boost::interprocess) 50 | endif() 51 | 52 | source_group( TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${Test}.cpp) 53 | 54 | boost_test( 55 | TYPE run 56 | PREFIX boost_lockfree 57 | NAME ${Test} 58 | SOURCES ${Test}.cpp 59 | LINK_LIBRARIES ${Libs} 60 | COMPILE_DEFINITIONS BOOST_TEST_NO_OLD_TOOLS 61 | ) 62 | set_target_properties( boost_lockfree-${Test} PROPERTIES CXX_STANDARD_REQUIRED 14) 63 | 64 | if (BOOST_LOCKFREE_TESTS_STRESSTEST) 65 | target_compile_definitions(boost_lockfree-${Test} PRIVATE BOOST_LOCKFREE_STRESS_TEST) 66 | endif() 67 | 68 | if (TARGET boost_lockfree-${Test}) 69 | add_dependencies(boost_lockfree_all_tests boost_lockfree-${Test} ) 70 | endif() 71 | endforeach() 72 | -------------------------------------------------------------------------------- /test/Jamfile.v2: -------------------------------------------------------------------------------- 1 | # (C) Copyright 2010: Tim Blechmann 2 | # Distributed under the Boost Software License, Version 1.0. 3 | # (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | import testing ; 6 | 7 | lib boost_chrono ; 8 | lib boost_interprocess ; 9 | lib boost_system ; 10 | lib boost_thread ; 11 | lib boost_unit_test_framework ; 12 | 13 | project 14 | : requirements 15 | /boost/lockfree//boost_lockfree 16 | /boost/test//boost_unit_test_framework 17 | /boost/thread//boost_thread 18 | /boost/interprocess//boost_interprocess 19 | ; 20 | 21 | 22 | rule test_all 23 | { 24 | local all_rules = ; 25 | 26 | for local fileb in [ glob *.cpp ] 27 | { 28 | all_rules += [ run $(fileb) 29 | : # additional args 30 | : # test-files 31 | : # requirements 32 | acc:-lrt 33 | acc-pa_risc:-lrt 34 | windows,gcc:"-lole32 -loleaut32 -lpsapi -ladvapi32" 35 | hpux,gcc:"-Wl,+as,mpas" 36 | linux,clang:"-latomic" 37 | multi 38 | static 39 | BOOST_TEST_NO_OLD_TOOLS 40 | ] ; 41 | } 42 | 43 | return $(all_rules) ; 44 | } 45 | 46 | test-suite lockfree : [ test_all r ] : multi ; 47 | -------------------------------------------------------------------------------- /test/cmake_install_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2018, 2019 Peter Dimov 2 | # Distributed under the Boost Software License, Version 1.0. 3 | # See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt 4 | 5 | cmake_minimum_required(VERSION 3.5...3.16) 6 | 7 | project(cmake_install_test LANGUAGES CXX) 8 | 9 | find_package(boost_lockfree REQUIRED) 10 | 11 | add_executable(main main.cpp) 12 | target_link_libraries(main Boost::lockfree) 13 | 14 | enable_testing() 15 | add_test(main main) 16 | 17 | add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $) 18 | -------------------------------------------------------------------------------- /test/cmake_install_test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | boost::lockfree::queue< int > q( 64 ); 6 | } 7 | -------------------------------------------------------------------------------- /test/cmake_subdir_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2018, 2019 Peter Dimov 2 | # Distributed under the Boost Software License, Version 1.0. 3 | # See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt 4 | 5 | cmake_minimum_required(VERSION 3.5...3.20) 6 | 7 | project(cmake_subdir_test LANGUAGES CXX) 8 | 9 | add_subdirectory(../.. boostorg/lockfree) 10 | 11 | set(deps 12 | align 13 | array 14 | assert 15 | atomic 16 | config 17 | core 18 | integer 19 | iterator 20 | mpl 21 | parameter 22 | predef 23 | static_assert 24 | tuple 25 | type_traits 26 | utility 27 | 28 | # transitive 29 | bind 30 | concept_check 31 | container_hash 32 | conversion 33 | describe 34 | detail 35 | function 36 | function_types 37 | functional 38 | fusion 39 | io 40 | move 41 | mp11 42 | optional 43 | preprocessor 44 | smart_ptr 45 | typeof 46 | throw_exception 47 | winapi 48 | ) 49 | 50 | foreach(dep IN LISTS deps) 51 | 52 | add_subdirectory(../../../${dep} boostorg/${dep}) 53 | 54 | endforeach() 55 | 56 | # --target check 57 | 58 | add_executable(main ../cmake_install_test/main.cpp) 59 | target_link_libraries(main Boost::lockfree) 60 | 61 | enable_testing() 62 | add_test(main main) 63 | 64 | add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $) 65 | -------------------------------------------------------------------------------- /test/destructor_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | #define BOOST_TEST_MAIN 11 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 12 | # include 13 | #else 14 | # include 15 | #endif 16 | 17 | 18 | int g_instance_counter = 0; 19 | 20 | struct tester 21 | { 22 | tester() 23 | { 24 | ++g_instance_counter; 25 | } 26 | 27 | tester( tester const& ) 28 | { 29 | ++g_instance_counter; 30 | } 31 | 32 | ~tester() 33 | { 34 | --g_instance_counter; 35 | } 36 | }; 37 | 38 | BOOST_AUTO_TEST_CASE( stack_instance_deleter_test ) 39 | { 40 | { 41 | boost::lockfree::stack< tester > q( 128 ); 42 | q.push( tester() ); 43 | q.push( tester() ); 44 | q.push( tester() ); 45 | q.push( tester() ); 46 | q.push( tester() ); 47 | } 48 | 49 | assert( g_instance_counter == 0 ); 50 | BOOST_TEST_REQUIRE( g_instance_counter == 0 ); 51 | } 52 | 53 | 54 | BOOST_AUTO_TEST_CASE( spsc_queue_instance_deleter_test ) 55 | { 56 | { 57 | boost::lockfree::spsc_queue< tester > q( 128 ); 58 | q.push( tester() ); 59 | q.push( tester() ); 60 | q.push( tester() ); 61 | q.push( tester() ); 62 | q.push( tester() ); 63 | } 64 | 65 | assert( g_instance_counter == 0 ); 66 | BOOST_TEST_REQUIRE( g_instance_counter == 0 ); 67 | } 68 | 69 | BOOST_AUTO_TEST_CASE( spsc_queue_fixed_sized_instance_deleter_test ) 70 | { 71 | { 72 | boost::lockfree::spsc_queue< tester, boost::lockfree::capacity< 128 > > q; 73 | q.push( tester() ); 74 | q.push( tester() ); 75 | q.push( tester() ); 76 | q.push( tester() ); 77 | q.push( tester() ); 78 | } 79 | 80 | assert( g_instance_counter == 0 ); 81 | BOOST_TEST_REQUIRE( g_instance_counter == 0 ); 82 | } 83 | 84 | struct no_default_init_tester 85 | { 86 | int value; 87 | 88 | no_default_init_tester( int value ) : 89 | value( value ) 90 | { 91 | ++g_instance_counter; 92 | } 93 | 94 | no_default_init_tester( no_default_init_tester const& t ) 95 | { 96 | value = t.value; 97 | 98 | ++g_instance_counter; 99 | } 100 | 101 | ~no_default_init_tester() 102 | { 103 | --g_instance_counter; 104 | } 105 | }; 106 | 107 | BOOST_AUTO_TEST_CASE( stack_instance_deleter_no_default_init_test ) 108 | { 109 | { 110 | boost::lockfree::stack< no_default_init_tester > q( 128 ); 111 | q.push( no_default_init_tester( 1 ) ); 112 | q.push( no_default_init_tester( 2 ) ); 113 | q.push( no_default_init_tester( 3 ) ); 114 | q.push( no_default_init_tester( 4 ) ); 115 | q.push( no_default_init_tester( 5 ) ); 116 | } 117 | 118 | assert( g_instance_counter == 0 ); 119 | BOOST_TEST_REQUIRE( g_instance_counter == 0 ); 120 | } 121 | 122 | 123 | BOOST_AUTO_TEST_CASE( spsc_queue_instance_deleter_no_default_init_test ) 124 | { 125 | { 126 | boost::lockfree::spsc_queue< no_default_init_tester > q( 128 ); 127 | q.push( no_default_init_tester( 1 ) ); 128 | q.push( no_default_init_tester( 2 ) ); 129 | q.push( no_default_init_tester( 3 ) ); 130 | q.push( no_default_init_tester( 4 ) ); 131 | q.push( no_default_init_tester( 5 ) ); 132 | } 133 | 134 | assert( g_instance_counter == 0 ); 135 | BOOST_TEST_REQUIRE( g_instance_counter == 0 ); 136 | } 137 | 138 | BOOST_AUTO_TEST_CASE( spsc_queue_fixed_sized_instance_deleter_no_default_init_test ) 139 | { 140 | { 141 | boost::lockfree::spsc_queue< no_default_init_tester, boost::lockfree::capacity< 128 > > q; 142 | q.push( no_default_init_tester( 1 ) ); 143 | q.push( no_default_init_tester( 2 ) ); 144 | q.push( no_default_init_tester( 3 ) ); 145 | q.push( no_default_init_tester( 4 ) ); 146 | q.push( no_default_init_tester( 5 ) ); 147 | } 148 | 149 | assert( g_instance_counter == 0 ); 150 | BOOST_TEST_REQUIRE( g_instance_counter == 0 ); 151 | } 152 | -------------------------------------------------------------------------------- /test/freelist_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | // enables error checks via dummy::~dtor 8 | #define BOOST_LOCKFREE_FREELIST_INIT_RUNS_DTOR 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #define BOOST_TEST_MAIN 16 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 17 | # include 18 | #else 19 | # include 20 | #endif 21 | 22 | #include 23 | 24 | #include "test_helpers.hpp" 25 | 26 | using boost::lockfree::detail::atomic; 27 | 28 | atomic< bool > test_running( false ); 29 | 30 | struct dummy 31 | { 32 | dummy( void ) 33 | { 34 | if ( test_running.load( boost::lockfree::detail::memory_order_relaxed ) ) 35 | assert( allocated == 0 ); 36 | allocated = 1; 37 | } 38 | 39 | ~dummy( void ) 40 | { 41 | if ( test_running.load( boost::lockfree::detail::memory_order_relaxed ) ) 42 | assert( allocated == 1 ); 43 | allocated = 0; 44 | } 45 | 46 | size_t padding[ 2 ]; // for used for the freelist node 47 | int allocated; 48 | }; 49 | 50 | template < typename freelist_type, bool threadsafe, bool bounded > 51 | void run_test( void ) 52 | { 53 | freelist_type fl( std::allocator< int >(), 8 ); 54 | 55 | std::set< dummy* > nodes; 56 | 57 | dummy d; 58 | if ( bounded ) 59 | test_running.store( true ); 60 | 61 | for ( int i = 0; i != 4; ++i ) { 62 | dummy* allocated = fl.template construct< threadsafe, bounded >(); 63 | BOOST_TEST_REQUIRE( ( nodes.find( allocated ) == nodes.end() ) ); 64 | nodes.insert( allocated ); 65 | } 66 | 67 | for ( dummy* d : nodes ) 68 | fl.template destruct< threadsafe >( d ); 69 | 70 | nodes.clear(); 71 | for ( int i = 0; i != 4; ++i ) 72 | nodes.insert( fl.template construct< threadsafe, bounded >() ); 73 | 74 | for ( dummy* d : nodes ) 75 | fl.template destruct< threadsafe >( d ); 76 | 77 | for ( int i = 0; i != 4; ++i ) 78 | nodes.insert( fl.template construct< threadsafe, bounded >() ); 79 | 80 | if ( bounded ) 81 | test_running.store( false ); 82 | } 83 | 84 | template < bool bounded > 85 | void run_tests( void ) 86 | { 87 | run_test< boost::lockfree::detail::freelist_stack< dummy >, true, bounded >(); 88 | run_test< boost::lockfree::detail::freelist_stack< dummy >, false, bounded >(); 89 | run_test< boost::lockfree::detail::fixed_size_freelist< dummy >, true, bounded >(); 90 | } 91 | 92 | BOOST_AUTO_TEST_CASE( freelist_tests ) 93 | { 94 | run_tests< false >(); 95 | run_tests< true >(); 96 | } 97 | 98 | template < typename freelist_type, bool threadsafe > 99 | void oom_test( void ) 100 | { 101 | const bool bounded = true; 102 | freelist_type fl( std::allocator< int >(), 8 ); 103 | 104 | for ( int i = 0; i != 8; ++i ) 105 | fl.template construct< threadsafe, bounded >(); 106 | 107 | dummy* allocated = fl.template construct< threadsafe, bounded >(); 108 | BOOST_TEST_REQUIRE( allocated == (dummy*)NULL ); 109 | } 110 | 111 | BOOST_AUTO_TEST_CASE( oom_tests ) 112 | { 113 | oom_test< boost::lockfree::detail::freelist_stack< dummy >, true >(); 114 | oom_test< boost::lockfree::detail::freelist_stack< dummy >, false >(); 115 | oom_test< boost::lockfree::detail::fixed_size_freelist< dummy >, true >(); 116 | oom_test< boost::lockfree::detail::fixed_size_freelist< dummy >, false >(); 117 | } 118 | 119 | 120 | template < typename freelist_type, bool bounded > 121 | struct freelist_tester 122 | { 123 | static const int size = 128; 124 | static const int thread_count = 4; 125 | #ifndef BOOST_LOCKFREE_STRESS_TEST 126 | static const int operations_per_thread = 1000; 127 | #else 128 | static const int operations_per_thread = 100000; 129 | #endif 130 | 131 | freelist_type fl; 132 | boost::lockfree::queue< dummy* > allocated_nodes; 133 | 134 | atomic< bool > running; 135 | static_hashed_set< dummy*, 1 << 16 > working_set; 136 | 137 | 138 | freelist_tester( void ) : 139 | fl( std::allocator< int >(), size ), 140 | allocated_nodes( 256 ) 141 | {} 142 | 143 | void run() 144 | { 145 | running = true; 146 | 147 | if ( bounded ) 148 | test_running.store( true ); 149 | boost::thread_group alloc_threads; 150 | boost::thread_group dealloc_threads; 151 | 152 | for ( int i = 0; i != thread_count; ++i ) 153 | dealloc_threads.create_thread( [ this ] { 154 | deallocate(); 155 | } ); 156 | 157 | for ( int i = 0; i != thread_count; ++i ) 158 | alloc_threads.create_thread( [ this ] { 159 | allocate(); 160 | } ); 161 | alloc_threads.join_all(); 162 | test_running.store( false ); 163 | running = false; 164 | dealloc_threads.join_all(); 165 | } 166 | 167 | void allocate( void ) 168 | { 169 | for ( long i = 0; i != operations_per_thread; ++i ) { 170 | for ( ;; ) { 171 | dummy* node = fl.template construct< true, bounded >(); 172 | if ( node ) { 173 | bool success = working_set.insert( node ); 174 | (void)success; 175 | assert( success ); 176 | allocated_nodes.push( node ); 177 | break; 178 | } 179 | } 180 | } 181 | } 182 | 183 | void deallocate( void ) 184 | { 185 | for ( ;; ) { 186 | dummy* node; 187 | if ( allocated_nodes.pop( node ) ) { 188 | bool success = working_set.erase( node ); 189 | (void)success; 190 | assert( success ); 191 | fl.template destruct< true >( node ); 192 | } 193 | 194 | if ( running.load() == false ) 195 | break; 196 | 197 | #ifdef __VXWORKS__ 198 | std::this_thread::yield(); 199 | #endif 200 | } 201 | 202 | dummy* node; 203 | while ( allocated_nodes.pop( node ) ) { 204 | bool success = working_set.erase( node ); 205 | (void)success; 206 | assert( success ); 207 | fl.template destruct< true >( node ); 208 | } 209 | } 210 | }; 211 | 212 | template < typename Tester > 213 | void run_tester() 214 | { 215 | std::unique_ptr< Tester > tester( new Tester ); 216 | tester->run(); 217 | } 218 | 219 | 220 | BOOST_AUTO_TEST_CASE( unbounded_freelist_test ) 221 | { 222 | typedef freelist_tester< boost::lockfree::detail::freelist_stack< dummy >, false > test_type; 223 | run_tester< test_type >(); 224 | } 225 | 226 | 227 | BOOST_AUTO_TEST_CASE( bounded_freelist_test ) 228 | { 229 | typedef freelist_tester< boost::lockfree::detail::freelist_stack< dummy >, true > test_type; 230 | run_tester< test_type >(); 231 | } 232 | 233 | BOOST_AUTO_TEST_CASE( fixed_size_freelist_test ) 234 | { 235 | typedef freelist_tester< boost::lockfree::detail::fixed_size_freelist< dummy >, true > test_type; 236 | run_tester< test_type >(); 237 | } 238 | -------------------------------------------------------------------------------- /test/queue_bounded_stress_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #define BOOST_TEST_MAIN 10 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 11 | # include 12 | #else 13 | # include 14 | #endif 15 | 16 | #include "test_common.hpp" 17 | 18 | BOOST_AUTO_TEST_CASE( queue_test_bounded ) 19 | { 20 | typedef queue_stress_tester< true > tester_type; 21 | std::unique_ptr< tester_type > tester( new tester_type( 4, 4 ) ); 22 | 23 | boost::lockfree::queue< long > q( 128 ); 24 | tester->run( q ); 25 | } 26 | -------------------------------------------------------------------------------- /test/queue_fixedsize_stress_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #define BOOST_TEST_MAIN 10 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 11 | # include 12 | #else 13 | # include 14 | #endif 15 | 16 | #include "test_common.hpp" 17 | 18 | 19 | BOOST_AUTO_TEST_CASE( queue_test_fixed_size ) 20 | { 21 | typedef queue_stress_tester<> tester_type; 22 | std::unique_ptr< tester_type > tester( new tester_type( 4, 4 ) ); 23 | 24 | boost::lockfree::queue< long, boost::lockfree::capacity< 8 > > q; 25 | tester->run( q ); 26 | } 27 | -------------------------------------------------------------------------------- /test/queue_interprocess_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | using namespace boost::interprocess; 14 | typedef allocator< int, managed_shared_memory::segment_manager > ShmemAllocator; 15 | typedef boost::lockfree::queue< int, boost::lockfree::allocator< ShmemAllocator >, boost::lockfree::capacity< 2048 > > queue; 16 | 17 | int main( int argc, char* argv[] ) 18 | { 19 | if ( argc == 1 ) { 20 | struct shm_remove 21 | { 22 | shm_remove() 23 | { 24 | shared_memory_object::remove( "boost_queue_interprocess_test_shm" ); 25 | } 26 | ~shm_remove() 27 | { 28 | shared_memory_object::remove( "boost_queue_interprocess_test_shm" ); 29 | } 30 | } remover; 31 | 32 | managed_shared_memory segment( create_only, "boost_queue_interprocess_test_shm", 1'048'576 ); 33 | ShmemAllocator alloc_inst( segment.get_segment_manager() ); 34 | 35 | queue* q = segment.construct< queue >( "queue" )( alloc_inst ); 36 | for ( int i = 0; i != 1024; ++i ) 37 | q->push( i ); 38 | 39 | std::string s( argv[ 0 ] ); 40 | s += " child "; 41 | if ( 0 != std::system( s.c_str() ) ) 42 | return 1; 43 | 44 | while ( !q->empty() ) 45 | std::this_thread::yield(); 46 | return 0; 47 | } else { 48 | managed_shared_memory segment( open_only, "boost_queue_interprocess_test_shm" ); 49 | queue* q = segment.find< queue >( "queue" ).first; 50 | 51 | int from_queue; 52 | for ( int i = 0; i != 1024; ++i ) { 53 | bool success = q->pop( from_queue ); 54 | (void)success; 55 | assert( success ); 56 | assert( from_queue == i ); 57 | } 58 | segment.destroy< queue >( "queue" ); 59 | } 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /test/queue_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #define BOOST_TEST_MAIN 13 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 14 | # include 15 | #else 16 | # include 17 | #endif 18 | 19 | #include 20 | 21 | 22 | using namespace boost::lockfree; 23 | 24 | BOOST_AUTO_TEST_CASE( simple_queue_test ) 25 | { 26 | queue< int > f( 64 ); 27 | 28 | BOOST_TEST_WARN( f.is_lock_free() ); 29 | 30 | BOOST_TEST_REQUIRE( f.empty() ); 31 | f.push( 1 ); 32 | f.push( 2 ); 33 | 34 | int i1( 0 ), i2( 0 ); 35 | 36 | BOOST_TEST_REQUIRE( f.pop( i1 ) ); 37 | BOOST_TEST_REQUIRE( i1 == 1 ); 38 | 39 | BOOST_TEST_REQUIRE( f.pop( i2 ) ); 40 | BOOST_TEST_REQUIRE( i2 == 2 ); 41 | BOOST_TEST_REQUIRE( f.empty() ); 42 | } 43 | 44 | BOOST_AUTO_TEST_CASE( simple_queue_test_capacity ) 45 | { 46 | queue< int, capacity< 64 > > f; 47 | 48 | BOOST_TEST_WARN( f.is_lock_free() ); 49 | 50 | BOOST_TEST_REQUIRE( f.empty() ); 51 | f.push( 1 ); 52 | f.push( 2 ); 53 | 54 | int i1( 0 ), i2( 0 ); 55 | 56 | BOOST_TEST_REQUIRE( f.pop( i1 ) ); 57 | BOOST_TEST_REQUIRE( i1 == 1 ); 58 | 59 | BOOST_TEST_REQUIRE( f.pop( i2 ) ); 60 | BOOST_TEST_REQUIRE( i2 == 2 ); 61 | BOOST_TEST_REQUIRE( f.empty() ); 62 | } 63 | 64 | 65 | BOOST_AUTO_TEST_CASE( unsafe_queue_test ) 66 | { 67 | queue< int > f( 64 ); 68 | 69 | BOOST_TEST_WARN( f.is_lock_free() ); 70 | BOOST_TEST_REQUIRE( f.empty() ); 71 | 72 | int i1( 0 ), i2( 0 ); 73 | 74 | f.unsynchronized_push( 1 ); 75 | f.unsynchronized_push( 2 ); 76 | 77 | BOOST_TEST_REQUIRE( f.unsynchronized_pop( i1 ) ); 78 | BOOST_TEST_REQUIRE( i1 == 1 ); 79 | 80 | BOOST_TEST_REQUIRE( f.unsynchronized_pop( i2 ) ); 81 | BOOST_TEST_REQUIRE( i2 == 2 ); 82 | BOOST_TEST_REQUIRE( f.empty() ); 83 | } 84 | 85 | 86 | BOOST_AUTO_TEST_CASE( queue_consume_one_test ) 87 | { 88 | queue< int > f( 64 ); 89 | 90 | BOOST_TEST_WARN( f.is_lock_free() ); 91 | BOOST_TEST_REQUIRE( f.empty() ); 92 | 93 | f.push( 1 ); 94 | f.push( 2 ); 95 | 96 | bool success1 = f.consume_one( []( int i ) { 97 | BOOST_TEST_REQUIRE( i == 1 ); 98 | } ); 99 | 100 | bool success2 = f.consume_one( []( int i ) mutable { 101 | BOOST_TEST_REQUIRE( i == 2 ); 102 | } ); 103 | 104 | BOOST_TEST_REQUIRE( success1 ); 105 | BOOST_TEST_REQUIRE( success2 ); 106 | 107 | BOOST_TEST_REQUIRE( f.empty() ); 108 | } 109 | 110 | BOOST_AUTO_TEST_CASE( queue_consume_all_test ) 111 | { 112 | queue< int > f( 64 ); 113 | 114 | BOOST_TEST_WARN( f.is_lock_free() ); 115 | BOOST_TEST_REQUIRE( f.empty() ); 116 | 117 | f.push( 1 ); 118 | f.push( 2 ); 119 | 120 | size_t consumed = f.consume_all( []( int i ) {} ); 121 | 122 | BOOST_TEST_REQUIRE( consumed == 2u ); 123 | 124 | BOOST_TEST_REQUIRE( f.empty() ); 125 | } 126 | 127 | 128 | BOOST_AUTO_TEST_CASE( queue_convert_pop_test ) 129 | { 130 | queue< int* > f( 128 ); 131 | BOOST_TEST_REQUIRE( f.empty() ); 132 | f.push( new int( 1 ) ); 133 | f.push( new int( 2 ) ); 134 | f.push( new int( 3 ) ); 135 | f.push( new int( 4 ) ); 136 | 137 | { 138 | int* i1; 139 | 140 | BOOST_TEST_REQUIRE( f.pop( i1 ) ); 141 | BOOST_TEST_REQUIRE( *i1 == 1 ); 142 | delete i1; 143 | } 144 | 145 | 146 | { 147 | boost::shared_ptr< int > i2; 148 | BOOST_TEST_REQUIRE( f.pop( i2 ) ); 149 | BOOST_TEST_REQUIRE( *i2 == 2 ); 150 | } 151 | 152 | { 153 | std::unique_ptr< int > i3; 154 | BOOST_TEST_REQUIRE( f.pop( i3 ) ); 155 | 156 | BOOST_TEST_REQUIRE( *i3 == 3 ); 157 | } 158 | 159 | { 160 | std::shared_ptr< int > i4; 161 | BOOST_TEST_REQUIRE( f.pop( i4 ) ); 162 | 163 | BOOST_TEST_REQUIRE( *i4 == 4 ); 164 | } 165 | 166 | 167 | BOOST_TEST_REQUIRE( f.empty() ); 168 | } 169 | 170 | BOOST_AUTO_TEST_CASE( reserve_test ) 171 | { 172 | typedef boost::lockfree::queue< void* > memory_queue; 173 | 174 | memory_queue ms( 1 ); 175 | ms.reserve( 1 ); 176 | ms.reserve_unsafe( 1 ); 177 | } 178 | 179 | BOOST_AUTO_TEST_CASE( queue_with_allocator ) 180 | { 181 | using allocator_type = std::allocator< char >; 182 | 183 | using queue_t = boost::lockfree::queue< char, boost::lockfree::allocator< allocator_type > >; 184 | using queue_with_capacity_t 185 | = boost::lockfree::queue< char, boost::lockfree::allocator< allocator_type >, boost::lockfree::capacity< 16 > >; 186 | 187 | auto allocator = queue_t::allocator {}; 188 | 189 | { 190 | queue_with_capacity_t q_with_allocator { 191 | allocator, 192 | }; 193 | queue_t q_with_size_and_allocator { 194 | 5, 195 | allocator, 196 | }; 197 | } 198 | { 199 | queue_with_capacity_t q_with_allocator { 200 | allocator_type {}, 201 | }; 202 | queue_t q_with_size_and_allocator { 203 | 5, 204 | allocator_type {}, 205 | }; 206 | } 207 | } 208 | 209 | BOOST_AUTO_TEST_CASE( move_semantics ) 210 | { 211 | boost::lockfree::queue< int, boost::lockfree::capacity< 128 > > stk; 212 | 213 | stk.push( 0 ); 214 | stk.push( 1 ); 215 | 216 | auto two = 2; 217 | stk.push( std::move( two ) ); 218 | 219 | int out; 220 | BOOST_TEST_REQUIRE( stk.pop( out ) ); 221 | BOOST_TEST_REQUIRE( out == 0 ); 222 | 223 | stk.consume_one( []( int one ) { 224 | BOOST_TEST_REQUIRE( one == 1 ); 225 | } ); 226 | 227 | stk.consume_all( []( int ) {} ); 228 | } 229 | 230 | #if !defined( BOOST_NO_CXX17_HDR_OPTIONAL ) 231 | 232 | BOOST_AUTO_TEST_CASE( queue_uses_optional ) 233 | { 234 | boost::lockfree::queue< int > stk( 5 ); 235 | 236 | bool pop_to_nullopt = stk.pop( boost::lockfree::uses_optional ) == std::nullopt; 237 | BOOST_TEST_REQUIRE( pop_to_nullopt ); 238 | 239 | stk.push( 53 ); 240 | bool pop_to_optional = stk.pop( boost::lockfree::uses_optional ) == 53; 241 | BOOST_TEST_REQUIRE( pop_to_optional ); 242 | } 243 | 244 | #endif 245 | -------------------------------------------------------------------------------- /test/queue_unbounded_stress_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #define BOOST_TEST_MAIN 10 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 11 | # include 12 | #else 13 | # include 14 | #endif 15 | 16 | #include "test_common.hpp" 17 | 18 | BOOST_AUTO_TEST_CASE( queue_test_unbounded ) 19 | { 20 | typedef queue_stress_tester< false > tester_type; 21 | std::unique_ptr< tester_type > tester( new tester_type( 4, 4 ) ); 22 | 23 | boost::lockfree::queue< long > q( 128 ); 24 | tester->run( q ); 25 | } 26 | -------------------------------------------------------------------------------- /test/spsc_queue_stress_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011-2013 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #define BOOST_TEST_MAIN 10 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 11 | # include 12 | #else 13 | # include 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "test_common.hpp" 22 | #include "test_helpers.hpp" 23 | 24 | using namespace boost; 25 | using namespace boost::lockfree; 26 | using namespace std; 27 | 28 | #ifndef BOOST_LOCKFREE_STRESS_TEST 29 | static const long nodes_per_thread = 100000; 30 | #else 31 | static const long nodes_per_thread = 100000000; 32 | #endif 33 | 34 | struct spsc_queue_tester 35 | { 36 | spsc_queue< int, capacity< 128 > > sf; 37 | 38 | std::atomic< long > spsc_queue_cnt, received_nodes; 39 | 40 | // In VxWorks one RTP just supports 65535 objects 41 | #ifndef __VXWORKS__ 42 | static_hashed_set< int, 1 << 16 > working_set; 43 | #else 44 | static_hashed_set< int, 1 << 15 > working_set; 45 | #endif 46 | 47 | spsc_queue_tester( void ) : 48 | spsc_queue_cnt( 0 ), 49 | received_nodes( 0 ) 50 | {} 51 | 52 | void add( void ) 53 | { 54 | for ( size_t i = 0; i != nodes_per_thread; ++i ) { 55 | int id = generate_id< int >(); 56 | working_set.insert( id ); 57 | 58 | while ( sf.push( id ) == false ) {} 59 | 60 | ++spsc_queue_cnt; 61 | } 62 | running = false; 63 | } 64 | 65 | bool get_element( void ) 66 | { 67 | int data; 68 | bool success = sf.pop( data ); 69 | 70 | if ( success ) { 71 | ++received_nodes; 72 | --spsc_queue_cnt; 73 | bool erased = working_set.erase( data ); 74 | (void)erased; 75 | assert( erased ); 76 | return true; 77 | } else 78 | return false; 79 | } 80 | 81 | std::atomic< bool > running; 82 | 83 | void get( void ) 84 | { 85 | for ( ;; ) { 86 | bool success = get_element(); 87 | if ( !running && !success ) 88 | break; 89 | } 90 | 91 | while ( get_element() ) 92 | ; 93 | } 94 | 95 | void run( void ) 96 | { 97 | running = true; 98 | 99 | BOOST_TEST_REQUIRE( sf.empty() ); 100 | 101 | std::thread reader( [ & ] { 102 | get(); 103 | } ); 104 | 105 | std::thread writer( [ & ] { 106 | add(); 107 | } ); 108 | cout << "reader and writer threads created" << endl; 109 | 110 | writer.join(); 111 | cout << "writer threads joined. waiting for readers to finish" << endl; 112 | 113 | reader.join(); 114 | 115 | BOOST_TEST_REQUIRE( received_nodes == nodes_per_thread ); 116 | BOOST_TEST_REQUIRE( spsc_queue_cnt == 0 ); 117 | BOOST_TEST_REQUIRE( sf.empty() ); 118 | BOOST_TEST_REQUIRE( working_set.count_nodes() == 0 ); 119 | } 120 | }; 121 | 122 | BOOST_AUTO_TEST_CASE( spsc_queue_test_caching ) 123 | { 124 | std::shared_ptr< spsc_queue_tester > test1( new spsc_queue_tester ); 125 | test1->run(); 126 | } 127 | 128 | struct spsc_queue_tester_buffering 129 | { 130 | spsc_queue< int, capacity< 128 > > sf; 131 | 132 | std::atomic< long > spsc_queue_cnt; 133 | 134 | // In VxWorks one RTP just supports 65535 objects 135 | #ifndef __VXWORKS__ 136 | static_hashed_set< int, 1 << 16 > working_set; 137 | #else 138 | static_hashed_set< int, 1 << 15 > working_set; 139 | #endif 140 | 141 | std::atomic< size_t > received_nodes; 142 | 143 | spsc_queue_tester_buffering( void ) : 144 | spsc_queue_cnt( 0 ), 145 | received_nodes( 0 ) 146 | {} 147 | 148 | static const size_t buf_size = 5; 149 | 150 | void add( void ) 151 | { 152 | std::array< int, buf_size > input_buffer; 153 | for ( size_t i = 0; i != nodes_per_thread; i += buf_size ) { 154 | for ( size_t i = 0; i != buf_size; ++i ) { 155 | int id = generate_id< int >(); 156 | working_set.insert( id ); 157 | input_buffer[ i ] = id; 158 | } 159 | 160 | size_t pushed = 0; 161 | 162 | do { 163 | pushed += sf.push( input_buffer.data() + pushed, input_buffer.size() - pushed ); 164 | } while ( pushed != buf_size ); 165 | 166 | spsc_queue_cnt += buf_size; 167 | } 168 | running = false; 169 | } 170 | 171 | bool get_elements( void ) 172 | { 173 | std::array< int, buf_size > output_buffer; 174 | 175 | size_t popd = sf.pop( output_buffer.data(), output_buffer.size() ); 176 | 177 | if ( popd ) { 178 | received_nodes += size_t( popd ); 179 | spsc_queue_cnt -= long( popd ); 180 | 181 | for ( size_t i = 0; i != popd; ++i ) { 182 | bool erased = working_set.erase( output_buffer[ i ] ); 183 | (void)erased; 184 | assert( erased ); 185 | } 186 | 187 | return true; 188 | } else 189 | return false; 190 | } 191 | 192 | std::atomic< bool > running; 193 | 194 | void get( void ) 195 | { 196 | for ( ;; ) { 197 | bool success = get_elements(); 198 | if ( !running && !success ) 199 | break; 200 | } 201 | 202 | while ( get_elements() ) 203 | ; 204 | } 205 | 206 | void run( void ) 207 | { 208 | running = true; 209 | 210 | std::thread reader( [ & ] { 211 | get(); 212 | } ); 213 | 214 | std::thread writer( [ & ] { 215 | add(); 216 | } ); 217 | 218 | cout << "reader and writer threads created" << endl; 219 | 220 | writer.join(); 221 | cout << "writer threads joined. waiting for readers to finish" << endl; 222 | 223 | reader.join(); 224 | 225 | BOOST_TEST_REQUIRE( received_nodes == nodes_per_thread ); 226 | BOOST_TEST_REQUIRE( spsc_queue_cnt == 0 ); 227 | BOOST_TEST_REQUIRE( sf.empty() ); 228 | BOOST_TEST_REQUIRE( working_set.count_nodes() == 0 ); 229 | } 230 | }; 231 | 232 | 233 | BOOST_AUTO_TEST_CASE( spsc_queue_test_buffering ) 234 | { 235 | std::shared_ptr< spsc_queue_tester_buffering > test1( new spsc_queue_tester_buffering ); 236 | test1->run(); 237 | } 238 | -------------------------------------------------------------------------------- /test/spsc_queue_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #define BOOST_TEST_MAIN 10 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 11 | # include 12 | #else 13 | # include 14 | #endif 15 | 16 | #include "test_common.hpp" 17 | 18 | using namespace boost; 19 | using namespace boost::lockfree; 20 | using namespace std; 21 | 22 | BOOST_AUTO_TEST_CASE( simple_spsc_queue_test ) 23 | { 24 | spsc_queue< int, capacity< 64 > > f; 25 | 26 | BOOST_TEST_REQUIRE( f.empty() ); 27 | f.push( 1 ); 28 | f.push( 2 ); 29 | 30 | int i1( 0 ), i2( 0 ); 31 | 32 | BOOST_TEST_REQUIRE( f.pop( i1 ) ); 33 | BOOST_TEST_REQUIRE( i1 == 1 ); 34 | 35 | BOOST_TEST_REQUIRE( f.pop( i2 ) ); 36 | BOOST_TEST_REQUIRE( i2 == 2 ); 37 | BOOST_TEST_REQUIRE( f.empty() ); 38 | } 39 | 40 | BOOST_AUTO_TEST_CASE( simple_spsc_queue_test_compile_time_size ) 41 | { 42 | spsc_queue< int > f( 64 ); 43 | 44 | BOOST_TEST_REQUIRE( f.empty() ); 45 | f.push( 1 ); 46 | f.push( 2 ); 47 | 48 | int i1( 0 ), i2( 0 ); 49 | 50 | BOOST_TEST_REQUIRE( f.pop( i1 ) ); 51 | BOOST_TEST_REQUIRE( i1 == 1 ); 52 | 53 | BOOST_TEST_REQUIRE( f.pop( i2 ) ); 54 | BOOST_TEST_REQUIRE( i2 == 2 ); 55 | BOOST_TEST_REQUIRE( f.empty() ); 56 | } 57 | 58 | BOOST_AUTO_TEST_CASE( ranged_push_test ) 59 | { 60 | spsc_queue< int > stk( 64 ); 61 | 62 | int data[ 2 ] = { 1, 2 }; 63 | 64 | BOOST_TEST_REQUIRE( stk.push( data, data + 2 ) == data + 2 ); 65 | 66 | int out; 67 | BOOST_TEST_REQUIRE( stk.pop( out ) ); 68 | BOOST_TEST_REQUIRE( out == 1 ); 69 | BOOST_TEST_REQUIRE( stk.pop( out ) ); 70 | BOOST_TEST_REQUIRE( out == 2 ); 71 | BOOST_TEST_REQUIRE( !stk.pop( out ) ); 72 | } 73 | 74 | BOOST_AUTO_TEST_CASE( spsc_queue_consume_one_test ) 75 | { 76 | spsc_queue< int > f( 64 ); 77 | 78 | BOOST_WARN( f.is_lock_free() ); 79 | BOOST_TEST_REQUIRE( f.empty() ); 80 | 81 | f.push( 1 ); 82 | f.push( 2 ); 83 | 84 | bool success1 = f.consume_one( []( int i ) { 85 | BOOST_TEST_REQUIRE( i == 1 ); 86 | } ); 87 | 88 | bool success2 = f.consume_one( []( int i ) { 89 | BOOST_TEST_REQUIRE( i == 2 ); 90 | } ); 91 | 92 | BOOST_TEST_REQUIRE( success1 ); 93 | BOOST_TEST_REQUIRE( success2 ); 94 | 95 | BOOST_TEST_REQUIRE( f.empty() ); 96 | } 97 | 98 | BOOST_AUTO_TEST_CASE( spsc_queue_consume_all_test ) 99 | { 100 | spsc_queue< int > f( 64 ); 101 | 102 | BOOST_WARN( f.is_lock_free() ); 103 | BOOST_TEST_REQUIRE( f.empty() ); 104 | 105 | f.push( 1 ); 106 | f.push( 2 ); 107 | 108 | size_t consumed = f.consume_all( []( int i ) {} ); 109 | 110 | BOOST_TEST_REQUIRE( consumed == 2u ); 111 | 112 | BOOST_TEST_REQUIRE( f.empty() ); 113 | } 114 | 115 | enum 116 | { 117 | pointer_and_size, 118 | reference_to_array, 119 | iterator_pair, 120 | span_, 121 | output_iterator_ 122 | }; 123 | 124 | BOOST_AUTO_TEST_CASE( spsc_queue_capacity_test ) 125 | { 126 | spsc_queue< int, capacity< 2 > > f; 127 | 128 | BOOST_TEST_REQUIRE( f.push( 1 ) ); 129 | BOOST_TEST_REQUIRE( f.push( 2 ) ); 130 | BOOST_TEST_REQUIRE( !f.push( 3 ) ); 131 | 132 | spsc_queue< int > g( 2 ); 133 | 134 | BOOST_TEST_REQUIRE( g.push( 1 ) ); 135 | BOOST_TEST_REQUIRE( g.push( 2 ) ); 136 | BOOST_TEST_REQUIRE( !g.push( 3 ) ); 137 | } 138 | 139 | template < typename QueueType > 140 | void spsc_queue_avail_test_run( QueueType& q ) 141 | { 142 | BOOST_TEST_REQUIRE( q.write_available() == 16 ); 143 | BOOST_TEST_REQUIRE( q.read_available() == 0 ); 144 | 145 | for ( size_t i = 0; i != 8; ++i ) { 146 | BOOST_TEST_REQUIRE( q.write_available() == 16 - i ); 147 | BOOST_TEST_REQUIRE( q.read_available() == i ); 148 | 149 | q.push( 1 ); 150 | } 151 | 152 | // empty queue 153 | int dummy; 154 | while ( q.pop( dummy ) ) {} 155 | 156 | for ( size_t i = 0; i != 16; ++i ) { 157 | BOOST_TEST_REQUIRE( q.write_available() == 16 - i ); 158 | BOOST_TEST_REQUIRE( q.read_available() == i ); 159 | 160 | q.push( 1 ); 161 | } 162 | } 163 | 164 | BOOST_AUTO_TEST_CASE( spsc_queue_avail_test ) 165 | { 166 | spsc_queue< int, capacity< 16 > > f; 167 | spsc_queue_avail_test_run( f ); 168 | 169 | spsc_queue< int > g( 16 ); 170 | spsc_queue_avail_test_run( g ); 171 | } 172 | 173 | 174 | template < int EnqueueMode > 175 | void spsc_queue_buffer_push_return_value( void ) 176 | { 177 | const size_t xqueue_size = 64; 178 | const size_t buffer_size = 100; 179 | spsc_queue< int, capacity< 100 > > rb; 180 | 181 | int data[ xqueue_size ]; 182 | for ( size_t i = 0; i != xqueue_size; ++i ) 183 | data[ i ] = (int)i * 2; 184 | 185 | switch ( EnqueueMode ) { 186 | case pointer_and_size: BOOST_TEST_REQUIRE( rb.push( data, xqueue_size ) == xqueue_size ); break; 187 | case reference_to_array: BOOST_TEST_REQUIRE( rb.push( data ) == xqueue_size ); break; 188 | case iterator_pair: BOOST_TEST_REQUIRE( rb.push( data, data + xqueue_size ) == data + xqueue_size ); break; 189 | case span_: BOOST_TEST_REQUIRE( rb.push( boost::span< const int >( data, xqueue_size ) ) == xqueue_size ); break; 190 | default: assert( false ); 191 | } 192 | 193 | switch ( EnqueueMode ) { 194 | case pointer_and_size: BOOST_TEST_REQUIRE( rb.push( data, xqueue_size ) == buffer_size - xqueue_size ); break; 195 | case reference_to_array: BOOST_TEST_REQUIRE( rb.push( data ) == buffer_size - xqueue_size ); break; 196 | case span_: 197 | BOOST_TEST_REQUIRE( rb.push( boost::span< const int >( data, xqueue_size ) ) == buffer_size - xqueue_size ); 198 | break; 199 | case iterator_pair: 200 | BOOST_TEST_REQUIRE( rb.push( data, data + xqueue_size ) == data + buffer_size - xqueue_size ); 201 | break; 202 | 203 | default: assert( false ); 204 | } 205 | } 206 | 207 | BOOST_AUTO_TEST_CASE( spsc_queue_buffer_push_return_value_test ) 208 | { 209 | spsc_queue_buffer_push_return_value< pointer_and_size >(); 210 | spsc_queue_buffer_push_return_value< reference_to_array >(); 211 | spsc_queue_buffer_push_return_value< iterator_pair >(); 212 | spsc_queue_buffer_push_return_value< span_ >(); 213 | } 214 | 215 | template < int EnqueueMode, int ElementCount, int BufferSize, int NumberOfIterations > 216 | void spsc_queue_buffer_push( void ) 217 | { 218 | const size_t xqueue_size = ElementCount; 219 | spsc_queue< int, capacity< BufferSize > > rb; 220 | 221 | int data[ xqueue_size ]; 222 | for ( size_t i = 0; i != xqueue_size; ++i ) 223 | data[ i ] = (int)i * 2; 224 | 225 | std::vector< int > vdata( data, data + xqueue_size ); 226 | 227 | for ( int i = 0; i != NumberOfIterations; ++i ) { 228 | BOOST_TEST_REQUIRE( rb.empty() ); 229 | switch ( EnqueueMode ) { 230 | case pointer_and_size: BOOST_TEST_REQUIRE( rb.push( data, xqueue_size ) == xqueue_size ); break; 231 | case reference_to_array: BOOST_TEST_REQUIRE( rb.push( data ) == xqueue_size ); break; 232 | case iterator_pair: BOOST_TEST_REQUIRE( rb.push( data, data + xqueue_size ) == data + xqueue_size ); break; 233 | case span_: 234 | BOOST_TEST_REQUIRE( rb.push( boost::span< const int >( data, xqueue_size ) ) == xqueue_size ); 235 | break; 236 | 237 | default: assert( false ); 238 | } 239 | 240 | int out[ xqueue_size ]; 241 | BOOST_TEST_REQUIRE( rb.pop( out, xqueue_size ) == xqueue_size ); 242 | for ( size_t i = 0; i != xqueue_size; ++i ) 243 | BOOST_TEST_REQUIRE( data[ i ] == out[ i ] ); 244 | } 245 | } 246 | 247 | BOOST_AUTO_TEST_CASE( spsc_queue_buffer_push_test ) 248 | { 249 | spsc_queue_buffer_push< pointer_and_size, 7, 16, 64 >(); 250 | spsc_queue_buffer_push< reference_to_array, 7, 16, 64 >(); 251 | spsc_queue_buffer_push< iterator_pair, 7, 16, 64 >(); 252 | spsc_queue_buffer_push< span_, 7, 16, 64 >(); 253 | } 254 | 255 | template < int EnqueueMode, int ElementCount, int BufferSize, int NumberOfIterations > 256 | void spsc_queue_buffer_pop( void ) 257 | { 258 | const size_t xqueue_size = ElementCount; 259 | spsc_queue< int, capacity< BufferSize > > rb; 260 | 261 | int data[ xqueue_size ]; 262 | for ( size_t i = 0; i != xqueue_size; ++i ) 263 | data[ i ] = (int)i * 2; 264 | 265 | std::vector< int > vdata( data, data + xqueue_size ); 266 | 267 | for ( int i = 0; i != NumberOfIterations; ++i ) { 268 | BOOST_TEST_REQUIRE( rb.empty() ); 269 | BOOST_TEST_REQUIRE( rb.push( data ) == xqueue_size ); 270 | 271 | int out[ xqueue_size ]; 272 | vector< int > vout; 273 | 274 | switch ( EnqueueMode ) { 275 | case pointer_and_size: BOOST_TEST_REQUIRE( rb.pop( out, xqueue_size ) == xqueue_size ); break; 276 | case reference_to_array: BOOST_TEST_REQUIRE( rb.pop( out ) == xqueue_size ); break; 277 | case output_iterator_: BOOST_TEST_REQUIRE( rb.pop( std::back_inserter( vout ) ) == xqueue_size ); break; 278 | default: assert( false ); 279 | } 280 | 281 | if ( EnqueueMode == output_iterator_ ) { 282 | BOOST_TEST_REQUIRE( vout.size() == xqueue_size ); 283 | for ( size_t i = 0; i != xqueue_size; ++i ) 284 | BOOST_TEST_REQUIRE( data[ i ] == vout[ i ] ); 285 | } else { 286 | for ( size_t i = 0; i != xqueue_size; ++i ) 287 | BOOST_TEST_REQUIRE( data[ i ] == out[ i ] ); 288 | } 289 | } 290 | } 291 | 292 | BOOST_AUTO_TEST_CASE( spsc_queue_buffer_pop_test ) 293 | { 294 | spsc_queue_buffer_pop< pointer_and_size, 7, 16, 64 >(); 295 | spsc_queue_buffer_pop< reference_to_array, 7, 16, 64 >(); 296 | spsc_queue_buffer_pop< output_iterator_, 7, 16, 64 >(); 297 | } 298 | 299 | // Test front() and pop() 300 | template < typename Queue > 301 | void spsc_queue_front_pop( Queue& queue ) 302 | { 303 | queue.push( 1 ); 304 | queue.push( 2 ); 305 | queue.push( 3 ); 306 | 307 | // front as ref and const ref 308 | int& rfront = queue.front(); 309 | const int& crfront = queue.front(); 310 | 311 | BOOST_TEST_REQUIRE( 1 == rfront ); 312 | BOOST_TEST_REQUIRE( 1 == crfront ); 313 | 314 | int front = 0; 315 | 316 | // access element pushed first 317 | front = queue.front(); 318 | BOOST_TEST_REQUIRE( 1 == front ); 319 | 320 | // front is still the same 321 | front = queue.front(); 322 | BOOST_TEST_REQUIRE( 1 == front ); 323 | 324 | queue.pop(); 325 | 326 | front = queue.front(); 327 | BOOST_TEST_REQUIRE( 2 == front ); 328 | 329 | queue.pop(); // pop 2 330 | 331 | bool pop_ret = queue.pop(); // pop 3 332 | BOOST_TEST_REQUIRE( pop_ret ); 333 | 334 | pop_ret = queue.pop(); // pop on empty queue 335 | BOOST_TEST_REQUIRE( !pop_ret ); 336 | } 337 | 338 | BOOST_AUTO_TEST_CASE( spsc_queue_buffer_front_and_pop_runtime_sized_test ) 339 | { 340 | spsc_queue< int, capacity< 64 > > queue; 341 | spsc_queue_front_pop( queue ); 342 | } 343 | 344 | BOOST_AUTO_TEST_CASE( spsc_queue_buffer_front_and_pop_compiletime_sized_test ) 345 | { 346 | spsc_queue< int > queue( 64 ); 347 | spsc_queue_front_pop( queue ); 348 | } 349 | 350 | BOOST_AUTO_TEST_CASE( spsc_queue_reset_test ) 351 | { 352 | spsc_queue< int, capacity< 64 > > f; 353 | 354 | BOOST_TEST_REQUIRE( f.empty() ); 355 | f.push( 1 ); 356 | f.push( 2 ); 357 | 358 | f.reset(); 359 | 360 | BOOST_TEST_REQUIRE( f.empty() ); 361 | } 362 | 363 | BOOST_AUTO_TEST_CASE( move_semantics ) 364 | { 365 | boost::lockfree::spsc_queue< std::unique_ptr< int >, boost::lockfree::capacity< 128 > > stk; 366 | 367 | stk.push( std::make_unique< int >( 0 ) ); 368 | stk.push( std::make_unique< int >( 1 ) ); 369 | 370 | auto two = std::make_unique< int >( 2 ); 371 | stk.push( std::move( two ) ); 372 | 373 | std::unique_ptr< int > out; 374 | BOOST_TEST_REQUIRE( stk.pop( out ) ); 375 | BOOST_TEST_REQUIRE( *out == 0 ); 376 | 377 | stk.consume_one( []( std::unique_ptr< int > one ) { 378 | BOOST_TEST_REQUIRE( *one == 1 ); 379 | } ); 380 | 381 | stk.consume_all( []( std::unique_ptr< int > ) {} ); 382 | } 383 | 384 | #if !defined( BOOST_NO_CXX17_HDR_OPTIONAL ) 385 | 386 | BOOST_AUTO_TEST_CASE( queue_uses_optional ) 387 | { 388 | boost::lockfree::spsc_queue< int > stk( 5 ); 389 | 390 | bool pop_to_nullopt = stk.pop( boost::lockfree::uses_optional ) == std::nullopt; 391 | BOOST_TEST_REQUIRE( pop_to_nullopt ); 392 | 393 | stk.push( 53 ); 394 | bool pop_to_optional = stk.pop( boost::lockfree::uses_optional ) == 53; 395 | BOOST_TEST_REQUIRE( pop_to_optional ); 396 | } 397 | 398 | #endif 399 | -------------------------------------------------------------------------------- /test/spsc_value_stress_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011-2024 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #define BOOST_TEST_MAIN 10 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 11 | # include 12 | #else 13 | # include 14 | #endif 15 | 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | // #define BOOST_LOCKFREE_STRESS_TEST 23 | 24 | #ifndef BOOST_LOCKFREE_STRESS_TEST 25 | static const uint64_t nodes_per_thread = 100000; 26 | #else 27 | static const uint64_t nodes_per_thread = 100000000; 28 | #endif 29 | 30 | 31 | BOOST_AUTO_TEST_CASE( spsc_value_stress_test ) 32 | { 33 | boost::lockfree::spsc_value< uint64_t > v; 34 | 35 | std::atomic< bool > done; 36 | 37 | std::thread producer( [ & ] { 38 | for ( uint64_t i = 0; i != nodes_per_thread; ++i ) 39 | v.write( i ); 40 | done = true; 41 | } ); 42 | 43 | boost::optional< uint64_t > consumed; 44 | while ( !done.load( std::memory_order_relaxed ) ) { 45 | uint64_t out; 46 | bool read_success = v.read( out ); 47 | 48 | if ( !read_success ) { 49 | std::this_thread::yield(); 50 | continue; 51 | } 52 | 53 | if ( consumed ) 54 | BOOST_TEST_REQUIRE( out > *consumed ); 55 | consumed = out; 56 | } 57 | 58 | producer.join(); 59 | } 60 | 61 | BOOST_AUTO_TEST_CASE( spsc_value_stress_test_allow_multiple_reads ) 62 | { 63 | boost::lockfree::spsc_value< uint64_t, boost::lockfree::allow_multiple_reads< true > > v; 64 | 65 | std::atomic< bool > done; 66 | 67 | std::thread producer( [ & ] { 68 | for ( uint64_t i = 0; i != nodes_per_thread; ++i ) { 69 | std::this_thread::yield(); 70 | v.write( i ); 71 | } 72 | done = true; 73 | } ); 74 | 75 | boost::optional< uint64_t > consumed; 76 | while ( !done.load( std::memory_order_relaxed ) ) { 77 | uint64_t out {}; 78 | bool read_success = v.read( out ); 79 | 80 | if ( !read_success ) { 81 | std::this_thread::yield(); 82 | continue; 83 | } 84 | 85 | if ( consumed ) 86 | BOOST_TEST_REQUIRE( out >= *consumed ); 87 | consumed = out; 88 | } 89 | 90 | producer.join(); 91 | } 92 | -------------------------------------------------------------------------------- /test/spsc_value_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011-2013 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #define BOOST_TEST_MAIN 10 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 11 | # include 12 | #else 13 | # include 14 | #endif 15 | 16 | #include 17 | 18 | BOOST_AUTO_TEST_CASE( spsc_value_test ) 19 | { 20 | boost::lockfree::spsc_value< uint64_t > v; 21 | 22 | auto validate_value = [ & ]( uint64_t expected ) { 23 | uint64_t out {}; 24 | BOOST_TEST_REQUIRE( v.read( out ) == true ); 25 | BOOST_TEST_REQUIRE( out == expected ); 26 | }; 27 | 28 | auto validate_no_pending_update = [ & ] { 29 | uint64_t out {}; 30 | BOOST_TEST_REQUIRE( v.read( out ) == false ); 31 | }; 32 | 33 | validate_no_pending_update(); 34 | 35 | v.write( 1 ); 36 | validate_value( 1 ); 37 | 38 | v.write( 2 ); 39 | validate_value( 2 ); 40 | v.write( 3 ); 41 | validate_value( 3 ); 42 | v.write( 4 ); 43 | validate_value( 4 ); 44 | validate_no_pending_update(); 45 | validate_no_pending_update(); 46 | validate_no_pending_update(); 47 | 48 | v.write( 5 ); 49 | v.write( 6 ); 50 | v.write( 7 ); 51 | validate_value( 7 ); 52 | } 53 | 54 | BOOST_AUTO_TEST_CASE( spsc_value_test_allow_duplicate_reads ) 55 | { 56 | boost::lockfree::spsc_value< uint64_t, boost::lockfree::allow_multiple_reads< true > > v { 0xff }; 57 | 58 | auto validate_value = [ & ]( uint64_t expected ) { 59 | uint64_t out {}; 60 | BOOST_TEST_REQUIRE( v.read( out ) == true ); 61 | BOOST_TEST_REQUIRE( out == expected ); 62 | }; 63 | 64 | validate_value( 0xff ); 65 | 66 | v.write( 1 ); 67 | validate_value( 1 ); 68 | 69 | v.write( 2 ); 70 | validate_value( 2 ); 71 | v.write( 3 ); 72 | validate_value( 3 ); 73 | v.write( 4 ); 74 | validate_value( 4 ); 75 | validate_value( 4 ); 76 | validate_value( 4 ); 77 | validate_value( 4 ); 78 | 79 | v.write( 5 ); 80 | v.write( 6 ); 81 | v.write( 7 ); 82 | validate_value( 7 ); 83 | } 84 | 85 | 86 | BOOST_AUTO_TEST_CASE( spsc_value_test_move_only_type ) 87 | { 88 | auto make_t = [ & ]( uint64_t val ) { 89 | return std::make_unique< uint64_t >( val ); 90 | }; 91 | 92 | boost::lockfree::spsc_value< std::unique_ptr< uint64_t > > v; 93 | 94 | auto validate_value = [ & ]( uint64_t expected ) { 95 | std::unique_ptr< uint64_t > out; 96 | BOOST_TEST_REQUIRE( v.read( out ) == true ); 97 | BOOST_TEST_REQUIRE( *out == expected ); 98 | }; 99 | 100 | auto t1 = make_t( 1 ); 101 | auto t2 = make_t( 2 ); 102 | auto t3 = make_t( 3 ); 103 | 104 | v.write( std::move( t1 ) ); 105 | validate_value( 1 ); 106 | 107 | v.write( std::move( t2 ) ); 108 | BOOST_TEST_REQUIRE( v.consume( []( const std::unique_ptr< uint64_t >& out ) { 109 | BOOST_TEST_REQUIRE( *out == uint64_t( 2 ) ); 110 | } ) ); 111 | 112 | v.write( std::move( t3 ) ); 113 | BOOST_TEST_REQUIRE( v.consume( []( std::unique_ptr< uint64_t > out ) { 114 | BOOST_TEST_REQUIRE( *out == uint64_t( 3 ) ); 115 | } ) ); 116 | 117 | BOOST_TEST_REQUIRE( v.consume( []( std::unique_ptr< uint64_t > out ) {} ) == false ); 118 | 119 | std::unique_ptr< uint64_t > out; 120 | BOOST_TEST_REQUIRE( v.read( out ) == false ); 121 | } 122 | -------------------------------------------------------------------------------- /test/stack_bounded_stress_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #include 10 | 11 | #define BOOST_TEST_MAIN 12 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 13 | # include 14 | #else 15 | # include 16 | #endif 17 | 18 | #include "test_common.hpp" 19 | 20 | BOOST_AUTO_TEST_CASE( stack_test_bounded ) 21 | { 22 | typedef queue_stress_tester< true > tester_type; 23 | std::unique_ptr< tester_type > tester( new tester_type( 4, 4 ) ); 24 | 25 | boost::lockfree::stack< long > q( 128 ); 26 | tester->run( q ); 27 | } 28 | -------------------------------------------------------------------------------- /test/stack_fixedsize_stress_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #define BOOST_TEST_MAIN 10 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 11 | # include 12 | #else 13 | # include 14 | #endif 15 | 16 | #include "test_common.hpp" 17 | 18 | 19 | BOOST_AUTO_TEST_CASE( stack_test_fixed_size ) 20 | { 21 | typedef queue_stress_tester<> tester_type; 22 | std::unique_ptr< tester_type > tester( new tester_type( 4, 4 ) ); 23 | 24 | boost::lockfree::stack< long, boost::lockfree::capacity< 8 > > q; 25 | tester->run( q ); 26 | } 27 | -------------------------------------------------------------------------------- /test/stack_interprocess_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include //std::system 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | using namespace boost::interprocess; 14 | typedef allocator< int, managed_shared_memory::segment_manager > ShmemAllocator; 15 | typedef boost::lockfree::stack< int, boost::lockfree::allocator< ShmemAllocator >, boost::lockfree::capacity< 2048 > > stack; 16 | 17 | int main( int argc, char* argv[] ) 18 | { 19 | if ( argc == 1 ) { 20 | struct shm_remove 21 | { 22 | shm_remove() 23 | { 24 | shared_memory_object::remove( "MySharedMemory" ); 25 | } 26 | ~shm_remove() 27 | { 28 | shared_memory_object::remove( "MySharedMemory" ); 29 | } 30 | } remover; 31 | 32 | managed_shared_memory segment( create_only, "MySharedMemory", 65536 ); 33 | ShmemAllocator alloc_inst( segment.get_segment_manager() ); 34 | 35 | stack* queue = segment.construct< stack >( "stack" )( alloc_inst ); 36 | for ( int i = 0; i != 1024; ++i ) 37 | queue->push( i ); 38 | 39 | std::string s( argv[ 0 ] ); 40 | s += " child "; 41 | if ( 0 != std::system( s.c_str() ) ) 42 | return 1; 43 | 44 | while ( !queue->empty() ) 45 | std::this_thread::yield(); 46 | return 0; 47 | } else { 48 | managed_shared_memory segment( open_only, "MySharedMemory" ); 49 | stack* queue = segment.find< stack >( "stack" ).first; 50 | 51 | int from_queue; 52 | for ( int i = 0; i != 1024; ++i ) { 53 | bool success = queue->pop( from_queue ); 54 | (void)success; 55 | assert( success ); 56 | assert( from_queue == 1023 - i ); 57 | } 58 | segment.destroy< stack >( "stack" ); 59 | } 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /test/stack_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | 8 | #include 9 | 10 | #define BOOST_TEST_MAIN 11 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 12 | # include 13 | #else 14 | # include 15 | #endif 16 | 17 | BOOST_AUTO_TEST_CASE( simple_stack_test ) 18 | { 19 | boost::lockfree::stack< long > stk( 128 ); 20 | 21 | stk.push( 1 ); 22 | stk.push( 2 ); 23 | long out; 24 | BOOST_TEST_REQUIRE( stk.pop( out ) ); 25 | BOOST_TEST_REQUIRE( out == 2 ); 26 | BOOST_TEST_REQUIRE( stk.pop( out ) ); 27 | BOOST_TEST_REQUIRE( out == 1 ); 28 | BOOST_TEST_REQUIRE( !stk.pop( out ) ); 29 | } 30 | 31 | BOOST_AUTO_TEST_CASE( unsafe_stack_test ) 32 | { 33 | boost::lockfree::stack< long > stk( 128 ); 34 | 35 | stk.unsynchronized_push( 1 ); 36 | stk.unsynchronized_push( 2 ); 37 | long out; 38 | BOOST_TEST_REQUIRE( stk.unsynchronized_pop( out ) ); 39 | BOOST_TEST_REQUIRE( out == 2 ); 40 | BOOST_TEST_REQUIRE( stk.unsynchronized_pop( out ) ); 41 | BOOST_TEST_REQUIRE( out == 1 ); 42 | BOOST_TEST_REQUIRE( !stk.unsynchronized_pop( out ) ); 43 | } 44 | 45 | BOOST_AUTO_TEST_CASE( ranged_push_test ) 46 | { 47 | boost::lockfree::stack< long > stk( 128 ); 48 | 49 | long data[ 2 ] = { 1, 2 }; 50 | 51 | BOOST_TEST_REQUIRE( stk.push( data, data + 2 ) == data + 2 ); 52 | 53 | long out; 54 | BOOST_TEST_REQUIRE( stk.unsynchronized_pop( out ) ); 55 | BOOST_TEST_REQUIRE( out == 2 ); 56 | BOOST_TEST_REQUIRE( stk.unsynchronized_pop( out ) ); 57 | BOOST_TEST_REQUIRE( out == 1 ); 58 | BOOST_TEST_REQUIRE( !stk.unsynchronized_pop( out ) ); 59 | } 60 | 61 | BOOST_AUTO_TEST_CASE( span_push_test ) 62 | { 63 | boost::lockfree::stack< long > stk( 128 ); 64 | 65 | long data[ 2 ] = { 1, 2 }; 66 | 67 | BOOST_TEST_REQUIRE( stk.push( boost::span< const long >( data ) ) == size_t( 2 ) ); 68 | 69 | long out; 70 | BOOST_TEST_REQUIRE( stk.unsynchronized_pop( out ) ); 71 | BOOST_TEST_REQUIRE( out == 2 ); 72 | BOOST_TEST_REQUIRE( stk.unsynchronized_pop( out ) ); 73 | BOOST_TEST_REQUIRE( out == 1 ); 74 | BOOST_TEST_REQUIRE( !stk.unsynchronized_pop( out ) ); 75 | } 76 | 77 | 78 | BOOST_AUTO_TEST_CASE( ranged_unsynchronized_push_test ) 79 | { 80 | boost::lockfree::stack< long > stk( 128 ); 81 | 82 | long data[ 2 ] = { 1, 2 }; 83 | 84 | BOOST_TEST_REQUIRE( stk.unsynchronized_push( data, data + 2 ) == data + 2 ); 85 | 86 | long out; 87 | BOOST_TEST_REQUIRE( stk.unsynchronized_pop( out ) ); 88 | BOOST_TEST_REQUIRE( out == 2 ); 89 | BOOST_TEST_REQUIRE( stk.unsynchronized_pop( out ) ); 90 | BOOST_TEST_REQUIRE( out == 1 ); 91 | BOOST_TEST_REQUIRE( !stk.unsynchronized_pop( out ) ); 92 | } 93 | 94 | BOOST_AUTO_TEST_CASE( span_unsynchronized_push_test ) 95 | { 96 | boost::lockfree::stack< long > stk( 128 ); 97 | 98 | long data[ 2 ] = { 1, 2 }; 99 | 100 | BOOST_TEST_REQUIRE( stk.unsynchronized_push( boost::span< const long >( data ) ) == 2 ); 101 | 102 | long out; 103 | BOOST_TEST_REQUIRE( stk.unsynchronized_pop( out ) ); 104 | BOOST_TEST_REQUIRE( out == 2 ); 105 | BOOST_TEST_REQUIRE( stk.unsynchronized_pop( out ) ); 106 | BOOST_TEST_REQUIRE( out == 1 ); 107 | BOOST_TEST_REQUIRE( !stk.unsynchronized_pop( out ) ); 108 | } 109 | 110 | 111 | BOOST_AUTO_TEST_CASE( fixed_size_stack_test ) 112 | { 113 | boost::lockfree::stack< long, boost::lockfree::capacity< 128 > > stk; 114 | 115 | stk.push( 1 ); 116 | stk.push( 2 ); 117 | long out; 118 | BOOST_TEST_REQUIRE( stk.pop( out ) ); 119 | BOOST_TEST_REQUIRE( out == 2 ); 120 | BOOST_TEST_REQUIRE( stk.pop( out ) ); 121 | BOOST_TEST_REQUIRE( out == 1 ); 122 | BOOST_TEST_REQUIRE( !stk.pop( out ) ); 123 | BOOST_TEST_REQUIRE( stk.empty() ); 124 | } 125 | 126 | BOOST_AUTO_TEST_CASE( fixed_size_stack_test_exhausted ) 127 | { 128 | boost::lockfree::stack< long, boost::lockfree::capacity< 2 > > stk; 129 | 130 | stk.push( 1 ); 131 | stk.push( 2 ); 132 | BOOST_TEST_REQUIRE( !stk.push( 3 ) ); 133 | long out; 134 | BOOST_TEST_REQUIRE( stk.pop( out ) ); 135 | BOOST_TEST_REQUIRE( out == 2 ); 136 | BOOST_TEST_REQUIRE( stk.pop( out ) ); 137 | BOOST_TEST_REQUIRE( out == 1 ); 138 | BOOST_TEST_REQUIRE( !stk.pop( out ) ); 139 | BOOST_TEST_REQUIRE( stk.empty() ); 140 | } 141 | 142 | BOOST_AUTO_TEST_CASE( bounded_stack_test_exhausted ) 143 | { 144 | boost::lockfree::stack< long > stk( 2 ); 145 | 146 | stk.bounded_push( 1 ); 147 | stk.bounded_push( 2 ); 148 | BOOST_TEST_REQUIRE( !stk.bounded_push( 3 ) ); 149 | long out; 150 | BOOST_TEST_REQUIRE( stk.pop( out ) ); 151 | BOOST_TEST_REQUIRE( out == 2 ); 152 | BOOST_TEST_REQUIRE( stk.pop( out ) ); 153 | BOOST_TEST_REQUIRE( out == 1 ); 154 | BOOST_TEST_REQUIRE( !stk.pop( out ) ); 155 | BOOST_TEST_REQUIRE( stk.empty() ); 156 | } 157 | 158 | BOOST_AUTO_TEST_CASE( stack_consume_one_test ) 159 | { 160 | boost::lockfree::stack< int > f( 64 ); 161 | 162 | BOOST_TEST_WARN( f.is_lock_free() ); 163 | BOOST_TEST_REQUIRE( f.empty() ); 164 | 165 | f.push( 1 ); 166 | f.push( 2 ); 167 | 168 | bool success1 = f.consume_one( []( int i ) { 169 | BOOST_TEST_REQUIRE( i == 2 ); 170 | } ); 171 | 172 | bool success2 = f.consume_one( []( int i ) mutable { 173 | BOOST_TEST_REQUIRE( i == 1 ); 174 | } ); 175 | 176 | BOOST_TEST_REQUIRE( success1 ); 177 | BOOST_TEST_REQUIRE( success2 ); 178 | 179 | BOOST_TEST_REQUIRE( f.empty() ); 180 | } 181 | 182 | BOOST_AUTO_TEST_CASE( stack_consume_all_test ) 183 | { 184 | boost::lockfree::stack< int > f( 64 ); 185 | 186 | BOOST_TEST_WARN( f.is_lock_free() ); 187 | BOOST_TEST_REQUIRE( f.empty() ); 188 | 189 | f.push( 1 ); 190 | f.push( 2 ); 191 | 192 | size_t consumed = f.consume_all( []( int i ) {} ); 193 | 194 | BOOST_TEST_REQUIRE( consumed == 2u ); 195 | 196 | BOOST_TEST_REQUIRE( f.empty() ); 197 | } 198 | 199 | BOOST_AUTO_TEST_CASE( stack_consume_all_atomic_test ) 200 | { 201 | boost::lockfree::stack< int > f( 64 ); 202 | 203 | BOOST_TEST_WARN( f.is_lock_free() ); 204 | BOOST_TEST_REQUIRE( f.empty() ); 205 | 206 | f.push( 1 ); 207 | f.push( 2 ); 208 | f.push( 3 ); 209 | 210 | size_t consumed = f.consume_all_atomic( []( int i ) {} ); 211 | 212 | BOOST_TEST_REQUIRE( consumed == 3u ); 213 | 214 | BOOST_TEST_REQUIRE( f.empty() ); 215 | } 216 | 217 | 218 | BOOST_AUTO_TEST_CASE( stack_consume_all_atomic_reversed_test ) 219 | { 220 | boost::lockfree::stack< int > f( 64 ); 221 | 222 | BOOST_TEST_WARN( f.is_lock_free() ); 223 | BOOST_TEST_REQUIRE( f.empty() ); 224 | 225 | f.push( 1 ); 226 | f.push( 2 ); 227 | f.push( 3 ); 228 | 229 | size_t consumed = f.consume_all_atomic_reversed( []( int i ) {} ); 230 | 231 | BOOST_TEST_REQUIRE( consumed == 3u ); 232 | 233 | BOOST_TEST_REQUIRE( f.empty() ); 234 | } 235 | 236 | 237 | BOOST_AUTO_TEST_CASE( reserve_test ) 238 | { 239 | typedef boost::lockfree::stack< void* > memory_stack; 240 | 241 | memory_stack ms( 1 ); 242 | ms.reserve( 1 ); 243 | ms.reserve_unsafe( 1 ); 244 | } 245 | 246 | BOOST_AUTO_TEST_CASE( stack_with_allocator ) 247 | { 248 | using allocator_type = std::allocator< char >; 249 | 250 | using stack_t = boost::lockfree::stack< char, boost::lockfree::allocator< allocator_type > >; 251 | using stack_with_capacity_t 252 | = boost::lockfree::stack< char, boost::lockfree::allocator< allocator_type >, boost::lockfree::capacity< 16 > >; 253 | 254 | auto allocator = stack_t::allocator {}; 255 | 256 | { 257 | stack_with_capacity_t stack_with_allocator { 258 | allocator, 259 | }; 260 | stack_t stack_with_size_and_allocator { 261 | 5, 262 | allocator, 263 | }; 264 | } 265 | { 266 | stack_with_capacity_t stack_with_allocator { 267 | allocator_type {}, 268 | }; 269 | stack_t stack_with_size_and_allocator { 270 | 5, 271 | allocator_type {}, 272 | }; 273 | } 274 | } 275 | 276 | BOOST_AUTO_TEST_CASE( move_semantics ) 277 | { 278 | boost::lockfree::stack< std::unique_ptr< int >, boost::lockfree::capacity< 128 > > stk; 279 | 280 | stk.push( std::make_unique< int >( 0 ) ); 281 | stk.push( std::make_unique< int >( 1 ) ); 282 | 283 | auto two = std::make_unique< int >( 2 ); 284 | stk.push( std::move( two ) ); 285 | 286 | std::unique_ptr< int > out; 287 | BOOST_TEST_REQUIRE( stk.pop( out ) ); 288 | BOOST_TEST_REQUIRE( *out == 2 ); 289 | 290 | stk.consume_one( []( std::unique_ptr< int > one ) { 291 | BOOST_TEST_REQUIRE( *one == 1 ); 292 | } ); 293 | 294 | stk.consume_all( []( std::unique_ptr< int > ) {} ); 295 | } 296 | 297 | #if !defined( BOOST_NO_CXX17_HDR_OPTIONAL ) 298 | 299 | BOOST_AUTO_TEST_CASE( queue_uses_optional ) 300 | { 301 | boost::lockfree::stack< int > stk( 5 ); 302 | 303 | bool pop_to_nullopt = stk.pop( boost::lockfree::uses_optional ) == std::nullopt; 304 | BOOST_TEST_REQUIRE( pop_to_nullopt ); 305 | 306 | stk.push( 53 ); 307 | bool pop_to_optional = stk.pop( boost::lockfree::uses_optional ) == 53; 308 | BOOST_TEST_REQUIRE( pop_to_optional ); 309 | } 310 | 311 | #endif 312 | -------------------------------------------------------------------------------- /test/stack_unbounded_stress_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #define BOOST_TEST_MAIN 10 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 11 | # include 12 | #else 13 | # include 14 | #endif 15 | 16 | #include "test_common.hpp" 17 | 18 | 19 | BOOST_AUTO_TEST_CASE( stack_test_unbounded ) 20 | { 21 | typedef queue_stress_tester< false > tester_type; 22 | std::unique_ptr< tester_type > tester( new tester_type( 4, 4 ) ); 23 | 24 | boost::lockfree::stack< long > q( 128 ); 25 | tester->run( q ); 26 | } 27 | -------------------------------------------------------------------------------- /test/tagged_ptr_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include 8 | 9 | #include 10 | 11 | #define BOOST_TEST_MAIN 12 | #ifdef BOOST_LOCKFREE_INCLUDE_TESTS 13 | # include 14 | #else 15 | # include 16 | #endif 17 | 18 | BOOST_AUTO_TEST_CASE( tagged_ptr_test ) 19 | { 20 | using namespace boost::lockfree::detail; 21 | int a( 1 ), b( 2 ); 22 | 23 | typedef tagged_ptr< int >::tag_t tag_t; 24 | const tag_t max_tag = ( std::numeric_limits< tag_t >::max )(); 25 | 26 | { 27 | tagged_ptr< int > i( &a, 0 ); 28 | tagged_ptr< int > j( &b, 1 ); 29 | 30 | i = j; 31 | 32 | BOOST_TEST_REQUIRE( i.get_ptr() == &b ); 33 | BOOST_TEST_REQUIRE( i.get_tag() == 1 ); 34 | } 35 | 36 | { 37 | tagged_ptr< int > i( &a, 0 ); 38 | tagged_ptr< int > j( i ); 39 | 40 | BOOST_TEST_REQUIRE( i.get_ptr() == j.get_ptr() ); 41 | BOOST_TEST_REQUIRE( i.get_tag() == j.get_tag() ); 42 | } 43 | 44 | { 45 | tagged_ptr< int > i( &a, 0 ); 46 | BOOST_TEST_REQUIRE( i.get_tag() + 1 == i.get_next_tag() ); 47 | } 48 | 49 | { 50 | tagged_ptr< int > j( &a, max_tag ); 51 | BOOST_TEST_REQUIRE( j.get_next_tag() == 0 ); 52 | } 53 | 54 | { 55 | tagged_ptr< int > j( &a, max_tag - 1 ); 56 | BOOST_TEST_REQUIRE( j.get_next_tag() == max_tag ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/test_common.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include "test_helpers.hpp" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #ifdef __VXWORKS__ 13 | # include 14 | #endif 15 | 16 | #include 17 | #include 18 | 19 | namespace impl { 20 | 21 | template < bool Bounded = false > 22 | struct queue_stress_tester 23 | { 24 | static const unsigned int buckets = 1 << 13; 25 | #ifndef BOOST_LOCKFREE_STRESS_TEST 26 | static const long node_count = 5000; 27 | #else 28 | static const long node_count = 5000000; 29 | #endif 30 | const int reader_threads; 31 | const int writer_threads; 32 | 33 | std::atomic< int > writers_finished; 34 | 35 | static_hashed_set< long, buckets > data; 36 | static_hashed_set< long, buckets > dequeued; 37 | std::array< std::set< long >, buckets > returned; 38 | 39 | std::atomic< int > push_count, pop_count; 40 | 41 | queue_stress_tester( int reader, int writer ) : 42 | reader_threads( reader ), 43 | writer_threads( writer ), 44 | push_count( 0 ), 45 | pop_count( 0 ) 46 | {} 47 | 48 | template < typename queue > 49 | void add_items( queue& stk ) 50 | { 51 | for ( long i = 0; i != node_count; ++i ) { 52 | long id = generate_id< long >(); 53 | 54 | bool inserted = data.insert( id ); 55 | assert( inserted ); 56 | (void)inserted; 57 | 58 | if ( Bounded ) 59 | while ( stk.bounded_push( id ) == false ) { 60 | #ifdef __VXWORKS__ 61 | std::this_thread::yield(); 62 | #endif 63 | } 64 | else 65 | while ( stk.push( id ) == false ) { 66 | #ifdef __VXWORKS__ 67 | std::this_thread::yield(); 68 | #endif 69 | } 70 | ++push_count; 71 | } 72 | writers_finished += 1; 73 | } 74 | 75 | std::atomic< bool > running; 76 | 77 | template < typename queue > 78 | bool consume_element( queue& q ) 79 | { 80 | long id; 81 | bool ret = q.pop( id ); 82 | 83 | if ( !ret ) 84 | return false; 85 | 86 | bool erased = data.erase( id ); 87 | bool inserted = dequeued.insert( id ); 88 | (void)erased; 89 | (void)inserted; 90 | assert( erased ); 91 | assert( inserted ); 92 | ++pop_count; 93 | return true; 94 | } 95 | 96 | template < typename queue > 97 | void get_items( queue& q ) 98 | { 99 | for ( ;; ) { 100 | bool received_element = consume_element( q ); 101 | if ( received_element ) 102 | continue; 103 | 104 | if ( writers_finished.load() == writer_threads ) 105 | break; 106 | 107 | #ifdef __VXWORKS__ 108 | std::this_thread::yield(); 109 | #endif 110 | } 111 | 112 | while ( consume_element( q ) ) 113 | ; 114 | } 115 | 116 | template < typename queue > 117 | void run( queue& stk ) 118 | { 119 | BOOST_WARN( stk.is_lock_free() ); 120 | writers_finished.store( 0 ); 121 | 122 | boost::thread_group writer; 123 | boost::thread_group reader; 124 | 125 | BOOST_TEST_REQUIRE( stk.empty() ); 126 | 127 | for ( int i = 0; i != reader_threads; ++i ) 128 | reader.create_thread( [ & ] { 129 | get_items( stk ); 130 | } ); 131 | 132 | for ( int i = 0; i != writer_threads; ++i ) 133 | writer.create_thread( [ & ] { 134 | add_items( stk ); 135 | } ); 136 | 137 | std::cout << "threads created" << std::endl; 138 | 139 | writer.join_all(); 140 | 141 | std::cout << "writer threads joined, waiting for readers" << std::endl; 142 | 143 | reader.join_all(); 144 | 145 | std::cout << "reader threads joined" << std::endl; 146 | 147 | BOOST_TEST_REQUIRE( data.count_nodes() == (size_t)0 ); 148 | BOOST_TEST_REQUIRE( stk.empty() ); 149 | 150 | BOOST_TEST_REQUIRE( push_count == pop_count ); 151 | BOOST_TEST_REQUIRE( push_count == writer_threads * node_count ); 152 | } 153 | }; 154 | 155 | } // namespace impl 156 | 157 | using impl::queue_stress_tester; 158 | -------------------------------------------------------------------------------- /test/test_helpers.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Tim Blechmann 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See 4 | // accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #ifndef BOOST_LOCKFREE_TEST_HELPERS 8 | #define BOOST_LOCKFREE_TEST_HELPERS 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | template < typename int_type > 18 | int_type generate_id( void ) 19 | { 20 | static std::atomic< int_type > generator( 0 ); 21 | return ++generator; 22 | } 23 | 24 | template < typename int_type, size_t buckets > 25 | class static_hashed_set 26 | { 27 | public: 28 | int calc_index( int_type id ) 29 | { 30 | // knuth hash ... does not need to be good, but has to be portable 31 | size_t factor = size_t( (float)buckets * 1.616f ); 32 | 33 | return ( (size_t)id * factor ) % buckets; 34 | } 35 | 36 | bool insert( int_type const& id ) 37 | { 38 | std::size_t index = calc_index( id ); 39 | 40 | std::lock_guard< std::mutex > lock( ref_mutex[ index ] ); 41 | 42 | auto p = data[ index ].insert( id ); 43 | 44 | return p.second; 45 | } 46 | 47 | bool find( int_type const& id ) 48 | { 49 | std::size_t index = calc_index( id ); 50 | 51 | std::lock_guard< std::mutex > lock( ref_mutex[ index ] ); 52 | 53 | return data[ index ].find( id ) != data[ index ].end(); 54 | } 55 | 56 | bool erase( int_type const& id ) 57 | { 58 | std::size_t index = calc_index( id ); 59 | 60 | std::lock_guard< std::mutex > lock( ref_mutex[ index ] ); 61 | 62 | if ( data[ index ].find( id ) != data[ index ].end() ) { 63 | data[ index ].erase( id ); 64 | assert( data[ index ].find( id ) == data[ index ].end() ); 65 | return true; 66 | } else 67 | return false; 68 | } 69 | 70 | std::size_t count_nodes( void ) const 71 | { 72 | std::size_t ret = 0; 73 | for ( int i = 0; i != buckets; ++i ) { 74 | std::lock_guard< std::mutex > lock( ref_mutex[ i ] ); 75 | ret += data[ i ].size(); 76 | } 77 | return ret; 78 | } 79 | 80 | private: 81 | std::array< std::set< int_type >, buckets > data; 82 | mutable std::array< std::mutex, buckets > ref_mutex; 83 | }; 84 | 85 | struct test_equal 86 | { 87 | test_equal( int i ) : 88 | i( i ) 89 | {} 90 | 91 | void operator()( int arg ) const 92 | { 93 | BOOST_TEST_REQUIRE( arg == i ); 94 | } 95 | 96 | int i; 97 | }; 98 | 99 | struct dummy_functor 100 | { 101 | void operator()( int /* arg */ ) const 102 | {} 103 | }; 104 | 105 | 106 | #endif 107 | --------------------------------------------------------------------------------