Repository: RcppCore/RcppParallel
Branch: master
Commit: e4e33f47ef06
Files: 399
Total size: 3.1 MB
Directory structure:
gitextract_0_xjhq3b/
├── .Rbuildignore
├── .gitattributes
├── .github/
│ ├── .gitignore
│ └── workflows/
│ └── R-CMD-check.yaml
├── .gitignore
├── DESCRIPTION
├── NAMESPACE
├── NEWS.md
├── R/
│ ├── RcppParallel-package.R
│ ├── aaa.R
│ ├── flags.R
│ ├── options.R
│ ├── platform.R
│ ├── plugin.R
│ ├── skeleton.R
│ ├── tbb-autodetected.R.in
│ ├── tbb.R
│ ├── utils.R
│ └── zzz.R
├── README.md
├── RcppParallel.Rproj
├── cleanup
├── cleanup.win
├── configure
├── configure.win
├── doc/
│ └── rtools_tbb_notes.md
├── inst/
│ ├── .gitignore
│ ├── include/
│ │ ├── .gitignore
│ │ ├── RcppParallel/
│ │ │ ├── Backend.h
│ │ │ ├── Common.h
│ │ │ ├── RMatrix.h
│ │ │ ├── RVector.h
│ │ │ ├── TBB.h
│ │ │ ├── Timer.h
│ │ │ └── TinyThread.h
│ │ ├── RcppParallel.h
│ │ └── tthread/
│ │ ├── fast_mutex.h
│ │ ├── tinythread.h
│ │ └── tinythread.inl
│ ├── presentations/
│ │ ├── .gitignore
│ │ └── rcpp_parallel_talk_jan2015.Rmd
│ ├── rstudio/
│ │ └── templates/
│ │ └── project/
│ │ └── RcppParallel.package.skeleton.dcf
│ ├── skeleton/
│ │ ├── vector-sum.Rd
│ │ └── vector-sum.cpp
│ └── tests/
│ ├── cpp/
│ │ ├── distance.cpp
│ │ ├── innerproduct.cpp
│ │ ├── sum.cpp
│ │ ├── transform.cpp
│ │ └── truefalse_macros.cpp
│ ├── runit.distance.R
│ ├── runit.innerproduct.R
│ ├── runit.sum.R
│ ├── runit.transform.R
│ └── runit.truefalse_macros.R
├── man/
│ ├── RcppParallel-package.Rd
│ ├── RcppParallel.package.skeleton.Rd
│ ├── flags.Rd
│ ├── setThreadOptions.Rd
│ └── tbbLibraryPath.Rd
├── patches/
│ └── windows_arm64.diff
├── src/
│ ├── .gitignore
│ ├── Makevars.in
│ ├── init.cpp
│ ├── install.libs.R
│ ├── options.cpp
│ ├── tbb/
│ │ ├── .gitignore
│ │ ├── CMakeLists.txt
│ │ ├── CODEOWNERS
│ │ ├── CODE_OF_CONDUCT.md
│ │ ├── CONTRIBUTING.md
│ │ ├── INSTALL.md
│ │ ├── LICENSE.txt
│ │ ├── README.md
│ │ ├── RELEASE_NOTES.md
│ │ ├── SECURITY.md
│ │ ├── SUPPORT.md
│ │ ├── SYSTEM_REQUIREMENTS.md
│ │ ├── WASM_Support.md
│ │ ├── cmake/
│ │ │ ├── README.md
│ │ │ ├── android/
│ │ │ │ ├── device_environment_cleanup.cmake
│ │ │ │ ├── environment.cmake
│ │ │ │ └── test_launcher.cmake
│ │ │ ├── compilers/
│ │ │ │ ├── AppleClang.cmake
│ │ │ │ ├── Clang.cmake
│ │ │ │ ├── GNU.cmake
│ │ │ │ ├── Intel.cmake
│ │ │ │ ├── IntelLLVM.cmake
│ │ │ │ ├── MSVC.cmake
│ │ │ │ └── QCC.cmake
│ │ │ ├── config_generation.cmake
│ │ │ ├── hwloc_detection.cmake
│ │ │ ├── memcheck.cmake
│ │ │ ├── packaging.cmake
│ │ │ ├── post_install/
│ │ │ │ └── CMakeLists.txt
│ │ │ ├── python/
│ │ │ │ └── test_launcher.cmake
│ │ │ ├── resumable_tasks.cmake
│ │ │ ├── sanitize.cmake
│ │ │ ├── scripts/
│ │ │ │ └── cmake_gen_github_configs.cmake
│ │ │ ├── suppressions/
│ │ │ │ ├── lsan.suppressions
│ │ │ │ └── tsan.suppressions
│ │ │ ├── templates/
│ │ │ │ ├── TBBConfig.cmake.in
│ │ │ │ └── TBBConfigVersion.cmake.in
│ │ │ ├── test_spec.cmake
│ │ │ ├── toolchains/
│ │ │ │ ├── mips.cmake
│ │ │ │ └── riscv64.cmake
│ │ │ ├── utils.cmake
│ │ │ └── vars_utils.cmake
│ │ ├── include/
│ │ │ ├── oneapi/
│ │ │ │ ├── tbb/
│ │ │ │ │ ├── blocked_range.h
│ │ │ │ │ ├── blocked_range2d.h
│ │ │ │ │ ├── blocked_range3d.h
│ │ │ │ │ ├── blocked_rangeNd.h
│ │ │ │ │ ├── cache_aligned_allocator.h
│ │ │ │ │ ├── collaborative_call_once.h
│ │ │ │ │ ├── combinable.h
│ │ │ │ │ ├── concurrent_hash_map.h
│ │ │ │ │ ├── concurrent_lru_cache.h
│ │ │ │ │ ├── concurrent_map.h
│ │ │ │ │ ├── concurrent_priority_queue.h
│ │ │ │ │ ├── concurrent_queue.h
│ │ │ │ │ ├── concurrent_set.h
│ │ │ │ │ ├── concurrent_unordered_map.h
│ │ │ │ │ ├── concurrent_unordered_set.h
│ │ │ │ │ ├── concurrent_vector.h
│ │ │ │ │ ├── detail/
│ │ │ │ │ │ ├── _aggregator.h
│ │ │ │ │ │ ├── _aligned_space.h
│ │ │ │ │ │ ├── _allocator_traits.h
│ │ │ │ │ │ ├── _assert.h
│ │ │ │ │ │ ├── _attach.h
│ │ │ │ │ │ ├── _concurrent_queue_base.h
│ │ │ │ │ │ ├── _concurrent_skip_list.h
│ │ │ │ │ │ ├── _concurrent_unordered_base.h
│ │ │ │ │ │ ├── _config.h
│ │ │ │ │ │ ├── _containers_helpers.h
│ │ │ │ │ │ ├── _exception.h
│ │ │ │ │ │ ├── _export.h
│ │ │ │ │ │ ├── _flow_graph_body_impl.h
│ │ │ │ │ │ ├── _flow_graph_cache_impl.h
│ │ │ │ │ │ ├── _flow_graph_impl.h
│ │ │ │ │ │ ├── _flow_graph_indexer_impl.h
│ │ │ │ │ │ ├── _flow_graph_item_buffer_impl.h
│ │ │ │ │ │ ├── _flow_graph_join_impl.h
│ │ │ │ │ │ ├── _flow_graph_node_impl.h
│ │ │ │ │ │ ├── _flow_graph_node_set_impl.h
│ │ │ │ │ │ ├── _flow_graph_nodes_deduction.h
│ │ │ │ │ │ ├── _flow_graph_tagged_buffer_impl.h
│ │ │ │ │ │ ├── _flow_graph_trace_impl.h
│ │ │ │ │ │ ├── _flow_graph_types_impl.h
│ │ │ │ │ │ ├── _hash_compare.h
│ │ │ │ │ │ ├── _intrusive_list_node.h
│ │ │ │ │ │ ├── _machine.h
│ │ │ │ │ │ ├── _mutex_common.h
│ │ │ │ │ │ ├── _namespace_injection.h
│ │ │ │ │ │ ├── _node_handle.h
│ │ │ │ │ │ ├── _pipeline_filters.h
│ │ │ │ │ │ ├── _pipeline_filters_deduction.h
│ │ │ │ │ │ ├── _range_common.h
│ │ │ │ │ │ ├── _rtm_mutex.h
│ │ │ │ │ │ ├── _rtm_rw_mutex.h
│ │ │ │ │ │ ├── _scoped_lock.h
│ │ │ │ │ │ ├── _segment_table.h
│ │ │ │ │ │ ├── _small_object_pool.h
│ │ │ │ │ │ ├── _string_resource.h
│ │ │ │ │ │ ├── _task.h
│ │ │ │ │ │ ├── _task_handle.h
│ │ │ │ │ │ ├── _template_helpers.h
│ │ │ │ │ │ ├── _utils.h
│ │ │ │ │ │ └── _waitable_atomic.h
│ │ │ │ │ ├── enumerable_thread_specific.h
│ │ │ │ │ ├── flow_graph.h
│ │ │ │ │ ├── flow_graph_abstractions.h
│ │ │ │ │ ├── global_control.h
│ │ │ │ │ ├── info.h
│ │ │ │ │ ├── memory_pool.h
│ │ │ │ │ ├── mutex.h
│ │ │ │ │ ├── null_mutex.h
│ │ │ │ │ ├── null_rw_mutex.h
│ │ │ │ │ ├── parallel_for.h
│ │ │ │ │ ├── parallel_for_each.h
│ │ │ │ │ ├── parallel_invoke.h
│ │ │ │ │ ├── parallel_pipeline.h
│ │ │ │ │ ├── parallel_reduce.h
│ │ │ │ │ ├── parallel_scan.h
│ │ │ │ │ ├── parallel_sort.h
│ │ │ │ │ ├── partitioner.h
│ │ │ │ │ ├── profiling.h
│ │ │ │ │ ├── queuing_mutex.h
│ │ │ │ │ ├── queuing_rw_mutex.h
│ │ │ │ │ ├── rw_mutex.h
│ │ │ │ │ ├── scalable_allocator.h
│ │ │ │ │ ├── spin_mutex.h
│ │ │ │ │ ├── spin_rw_mutex.h
│ │ │ │ │ ├── task.h
│ │ │ │ │ ├── task_arena.h
│ │ │ │ │ ├── task_group.h
│ │ │ │ │ ├── task_scheduler_observer.h
│ │ │ │ │ ├── tbb_allocator.h
│ │ │ │ │ ├── tbbmalloc_proxy.h
│ │ │ │ │ ├── tick_count.h
│ │ │ │ │ └── version.h
│ │ │ │ └── tbb.h
│ │ │ └── tbb/
│ │ │ ├── blocked_range.h
│ │ │ ├── blocked_range2d.h
│ │ │ ├── blocked_range3d.h
│ │ │ ├── blocked_rangeNd.h
│ │ │ ├── cache_aligned_allocator.h
│ │ │ ├── collaborative_call_once.h
│ │ │ ├── combinable.h
│ │ │ ├── concurrent_hash_map.h
│ │ │ ├── concurrent_lru_cache.h
│ │ │ ├── concurrent_map.h
│ │ │ ├── concurrent_priority_queue.h
│ │ │ ├── concurrent_queue.h
│ │ │ ├── concurrent_set.h
│ │ │ ├── concurrent_unordered_map.h
│ │ │ ├── concurrent_unordered_set.h
│ │ │ ├── concurrent_vector.h
│ │ │ ├── enumerable_thread_specific.h
│ │ │ ├── flow_graph.h
│ │ │ ├── flow_graph_abstractions.h
│ │ │ ├── global_control.h
│ │ │ ├── info.h
│ │ │ ├── memory_pool.h
│ │ │ ├── mutex.h
│ │ │ ├── null_mutex.h
│ │ │ ├── null_rw_mutex.h
│ │ │ ├── parallel_for.h
│ │ │ ├── parallel_for_each.h
│ │ │ ├── parallel_invoke.h
│ │ │ ├── parallel_pipeline.h
│ │ │ ├── parallel_reduce.h
│ │ │ ├── parallel_scan.h
│ │ │ ├── parallel_sort.h
│ │ │ ├── partitioner.h
│ │ │ ├── profiling.h
│ │ │ ├── queuing_mutex.h
│ │ │ ├── queuing_rw_mutex.h
│ │ │ ├── rw_mutex.h
│ │ │ ├── scalable_allocator.h
│ │ │ ├── spin_mutex.h
│ │ │ ├── spin_rw_mutex.h
│ │ │ ├── task.h
│ │ │ ├── task_arena.h
│ │ │ ├── task_group.h
│ │ │ ├── task_scheduler_observer.h
│ │ │ ├── tbb.h
│ │ │ ├── tbb_allocator.h
│ │ │ ├── tbbmalloc_proxy.h
│ │ │ ├── tick_count.h
│ │ │ └── version.h
│ │ ├── integration/
│ │ │ ├── cmake/
│ │ │ │ └── generate_vars.cmake
│ │ │ ├── linux/
│ │ │ │ ├── env/
│ │ │ │ │ ├── vars.sh
│ │ │ │ │ └── vars.sh.in
│ │ │ │ ├── modulefiles/
│ │ │ │ │ ├── tbb
│ │ │ │ │ └── tbb32
│ │ │ │ ├── oneapi/
│ │ │ │ │ └── vars.sh
│ │ │ │ └── sys_check/
│ │ │ │ └── sys_check.sh
│ │ │ ├── mac/
│ │ │ │ └── env/
│ │ │ │ ├── vars.sh
│ │ │ │ └── vars.sh.in
│ │ │ ├── pkg-config/
│ │ │ │ └── tbb.pc.in
│ │ │ └── windows/
│ │ │ ├── env/
│ │ │ │ ├── vars.bat
│ │ │ │ └── vars.bat.in
│ │ │ ├── nuget/
│ │ │ │ ├── inteltbb.devel.win.targets
│ │ │ │ └── inteltbb.redist.win.targets
│ │ │ ├── oneapi/
│ │ │ │ └── vars.bat
│ │ │ └── sys_check/
│ │ │ └── sys_check.bat
│ │ ├── src/
│ │ │ ├── tbb/
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ ├── address_waiter.cpp
│ │ │ │ ├── allocator.cpp
│ │ │ │ ├── arena.cpp
│ │ │ │ ├── arena.h
│ │ │ │ ├── arena_slot.cpp
│ │ │ │ ├── arena_slot.h
│ │ │ │ ├── assert_impl.h
│ │ │ │ ├── cancellation_disseminator.h
│ │ │ │ ├── co_context.h
│ │ │ │ ├── concurrent_bounded_queue.cpp
│ │ │ │ ├── concurrent_monitor.h
│ │ │ │ ├── concurrent_monitor_mutex.h
│ │ │ │ ├── def/
│ │ │ │ │ ├── lin32-tbb.def
│ │ │ │ │ ├── lin64-tbb.def
│ │ │ │ │ ├── mac64-tbb.def
│ │ │ │ │ ├── win32-tbb.def
│ │ │ │ │ └── win64-tbb.def
│ │ │ │ ├── dynamic_link.cpp
│ │ │ │ ├── dynamic_link.h
│ │ │ │ ├── environment.h
│ │ │ │ ├── exception.cpp
│ │ │ │ ├── global_control.cpp
│ │ │ │ ├── governor.cpp
│ │ │ │ ├── governor.h
│ │ │ │ ├── intrusive_list.h
│ │ │ │ ├── itt_notify.cpp
│ │ │ │ ├── itt_notify.h
│ │ │ │ ├── mailbox.h
│ │ │ │ ├── main.cpp
│ │ │ │ ├── main.h
│ │ │ │ ├── market.cpp
│ │ │ │ ├── market.h
│ │ │ │ ├── market_concurrent_monitor.h
│ │ │ │ ├── misc.cpp
│ │ │ │ ├── misc.h
│ │ │ │ ├── misc_ex.cpp
│ │ │ │ ├── observer_proxy.cpp
│ │ │ │ ├── observer_proxy.h
│ │ │ │ ├── parallel_pipeline.cpp
│ │ │ │ ├── permit_manager.h
│ │ │ │ ├── pm_client.h
│ │ │ │ ├── private_server.cpp
│ │ │ │ ├── profiling.cpp
│ │ │ │ ├── queuing_rw_mutex.cpp
│ │ │ │ ├── rml_base.h
│ │ │ │ ├── rml_tbb.cpp
│ │ │ │ ├── rml_tbb.h
│ │ │ │ ├── rml_thread_monitor.h
│ │ │ │ ├── rtm_mutex.cpp
│ │ │ │ ├── rtm_rw_mutex.cpp
│ │ │ │ ├── scheduler_common.h
│ │ │ │ ├── semaphore.cpp
│ │ │ │ ├── semaphore.h
│ │ │ │ ├── small_object_pool.cpp
│ │ │ │ ├── small_object_pool_impl.h
│ │ │ │ ├── task.cpp
│ │ │ │ ├── task_dispatcher.cpp
│ │ │ │ ├── task_dispatcher.h
│ │ │ │ ├── task_group_context.cpp
│ │ │ │ ├── task_stream.h
│ │ │ │ ├── tbb.rc
│ │ │ │ ├── tcm.h
│ │ │ │ ├── tcm_adaptor.cpp
│ │ │ │ ├── tcm_adaptor.h
│ │ │ │ ├── thread_control_monitor.h
│ │ │ │ ├── thread_data.h
│ │ │ │ ├── thread_dispatcher.cpp
│ │ │ │ ├── thread_dispatcher.h
│ │ │ │ ├── thread_dispatcher_client.h
│ │ │ │ ├── thread_request_serializer.cpp
│ │ │ │ ├── thread_request_serializer.h
│ │ │ │ ├── threading_control.cpp
│ │ │ │ ├── threading_control.h
│ │ │ │ ├── threading_control_client.h
│ │ │ │ ├── tls.h
│ │ │ │ ├── tools_api/
│ │ │ │ │ ├── disable_warnings.h
│ │ │ │ │ ├── ittnotify.h
│ │ │ │ │ ├── ittnotify_config.h
│ │ │ │ │ ├── ittnotify_static.c
│ │ │ │ │ ├── ittnotify_static.h
│ │ │ │ │ ├── ittnotify_types.h
│ │ │ │ │ └── legacy/
│ │ │ │ │ └── ittnotify.h
│ │ │ │ ├── version.cpp
│ │ │ │ └── waiters.h
│ │ │ ├── tbbbind/
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ ├── def/
│ │ │ │ │ ├── lin32-tbbbind.def
│ │ │ │ │ ├── lin64-tbbbind.def
│ │ │ │ │ ├── mac64-tbbbind.def
│ │ │ │ │ ├── win32-tbbbind.def
│ │ │ │ │ └── win64-tbbbind.def
│ │ │ │ ├── tbb_bind.cpp
│ │ │ │ └── tbb_bind.rc
│ │ │ ├── tbbmalloc/
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ ├── Customize.h
│ │ │ │ ├── MapMemory.h
│ │ │ │ ├── Statistics.h
│ │ │ │ ├── Synchronize.h
│ │ │ │ ├── TypeDefinitions.h
│ │ │ │ ├── backend.cpp
│ │ │ │ ├── backend.h
│ │ │ │ ├── backref.cpp
│ │ │ │ ├── def/
│ │ │ │ │ ├── lin32-tbbmalloc.def
│ │ │ │ │ ├── lin64-tbbmalloc.def
│ │ │ │ │ ├── mac64-tbbmalloc.def
│ │ │ │ │ ├── win32-tbbmalloc.def
│ │ │ │ │ └── win64-tbbmalloc.def
│ │ │ │ ├── frontend.cpp
│ │ │ │ ├── large_objects.cpp
│ │ │ │ ├── large_objects.h
│ │ │ │ ├── shared_utils.h
│ │ │ │ ├── tbbmalloc.cpp
│ │ │ │ ├── tbbmalloc.rc
│ │ │ │ ├── tbbmalloc_internal.h
│ │ │ │ └── tbbmalloc_internal_api.h
│ │ │ └── tbbmalloc_proxy/
│ │ │ ├── CMakeLists.txt
│ │ │ ├── def/
│ │ │ │ ├── lin32-proxy.def
│ │ │ │ └── lin64-proxy.def
│ │ │ ├── function_replacement.cpp
│ │ │ ├── function_replacement.h
│ │ │ ├── proxy.cpp
│ │ │ ├── proxy.h
│ │ │ ├── proxy_overload_osx.h
│ │ │ └── tbbmalloc_proxy.rc
│ │ └── third-party-programs.txt
│ ├── tbb-compat/
│ │ └── tbb-compat.cpp
│ └── tbb.cpp
├── tests/
│ └── doRUnit.R
└── tools/
├── config/
│ ├── cleanup.R
│ └── configure.R
├── config.R
└── tbb/
├── disable-pragmas.R
├── fix-memset.R
└── update-tbb.R
================================================
FILE CONTENTS
================================================
================================================
FILE: .Rbuildignore
================================================
^.*\.Rproj$
^.travis.yml
^README.md
^\.Rprofile$
^\.Rproj\.user$
^appveyor\.yml$
^check$
^doc$
^gen$
^libs$
^inst/lib$
^inst/libs$
^revdep$
^src/.*\.o$
^src/tbb/build$
^tags$
^tests/testthat/pkg/RcppParallelTest/src/.*\.dll$
^tests/testthat/pkg/RcppParallelTest/src/.*\.s?o$
^tools/tbb$
^\.github$
^patches
================================================
FILE: .gitattributes
================================================
Makevars text eol=lf
================================================
FILE: .github/.gitignore
================================================
*.html
================================================
FILE: .github/workflows/R-CMD-check.yaml
================================================
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
name: R-CMD-check
jobs:
R-CMD-check:
runs-on: ${{ matrix.config.os }}
name: ${{ matrix.config.os }} (${{ matrix.config.r }})
strategy:
fail-fast: false
matrix:
config:
- {os: macOS-latest, r: 'release'}
- {os: ubuntu-latest, r: 'release'}
- {os: windows-latest, r: 'release'}
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
R_KEEP_PKG_SOURCE: yes
steps:
- uses: actions/checkout@v3
- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true
- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::rcmdcheck
needs: check
- uses: r-lib/actions/check-r-package@v2
================================================
FILE: .gitignore
================================================
.Rprofile
.Rproj.user
.Rhistory
.RData
.DS_Store
check
inst/doc
inst/lib
inst/libs
libs
revdep
src-i386
src-x64
tbb.log
src/tbb/build
src/tbb/build-tbb
R/tbb-autodetected.R
================================================
FILE: DESCRIPTION
================================================
Package: RcppParallel
Type: Package
Title: Parallel Programming Tools for 'Rcpp'
Version: 5.1.10.9000
Authors@R: c(
person("Kevin", "Ushey", role = c("aut", "cre"), email = "kevin@rstudio.com",
comment = c(ORCID = "0000-0003-2880-7407")),
person("JJ", "Allaire", role = c("aut"), email = "jj@rstudio.com"),
person("Romain", "Francois", role = c("aut", "cph")),
person("Gregory", "Vandenbrouck", role = "aut"),
person("Marcus", "Geelnard", role = c("aut", "cph"),
comment = "TinyThread library, https://tinythreadpp.bitsnbites.eu/"),
person("Hamada S.", "Badr", email = "badr@jhu.edu", role = c("ctb"),
comment = c(ORCID = "0000-0002-9808-2344")),
person("Dirk", "Eddelbuettel", role = c("aut"), email = "edd@debian.org",
comment = c(ORCID = "0000-0001-6419-907X")),
person(family = "Intel", role = c("aut", "cph"), comment = "oneTBB library"),
person(family = "UXL Foundation", role = c("aut", "cph"), comment = "oneTBB library"),
person(family = "Microsoft", role = "cph"),
person(family = "Posit, PBC", role = "cph")
)
Description: High level functions for parallel programming with 'Rcpp'.
For example, the 'parallelFor()' function can be used to convert the work of
a standard serial "for" loop into a parallel one and the 'parallelReduce()'
function can be used for accumulating aggregate or other values.
Depends: R (>= 3.6.0)
Suggests:
Rcpp,
RUnit,
knitr,
rmarkdown
Roxygen: list(markdown = TRUE)
SystemRequirements: CMake (>= 3.5)
License: GPL (>= 3)
URL: https://rcppcore.github.io/RcppParallel/, https://github.com/RcppCore/RcppParallel
BugReports: https://github.com/RcppCore/RcppParallel/issues
Biarch: TRUE
RoxygenNote: 7.3.2
Encoding: UTF-8
================================================
FILE: NAMESPACE
================================================
# Generated by roxygen2: do not edit by hand
export(CxxFlags)
export(LdFlags)
export(RcppParallel.package.skeleton)
export(RcppParallelLibs)
export(defaultNumThreads)
export(setThreadOptions)
export(tbbLibraryPath)
================================================
FILE: NEWS.md
================================================
## RcppParallel 6.0.0 (UNRELEASED)
* RcppParallel no longer includes tbb headers as part of the RcppParallel/TBB.h
header, and instead only exposes its TBB-specific APIs for parallel work.
* RcppParallel now bundles oneTBB 2022.0.0. Note that the TBB ABI has changed;
packages which depend on RcppParallel may need to be rebuilt.
* On Windows, RcppParallel now uses the copy of TBB provided by Rtools.
If TBB is not available, RcppParallel will use only the fallback 'tinythread'
implementation. In practice, this implies that RcppParallel will now only
provide a TBB backend with R (>= 4.2.0).
## RcppParallel 5.1.11
* Compatibility fixes for LLVM 21.
## RcppParallel 5.1.10
* Fixed an issue where packages linking to RcppParallel could inadverently
depend on internals of the TBB library available during compilation, even
if the package did not explicitly use TBB itself.
## RcppParallel 5.1.9
* RcppParallel no longer passes `-rpath` when building / linking on Windows.
This fixes build issues when building RcppParallel when using the LLVM
linker on Windows. (@kalibera)
## RcppParallel 5.1.8
* RcppParallel now explicitly links to the bundled copy of TBB on macOS. (#206; @jeroen)
## RcppParallel 5.1.7
* Remove deprecated `std::iterator`. (#192; @Enchufa2)
## RcppParallel 5.1.6
* Patch for TBB to allow compilation with gcc-13.
* Fixed a memory leak that could occur when using TinyThread on POSIX systems.
(#185; @dipertix and and @kevinushey)
## RcppParallel 5.1.5
* Patches to ensure compatibility with the R 4.2.0 UCRT toolchain on Windows,
adapted from patches contributed by Tomas Kalibera.
* Fixed an issue where setting `TBB_ROOT` (or `TBB_INC` / `TBB_LIB`) would
copy rather than symlink the associated libraries. (#161)
## RcppParallel 5.1.4
* Fixed an issue causing client packages of RcppParallel to fail to compile
on Solaris.
## RcppParallel 5.1.3
* Fixed an issue that prevented compilation of RcppParallel with R (< 4.0.0)
of R on Windows.
* The `RCPP_PARALLEL_USE_TBBMALLOC_PROXY` environment variable can now be used
to control whether RcppParallel loads the `tbbmalloc_proxy` library on load.
See https://www.threadingbuildingblocks.org/docs/help/tbb_userguide/Automically_Replacing_malloc.html
for more information.
## RcppParallel 5.1.2
* `RcppParallel` gains the `tbbLibraryPath()` function, to be used when attempting
to query the location of the TBB libraries that `RcppParallel` has been
configured to use. This may be useful for R packages which wish to explicitly
use, or link to, these libraries.
## RcppParallel 5.1.1
* Updated bundled version of TBB (Intel TBB 2019 Update 8).
* RcppParallel can now be configured to use an external copy of TBB, via the
`TBB_LIB` and `TBB_INC` environment variables. These should be set to the
directories containing the TBB libraries and headers, respectively.
* Added support for the latest versions of Intel oneAPI TBB / oneTBB.
* Updated TBB functionality for the new interface.
* Falling back to building TBB from local source code.
* Backward TBB compatibility based on `__TBB_tbb_stddef_H`.
* Resolved conflicts between system and local TBB headers.
* Fixed URLs, used HTTPS, and minor cleanups.
* Updated package DESCRIPTION and bumped version.
* `setThreadOptions(...)` can again be called multiple times per session.
The requested number of threads will be used for invocations to `parallelFor()`
and `parallelReduce()` that don't explicitly request a specific number of threads.
* The `parallelFor()` and `parallelReduce()` functions gain the `numThreads`
argument, allowing one to limit the number of threads used for a
particular computation.
## RcppParallel 5.0.3
* Fixed compilation on macOS M1 machines.
## RcppParallel 5.0.2
* `setThreadOptions(...)` can now only be called once per session, to avoid
segfaults when compiling RcppParallel / TBB with gcc 10.1. Subsequent
calls to `setThreadOptions(...)` are ignored.
## RcppParallel 5.0.1
* Fixed compilation issue on OpenSUSE Tumbleweed with -flto=auto
* Fixed compilation when CPPFLAGS = -I/usr/local/include and a version
of libtbb is installed there
## RcppParallel 5.0.0
* RcppParallel backend can now be customized with RCPP_PARALLEL_BACKEND
environment variable (supported values are 'tbb' and 'tinythread')
* Fixed issue when compiling RcppParallel on macOS Catalina
* Fixed issue when compiling RcppParallel with Rtools40
## RcppParallel 4.4.4
* Fixed an issue when compiling RcppParallel with clang-9 on Fedora
## RcppParallel 4.4.3
* Suppress gcc-9 warnings related -Wclass-memaccess
* Added TBB headers for serial TBB operations (#90, @mikejiang)
* Fixed row iterator constructor (#87, @wtianyi)
* Fixed compilation on FreeBSD
## RcppParallel 4.4.2
* Suppress gcc-8 warnings related to -Wclass-memaccess
* Use PKG_CXXFLAGS rather than PKG_CPPFLAGS
* Remove unused dependency on the BH package
## RcppParallel 4.4.1
* Ensure user-specified R configuration passed to TBB
* Work around warnings emitted by gcc 8
## RcppParallel 4.4.0
* Respect user-defined compiler settings (e.g. from ~/.R/Makevars).
* Remove TBB's attempts to suppress compiler diagnostics.
* Allow setting the number of threads to use via RCPP_PARALLEL_NUM_THREADS
environment variable.
* Update to TBB 2018 Update 1.
* Add native registration of compiled functions.
## RcppParallel 4.3.20
* Add support for Rtools 3.3 w/ GCC 4.9
## RcppParallel 4.3.14
* Add support for TBB on Solaris
* Fix failure to compile on OS X Snow Leopard R toolchain
* Add const and non-const operator[] for RMatrix class
## RcppParallel 4.3.8
* Add tbbmalloc library
* Correctly pass clang to TBB configure when R is using clang
## RcppParallel 4.3.6
* Support for TBB on Windows
## RcppParallel 4.3.3
* Update to TBB 4.3 (fixes clang compilation error in platform.h)
* Forward CXX to TBB Makefile
## RcppParallel 4.2.5
* Initial release
================================================
FILE: R/RcppParallel-package.R
================================================
#' Parallel programming tools for Rcpp
#'
#' High level functions for doing parallel programming with Rcpp. For example,
#' the `parallelFor()` function can be used to convert the work of a
#' standard serial "for" loop into a parallel one, and the `parallelReduce()`
#' function can be used for accumulating aggregate or other values.
#'
#' The high level interface enables safe and robust parallel programming
#' without direct manipulation of operating system threads. On Windows, macOS,
#' and Linux systems the underlying implementation is based on Intel TBB
#' (Threading Building Blocks). On other platforms, a less-performant fallback
#' implementation based on the TinyThread library is used.
#'
#' For additional documentation, see the package website at:
#'
#'
#'
#'
#' @name RcppParallel-package
#' @docType package
#' @aliases RcppParallel RcppParallel-package
#' @keywords package parallel
NULL
================================================
FILE: R/aaa.R
================================================
# stubs that get overridden via configure script
TBB_ENABLED <- TRUE
TBB_LIB <- ""
TBB_INC <- ""
TBB_NAME <- "tbb"
TBB_MALLOC_NAME <- "tbbmalloc"
================================================
FILE: R/flags.R
================================================
#' Compilation flags for RcppParallel
#'
#' Output the compiler or linker flags required to build against RcppParallel.
#'
#' These functions are typically called from `Makevars` as follows:
#'
#' ```
#' PKG_LIBS += $(shell "${R_HOME}/bin/Rscript" -e "RcppParallel::LdFlags()")
#' ```
#'
#' On Windows, the flags ensure that the package links with the built-in TBB
#' library. On Linux and macOS, the output is empty, because TBB is loaded
#' dynamically on load by `RcppParallel`.
#'
#' \R packages using RcppParallel should also add the following to their
#' `NAMESPACE` file:
#'
#' ```
#' importFrom(RcppParallel, RcppParallelLibs)
#' ```
#'
#' This is necessary to ensure that \pkg{RcppParallel} (and so, TBB) is loaded
#' and available.
#'
#' @name flags
#' @rdname flags
#' @aliases RcppParallelLibs LdFlags CxxFlags
#'
#' @return Returns \code{NULL}, invisibly. These functions are called for
#' their side effects (writing the associated flags to stdout).
#'
NULL
#' @name flags
#' @export
CxxFlags <- function() {
cat(tbbCxxFlags())
}
#' @name flags
#' @export
LdFlags <- function() {
cat(tbbLdFlags())
}
#' @name flags
#' @export
RcppParallelLibs <- function() {
LdFlags()
}
================================================
FILE: R/options.R
================================================
#' Thread options for RcppParallel
#'
#' Set thread options (number of threads to use for task scheduling and stack
#' size per-thread) for RcppParallel.
#'
#' RcppParallel is automatically initialized with the default number of threads
#' and thread stack size when it loads. You can call `setThreadOptions()` at
#' any time to change the defaults.
#'
#' The `parallelFor()` and `parallelReduce()` also accept `numThreads` as
#' an argument, if you'd like to control the number of threads specifically
#' to be made available for a particular parallel function call. Note that
#' this value is advisory, and TBB may choose a smaller number of threads
#' if the number of requested threads cannot be honored on the system.
#'
#' @aliases setThreadOptions defaultNumThreads
#'
#' @param numThreads
#' Number of threads to use for task scheduling. Call `defaultNumThreads()`
#' to determine the the default value used for "auto".
#'
#' @param stackSize
#' Stack size (in bytes) to use for worker threads. The
#' default used for "auto" is 2MB on 32-bit systems and 4MB on 64-bit systems
#' (note that this parameter has no effect on Windows).
#'
#' @return
#' `defaultNumThreads()` returns the default number of threads used by
#' RcppParallel, if another value isn't specified either via
#' `setThreadOptions()` or explicitly in calls to `parallelFor()` and
#' `parallelReduce()`.
#'
#' @examples
#'
#' \dontrun{
#' library(RcppParallel)
#' setThreadOptions(numThreads = 4)
#' defaultNumThreads()
#' }
#'
#' @export setThreadOptions
setThreadOptions <- function(numThreads = "auto",
stackSize = "auto")
{
# validate and resolve numThreads
if (identical(numThreads, "auto"))
numThreads <- -1L
else if (!is.numeric(numThreads))
stop("numThreads must be an integer")
else
numThreads <- as.integer(numThreads)
# validate and resolve stackSize
if (identical(stackSize, "auto"))
stackSize <- 0L
else if (!is.numeric(stackSize))
stop("stackSize must be an integer")
else
stackSize <- as.integer(stackSize)
# set RCPP_PARALLEL_NUM_THREADS
if (numThreads == -1L)
Sys.unsetenv("RCPP_PARALLEL_NUM_THREADS")
else
Sys.setenv(RCPP_PARALLEL_NUM_THREADS = numThreads)
# set RCPP_PARALLEL_STACK_SIZE
if (stackSize == 0L)
Sys.unsetenv("RCPP_PARALLEL_STACK_SIZE")
else
Sys.setenv(RCPP_PARALLEL_STACK_SIZE = stackSize)
}
#' @rdname setThreadOptions
#' @export
defaultNumThreads <- function() {
.Call("defaultNumThreads", PACKAGE = "RcppParallel")
}
isUsingTbb <- function() {
backend <- Sys.getenv("RCPP_PARALLEL_BACKEND", "tbb")
identical(backend, "tbb")
}
================================================
FILE: R/platform.R
================================================
is_windows <- function() {
.Platform$OS.type == "windows"
}
is_mac <- function() {
Sys.info()[["sysname"]] == "Darwin"
}
is_unix <- function() {
.Platform$OS.type == "unix"
}
is_solaris <- function() {
Sys.info()[["sysname"]] == "SunOS"
}
is_sparc <- function() {
info <- Sys.info()
all(
info[["sysname"]] == "SunOS",
info[["machine"]] != "i86pc"
)
}
================================================
FILE: R/plugin.R
================================================
# Inline plugin used by sourceCpp.
inlineCxxPlugin <- function() {
list(
env = list(
PKG_CXXFLAGS = tbbCxxFlags(),
PKG_LIBS = tbbLdFlags()
),
includes = "#include ",
LinkingTo = "RcppParallel",
body = identity,
Depends = "RcppParallel"
)
}
================================================
FILE: R/skeleton.R
================================================
#' Create a skeleton for a new package depending on RcppParallel
#'
#' \code{RcppParallel.package.skeleton} automates the creation of a new source
#' package that intends to use features of RcppParallel.
#'
#' It is based on the \link[utils]{package.skeleton} function which it executes
#' first.
#'
#' In addition to \link[Rcpp]{Rcpp.package.skeleton} :
#'
#' The \samp{DESCRIPTION} file gains an Imports line requesting that the
#' package depends on RcppParallel and a LinkingTo line so that the package
#' finds RcppParallel header files.
#'
#' The \samp{NAMESPACE} gains a \code{useDynLib} directive as well as an
#' \code{importFrom(RcppParallel, evalCpp} to ensure instantiation of
#' RcppParallel.
#'
#' The \samp{src} directory is created if it does not exists and a
#' \samp{Makevars} file is added setting the environment variables
#' \samp{PKG_LIBS} to accomodate the necessary flags to link with the
#' RcppParallel library.
#'
#' If the \code{example_code} argument is set to \code{TRUE}, example files
#' \samp{vector-sum.cpp} is created in the \samp{src} directory.
#' \code{Rcpp::compileAttributes()} is then called to generate
#' \code{src/RcppExports.cpp} and \code{R/RcppExports.R}. These files are given
#' as an example and should eventually by removed from the generated package.
#'
#' @param name The name of your R package.
#' @param example_code If \code{TRUE}, example C++ code using RcppParallel is
#' added to the package.
#' @param ... Optional arguments passed to \link[Rcpp]{Rcpp.package.skeleton}.
#' @return Nothing, used for its side effects
#' @seealso \link[utils]{package.skeleton}
#' @references Read the \emph{Writing R Extensions} manual for more details.
#'
#' Once you have created a \emph{source} package you need to install it: see
#' the \emph{R Installation and Administration} manual, \code{\link{INSTALL}}
#' and \code{\link{install.packages}}.
#' @keywords programming
#' @examples
#'
#' \dontrun{
#' # simple package
#' RcppParallel.package.skeleton("foobar")
#' }
#'
#' @export RcppParallel.package.skeleton
RcppParallel.package.skeleton <- function(name = "anRpackage",
example_code = TRUE,
...)
{
# call Rcpp.package.skeleton() -- provide 'list' explicitly
# and clean up after
env <- new.env(parent = emptyenv())
env$dummy <- NULL
Rcpp::Rcpp.package.skeleton(
name = name,
attributes = FALSE,
module = FALSE,
example_code = FALSE,
environment = env,
list = "dummy",
...
)
# move to generated package directory
owd <- setwd(name)
on.exit(setwd(owd), add = TRUE)
# remove dummy stuff
unlink("data", recursive=TRUE)
unlink("man/dummy.Rd")
unlink("Read-and-delete-me")
lns <- readLines("NAMESPACE")
writeLines(lns[!grepl("dummy", lns)], "NAMESPACE")
unlink("src/init.c")
message("\nAdding RcppParallel settings")
# update DESCRIPTION file
desc <- read.dcf("DESCRIPTION", all = TRUE, keep.white = TRUE)
version <- sprintf("RcppParallel (>= %s)", utils::packageVersion("RcppParallel"))
desc$Imports <- paste0(desc$Imports, ", ", version)
message(" >> added Imports: ", desc$Imports)
desc$LinkingTo <- paste0(desc$LinkingTo, ", RcppParallel")
message(" >> added LinkingTo: ", desc$LinkingTo)
desc$SystemRequirements <- "GNU make"
message(" >> added SystemRequirements: GNU make")
write.dcf(desc, file = "DESCRIPTION", keep.white = TRUE)
# update NAMESPACE file
message(" >> added importFrom(RcppParallel,RcppParallelLibs) directive to NAMESPACE")
cat("importFrom(RcppParallel,RcppParallelLibs)",
file = "NAMESPACE",
sep = "\n",
append = TRUE)
# write Makevars files
dir.create("src", showWarnings = FALSE)
# src/Makevars
message(" >> added src/Makevars")
cat(
c(
'# We also need importFrom(RcppParallel,RcppParallelLibs) in NAMESPACE',
'PKG_LIBS += $(shell ${R_HOME}/bin/Rscript -e "RcppParallel::RcppParallelLibs()")'
),
file = "src/Makevars",
sep = "\n"
)
# src/Makevars.win
message(" >> added src/Makevars.win")
cat(
c(
'PKG_CXXFLAGS += -DRCPP_PARALLEL_USE_TBB=1',
'PKG_LIBS += $(shell "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" -e "RcppParallel::RcppParallelLibs()")'
),
file = "src/Makevars.win",
sep = "\n"
)
# write an example script using RcppParallel
if (example_code) {
message(" >> added example file src/vector-sum.cpp")
file.copy(
system.file("skeleton/vector-sum.cpp", package = "RcppParallel"),
"src/vector-sum.cpp"
)
message(" >> added example documentation man/vector-sum.Rd")
file.copy(
system.file("skeleton/vector-sum.Rd", package = "RcppParallel"),
"man/vector-sum.Rd"
)
message(" >> compiled Rcpp attributes")
Rcpp::compileAttributes()
}
TRUE
}
================================================
FILE: R/tbb-autodetected.R.in
================================================
TBB_ENABLED <- @TBB_ENABLED@
TBB_LIB <- "@TBB_LIB@"
TBB_INC <- "@TBB_INC@"
TBB_NAME <- "@TBB_NAME@"
TBB_MALLOC_NAME <- "@TBB_MALLOC_NAME@"
================================================
FILE: R/tbb.R
================================================
#' Get the Path to a TBB Library
#'
#' Retrieve the path to a TBB library. This can be useful for \R packages
#' using RcppParallel that wish to use, or re-use, the version of TBB that
#' RcppParallel has been configured to use.
#'
#' @param name
#' The name of the TBB library to be resolved. Normally, this is one of
#' `tbb`, `tbbmalloc`, or `tbbmalloc_proxy`. When `NULL`, the library
#' path containing the TBB libraries is returned instead.
#'
#' @export
tbbLibraryPath <- function(name = NULL) {
# library paths for different OSes
sysname <- Sys.info()[["sysname"]]
# find root for TBB install
tbbRoot <- Sys.getenv("TBB_LIB", unset = tbbRoot())
if (is.null(name))
return(tbbRoot)
# form library names
tbbLibNames <- list(
"Darwin" = paste0("lib", name, ".dylib"),
"Windows" = paste0("lib", name, c("12", ""), ".a"),
"SunOS" = paste0("lib", name, ".so"),
"Linux" = paste0("lib", name, c(".so.2", ".so"))
)
# skip systems that we know not to be compatible
isCompatible <- !is_sparc() && !is.null(tbbLibNames[[sysname]])
if (!isCompatible)
return(NULL)
# find the request library (if any)
libNames <- tbbLibNames[[sysname]]
for (libName in libNames) {
tbbName <- file.path(tbbRoot, libName)
if (file.exists(tbbName))
return(tbbName)
arch <- if (nzchar(.Platform$r_arch)) .Platform$r_arch
suffix <- paste(c("lib", arch, libName), collapse = "/")
tbbName <- system.file(suffix, package = "RcppParallel")
if (file.exists(tbbName))
return(tbbName)
}
}
tbbCxxFlags <- function() {
if (!TBB_ENABLED)
return("-DRCPP_PARALLEL_USE_TBB=0")
flags <- c("-DRCPP_PARALLEL_USE_TBB=1")
# if TBB_INC is set, apply those library paths
tbbInc <- Sys.getenv("TBB_INC", unset = TBB_INC)
if (!file.exists(tbbInc)) {
tbbInc <- system.file("include", package = "RcppParallel")
}
# add include path
if (nzchar(tbbInc) && file.exists(tbbInc)) {
# prefer new interface if version.h exists -- we keep this
# for compatibility with packages like StanHeaders, rstan
versionPath <- file.path(tbbInc, "tbb/version.h")
if (file.exists(versionPath))
flags <- c(flags, "-DTBB_INTERFACE_NEW")
# now add the include path
flags <- c(flags, paste0("-I", asBuildPath(tbbInc)))
}
# return flags as string
paste(flags, collapse = " ")
}
# Return the linker flags required for TBB on this platform
tbbLdFlags <- function() {
# on Windows, we statically link to oneTBB
if (is_windows()) {
libPath <- system.file("libs", package = "RcppParallel")
if (nzchar(.Platform$r_arch))
libPath <- file.path(libPath, .Platform$r_arch)
ldFlags <- sprintf("-L%s -lRcppParallel", asBuildPath(libPath))
return(ldFlags)
}
# shortcut if TBB_LIB defined
tbbLib <- Sys.getenv("TBB_LINK_LIB", Sys.getenv("TBB_LIB", unset = TBB_LIB))
if (nzchar(tbbLib)) {
if (R.version$os == "emscripten") {
fmt <- "-L%1$s -l%2$s"
return(sprintf(fmt, asBuildPath(tbbLib), TBB_NAME))
}
fmt <- "-L%1$s -Wl,-rpath,%1$s -l%2$s -l%3$s"
return(sprintf(fmt, asBuildPath(tbbLib), TBB_NAME, TBB_MALLOC_NAME))
}
# explicitly link on macOS
# https://github.com/RcppCore/RcppParallel/issues/206
if (is_mac()) {
fmt <- "-L%s -l%s -l%s"
return(sprintf(fmt, asBuildPath(tbbLibraryPath()), TBB_NAME, TBB_MALLOC_NAME))
}
# nothing required on other platforms
""
}
tbbRoot <- function() {
if (nzchar(TBB_LIB))
return(TBB_LIB)
rArch <- .Platform$r_arch
parts <- c("lib", if (nzchar(rArch)) rArch)
libDir <- paste(parts, collapse = "/")
system.file(libDir, package = "RcppParallel")
}
================================================
FILE: R/utils.R
================================================
# generate paths consumable by the compilers and linkers
# in particular, on Windows and Solaris, this means the path _cannot_ be quoted !!
asBuildPath <- function(path) {
# normalize paths using forward slashes
path <- normalizePath(path, winslash = "/", mustWork = FALSE)
# prefer short path names if the path has spaces
if (is_windows() && grepl(" ", path, fixed = TRUE))
path <- utils::shortPathName(path)
# if we still have spaces, and we're not Windows or Solaris, try quoting
if (grepl(" ", path, fixed = TRUE) && !is_solaris())
path <- shQuote(path)
# ensure we use forward slashes, even on Windows
path <- chartr("\\", "/", path)
# return path
path
}
================================================
FILE: R/zzz.R
================================================
# !diagnostics suppress=.dllInfo,.tbbDllInfo,.tbbMallocDllInfo,.tbbMallocProxyDllInfo
# NOTE: we intentionally do _not_ load tbbmalloc_proxy by default, as its
# intended use is to replace the default allocator, something that may be
# dangerous to do by default. in addition, TBB's documentation recommends
# only loading explicitly via e.g. LD_PRELOAD
.dllInfo <- NULL
.tbbDllInfo <- NULL
.tbbMallocDllInfo <- NULL
.tbbMallocProxyDllInfo <- NULL
loadTbbLibrary <- function(name) {
# TBB is statically linked on Windows
if (is_windows()) {
return(NULL)
}
path <- tbbLibraryPath(name)
if (is.null(path))
return(NULL)
if (!file.exists(path)) {
warning("TBB library ", shQuote(name), " not found.")
return(NULL)
}
dyn.load(path, local = FALSE, now = TRUE)
}
.onLoad <- function(libname, pkgname) {
# on Windows, load RcppParallel first
if (.Platform$OS.type == "windows") {
.dllInfo <<- library.dynam("RcppParallel", pkgname, libname)
}
# load tbb, tbbmalloc
.tbbDllInfo <<- loadTbbLibrary("tbb")
.tbbMallocDllInfo <<- loadTbbLibrary("tbbmalloc")
# load tbbmalloc_proxy, but only if requested
useTbbMallocProxy <- Sys.getenv("RCPP_PARALLEL_USE_TBBMALLOC_PROXY", unset = "FALSE")
if (useTbbMallocProxy %in% c("TRUE", "True", "true", "1"))
.tbbMallocProxyDllInfo <<- loadTbbLibrary("tbbmalloc_proxy")
# load RcppParallel library if available
if (.Platform$OS.type != "windows") {
.dllInfo <<- library.dynam("RcppParallel", pkgname, libname, local = FALSE)
}
}
.onUnload <- function(libpath) {
# unload the package library
if (!is.null(.dllInfo))
library.dynam.unload("RcppParallel", libpath)
# NOTE: we do not explicitly unload tbbmalloc_proxy as switching
# the allocator at runtime can cause issues
# unload tbbmalloc if we loaded it
if (!is.null(.tbbMallocDllInfo))
dyn.unload(.tbbMallocDllInfo[["path"]])
# unload tbb if we loaded it
if (!is.null(.tbbDllInfo))
dyn.unload(.tbbDllInfo[["path"]])
}
================================================
FILE: README.md
================================================
## RcppParallel
[](https://cran.r-project.org/package=RcppParallel)
[](https://github.com/RcppCore/RcppParallel/actions/workflows/R-CMD-check.yaml)
High level functions for parallel programming with Rcpp. The `parallelFor()` function can be used to convert the work of a standard serial "for" loop into a parallel one, and the `parallelReduce()` function can be used for accumulating aggregate or other values.
The high level interface enables safe and robust parallel programming without direct manipulation of operating system threads. On Windows, macOS, and Linux systems, the underlying implementation is based on [Intel TBB](https://github.com/oneapi-src/oneTBB) (Threading Building Blocks). On other platforms, a less-performant fallback implementation based on the [TinyThread](https://tinythreadpp.bitsnbites.eu/) library is used.
For additional documentation on using RcppParallel see the package website at http://rcppcore.github.io/RcppParallel/.
### Intel TBB
`RcppParallel` supports the new interface of Intel TBB, and can be configured to use an external copy of TBB (e.g., with [`oneTBB`](https://github.com/oneapi-src/oneTBB) or the system TBB library), using the `TBB_LIB` and `TBB_INC` environment variables.
To build the development version of `RcppParallel` with [`oneTBB`](https://github.com/oneapi-src/oneTBB):
- Install [`oneTBB`](https://github.com/oneapi-src/oneTBB).
For example, installing [`oneTBB`](https://github.com/oneapi-src/oneTBB) on Linux 64-bit (`x86_64`) to `$HOME` directory (change if needed!):
```bash
TBB_RELEASE="https://api.github.com/repos/oneapi-src/oneTBB/releases/latest"
TBB_TAG=$(curl --silent $TBB_RELEASE | grep -Po '"tag_name": "\K.*?(?=")')
TBB_VERSION=${TBB_TAG#?}
wget https://github.com/oneapi-src/oneTBB/releases/download/v$TBB_VERSION/oneapi-tbb-$TBB_VERSION-lin.tgz
tar zxvf oneapi-tbb-$TBB_VERSION-lin.tgz -C $HOME
export TBB="$HOME/oneapi-tbb-$TBB_VERSION"
```
Note that you may replace `TBB_VERSION=${TBB_TAG#?}` with a custom version number if needed ( check available releases [here](https://github.com/oneapi-src/oneTBB/releases) ).
- Set the TBB environment variables (specifically: `TBB` for the installation prefix, `TBB_INC` for the directory that includes the header files, and `TBB_LIB` for the libraries directory).
For example, installing [`oneTBB`](https://github.com/oneapi-src/oneTBB) on Linux 64-bit (`x86_64`) to `$HOME` directory (change if needed!):
```bash
source $TBB/env/vars.sh intel64
export TBB_INC="$TBB/include"
export TBB_LIB="$TBB/lib/intel64/gcc4.8"
```
- Build the development version of `RcppParallel`:
```r
install.packages("remotes")
remotes::install_github("RcppCore/RcppParallel")
```
### License
The RcppParallel package is made available under the [GPLv2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) license.
The [TinyThread library](https://tinythreadpp.bitsnbites.eu/) is licensed under the [zlib/libpng](https://opensource.org/licenses/zlib-license.php) license.
The Intel TBB Library is licensed under the Apache 2.0 license, as described at https://github.com/oneapi-src/oneTBB/blob/master/LICENSE.txt.
================================================
FILE: RcppParallel.Rproj
================================================
Version: 1.0
ProjectId: 8e3d73b0-404c-42f5-b2ef-46f759f65dd4
RestoreWorkspace: No
SaveWorkspace: No
AlwaysSaveHistory: No
EnableCodeIndexing: Yes
UseSpacesForTab: Yes
NumSpacesForTab: 3
Encoding: UTF-8
RnwWeave: Sweave
LaTeX: pdfLaTeX
AutoAppendNewline: Yes
BuildType: Package
PackageCleanBeforeInstall: No
PackageInstallArgs: --with-keep.source
PackageCheckArgs: --as-cran
PackageRoxygenize: rd,collate,namespace
================================================
FILE: cleanup
================================================
#!/usr/bin/env sh
: "${R_HOME=`R RHOME`}"
"${R_HOME}/bin/Rscript" tools/config.R cleanup "$@"
================================================
FILE: cleanup.win
================================================
#!/usr/bin/env sh
"${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" tools/config.R cleanup "$@"
================================================
FILE: configure
================================================
#!/usr/bin/env sh
: "${R_HOME=`R RHOME`}"
"${R_HOME}/bin/Rscript" tools/config.R configure "$@"
================================================
FILE: configure.win
================================================
#!/usr/bin/env sh
"${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" tools/config.R configure "$@"
================================================
FILE: doc/rtools_tbb_notes.md
================================================
# Differences MinGW / Rtools
## cmd
### MinGW
cmd is an sh wrapper which invokes the 32bit version of Windows' shell:
$ where cmd
C:\MinGW\msys\1.0\bin\cmd
c:\Windows\System32\cmd.exe
$ cat /c/MinGW/msys/1.0/bin/cmd
#!/bin/sh
# Copyright (C) 2002, Earnie Boyd
# mailto:earnie@users.sf.net
# This file is part of Minimal SYStem.
# http://www.mingw.org/msys.shtml
# File: cmd
"$COMSPEC" "$@"
$ echo $COMSPEC
C:\windows\SysWOW64\cmd.exe
### Rtools
No wrapper; cmd is the default Windows shell (64bit on a 64bit OS):
$ where cmd
C:\Windows\System32\cmd.exe
## paths
### MinGW
- 3 styles supported: `/drive/path`, `drive:/path`, `drive:\path` (properly escaped).
- The *actual* path (returned when querying) is `/drive/path`.
$ cd f:/tmp/testpath
$ pwd
/f/tmp/testpath
$ cd f:\\tmp\\testpath
$ pwd
/f/tmp/testpath
$ cd /f/tmp/testpath
$ pwd
/f/tmp/testpath
### Rtools
- 3 styles supported: `/cygdrive/drive/path`, `drive:/path`, `drive:\path` (properly escaped).
- The *actual* path (returned when querying) is `/cygdrive/drive/path`.
- `/drive/path` is **not** supported.
$ cd f:/tmp/testpath
$ pwd
/cygdrive/f/tmp/testpath
$ cd f:\\tmp\\testpath
$ pwd
/cygdrive/f/tmp/testpath
$ cd /f/tmp/testpath
cd: can't cd to /f/tmp/testpath
$ cd /cygdrive/f/tmp/testpath
$ pwd
/cygdrive/f/tmp/testpath
## parameters
### MinGW
Shell will sometimes automatically replace parameters starting with forward slashes using the rules described [here](http://www.mingw.org/wiki/Posix_path_conversion).
Since forward slashes are often used to mark option parameters in Windows command line applications (/o is the Windows equivalent of -o and --option for Unix), this can cause very subtle and hard to debug issues. The fix is to double the forward slashes, or to quote (when possible)
$ ls /d
$RECYCLE.BIN System Volume Information pagefile.sys
$ cmd /c "echo hi"
hi
$ cmd /C "echo hi"
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
f:\tmp\testpath>exit
$ cmd /C "echo hi /runtime"
'untime"' is not recognized as an internal or external command,
operable program or batch file.
$ cmd //C "echo hi /runtime"
hi /runtime
### Rtools
No parameter replacements. What you type is what you get.
$ ls /d
ls: cannot access /d: No such file or directory
$ cmd /c "echo hi"
hi
$ cmd /C "echo hi"
hi
$ cmd /C "echo hi /runtime"
hi /runtime
## uname
Only present in MinGW
## g++
### MinGW
default full paths supported
$ g++ /f/tmp/testpath/test.cpp
$ echo $?
0
### Rtools
default full paths are not supported
$ ls /cygdrive/f/tmp/testpath/test.cpp
/cygdrive/f/tmp/testpath/test.cpp
$ cat /cygdrive/f/tmp/testpath/test.cpp
int main() {return 0;}
$ g++ /cygdrive/f/tmp/testpath/test.cpp
g++.exe: error: /cygdrive/f/tmp/testpath/test.cpp: No such file or directory
g++.exe: fatal error: no input files
compilation terminated.
This means that great care must be taken to ensure paths are never queried and instead always manually constructed. Examples of queried paths that end up being g++ incompatible in the context of a Makefile:
- don't use `$(shell pwd)`
- don't use `$(CURDIR)`
- only use **relative paths** in `VPATH`
$ cat Makefile
.PHONY: all
all: test.o foo.o
@echo DIR=$(CURDIR)
%.o: %.cpp
@echo $<
$ make VPATH=f:/tmp/testpath/subdir
test.cpp
/cygdrive/f/tmp/testpath/subdir/foo.cpp
DIR=/cygdrive/f/tmp/testpath
$ make VPATH=subdir
test.cpp
subdir/foo.cpp
DIR=/cygdrive/f/tmp/testpath
# Bugs and tips
## Modal dialog asking to insert disk in drive
This bug is in MinGW only (not Rtools). This is due to hard-coded paths pointing to I: drive in g++
$ strings c:/MinGW/bin/g++.exe | grep i: | grep mingw
i:/p/giaw/mingw/share/locale
i:/p/giaw/mingw/share/locale
If no I: drive exists, or the path does not exist, then g++ silently ignores it. *However*, if I: happens to point to a removable drive, then you get a modal dialog.
Fix: go in "Disk Management" and rename the drive.
## Full paths
g++ is not the only tool affected by full paths in the context of Rtools. Since Rtools doesn't perform any auto-conversion and `/` is used as a option marker for many Windows command line applications,
some of them end up being confused. For example:
$ cat test.js
WScript.Echo( "Hi" );
$ cscript /nologo test.js
Hi
$ pwd
/cygdrive/f/tmp/testpath
$ cscript /nologo /cygdrive/f/tmp/testpath/test.js
Input Error: Unknown option "/cygdrive/f/tmp/testpath/test.js" specified.
$ cscript /nologo f:/tmp/testpath/test.js
Hi
$
Recommendation is to always use relative paths.
## Changing the shell in make
The `.SHELLFLAGS` variable doesn't work in the context of Rtools (silently ignored), making it impossible to change default shell to cmd: the default for SHELLFLAGS is `-c`, which is suitable for `sh` and not `cmd`:
$ cmd /c dir /b notfound
File Not Found
$ cmd -c dir /b notfound
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
F:\tmp\testpath>exit
$ cat Makefile
SHELL=cmd
.SHELLFLAGS=/c
TEST=$(shell dir /b notfound)
.PHONY: all
all:
@echo hi
$ make
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.
F:\tmp\testpath>exit
The Rtools-only `--win32` command line option for make didn't properly work for me (but I was dealing with complex Makefiles).
## Hangs in make
There does not appear to be a built-in tracing or time-out mechanism in make regarding sub-processes. One effective way to figure out what's hanging is "Task Manager", "Details", then add column "Command Line".
In my case, most hangs were due to some variation of `cmd -c something` or `cmd c: something` (both variations end up leaving cmd.exe running) instead of `cmd /c something`. The first one is due to SHELLFLAGS not working, the second to auto param replacement.
## Rule not found errors
i.e. "no rule to make target" errors.
In some cases, the `VPATH` parser gets confused, and then **all** paths specified are *silently* ignored. This can even happen for simple (no spaces, etc.), valid (exist) paths.
$ cat Makefile
.PHONY: all
all: test.o foo.o
@echo hi
%.o: %.cpp
@echo $<
$ pwd
/cygdrive/f/tmp/testpath
$ make "VPATH=f:/tmp/testpath/subdir"
test.cpp
/cygdrive/f/tmp/testpath/subdir/foo.cpp
hi
$ make "VPATH=f:/tmp/testpath subdir"
test.cpp
subdir/foo.cpp
hi
$ make "VPATH=subdir f:/tmp/testpath"
test.cpp
subdir/foo.cpp
hi
$ make "VPATH=f:/tmp/testpath f:/tmp/testpath/subdir"
test.cpp
make: *** No rule to make target `foo.o', needed by `all'. Stop.
### Incorrect rule firing
These can be caused by the previous issue: VPATH not working, therefore target file not found, therefore other rule firing.
This can also be caused by MinGW and RTools using a different version of make. A quick way to test it is to manually/explicitly create the rule, and see if it is firing.
================================================
FILE: inst/.gitignore
================================================
lib/
libs/
================================================
FILE: inst/include/.gitignore
================================================
# These TBB libraries are copied in at configure time.
/index.html
/oneapi
/serial
/tbb
================================================
FILE: inst/include/RcppParallel/Backend.h
================================================
#ifndef __RCPP_PARALLEL_BACKEND__
#define __RCPP_PARALLEL_BACKEND__
#include
#include
extern "C" {
void REprintf(const char*, ...);
}
namespace RcppParallel {
namespace internal {
enum backend_type {
BACKEND_TBB,
BACKEND_TINYTHREAD
};
#if RCPP_PARALLEL_USE_TBB
inline backend_type defaultBackend()
{
return BACKEND_TBB;
}
#else
inline backend_type defaultBackend()
{
return BACKEND_TINYTHREAD;
}
#endif
inline const char* backendToString(backend_type backend)
{
switch (backend)
{
case BACKEND_TBB:
return "tbb";
case BACKEND_TINYTHREAD:
return "tinythread";
}
// shouldn't be reached but need to silence compiler warnings
return "tbb";
}
inline backend_type backend()
{
const char* requestedBackend = std::getenv("RCPP_PARALLEL_BACKEND");
if (requestedBackend == NULL)
{
return defaultBackend();
}
else if (std::strcmp(requestedBackend, "tbb") == 0)
{
#if RCPP_PARALLEL_USE_TBB
return BACKEND_TBB;
#else
const char* msg =
"tbb backend is not available; using tinythread instead";
REprintf("%s\n", msg);
return BACKEND_TINYTHREAD;
#endif
}
else if (strcmp(requestedBackend, "tinythread") == 0)
{
return BACKEND_TINYTHREAD;
}
else
{
const char* fmt = "unknown parallel backend '%s'; using '%s' instead\n";
REprintf(fmt, requestedBackend, backendToString(defaultBackend()));
return defaultBackend();
}
}
} // namespace internal
} // namespace RcppParallel
#endif /* __RCPP_PARALLEL_BACKEND__ */
================================================
FILE: inst/include/RcppParallel/Common.h
================================================
#ifndef __RCPP_PARALLEL_COMMON__
#define __RCPP_PARALLEL_COMMON__
#include
#include
#include
#include
#include
#include
#include
namespace RcppParallel {
template
inline int resolveValue(const char* envvar,
T requestedValue,
U defaultValue)
{
// if the requested value is non-zero and not the default, we can use it
bool useRequestedValue =
requestedValue != static_cast(defaultValue) &&
requestedValue > 0;
if (useRequestedValue)
return requestedValue;
// otherwise, try reading the default from associated envvar
// if the environment variable is unset, use the default
const char* var = getenv(envvar);
if (var == NULL)
return defaultValue;
// try to convert the string to a number
// if an error occurs during conversion, just use default
errno = 0;
char* end;
long value = strtol(var, &end, 10);
// check for conversion failure
if (end == var || *end != '\0' || errno == ERANGE)
return defaultValue;
// okay, return the parsed environment variable value
return value;
}
// Tag type used for disambiguating splitting constructors
struct Split {};
// Work executed within a background thread. We implement dynamic
// dispatch using vtables so we can have a stable type to cast
// to from the void* passed to the worker thread (required because
// the tinythreads interface allows to pass only a void* to the
// thread main rather than a generic type / template)
struct Worker
{
// construct and destruct (delete virtually)
Worker() {}
virtual ~Worker() {}
// dispatch work over a range of values
virtual void operator()(std::size_t begin, std::size_t end) = 0;
private:
// disable copying and assignment
Worker(const Worker&);
void operator=(const Worker&);
};
// Used for controlling the stack size for threads / tasks within a scope.
class ThreadStackSizeControl
{
public:
ThreadStackSizeControl();
~ThreadStackSizeControl();
private:
// COPYING: not copyable
ThreadStackSizeControl(const ThreadStackSizeControl&);
ThreadStackSizeControl& operator=(const ThreadStackSizeControl&);
};
} // namespace RcppParallel
#endif // __RCPP_PARALLEL_COMMON__
================================================
FILE: inst/include/RcppParallel/RMatrix.h
================================================
#ifndef __RCPP_PARALLEL_RMATRIX__
#define __RCPP_PARALLEL_RMATRIX__
#include
#include
namespace RcppParallel {
template
class RMatrix {
public:
class Row {
public:
template
class row_iterator {
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = V;
using difference_type = std::size_t;
using pointer = value_type*;
using reference = value_type&;
inline row_iterator(Row& row, difference_type i)
: start_(row.start_), parentNrow_(row.parent_.nrow()), index_(i)
{
}
inline row_iterator(pointer start, difference_type parentNrow, difference_type index)
: start_(start), parentNrow_(parentNrow), index_(index)
{
}
inline row_iterator(const row_iterator& other)
: start_(other.start_),
parentNrow_(other.parentNrow_),
index_(other.index_)
{
}
inline row_iterator& operator++() {
index_++;
return *this;
}
inline row_iterator operator++(int) {
row_iterator tmp(*this);
operator++();
return tmp;
}
inline row_iterator& operator--() {
index_-- ;
return *this ;
}
inline row_iterator operator--(int) {
row_iterator tmp(*this);
index_-- ;
return tmp ;
}
row_iterator operator+(difference_type n) const {
return row_iterator(start_, parentNrow_ ,index_ + n ) ;
}
row_iterator operator-(difference_type n) const {
return row_iterator(start_, parentNrow_, index_ - n ) ;
}
difference_type operator+(const row_iterator& other) const {
return index_ + other.index_;
}
difference_type operator-(const row_iterator& other) const {
return index_ - other.index_ ;
}
row_iterator& operator+=(difference_type n) { index_ += n ; return *this; }
row_iterator& operator-=(difference_type n) { index_ -= n ; return *this; }
bool operator==(const row_iterator& other) const { return index_ == other.index_; }
bool operator!=(const row_iterator& other) const { return index_ != other.index_; }
bool operator<(const row_iterator& other) const { return index_ < other.index_; }
bool operator>(const row_iterator& other) const { return index_ > other.index_; }
bool operator<=(const row_iterator& other) const { return index_ <= other.index_; }
bool operator>=(const row_iterator& other) const { return index_ >= other.index_; }
inline reference operator*() { return start_[index_ * parentNrow_]; }
inline pointer operator->() { return &(start_[index_ * parentNrow_]); }
inline reference operator[](int i) { return start_[(index_+i) * parentNrow_]; }
private:
pointer start_;
difference_type parentNrow_;
difference_type index_;
};
typedef row_iterator iterator;
typedef row_iterator const_iterator;
inline Row(RMatrix& parent, std::size_t i)
: parent_(parent),
start_(parent.begin() + i)
{
}
inline Row(const Row& other)
: parent_(other.parent_),
start_(other.start_)
{
}
inline iterator begin() {
return iterator(*this, 0);
}
inline iterator end() {
return iterator(*this, parent_.ncol());
}
inline const_iterator begin() const {
return const_iterator(*this, 0);
}
inline const_iterator end() const {
return const_iterator(*this, parent_.ncol());
}
inline size_t length() const {
return parent_.ncol();
}
inline size_t size() const {
return parent_.ncol();
}
inline T& operator[](std::size_t i) {
return start_[i * parent_.nrow()];
}
inline const T& operator[](std::size_t i) const {
return start_[i * parent_.nrow()];
}
private:
RMatrix& parent_;
T* start_;
};
class Column {
public:
typedef T* iterator;
typedef const T* const_iterator;
inline Column(RMatrix& parent, std::size_t i)
: begin_(parent.begin() + (i * parent.nrow())),
end_(begin_ + parent.nrow())
{
}
inline Column(const Column& other)
: begin_(other.begin_), end_(other.end_)
{
}
inline Column& operator=(const Column& rhs) {
begin_ = rhs.begin_;
end_ = rhs.end_;
return *this;
}
inline iterator begin() { return begin_; }
inline iterator end() { return end_; }
inline const_iterator begin() const { return begin_; }
inline const_iterator end() const { return end_; }
inline size_t length() const { return end_ - begin_; }
inline size_t size() const { return end_ - begin_; }
inline T& operator[](std::size_t i) {
return *(begin_ + i);
}
inline const T& operator[](std::size_t i) const {
return *(begin_ + i);
}
private:
T* begin_;
T* end_;
};
typedef T* iterator;
typedef const T* const_iterator;
template
inline explicit RMatrix(const Source& source)
: data_(const_cast(source).begin()),
nrow_(source.nrow()),
ncol_(source.ncol())
{
}
inline RMatrix(T* data, std::size_t nrow, std::size_t ncol)
: data_(data), nrow_(nrow), ncol_(ncol)
{
}
inline iterator begin() { return data_; }
inline iterator end() { return data_ + length(); }
inline const_iterator begin() const { return data_; }
inline const_iterator end() const { return data_ + length(); }
inline std::size_t length() const { return nrow_ * ncol_; }
inline std::size_t nrow() const { return nrow_; }
inline std::size_t ncol() const { return ncol_; }
inline T& operator()(std::size_t i, std::size_t j) {
return *(data_ + (i + j * nrow_));
}
inline const T& operator()(std::size_t i, std::size_t j) const {
return *(data_ + (i + j * nrow_));
}
inline Row row(std::size_t i) {
return Row(*this, i);
}
inline const Row row(std::size_t i) const {
return Row(*const_cast(this), i);
}
inline Column column(std::size_t i) {
return Column(*this, i);
}
inline const Column column(std::size_t i) const {
return Column(*const_cast(this), i);
}
inline T& operator[](std::size_t i) {
return *(data_ + i);
}
inline const T& operator[](std::size_t i) const {
return *(data_ + i);
}
private:
T* data_;
std::size_t nrow_;
std::size_t ncol_;
};
} // namespace RcppParallel
#endif // __RCPP_PARALLEL_RMATRIX__
================================================
FILE: inst/include/RcppParallel/RVector.h
================================================
#ifndef __RCPP_PARALLEL_RVECTOR__
#define __RCPP_PARALLEL_RVECTOR__
#include
namespace RcppParallel {
template
class RVector {
public:
typedef T* iterator;
typedef const T* const_iterator;
template
inline explicit RVector(const Source& source)
: begin_(const_cast(source).begin()),
end_(begin_ + source.length())
{
}
inline RVector(std::size_t begin, std::size_t end)
: begin_(begin), end_(end)
{
}
inline RVector(const RVector& other)
: begin_(other.begin_), end_(other.end_)
{
}
inline RVector& operator=(const RVector& rhs) {
begin_ = rhs.begin_;
end_ = rhs.end_;
return *this;
}
inline iterator begin() { return begin_; }
inline iterator end() { return end_; }
inline const_iterator begin() const { return begin_; }
inline const_iterator end() const { return end_; }
inline std::size_t size() const { return end_ - begin_; }
inline std::size_t length() const { return end_ - begin_; }
inline T& operator[](std::size_t i) {
return *(begin_ + i);
}
inline const T& operator[](std::size_t i) const {
return *(begin_ + i);
}
private:
T* begin_;
T* end_;
};
} // namespace RcppParallel
#endif // __RCPP_PARALLEL_RVECTOR__
================================================
FILE: inst/include/RcppParallel/TBB.h
================================================
#ifndef __RCPP_PARALLEL_TBB__
#define __RCPP_PARALLEL_TBB__
#include "Common.h"
#ifndef TBB_PREVIEW_GLOBAL_CONTROL
# define TBB_PREVIEW_GLOBAL_CONTROL 1
#endif
// For compatibility with existing packages on CRAN.
#include "tbb/blocked_range.h"
#include "tbb/concurrent_unordered_set.h"
#include "tbb/concurrent_unordered_map.h"
#include "tbb/global_control.h"
#include "tbb/mutex.h"
#include "tbb/parallel_for.h"
#include "tbb/parallel_for_each.h"
#include "tbb/parallel_reduce.h"
#include "tbb/parallel_sort.h"
#include "tbb/spin_mutex.h"
// For compatibility with older R packages.
namespace tbb {
#ifndef __TBB_task_scheduler_init_H
#define __TBB_task_scheduler_init_H
class task_scheduler_init {
public:
task_scheduler_init(
int number_of_threads = -1,
std::size_t stack_size = 0)
{
}
static int default_num_threads()
{
return 2;
}
static const int automatic = -1;
static const int deferred = -2;
};
#endif
} // end namespace tbb
namespace RcppParallel {
// This class is primarily used to implement type erasure. The goals here were:
//
// 1. Hide the tbb symbols / implementation details from client R packages.
// That is, they should get the tools they need only via RcppParallel.
//
// 2. Do this in a way that preserves binary compatibility with pre-existing
// classes that make use of parallelReduce().
//
// 3. Ensure that those packages, when re-compiled without source changes,
// can still function as expected.
//
// The downside here is that all the indirection through std::function<>
// and the requirement for RTTI is probably expensive, but I couldn't find
// a better way forward that could also preserve binary compatibility with
// existing pre-built pacakges.
//
// Hopefully, in a future release, we can do away with this wrapper, once
// packages have been rebuilt and no longer implicitly depend on TBB internals.
struct ReducerWrapper {
template
ReducerWrapper(T* reducer)
{
self_ = reinterpret_cast(reducer);
owned_ = false;
work_ = [&](void* self, std::size_t begin, std::size_t end)
{
(*reinterpret_cast(self))(begin, end);
};
split_ = [&](void* object, Split split)
{
return new T(*reinterpret_cast(object), split);
};
join_ = [&](void* self, void* other)
{
(*reinterpret_cast(self)).join(*reinterpret_cast(other));
};
deleter_ = [&](void* object)
{
delete (T*) object;
};
}
~ReducerWrapper()
{
if (owned_)
{
deleter_(self_);
self_ = nullptr;
}
}
void operator()(std::size_t begin, std::size_t end) const
{
work_(self_, begin, end);
}
ReducerWrapper(const ReducerWrapper& rhs, Split split)
{
self_ = rhs.split_(rhs.self_, split);
owned_ = true;
work_ = rhs.work_;
split_ = rhs.split_;
join_ = rhs.join_;
deleter_ = rhs.deleter_;
}
void join(const ReducerWrapper& rhs) const
{
join_(self_, rhs.self_);
}
private:
void* self_ = nullptr;
bool owned_ = false;
std::function work_;
std::function split_;
std::function join_;
std::function deleter_;
};
void tbbParallelFor(std::size_t begin,
std::size_t end,
Worker& worker,
std::size_t grainSize = 1,
int numThreads = -1);
void tbbParallelReduceImpl(std::size_t begin,
std::size_t end,
ReducerWrapper& wrapper,
std::size_t grainSize = 1,
int numThreads = -1);
template
void tbbParallelReduce(std::size_t begin,
std::size_t end,
Reducer& reducer,
std::size_t grainSize = 1,
int numThreads = -1)
{
ReducerWrapper wrapper(&reducer);
tbbParallelReduceImpl(begin, end, wrapper, grainSize, numThreads);
}
} // namespace RcppParallel
#endif // __RCPP_PARALLEL_TBB__
================================================
FILE: inst/include/RcppParallel/Timer.h
================================================
#ifndef __RCPP_PARALLEL_TIMER__
#define __RCPP_PARALLEL_TIMER__
namespace RcppParallel {
typedef uint64_t nanotime_t;
template
class ProportionTimer {
public:
ProportionTimer() :
timer(), n(0), id(0)
{}
ProportionTimer( nanotime_t origin, int id_ ) :
timer(origin), n(0), id(id_)
{}
inline operator SEXP() const {
Rcpp::NumericVector out = (SEXP)timer ;
out.attr("n") = n ;
return out ;
}
inline nanotime_t origin() const{
return timer.origin() ;
}
inline int get_n() const {
return n ;
}
inline void step( const std::string& name) {
timer.step(name) ;
}
Timer timer ;
int n ;
int id ;
} ;
template
class SingleTimer {
public:
SingleTimer() : timer(){}
inline operator SEXP(){
Rcpp::List out = Rcpp::List::create(timer) ;
out.attr("class") = Rcpp::CharacterVector::create( "SingleTimer", "Timer" );
return out ;
}
inline void step( const char* name ){
timer.step(name) ;
}
private:
Timer timer ;
} ;
template
class FixedSizeTimers {
public:
FixedSizeTimers( int n, int ndata_ ) :
timers(n), ndata(ndata_)
{}
inline ProportionTimer& get(int i) {
return timers[i] ;
}
inline ProportionTimer& operator[](int i){
return timers[i];
}
inline operator SEXP(){
Rcpp::List out = wrap(timers) ;
out.attr("class") = Rcpp::CharacterVector::create("FixedSizeTimers", "Timer") ;
out.attr("n") = ndata ;
return out ;
}
private:
std::vector< ProportionTimer > timers ;
int ndata ;
} ;
template
class TimersList {
public:
TimersList(int n_):
timers(), origin(Rcpp::get_nanotime()), n(n_)
{
timers.push_back( ProportionTimer( origin, 0 ) ) ;
childs.push_back( std::vector() );
}
ProportionTimer& front(){
return timers.front() ;
}
ProportionTimer& get_new_timer(int parent_id){
Locker lock(mutex) ;
int id = timers.size() ;
timers.push_back( ProportionTimer( origin, id ) ) ;
childs.push_back( std::vector() );
std::list< std::vector >::iterator it = childs.begin() ;
for(int i=0; ipush_back(id) ;
return timers.back() ;
}
inline operator SEXP(){
int nt = timers.size() ;
Rcpp::List data(nt) ;
typename std::list >::const_iterator timers_it = timers.begin() ;
std::list >::const_iterator childs_it = childs.begin() ;
for( int i=0; i > timers ;
std::list< std::vector > childs ;
Mutex mutex ;
nanotime_t origin ;
int n ;
private:
TimersList( const TimersList& ) ;
} ;
template
class TimedReducer : public Worker {
public:
typedef TimersList Timers ;
Reducer* reducer ;
Timers& timers ;
ProportionTimer& timer ;
bool owner ;
TimedReducer( Reducer& reducer_, Timers& timers_) :
reducer(&reducer_),
timers(timers_),
timer(timers.get_new_timer(0)),
owner(false)
{
timer.step("init");
}
TimedReducer( const TimedReducer& other, Split s) :
reducer( new Reducer(*other.reducer, s) ),
timers( other.timers ),
timer( timers.get_new_timer(other.timer.id) ),
owner(true)
{
timer.step("init") ;
}
~TimedReducer(){
if(owner && reducer) {
delete reducer ;
}
reducer = 0 ;
}
inline void operator()( size_t begin, size_t end){
timer.n += (end-begin) ;
timer.step("start") ;
reducer->operator()(begin, end) ;
timer.step("work") ;
}
inline void join(const TimedReducer& rhs){
rhs.timer.step("start") ;
reducer->join(*rhs.reducer) ;
rhs.timer.step("join") ;
}
inline Rcpp::List get() const {
timers.front().step("start") ;
OUT out = reducer->get() ;
timers.front().step("structure") ;
Rcpp::List res = Rcpp::List::create( (SEXP)timers, out );
return res ;
}
private:
// just to be on the safe side, making sure these are not called
TimedReducer() ;
TimedReducer( const TimedReducer& ) ;
} ;
} // namespace RcppParallel
#endif // __RCPP_PARALLEL_COMMON__
================================================
FILE: inst/include/RcppParallel/TinyThread.h
================================================
#ifndef __RCPP_PARALLEL_TINYTHREAD__
#define __RCPP_PARALLEL_TINYTHREAD__
#include
#include
#include "Common.h"
#include
#include
namespace RcppParallel {
namespace {
// Class which represents a range of indexes to perform work on
// (worker functions are passed this range so they know which
// elements are safe to read/write to)
class IndexRange {
public:
// Initizlize with a begin and (exclusive) end index
IndexRange(std::size_t begin, std::size_t end)
: begin_(begin), end_(end)
{
}
// Access begin() and end()
std::size_t begin() const { return begin_; }
std::size_t end() const { return end_; }
std::size_t size() const { return end_ - begin_ ; }
private:
std::size_t begin_;
std::size_t end_;
};
// Because tinythread allows us to pass only a plain C function
// we need to pass our worker and range within a struct that we
// can cast to/from void*
struct Work {
Work(IndexRange range, Worker& worker)
: range(range), worker(worker)
{
}
IndexRange range;
Worker& worker;
};
// Thread which performs work (then deletes the work object
// when it's done)
extern "C" inline void workerThread(void* data) {
try
{
Work* pWork = static_cast(data);
pWork->worker(pWork->range.begin(), pWork->range.end());
delete pWork;
}
catch(...)
{
}
}
// Function to calculate the ranges for a given input
std::vector splitInputRange(const IndexRange& range,
std::size_t grainSize) {
// determine max number of threads
std::size_t threads = tthread::thread::hardware_concurrency();
char* numThreads = ::getenv("RCPP_PARALLEL_NUM_THREADS");
if (numThreads != NULL) {
int parsedThreads = ::atoi(numThreads);
if (parsedThreads > 0)
threads = parsedThreads;
}
// compute grainSize (including enforcing requested minimum)
std::size_t length = range.end() - range.begin();
if (threads == 1)
grainSize = length;
else if ((length % threads) == 0) // perfect division
grainSize = std::max(length / threads, grainSize);
else // imperfect division, divide by threads - 1
grainSize = std::max(length / (threads-1), grainSize);
// allocate ranges
std::vector ranges;
std::size_t begin = range.begin();
std::size_t end = begin;
while (begin < range.end()) {
if ((range.end() - (begin + grainSize)) < grainSize)
end = range.end();
else
end = std::min(begin + grainSize, range.end());
ranges.push_back(IndexRange(begin, end));
begin = end;
}
// return ranges
return ranges;
}
} // anonymous namespace
// Execute the Worker over the IndexRange in parallel
inline void ttParallelFor(std::size_t begin,
std::size_t end,
Worker& worker,
std::size_t grainSize = 1)
{
// split the work
IndexRange inputRange(begin, end);
std::vector ranges = splitInputRange(inputRange, grainSize);
// create threads
std::vector threads;
for (std::size_t i = 0; ijoin();
delete threads[i];
}
}
// Execute the IWorker over the range in parallel then join results
template
inline void ttParallelReduce(std::size_t begin,
std::size_t end,
Reducer& reducer,
std::size_t grainSize = 1)
{
// split the work
IndexRange inputRange(begin, end);
std::vector ranges = splitInputRange(inputRange, grainSize);
// create threads (split for each thread and track the allocated workers)
std::vector threads;
std::vector workers;
for (std::size_t i = 0; ijoin();
// join the results
reducer.join(static_cast(*workers[i]));
// delete the worker (which we split above) and the thread
delete workers[i];
delete threads[i];
}
}
} // namespace RcppParallel
#endif // __RCPP_PARALLEL_TINYTHREAD__
================================================
FILE: inst/include/RcppParallel.h
================================================
#ifndef __RCPP_PARALLEL__
#define __RCPP_PARALLEL__
// TinyThread implementation
#include "RcppParallel/TinyThread.h"
// Use TBB only where it's known to compile and work correctly
// (NOTE: Windows TBB is temporarily opt-in for packages for
// compatibility with CRAN packages not previously configured
// to link to TBB in Makevars.win)
#ifndef RCPP_PARALLEL_USE_TBB
# if defined(__APPLE__) || defined(__gnu_linux__) || (defined(__sun) && defined(__SVR4) && !defined(__sparc))
# define RCPP_PARALLEL_USE_TBB 1
# else
# define RCPP_PARALLEL_USE_TBB 0
# endif
#endif
#if RCPP_PARALLEL_USE_TBB
# include "RcppParallel/TBB.h"
#endif
#include "RcppParallel/Backend.h"
#include "RcppParallel/RVector.h"
#include "RcppParallel/RMatrix.h"
namespace RcppParallel {
inline void parallelFor(std::size_t begin,
std::size_t end,
Worker& worker,
std::size_t grainSize = 1,
int numThreads = -1)
{
grainSize = resolveValue("RCPP_PARALLEL_GRAIN_SIZE", grainSize, std::size_t(1));
numThreads = resolveValue("RCPP_PARALLEL_NUM_THREADS", numThreads, -1);
#if RCPP_PARALLEL_USE_TBB
if (internal::backend() == internal::BACKEND_TBB)
tbbParallelFor(begin, end, worker, grainSize, numThreads);
else
ttParallelFor(begin, end, worker, grainSize);
#else
ttParallelFor(begin, end, worker, grainSize);
#endif
}
template
inline void parallelReduce(std::size_t begin,
std::size_t end,
Reducer& reducer,
std::size_t grainSize = 1,
int numThreads = -1)
{
grainSize = resolveValue("RCPP_PARALLEL_GRAIN_SIZE", grainSize, std::size_t(1));
numThreads = resolveValue("RCPP_PARALLEL_NUM_THREADS", numThreads, -1);
#if RCPP_PARALLEL_USE_TBB
if (internal::backend() == internal::BACKEND_TBB)
tbbParallelReduce(begin, end, reducer, grainSize, numThreads);
else
ttParallelReduce(begin, end, reducer, grainSize);
#else
ttParallelReduce(begin, end, reducer, grainSize);
#endif
}
} // end namespace RcppParallel
// TRUE and FALSE macros that may come with system headers on some systems
// But conflict with R.h (R_ext/Boolean.h)
// TRUE and FALSE macros should be undef in RcppParallel.h
#ifdef TRUE
#undef TRUE
#endif
#ifdef FALSE
#undef FALSE
#endif
#endif // __RCPP_PARALLEL__
================================================
FILE: inst/include/tthread/fast_mutex.h
================================================
/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*-
Copyright (c) 2010-2012 Marcus Geelnard
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef _FAST_MUTEX_H_
#define _FAST_MUTEX_H_
/// @file
// Which platform are we on?
#if !defined(_TTHREAD_PLATFORM_DEFINED_)
#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
#define _TTHREAD_WIN32_
#else
#define _TTHREAD_POSIX_
#endif
#define _TTHREAD_PLATFORM_DEFINED_
#endif
// Check if we can support the assembly language level implementation (otherwise
// revert to the system API)
#if (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) || \
(defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))) || \
(defined(__GNUC__) && (defined(__ppc__)))
#define _FAST_MUTEX_ASM_
#else
#define _FAST_MUTEX_SYS_
#endif
#if defined(_TTHREAD_WIN32_)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#define __UNDEF_LEAN_AND_MEAN
#endif
#include
#ifdef __UNDEF_LEAN_AND_MEAN
#undef WIN32_LEAN_AND_MEAN
#undef __UNDEF_LEAN_AND_MEAN
#endif
#else
#ifdef _FAST_MUTEX_ASM_
#include
#else
#include
#endif
#endif
namespace tthread {
/// Fast mutex class.
/// This is a mutual exclusion object for synchronizing access to shared
/// memory areas for several threads. It is similar to the tthread::mutex class,
/// but instead of using system level functions, it is implemented as an atomic
/// spin lock with very low CPU overhead.
///
/// The \c fast_mutex class is NOT compatible with the \c condition_variable
/// class (however, it IS compatible with the \c lock_guard class). It should
/// also be noted that the \c fast_mutex class typically does not provide
/// as accurate thread scheduling as a the standard \c mutex class does.
///
/// Because of the limitations of the class, it should only be used in
/// situations where the mutex needs to be locked/unlocked very frequently.
///
/// @note The "fast" version of this class relies on inline assembler language,
/// which is currently only supported for 32/64-bit Intel x86/AMD64 and
/// PowerPC architectures on a limited number of compilers (GNU g++ and MS
/// Visual C++).
/// For other architectures/compilers, system functions are used instead.
class fast_mutex {
public:
/// Constructor.
#if defined(_FAST_MUTEX_ASM_)
fast_mutex() : mLock(0) {}
#else
fast_mutex()
{
#if defined(_TTHREAD_WIN32_)
InitializeCriticalSection(&mHandle);
#elif defined(_TTHREAD_POSIX_)
pthread_mutex_init(&mHandle, NULL);
#endif
}
#endif
#if !defined(_FAST_MUTEX_ASM_)
/// Destructor.
~fast_mutex()
{
#if defined(_TTHREAD_WIN32_)
DeleteCriticalSection(&mHandle);
#elif defined(_TTHREAD_POSIX_)
pthread_mutex_destroy(&mHandle);
#endif
}
#endif
/// Lock the mutex.
/// The method will block the calling thread until a lock on the mutex can
/// be obtained. The mutex remains locked until \c unlock() is called.
/// @see lock_guard
inline void lock()
{
#if defined(_FAST_MUTEX_ASM_)
bool gotLock;
do {
gotLock = try_lock();
if(!gotLock)
{
#if defined(_TTHREAD_WIN32_)
Sleep(0);
#elif defined(_TTHREAD_POSIX_)
sched_yield();
#endif
}
} while(!gotLock);
#else
#if defined(_TTHREAD_WIN32_)
EnterCriticalSection(&mHandle);
#elif defined(_TTHREAD_POSIX_)
pthread_mutex_lock(&mHandle);
#endif
#endif
}
/// Try to lock the mutex.
/// The method will try to lock the mutex. If it fails, the function will
/// return immediately (non-blocking).
/// @return \c true if the lock was acquired, or \c false if the lock could
/// not be acquired.
inline bool try_lock()
{
#if defined(_FAST_MUTEX_ASM_)
int oldLock;
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
asm volatile (
"movl $1,%%eax\n\t"
"xchg %%eax,%0\n\t"
"movl %%eax,%1\n\t"
: "=m" (mLock), "=m" (oldLock)
:
: "%eax", "memory"
);
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
int *ptrLock = &mLock;
__asm {
mov eax,1
mov ecx,ptrLock
xchg eax,[ecx]
mov oldLock,eax
}
#elif defined(__GNUC__) && (defined(__ppc__))
int newLock = 1;
asm volatile (
"\n1:\n\t"
"lwarx %0,0,%1\n\t"
"cmpwi 0,%0,0\n\t"
"bne- 2f\n\t"
"stwcx. %2,0,%1\n\t"
"bne- 1b\n\t"
"isync\n"
"2:\n\t"
: "=&r" (oldLock)
: "r" (&mLock), "r" (newLock)
: "cr0", "memory"
);
#endif
return (oldLock == 0);
#else
#if defined(_TTHREAD_WIN32_)
return TryEnterCriticalSection(&mHandle) ? true : false;
#elif defined(_TTHREAD_POSIX_)
return (pthread_mutex_trylock(&mHandle) == 0) ? true : false;
#endif
#endif
}
/// Unlock the mutex.
/// If any threads are waiting for the lock on this mutex, one of them will
/// be unblocked.
inline void unlock()
{
#if defined(_FAST_MUTEX_ASM_)
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
asm volatile (
"movl $0,%%eax\n\t"
"xchg %%eax,%0\n\t"
: "=m" (mLock)
:
: "%eax", "memory"
);
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
int *ptrLock = &mLock;
__asm {
mov eax,0
mov ecx,ptrLock
xchg eax,[ecx]
}
#elif defined(__GNUC__) && (defined(__ppc__))
asm volatile (
"sync\n\t" // Replace with lwsync where possible?
: : : "memory"
);
mLock = 0;
#endif
#else
#if defined(_TTHREAD_WIN32_)
LeaveCriticalSection(&mHandle);
#elif defined(_TTHREAD_POSIX_)
pthread_mutex_unlock(&mHandle);
#endif
#endif
}
private:
#if defined(_FAST_MUTEX_ASM_)
int mLock;
#else
#if defined(_TTHREAD_WIN32_)
CRITICAL_SECTION mHandle;
#elif defined(_TTHREAD_POSIX_)
pthread_mutex_t mHandle;
#endif
#endif
};
}
#endif // _FAST_MUTEX_H_
================================================
FILE: inst/include/tthread/tinythread.h
================================================
/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*-
Copyright (c) 2010-2012 Marcus Geelnard
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef _TINYTHREAD_H_
#define _TINYTHREAD_H_
/// @file
/// @mainpage TinyThread++ API Reference
///
/// @section intro_sec Introduction
/// TinyThread++ is a minimal, portable implementation of basic threading
/// classes for C++.
///
/// They closely mimic the functionality and naming of the C++11 standard, and
/// should be easily replaceable with the corresponding std:: variants.
///
/// @section port_sec Portability
/// The Win32 variant uses the native Win32 API for implementing the thread
/// classes, while for other systems, the POSIX threads API (pthread) is used.
///
/// @section class_sec Classes
/// In order to mimic the threading API of the C++11 standard, subsets of
/// several classes are provided. The fundamental classes are:
/// @li tthread::thread
/// @li tthread::mutex
/// @li tthread::recursive_mutex
/// @li tthread::condition_variable
/// @li tthread::lock_guard
/// @li tthread::fast_mutex
///
/// @section misc_sec Miscellaneous
/// The following special keywords are available: #thread_local.
///
/// For more detailed information (including additional classes), browse the
/// different sections of this documentation. A good place to start is:
/// tinythread.h.
// Which platform are we on?
#if !defined(_TTHREAD_PLATFORM_DEFINED_)
#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
#define _TTHREAD_WIN32_
#else
#define _TTHREAD_POSIX_
#endif
#define _TTHREAD_PLATFORM_DEFINED_
#endif
// Platform specific includes
#if defined(_TTHREAD_WIN32_)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#define __UNDEF_LEAN_AND_MEAN
#endif
#include
#ifdef __UNDEF_LEAN_AND_MEAN
#undef WIN32_LEAN_AND_MEAN
#undef __UNDEF_LEAN_AND_MEAN
#endif
#else
#include
#include
#include
#include
#include
#endif
// Generic includes
#include
/// TinyThread++ version (major number).
#define TINYTHREAD_VERSION_MAJOR 1
/// TinyThread++ version (minor number).
#define TINYTHREAD_VERSION_MINOR 1
/// TinyThread++ version (full version).
#define TINYTHREAD_VERSION (TINYTHREAD_VERSION_MAJOR * 100 + TINYTHREAD_VERSION_MINOR)
// Do we have a fully featured C++11 compiler?
#if (__cplusplus > 199711L) || (defined(__STDCXX_VERSION__) && (__STDCXX_VERSION__ >= 201001L))
#define _TTHREAD_CPP11_
#endif
// ...at least partial C++11?
#if defined(_TTHREAD_CPP11_) || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__)
#define _TTHREAD_CPP11_PARTIAL_
#endif
// Macro for disabling assignments of objects.
#ifdef _TTHREAD_CPP11_PARTIAL_
#define _TTHREAD_DISABLE_ASSIGNMENT(name) \
name(const name&) = delete; \
name& operator=(const name&) = delete;
#else
#define _TTHREAD_DISABLE_ASSIGNMENT(name) \
name(const name&); \
name& operator=(const name&);
#endif
/// @def thread_local
/// Thread local storage keyword.
/// A variable that is declared with the @c thread_local keyword makes the
/// value of the variable local to each thread (known as thread-local storage,
/// or TLS). Example usage:
/// @code
/// // This variable is local to each thread.
/// thread_local int variable;
/// @endcode
/// @note The @c thread_local keyword is a macro that maps to the corresponding
/// compiler directive (e.g. @c __declspec(thread)). While the C++11 standard
/// allows for non-trivial types (e.g. classes with constructors and
/// destructors) to be declared with the @c thread_local keyword, most pre-C++11
/// compilers only allow for trivial types (e.g. @c int). So, to guarantee
/// portable code, only use trivial types for thread local storage.
/// @note This directive is currently not supported on Mac macOS (it will give
/// a compiler error), since compile-time TLS is not supported in the Mac macOS
/// executable format. Also, some older versions of MinGW (before GCC 4.x) do
/// not support this directive.
/// @hideinitializer
#if !defined(_TTHREAD_CPP11_) && !defined(thread_local)
#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
#define thread_local __thread
#else
#define thread_local __declspec(thread)
#endif
#endif
/// Main name space for TinyThread++.
/// This namespace is more or less equivalent to the @c std namespace for the
/// C++11 thread classes. For instance, the tthread::mutex class corresponds to
/// the std::mutex class.
namespace tthread {
/// Mutex class.
/// This is a mutual exclusion object for synchronizing access to shared
/// memory areas for several threads. The mutex is non-recursive (i.e. a
/// program may deadlock if the thread that owns a mutex object calls lock()
/// on that object).
/// @see recursive_mutex
class mutex {
public:
/// Constructor.
mutex()
#if defined(_TTHREAD_WIN32_)
: mAlreadyLocked(false)
#endif
{
#if defined(_TTHREAD_WIN32_)
InitializeCriticalSection(&mHandle);
#else
pthread_mutex_init(&mHandle, NULL);
#endif
}
/// Destructor.
~mutex()
{
#if defined(_TTHREAD_WIN32_)
DeleteCriticalSection(&mHandle);
#else
pthread_mutex_destroy(&mHandle);
#endif
}
/// Lock the mutex.
/// The method will block the calling thread until a lock on the mutex can
/// be obtained. The mutex remains locked until @c unlock() is called.
/// @see lock_guard
inline void lock()
{
#if defined(_TTHREAD_WIN32_)
EnterCriticalSection(&mHandle);
while(mAlreadyLocked) Sleep(1000); // Simulate deadlock...
mAlreadyLocked = true;
#else
pthread_mutex_lock(&mHandle);
#endif
}
/// Try to lock the mutex.
/// The method will try to lock the mutex. If it fails, the function will
/// return immediately (non-blocking).
/// @return @c true if the lock was acquired, or @c false if the lock could
/// not be acquired.
inline bool try_lock()
{
#if defined(_TTHREAD_WIN32_)
bool ret = (TryEnterCriticalSection(&mHandle) ? true : false);
if(ret && mAlreadyLocked)
{
LeaveCriticalSection(&mHandle);
ret = false;
}
return ret;
#else
return (pthread_mutex_trylock(&mHandle) == 0) ? true : false;
#endif
}
/// Unlock the mutex.
/// If any threads are waiting for the lock on this mutex, one of them will
/// be unblocked.
inline void unlock()
{
#if defined(_TTHREAD_WIN32_)
mAlreadyLocked = false;
LeaveCriticalSection(&mHandle);
#else
pthread_mutex_unlock(&mHandle);
#endif
}
_TTHREAD_DISABLE_ASSIGNMENT(mutex)
private:
#if defined(_TTHREAD_WIN32_)
CRITICAL_SECTION mHandle;
bool mAlreadyLocked;
#else
pthread_mutex_t mHandle;
#endif
friend class condition_variable;
};
/// Recursive mutex class.
/// This is a mutual exclusion object for synchronizing access to shared
/// memory areas for several threads. The mutex is recursive (i.e. a thread
/// may lock the mutex several times, as long as it unlocks the mutex the same
/// number of times).
/// @see mutex
class recursive_mutex {
public:
/// Constructor.
recursive_mutex()
{
#if defined(_TTHREAD_WIN32_)
InitializeCriticalSection(&mHandle);
#else
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mHandle, &attr);
#endif
}
/// Destructor.
~recursive_mutex()
{
#if defined(_TTHREAD_WIN32_)
DeleteCriticalSection(&mHandle);
#else
pthread_mutex_destroy(&mHandle);
#endif
}
/// Lock the mutex.
/// The method will block the calling thread until a lock on the mutex can
/// be obtained. The mutex remains locked until @c unlock() is called.
/// @see lock_guard
inline void lock()
{
#if defined(_TTHREAD_WIN32_)
EnterCriticalSection(&mHandle);
#else
pthread_mutex_lock(&mHandle);
#endif
}
/// Try to lock the mutex.
/// The method will try to lock the mutex. If it fails, the function will
/// return immediately (non-blocking).
/// @return @c true if the lock was acquired, or @c false if the lock could
/// not be acquired.
inline bool try_lock()
{
#if defined(_TTHREAD_WIN32_)
return TryEnterCriticalSection(&mHandle) ? true : false;
#else
return (pthread_mutex_trylock(&mHandle) == 0) ? true : false;
#endif
}
/// Unlock the mutex.
/// If any threads are waiting for the lock on this mutex, one of them will
/// be unblocked.
inline void unlock()
{
#if defined(_TTHREAD_WIN32_)
LeaveCriticalSection(&mHandle);
#else
pthread_mutex_unlock(&mHandle);
#endif
}
_TTHREAD_DISABLE_ASSIGNMENT(recursive_mutex)
private:
#if defined(_TTHREAD_WIN32_)
CRITICAL_SECTION mHandle;
#else
pthread_mutex_t mHandle;
#endif
friend class condition_variable;
};
/// Lock guard class.
/// The constructor locks the mutex, and the destructor unlocks the mutex, so
/// the mutex will automatically be unlocked when the lock guard goes out of
/// scope. Example usage:
/// @code
/// mutex m;
/// int counter;
///
/// void increment()
/// {
/// lock_guard guard(m);
/// ++ counter;
/// }
/// @endcode
template
class lock_guard {
public:
typedef T mutex_type;
lock_guard() : mMutex(0) {}
/// The constructor locks the mutex.
explicit lock_guard(mutex_type &aMutex)
{
mMutex = &aMutex;
mMutex->lock();
}
/// The destructor unlocks the mutex.
~lock_guard()
{
if(mMutex)
mMutex->unlock();
}
private:
mutex_type * mMutex;
};
/// Condition variable class.
/// This is a signalling object for synchronizing the execution flow for
/// several threads. Example usage:
/// @code
/// // Shared data and associated mutex and condition variable objects
/// int count;
/// mutex m;
/// condition_variable cond;
///
/// // Wait for the counter to reach a certain number
/// void wait_counter(int targetCount)
/// {
/// lock_guard guard(m);
/// while(count < targetCount)
/// cond.wait(m);
/// }
///
/// // Increment the counter, and notify waiting threads
/// void increment()
/// {
/// lock_guard guard(m);
/// ++ count;
/// cond.notify_all();
/// }
/// @endcode
class condition_variable {
public:
/// Constructor.
#if defined(_TTHREAD_WIN32_)
condition_variable();
#else
condition_variable()
{
pthread_cond_init(&mHandle, NULL);
}
#endif
/// Destructor.
#if defined(_TTHREAD_WIN32_)
~condition_variable();
#else
~condition_variable()
{
pthread_cond_destroy(&mHandle);
}
#endif
/// Wait for the condition.
/// The function will block the calling thread until the condition variable
/// is woken by @c notify_one(), @c notify_all() or a spurious wake up.
/// @param[in] aMutex A mutex that will be unlocked when the wait operation
/// starts, an locked again as soon as the wait operation is finished.
template
inline void wait(_mutexT &aMutex)
{
#if defined(_TTHREAD_WIN32_)
// Increment number of waiters
EnterCriticalSection(&mWaitersCountLock);
++ mWaitersCount;
LeaveCriticalSection(&mWaitersCountLock);
// Release the mutex while waiting for the condition (will decrease
// the number of waiters when done)...
aMutex.unlock();
_wait();
aMutex.lock();
#else
pthread_cond_wait(&mHandle, &aMutex.mHandle);
#endif
}
/// Notify one thread that is waiting for the condition.
/// If at least one thread is blocked waiting for this condition variable,
/// one will be woken up.
/// @note Only threads that started waiting prior to this call will be
/// woken up.
#if defined(_TTHREAD_WIN32_)
void notify_one();
#else
inline void notify_one()
{
pthread_cond_signal(&mHandle);
}
#endif
/// Notify all threads that are waiting for the condition.
/// All threads that are blocked waiting for this condition variable will
/// be woken up.
/// @note Only threads that started waiting prior to this call will be
/// woken up.
#if defined(_TTHREAD_WIN32_)
void notify_all();
#else
inline void notify_all()
{
pthread_cond_broadcast(&mHandle);
}
#endif
_TTHREAD_DISABLE_ASSIGNMENT(condition_variable)
private:
#if defined(_TTHREAD_WIN32_)
void _wait();
HANDLE mEvents[2]; ///< Signal and broadcast event HANDLEs.
unsigned int mWaitersCount; ///< Count of the number of waiters.
CRITICAL_SECTION mWaitersCountLock; ///< Serialize access to mWaitersCount.
#else
pthread_cond_t mHandle;
#endif
};
/// Thread class.
class thread {
public:
#if defined(_TTHREAD_WIN32_)
typedef HANDLE native_handle_type;
#else
typedef pthread_t native_handle_type;
#endif
class id;
/// Default constructor.
/// Construct a @c thread object without an associated thread of execution
/// (i.e. non-joinable).
thread() : mHandle(0), mJoinable(false)
#if defined(_TTHREAD_WIN32_)
, mWin32ThreadID(0)
#endif
{}
/// Thread starting constructor.
/// Construct a @c thread object with a new thread of execution.
/// @param[in] aFunction A function pointer to a function of type:
/// void fun(void * arg)
/// @param[in] aArg Argument to the thread function.
/// @note This constructor is not fully compatible with the standard C++
/// thread class. It is more similar to the pthread_create() (POSIX) and
/// CreateThread() (Windows) functions.
thread(void (*aFunction)(void *), void * aArg);
/// Destructor.
/// @note If the thread is joinable upon destruction, @c std::terminate()
/// will be called, which terminates the process. It is always wise to do
/// @c join() before deleting a thread object.
~thread();
/// Wait for the thread to finish (join execution flows).
/// After calling @c join(), the thread object is no longer associated with
/// a thread of execution (i.e. it is not joinable, and you may not join
/// with it nor detach from it).
void join();
/// Check if the thread is joinable.
/// A thread object is joinable if it has an associated thread of execution.
bool joinable() const;
/// Detach from the thread.
/// After calling @c detach(), the thread object is no longer assicated with
/// a thread of execution (i.e. it is not joinable). The thread continues
/// execution without the calling thread blocking, and when the thread
/// ends execution, any owned resources are released.
void detach();
/// Return the thread ID of a thread object.
id get_id() const;
/// Get the native handle for this thread.
/// @note Under Windows, this is a @c HANDLE, and under POSIX systems, this
/// is a @c pthread_t.
inline native_handle_type native_handle()
{
return mHandle;
}
/// Determine the number of threads which can possibly execute concurrently.
/// This function is useful for determining the optimal number of threads to
/// use for a task.
/// @return The number of hardware thread contexts in the system.
/// @note If this value is not defined, the function returns zero (0).
static unsigned hardware_concurrency();
_TTHREAD_DISABLE_ASSIGNMENT(thread)
private:
native_handle_type mHandle; ///< Thread handle.
mutable mutex mDataMutex; ///< Serializer for access to the thread private data.
bool mJoinable; ///< Is the thread joinable?
#if defined(_TTHREAD_WIN32_)
unsigned int mWin32ThreadID; ///< Unique thread ID (filled out by _beginthreadex).
#endif
// This is the internal thread wrapper function.
#if defined(_TTHREAD_WIN32_)
static unsigned WINAPI wrapper_function(void * aArg);
#else
static void * wrapper_function(void * aArg);
#endif
};
/// Thread ID.
/// The thread ID is a unique identifier for each thread.
/// @see thread::get_id()
class thread::id {
public:
/// Default constructor.
/// The default constructed ID is that of thread without a thread of
/// execution.
id() : mId(0) {};
id(unsigned long int aId) : mId(aId) {};
id(const id& aId) : mId(aId.mId) {};
inline id & operator=(const id &aId)
{
mId = aId.mId;
return *this;
}
inline friend bool operator==(const id &aId1, const id &aId2)
{
return (aId1.mId == aId2.mId);
}
inline friend bool operator!=(const id &aId1, const id &aId2)
{
return (aId1.mId != aId2.mId);
}
inline friend bool operator<=(const id &aId1, const id &aId2)
{
return (aId1.mId <= aId2.mId);
}
inline friend bool operator<(const id &aId1, const id &aId2)
{
return (aId1.mId < aId2.mId);
}
inline friend bool operator>=(const id &aId1, const id &aId2)
{
return (aId1.mId >= aId2.mId);
}
inline friend bool operator>(const id &aId1, const id &aId2)
{
return (aId1.mId > aId2.mId);
}
inline friend std::ostream& operator <<(std::ostream &os, const id &obj)
{
os << obj.mId;
return os;
}
private:
unsigned long int mId;
};
// Related to - minimal to be able to support chrono.
typedef long long __intmax_t;
/// Minimal implementation of the @c ratio class. This class provides enough
/// functionality to implement some basic @c chrono classes.
template <__intmax_t N, __intmax_t D = 1> class ratio {
public:
static double _as_double() { return double(N) / double(D); }
};
/// Minimal implementation of the @c chrono namespace.
/// The @c chrono namespace provides types for specifying time intervals.
namespace chrono {
/// Duration template class. This class provides enough functionality to
/// implement @c this_thread::sleep_for().
template > class duration {
private:
_Rep rep_;
public:
typedef _Rep rep;
typedef _Period period;
/// Construct a duration object with the given duration.
template
explicit duration(const _Rep2& r) : rep_(r) {}
/// Return the value of the duration object.
rep count() const
{
return rep_;
}
};
// Standard duration types.
typedef duration<__intmax_t, ratio<1, 1000000000> > nanoseconds; ///< Duration with the unit nanoseconds.
typedef duration<__intmax_t, ratio<1, 1000000> > microseconds; ///< Duration with the unit microseconds.
typedef duration<__intmax_t, ratio<1, 1000> > milliseconds; ///< Duration with the unit milliseconds.
typedef duration<__intmax_t> seconds; ///< Duration with the unit seconds.
typedef duration<__intmax_t, ratio<60> > minutes; ///< Duration with the unit minutes.
typedef duration<__intmax_t, ratio<3600> > hours; ///< Duration with the unit hours.
}
/// The namespace @c this_thread provides methods for dealing with the
/// calling thread.
namespace this_thread {
/// Return the thread ID of the calling thread.
thread::id get_id();
/// Yield execution to another thread.
/// Offers the operating system the opportunity to schedule another thread
/// that is ready to run on the current processor.
inline void yield()
{
#if defined(_TTHREAD_WIN32_)
Sleep(0);
#else
sched_yield();
#endif
}
/// Blocks the calling thread for a period of time.
/// @param[in] aTime Minimum time to put the thread to sleep.
/// Example usage:
/// @code
/// // Sleep for 100 milliseconds
/// this_thread::sleep_for(chrono::milliseconds(100));
/// @endcode
/// @note Supported duration types are: nanoseconds, microseconds,
/// milliseconds, seconds, minutes and hours.
template void sleep_for(const chrono::duration<_Rep, _Period>& aTime)
{
#if defined(_TTHREAD_WIN32_)
Sleep(int(double(aTime.count()) * (1000.0 * _Period::_as_double()) + 0.5));
#else
usleep(int(double(aTime.count()) * (1000000.0 * _Period::_as_double()) + 0.5));
#endif
}
}
}
// Define/macro cleanup
#undef _TTHREAD_DISABLE_ASSIGNMENT
//////////////////////////////////////////////////////////////////////////
// Inline implementation (ported from tinythread.cpp)
/////////////////////////////////////////////////////////////////////////
#include
#if defined(_TTHREAD_POSIX_)
#include
#include