Import Geant4 10.7.0.beta source tree

This commit is contained in:
Gabriele Cosmo
2020-06-26 10:23:25 +02:00
parent c02c370437
commit 67ba86d073
1871 changed files with 174422 additions and 131884 deletions
+40
View File
@@ -33,3 +33,43 @@ endif()
if(NOT GEANT4_USE_SYSTEM_ZLIB)
add_subdirectory(zlib)
endif()
#----------------------------------------------------------------------------
# Geant4 internal PTL build optional
#
if(NOT GEANT4_USE_SYSTEM_PTL)
set(CMAKE_INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}-${${PROJECT_NAME}_VERSION}
CACHE PATH "CMake install directory" FORCE)
add_subdirectory(ptl)
# Needs to be in defined category list for static/shared to be linked correctly.
set_property(GLOBAL APPEND PROPERTY GEANT4_DEFINED_CATEGORIES G4ptl)
endif()
#----------------------------------------------------------------------------
# Alias the tasking libraries to Geant4 target naming convention
#
if(TARGET ptl-shared)
add_library(G4ptl INTERFACE)
add_library(PTL::ptl-shared ALIAS ptl-shared)
target_link_libraries(G4ptl INTERFACE PTL::ptl-shared)
install(TARGETS G4ptl
EXPORT Geant4LibraryDepends
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Runtime
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
add_library(Geant4::G4ptl ALIAS G4ptl)
endif()
if(TARGET ptl-static)
add_library(G4ptl-static INTERFACE)
add_library(PTL::ptl-static ALIAS ptl-static)
target_link_libraries(G4ptl-static INTERFACE PTL::ptl-static)
install(TARGETS G4ptl-static
EXPORT Geant4LibraryDepends
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Runtime
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Development
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
add_library(Geant4::G4ptl-static ALIAS G4ptl-static)
endif()
+4
View File
@@ -16,6 +16,10 @@ include $(G4INSTALL)/config/architecture.gmk
SUBDIRS :=
SUBLIBS :=
ifdef G4LIB_USE_PTL
SUBDIRS += ptl
SUBLIBS += G4ptl
endif
ifdef G4LIB_USE_CLHEP
SUBDIRS += clhep
SUBLIBS += G4clhep
+12 -1
View File
@@ -16,6 +16,17 @@ committal in the CVS repository !
* Reverse chronological order (last date on top), please *
----------------------------------------------------------
10 June 2020 - J. Madsen (externals-V10-06-02)
- Migrated PTL's used of CMAKE_BINARY_DIR to PROJECT_BINARY_DIR
- Fixed PTLConfig.cmake.in for testing
- Reduced cmake to 3.8 to match Geant4
4 June 2020 - J. Madsen (externals-V10-06-01)
- Fixed ptl installation of interface libraries
4 June 2020 - J. Madsen (externals-V10-06-00)
- Added ptl (Parallel Tasking Library) source code
19 November 2019 - G.Cosmo (externals-V10-05-05)
- CLHEP: Fixed warnings for cases of implicit type conversions in
Rand*Ziggurat classes.
@@ -35,7 +46,7 @@ committal in the CVS repository !
MixMaxRng: throw if seed is zero. Use throw instead of exit() elsewhere.
07 June 2019 - G.Cosmo (externals-V10-05-01)
- CLHEP: defaulted operator=() also for Plan3D; fixing deprecation warnings
- CLHEP: defaulted operator=() also for Plan3D; fixing deprecation warnings
on gcc-9.1.
Fixed shadowing compilation warnings in Transform3D as reported on gcc-9.1.
+5 -1
View File
@@ -106,4 +106,8 @@ GEANT4_DEFINE_MODULE(NAME G4expat
)
# List any source specific properties here
if(GEANT4_USE_NEW_CMAKE)
geant4_module_include_directories(G4expat
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src;${CMAKE_CURRENT_BINARY_DIR}>
)
endif()
+65
View File
@@ -0,0 +1,65 @@
# requires clang-tidy version 6.0+
---
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
AlignEscapedNewlinesLeft: false
AlignOperands: true
AlignTrailingComments: true
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: TopLevel
AlwaysBreakAfterReturnType: TopLevel
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BasedOnStyle: Mozilla
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: false
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: true
ColumnLimit: 90
CompactNamespaces: true
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 0
ContinuationIndentWidth: 4
FixNamespaceComments: true
IndentCaseLabels: true
IndentPPDirectives: AfterHash
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
PointerAlignment: Left
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Never
...
+40
View File
@@ -0,0 +1,40 @@
---
Checks: "-*,\
google-readability-casting,\
misc-*,\
-misc-incorrect-roundings,\
-misc-macro-parentheses,\
-misc-misplaced-widening-cast,\
-misc-static-assert,\
modernize-*,\
-modernize-deprecated-headers,\
-modernize-pass-by-value,\
-modernize-raw-string-literal,\
-modernize-return-braced-init-list,\
-modernize-use-auto,\
-modernize-use-default-member-init,\
-modernize-use-emplace,\
-modernize-use-equals-default,\
-modernize-use-equals-delete,\
-modernize-use-noexcept,\
-modernize-use-transparent-functors,\
-modernize-use-using,\
performance-*,\
-performance-inefficient-string-concatenation,\
readability-*,\
-readability-function-size,\
-readability-identifier-naming,\
-readability-implicit-bool-cast,\
-readability-inconsistent-declaration-parameter-name,\
-readability-named-parameter,\
-readability-redundant-declaration,\
-readability-redundant-member-init,\
-readability-simplify-boolean-expr,\
"
HeaderFilterRegex: 'source/[^/]*\.(hh|cc)$'
CheckOptions:
- key: readability-braces-around-statements.ShortStatementLines
value: '2'
- key: readability-implicit-bool-conversion.AllowPointerConditions
value: '1'
...
+170
View File
@@ -0,0 +1,170 @@
################################################################################
# cmake settings
#
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
# Add allocation export symbol for the PLT module
add_definitions(-DPTL_ALLOC_EXPORT)
cmake_policy(SET CMP0048 NEW)
cmake_policy(SET CMP0042 NEW)
# Check if project is being used directly or via add_subdirectory
set(PTL_MASTER_PROJECT ON)
if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(PTL_MASTER_PROJECT OFF)
endif()
################################################################################
# version
#
file(READ "${CMAKE_CURRENT_LIST_DIR}/VERSION" VERSION_STRING LIMIT_COUNT 1)
string(REGEX REPLACE "(\n|\r)" "" VERSION_STRING "${VERSION_STRING}")
string(REGEX REPLACE "[A-Za-z].*" "" VERSION_STRING "${VERSION_STRING}")
set(PTL_VERSION "${VERSION_STRING}")
################################################################################
# project
#
project(PTL LANGUAGES C CXX VERSION ${PTL_VERSION})
if(NOT PTL_MASTER_PROJECT)
unset(${PROJECT_NAME}_C_FLAGS CACHE)
unset(${PROJECT_NAME}_CXX_FLAGS CACHE)
endif()
set(CMAKE_DIRECTORY_LABELS "PTL")
################################################################################
#
#
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake/Modules ${CMAKE_MODULE_PATH})
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME development)
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
set(CMAKE_BUILD_TYPE Release CACHE STRING "CMake build type" FORCE)
endif()
################################################################################
include(MacroUtilities)
#include(Options)
include(Compilers)
include(GNUInstallDirs)
include(ProjectSettings)
include(BuildSettings)
include(Packages)
include(ClangFormat)
################################################################################
# PTL source
################################################################################
cmake_policy(SET CMP0053 NEW)
################################################################################
# #
# General #
# #
################################################################################
if(PTL_USE_TBB)
list(APPEND ${PROJECT_NAME}_DEFINITIONS PTL_USE_TBB)
endif()
# libraries to install
set(INSTALL_LIBRARIES )
#------------------------------------------------------------------------------#
# Locate sources and headers for this project
# - headers are included so they will show up in IDEs
file(GLOB_RECURSE ptl_headers ${CMAKE_CURRENT_LIST_DIR}/include/PTL/*.hh
${CMAKE_CURRENT_LIST_DIR}/include/PTL/*.icc)
file(GLOB_RECURSE ptl_sources ${CMAKE_CURRENT_LIST_DIR}/src/*.cc)
get_property(PTL_INTERFACE_LIBRARIES GLOBAL PROPERTY ${PROJECT_NAME}_INTERFACE_LIBRARIES)
################################################################################
# #
# PTL Library #
# #
################################################################################
if(BUILD_SHARED_LIBS)
build_library(VERSION
TYPE SHARED
TARGET_NAME ptl-shared
OUTPUT_NAME G4ptl
SOURCES ${ptl_headers} ${ptl_sources}
EXTRA_ARGS POSITION_INDEPENDENT_CODE ON
WINDOWS_EXPORT_ALL_SYMBOLS ON)
target_link_libraries(ptl-shared PUBLIC ptl-external-packages)
target_link_libraries(ptl-shared PRIVATE ptl-external-libraries ptl-compile-options)
target_include_directories(ptl-shared PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
list(APPEND INSTALL_LIBRARIES ptl-shared)
export(TARGETS ptl-shared ${PTL_INTERFACE_LIBRARIES}
FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-shared.cmake)
endif()
if(BUILD_STATIC_LIBS)
build_library(
TYPE STATIC
TARGET_NAME ptl-static
OUTPUT_NAME G4ptl
SOURCES ${ptl_headers} ${ptl_sources}
EXTRA_ARGS POSITION_INDEPENDENT_CODE ON)
target_link_libraries(ptl-static PUBLIC ptl-external-packages)
target_link_libraries(ptl-static PRIVATE ptl-external-libraries ptl-compile-options)
target_include_directories(ptl-static PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
list(APPEND INSTALL_LIBRARIES ptl-static)
export(TARGETS ptl-static ${PTL_INTERFACE_LIBRARIES}
FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-static.cmake)
endif()
################################################################################
# #
# Installation #
# #
################################################################################
# Install the targets and export libraries
install(TARGETS ${INSTALL_LIBRARIES}
DESTINATION ${CMAKE_INSTALL_LIBDIR}
EXPORT ${PROJECT_NAME}Targets
COMPONENT development)
# install export
install(EXPORT ${PROJECT_NAME}Targets
NAMESPACE PTL::
DESTINATION ${CMAKE_INSTALL_CONFIGDIR}
COMPONENT development)
# headers
install(FILES ${ptl_headers}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/PTL
COMPONENT development)
# set
set(PTL_BUILT_LIBRARIES "${INSTALL_LIBRARIES}" CACHE INTERNAL "Built libraries" FORCE)
################################################################################
# Installation and info
################################################################################
include(ConfigurePackage)
if(PTL_MASTER_PROJECT)
print_features()
endif()
+22
View File
@@ -0,0 +1,22 @@
# ------------------------------------------------------------
# GNUmakefile for internal PTL library. Gabriele Cosmo, 18/6/20.
# ------------------------------------------------------------
name := G4ptl
ifndef G4INSTALL
G4INSTALL = ../../..
endif
GLOBLIBS =
include $(G4INSTALL)/config/architecture.gmk
CPPFLAGS += -DPTL_ALLOC_EXPORT
include $(G4INSTALL)/config/common.gmk
.PHONY: global
global: lib
+36
View File
@@ -0,0 +1,36 @@
-------------------------------------------------------------------
=========================================================
Geant4 - an Object-Oriented Toolkit for Simulation in HEP
=========================================================
Category History file
---------------------
This file should be used by G4 developers and category coordinators
to briefly summarize all major modifications introduced in the code
and keep track of all category-tags.
It DOES NOT substitute the log-message one should put at every
committal in the code repository !
----------------------------------------------------------
* Reverse chronological order (last date on top), please *
----------------------------------------------------------
23 June 2020 - J.Madsen (ptl-V10-06-03)
- Tweaked VTaskGroup to use a vector instead of list
- Declared the default ctor/dtor/assign-op for
JoinFunction class in TaskGroup
22 June 2020 - B.Morgan (ptl-V10-06-02)
- Force use of -pthread on non-Win32 platforms
18 June 2020 - G.Cosmo (ptl-V10-06-01)
- Adapted files organisation to Geant4 and fixed porting on Windows DLLs.
- Changed library name to "G4ptl", to distinguish from external installation.
- Integrated with GNUmake system.
8 May 2020 - J.Madsen (ptl-V10-06-00)
- New module for built-in Parallel Tasking Library (PTL), a Lightweight
C++11 multithreading tasking system featuring thread-pool, task-groups,
and lock-free task queue.
Original version 0.0.2.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Jonathan R. Madsen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+2
View File
@@ -0,0 +1,2 @@
# Parallel Tasking Library (PTL)
Lightweight C++11 multithreading tasking system featuring thread-pool, task-groups, and lock-free task queue
+1
View File
@@ -0,0 +1 @@
0.0.2
+47
View File
@@ -0,0 +1,47 @@
################################################################################
#
# Handles the build settings
#
################################################################################
#
set(LIBNAME ptl)
include(GNUInstallDirs)
include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
include(Compilers)
include(MacroUtilities)
ptl_add_interface_library(ptl-compile-options)
ptl_add_interface_library(ptl-external-libraries)
# ---------------------------------------------------------------------------- #
#
#set(SANITIZE_TYPE leak CACHE STRING "-fsantitize=<TYPE>")
# ---------------------------------------------------------------------------- #
# if master project, set the output directory (critical on Windows and Xcode)
#
if("${CMAKE_PROJECT_NAME}" STREQUAL "${PROJECT_NAME}")
set(_BIN_DIR ${PROJECT_BINARY_DIR})
if(WIN32)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${_BIN_DIR}/outputs/runtime)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${_BIN_DIR}/outputs/library)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${_BIN_DIR}/outputs/archive)
else()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${_BIN_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${_BIN_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${_BIN_DIR})
endif()
endif()
# ---------------------------------------------------------------------------- #
# debug macro
#
string(TOUPPER "${CMAKE_BUILD_TYPE}" UC_BUILD_TYPE)
if("${UC_BUILD_TYPE}" STREQUAL "DEBUG")
target_compile_definitions(ptl-compile-options INTERFACE DEBUG)
else()
target_compile_definitions(ptl-compile-options INTERFACE NDEBUG)
endif()
+51
View File
@@ -0,0 +1,51 @@
################################################################################
#
# Creates a 'format' target that runs clang-format
#
################################################################################
find_program(CLANG_FORMATTER
NAMES
clang-format-8.0
clang-format-7.0
clang-format-6.0
clang-format)
if(CLANG_FORMATTER)
set(_Source_DIR ${PROJECT_SOURCE_DIR})
set(_Example_DIR ${PROJECT_SOURCE_DIR}/examples)
set(_Header_DIR ${_Source_DIR}/PTL)
set(_Basic_DIR ${_Example_DIR}/basic)
set(_Common_DIR ${_Example_DIR}/common)
set(_Extended_DIR ${_Example_DIR}/extended/rotation)
set(_Gpu_DIR ${_Example_DIR}/gpu)
file(GLOB headers
${_Header_DIR}/*.hh ${_Header_DIR}/*.icc
${_Basic_DIR}/*.hh ${_Common_DIR}/*.hh
${_Gpu_DIR}/*.h ${_Gpu_DIR}/*.hh
${_Extended_DIR}/*.h ${_Extended_DIR}/*.hh)
file(GLOB sources
${_Source_DIR}/*.cc ${_Extended_DIR}/*.cc
${_Basic_DIR}/*.cc ${_Common_DIR}/*.cc
${_Gpu_DIR}/*.cc ${_Gpu_DIR}/*.cu
${_Extended_DIR}/*.cc)
file(GLOB_RECURSE extended
${_Extended_DIR}/*.hh ${_Extended_DIR}/*.cc)
# avoid conflicting format targets
set(FORMAT_NAME format)
if(TARGET format)
set(FORMAT_NAME format-ptl)
endif()
add_custom_target(${FORMAT_NAME}
COMMAND ${CLANG_FORMATTER} -i ${headers} ${sources} ${extended}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMENT "Running '${CLANG_FORMATTER}' on '${_Source_DIR}' and '${_Example_DIR}..."
SOURCES ${headers} ${sources})
endif()
+396
View File
@@ -0,0 +1,396 @@
################################################################################
#
# Compilers
#
################################################################################
#
# sets (cached):
#
# CMAKE_C_COMPILER_IS_<TYPE>
# CMAKE_CXX_COMPILER_IS_<TYPE>
#
# where TYPE is:
# - GNU
# - CLANG
# - INTEL
# - INTEL_ICC
# - INTEL_ICPC
# - PGI
# - XLC
# - HP_ACC
# - MIPS
# - MSVC
#
include(CheckLanguage)
include(CheckCCompilerFlag)
include(CheckCSourceCompiles)
include(CheckCSourceRuns)
include(CheckCXXCompilerFlag)
include(CheckCXXSourceCompiles)
include(CheckCXXSourceRuns)
include(MacroUtilities)
if("${LIBNAME}" STREQUAL "")
string(TOLOWER "${PROJECT_NAME}" LIBNAME)
endif()
ptl_add_interface_library(${LIBNAME}-compile-options)
#----------------------------------------------------------------------------------------#
# macro converting string to list
#----------------------------------------------------------------------------------------#
macro(to_list _VAR _STR)
STRING(REPLACE " " " " ${_VAR} "${_STR}")
STRING(REPLACE " " ";" ${_VAR} "${_STR}")
endmacro(to_list _VAR _STR)
#----------------------------------------------------------------------------------------#
# macro converting string to list
#----------------------------------------------------------------------------------------#
macro(to_string _VAR _STR)
STRING(REPLACE ";" " " ${_VAR} "${_STR}")
endmacro(to_string _VAR _STR)
#----------------------------------------------------------------------------------------#
# Macro to add to string
#----------------------------------------------------------------------------------------#
macro(add _VAR _FLAG)
if(NOT "${_FLAG}" STREQUAL "")
if("${${_VAR}}" STREQUAL "")
set(${_VAR} "${_FLAG}")
else()
set(${_VAR} "${${_VAR}} ${_FLAG}")
endif()
endif()
endmacro()
#----------------------------------------------------------------------------------------#
# macro to remove duplicates from string
#----------------------------------------------------------------------------------------#
macro(set_no_duplicates _VAR)
if(NOT "${ARGN}" STREQUAL "")
set(${_VAR} "${ARGN}")
endif()
# remove the duplicates
if(NOT "${${_VAR}}" STREQUAL "")
# create list of flags
to_list(_VAR_LIST "${${_VAR}}")
list(REMOVE_DUPLICATES _VAR_LIST)
to_string(${_VAR} "${_VAR_LIST}")
endif(NOT "${${_VAR}}" STREQUAL "")
endmacro(set_no_duplicates _VAR)
##########################################################################################
#
# C compiler flags
#
##########################################################################################
#----------------------------------------------------------------------------------------#
# add C flag to target
#----------------------------------------------------------------------------------------#
macro(ADD_TARGET_C_FLAG _TARG)
target_compile_options(${_TARG} INTERFACE $<$<COMPILE_LANGUAGE:C>:${ARGN}>)
endmacro()
#----------------------------------------------------------------------------------------#
# add C flag w/o check
#----------------------------------------------------------------------------------------#
macro(ADD_C_FLAG FLAG)
set(_TARG )
set(_LTARG )
if(NOT "${ARGN}" STREQUAL "")
set(_TARG ${ARGN})
string(TOLOWER "_${ARGN}" _LTARG)
endif()
if(NOT "${FLAG}" STREQUAL "")
if("${_LTARG}" STREQUAL "")
list(APPEND ${PROJECT_NAME}_C_FLAGS "${FLAG}")
list(APPEND ${PROJECT_NAME}_C_COMPILE_OPTIONS "${FLAG}")
add_target_c_flag(${LIBNAME}-compile-options ${FLAG})
else()
add_target_c_flag(${_TARG} ${FLAG})
endif()
endif()
endmacro()
#----------------------------------------------------------------------------------------#
# check C flag
#----------------------------------------------------------------------------------------#
macro(ADD_C_FLAG_IF_AVAIL FLAG)
set(_TARG )
set(_LTARG )
if(NOT "${ARGN}" STREQUAL "")
set(_TARG ${ARGN})
string(TOLOWER "_${ARGN}" _LTARG)
endif()
if(NOT "${FLAG}" STREQUAL "")
string(REGEX REPLACE "^/" "c${_LTARG}_" FLAG_NAME "${FLAG}")
string(REGEX REPLACE "^-" "c${_LTARG}_" FLAG_NAME "${FLAG}")
string(REPLACE "-" "_" FLAG_NAME "${FLAG_NAME}")
string(REPLACE " " "_" FLAG_NAME "${FLAG_NAME}")
string(REPLACE "=" "_" FLAG_NAME "${FLAG_NAME}")
check_c_compiler_flag("${FLAG}" ${FLAG_NAME})
if(${FLAG_NAME})
if("${_LTARG}" STREQUAL "")
list(APPEND ${PROJECT_NAME}_C_FLAGS "${FLAG}")
list(APPEND ${PROJECT_NAME}_C_COMPILE_OPTIONS "${FLAG}")
add_target_c_flag(${LIBNAME}-compile-options ${FLAG})
else()
add_target_c_flag(${_TARG} ${FLAG})
endif()
endif()
endif()
endmacro()
#----------------------------------------------------------------------------------------#
# add C flag to target
#----------------------------------------------------------------------------------------#
macro(ADD_TARGET_C_FLAG_IF_AVAIL _TARG)
foreach(_FLAG ${ARGN})
add_c_flag_if_avail(${_FLAG} ${_TARG})
endforeach()
endmacro()
##########################################################################################
#
# CXX compiler flags
#
##########################################################################################
#----------------------------------------------------------------------------------------#
# add CXX flag to target
#----------------------------------------------------------------------------------------#
macro(ADD_TARGET_CXX_FLAG _TARG)
target_compile_options(${_TARG} INTERFACE $<$<COMPILE_LANGUAGE:CXX>:${ARGN}>)
get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
if(CMAKE_CUDA_COMPILER AND "CUDA" IN_LIST LANGUAGES)
target_compile_options(${_TARG} INTERFACE $<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=${ARGN}>)
endif()
endmacro()
#----------------------------------------------------------------------------------------#
# add CXX flag w/o check
#----------------------------------------------------------------------------------------#
macro(ADD_CXX_FLAG FLAG)
set(_TARG )
set(_LTARG )
if(NOT "${ARGN}" STREQUAL "")
set(_TARG ${ARGN})
string(TOLOWER "_${ARGN}" _LTARG)
endif()
if(NOT "${FLAG}" STREQUAL "")
if("${_LTARG}" STREQUAL "")
list(APPEND ${PROJECT_NAME}_CXX_FLAGS "${FLAG}")
list(APPEND ${PROJECT_NAME}_CXX_COMPILE_OPTIONS "${FLAG}")
add_target_cxx_flag(${LIBNAME}-compile-options ${FLAG})
else()
add_target_cxx_flag(${_TARG} ${FLAG})
endif()
endif()
endmacro()
#----------------------------------------------------------------------------------------#
# check CXX flag
#----------------------------------------------------------------------------------------#
macro(ADD_CXX_FLAG_IF_AVAIL FLAG)
set(_TARG )
set(_LTARG )
if(NOT "${ARGN}" STREQUAL "")
set(_TARG ${ARGN})
string(TOLOWER "_${ARGN}" _LTARG)
endif()
if(NOT "${FLAG}" STREQUAL "")
string(REGEX REPLACE "^/" "cxx${_LTARG}_" FLAG_NAME "${FLAG}")
string(REGEX REPLACE "^-" "cxx${_LTARG}_" FLAG_NAME "${FLAG}")
string(REPLACE "-" "_" FLAG_NAME "${FLAG_NAME}")
string(REPLACE " " "_" FLAG_NAME "${FLAG_NAME}")
string(REPLACE "=" "_" FLAG_NAME "${FLAG_NAME}")
check_cxx_compiler_flag("${FLAG}" ${FLAG_NAME})
if(${FLAG_NAME})
if("${_LTARG}" STREQUAL "")
list(APPEND ${PROJECT_NAME}_CXX_FLAGS "${FLAG}")
list(APPEND ${PROJECT_NAME}_CXX_COMPILE_OPTIONS "${FLAG}")
add_target_cxx_flag(${LIBNAME}-compile-options ${FLAG})
else()
add_target_cxx_flag(${_TARG} ${FLAG})
endif()
endif()
endif()
endmacro()
#----------------------------------------------------------------------------------------#
# add CXX flag to target
#----------------------------------------------------------------------------------------#
macro(ADD_TARGET_CXX_FLAG_IF_AVAIL _TARG)
foreach(_FLAG ${ARGN})
add_cxx_flag_if_avail(${_FLAG} ${_TARG})
endforeach()
endmacro()
##########################################################################################
#
# Common
#
##########################################################################################
#----------------------------------------------------------------------------------------#
# add C and CXX flag w/o checking
#----------------------------------------------------------------------------------------#
macro(ADD_TARGET_FLAG _TARG)
foreach(_ARG ${ARGN})
ADD_TARGET_C_FLAG(${_TARG} ${_ARG})
ADD_TARGET_CXX_FLAG(${_TARG} ${_ARG})
endforeach()
endmacro()
#----------------------------------------------------------------------------------------#
# check C and CXX flag
#----------------------------------------------------------------------------------------#
macro(ADD_FLAG_IF_AVAIL)
foreach(_ARG ${ARGN})
ADD_C_FLAG_IF_AVAIL("${_ARG}")
ADD_CXX_FLAG_IF_AVAIL("${_ARG}")
endforeach()
endmacro()
#----------------------------------------------------------------------------------------#
# check C and CXX flag
#----------------------------------------------------------------------------------------#
macro(ADD_TARGET_FLAG_IF_AVAIL _TARG)
foreach(_ARG ${ARGN})
ADD_TARGET_C_FLAG_IF_AVAIL(${_TARG} ${_ARG})
ADD_TARGET_CXX_FLAG_IF_AVAIL(${_TARG} ${_ARG})
endforeach()
endmacro()
#----------------------------------------------------------------------------------------#
# add to any language
#----------------------------------------------------------------------------------------#
function(ADD_USER_FLAGS _TARGET _LANGUAGE)
set(_FLAGS ${${_LANGUAGE}FLAGS} $ENV{${_LANGUAGE}FLAGS}
${${_LANGUAGE}_FLAGS} $ENV{${_LANGUAGE}_FLAGS})
string(REPLACE " " ";" _FLAGS "${_FLAGS}")
set(${PROJECT_NAME}_${_LANGUAGE}_FLAGS
${${PROJECT_NAME}_${_LANGUAGE}_FLAGS} ${_FLAGS} PARENT_SCOPE)
set(${PROJECT_NAME}_${_LANGUAGE}_COMPILE_OPTIONS
${${PROJECT_NAME}_${_LANGUAGE}_COMPILE_OPTIONS} ${_FLAGS} PARENT_SCOPE)
target_compile_options(${_TARGET} INTERFACE
$<$<COMPILE_LANGUAGE:${_LANGUAGE}>:${_FLAGS}>)
endfunction()
#----------------------------------------------------------------------------------------#
# determine compiler types for each language
#----------------------------------------------------------------------------------------#
foreach(LANG C CXX)
macro(SET_COMPILER_VAR VAR _BOOL)
set(CMAKE_${LANG}_COMPILER_IS_${VAR} ${_BOOL})
endmacro()
if(("${LANG}" STREQUAL "C" AND CMAKE_COMPILER_IS_GNUCC)
OR
("${LANG}" STREQUAL "CXX" AND CMAKE_COMPILER_IS_GNUCXX))
# GNU compiler
SET_COMPILER_VAR( GNU ON)
elseif(CMAKE_${LANG}_COMPILER MATCHES "icc.*")
# Intel icc compiler
SET_COMPILER_VAR( INTEL ON)
SET_COMPILER_VAR( INTEL_ICC ON)
elseif(CMAKE_${LANG}_COMPILER MATCHES "icpc.*")
# Intel icpc compiler
SET_COMPILER_VAR( INTEL ON)
SET_COMPILER_VAR( INTEL_ICPC ON)
elseif(CMAKE_${LANG}_COMPILER_ID MATCHES "Clang" OR
CMAKE_${LANG}_COMPILER_ID MATCHES "AppleClang")
# Clang/LLVM compiler
SET_COMPILER_VAR( CLANG ON)
elseif(CMAKE_${LANG}_COMPILER_ID MATCHES "PGI")
# PGI compiler
SET_COMPILER_VAR( PGI ON)
elseif(CMAKE_${LANG}_COMPILER MATCHES "xlC" AND UNIX)
# IBM xlC compiler
SET_COMPILER_VAR( XLC ON)
elseif(CMAKE_${LANG}_COMPILER MATCHES "aCC" AND UNIX)
# HP aC++ compiler
SET_COMPILER_VAR( HP_ACC ON)
elseif(CMAKE_${LANG}_COMPILER MATCHES "CC" AND
CMAKE_SYSTEM_NAME MATCHES "IRIX" AND UNIX)
# IRIX MIPSpro CC Compiler
SET_COMPILER_VAR( MIPS ON)
elseif(CMAKE_${LANG}_COMPILER_ID MATCHES "Intel")
SET_COMPILER_VAR( INTEL ON)
set(CTYPE ICC)
if("${LANG}" STREQUAL "CXX")
set(CTYPE ICPC)
endif()
SET_COMPILER_VAR( INTEL_${CTYPE} ON)
elseif(CMAKE_${LANG}_COMPILER MATCHES "MSVC")
# Windows Visual Studio compiler
SET_COMPILER_VAR( MSVC ON)
endif()
# set other to no
foreach(TYPE GNU INTEL INTEL_ICC INTEL_ICPC CLANG PGI XLC HP_ACC MIPS MSVC)
if(NOT ${CMAKE_${LANG}_COMPILER_IS_${TYPE}})
SET_COMPILER_VAR(${TYPE} OFF)
endif()
endforeach()
if(APPLE OR ("${CMAKE_INCLUDE_SYSTEM_FLAG_${LANG}}" STREQUAL "-I" AND NOT WIN32))
# set(CMAKE_INCLUDE_SYSTEM_FLAG_${LANG} "-isystem ")
endif()
endforeach()
@@ -0,0 +1,43 @@
################################################################################
#
# PTL Package installation
#
################################################################################
include(CMakePackageConfigHelpers)
set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
set(LIB_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR})
configure_package_config_file(
${PROJECT_SOURCE_DIR}/cmake/Templates/${PROJECT_NAME}Config.cmake.in
${PROJECT_BINARY_DIR}/installation/${PROJECT_NAME}Config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_CONFIGDIR}
INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}
PATH_VARS
INCLUDE_INSTALL_DIR
LIB_INSTALL_DIR)
write_basic_package_version_file(
${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion)
install(FILES ${PROJECT_BINARY_DIR}/installation/${PROJECT_NAME}Config.cmake
${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_CONFIGDIR})
install(FILES ${PROJECT_SOURCE_DIR}/cmake/Modules/FindTBB.cmake
DESTINATION ${CMAKE_INSTALL_CONFIGDIR}/Modules)
set(BUILD_TREE ON)
configure_package_config_file(
${PROJECT_SOURCE_DIR}/cmake/Templates/${PROJECT_NAME}Config.cmake.in
${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
INSTALL_DESTINATION ${PROJECT_BINARY_DIR}
INSTALL_PREFIX ${PROJECT_BINARY_DIR}
PATH_VARS
INCLUDE_INSTALL_DIR
LIB_INSTALL_DIR)
+44
View File
@@ -0,0 +1,44 @@
#------------------------------------------------------------------------------#
# CUDA function
#
function(NVCUDA_COMPILE_PTX)
set(options "")
set(oneValueArgs TARGET_PATH GENERATED_FILES OUTPUT_DIR)
set(multiValueArgs NVCC_OPTIONS SOURCES)
cmake_parse_arguments(NVCUDA_COMPILE_PTX "${options}" "${oneValueArgs}"
"${multiValueArgs}" ${ARGN})
# Match the bitness of the ptx to the bitness of the application
set( MACHINE "--machine=32" )
if( CMAKE_SIZEOF_VOID_P EQUAL 8)
set( MACHINE "--machine=64" )
endif()
set(_OUTPUT_DIR ${NVCUDA_COMPILE_PTX_OUTPUT_DIR})
if("${_OUTPUT_DIR}" STREQUAL "")
set(_OUTPUT_DIR ${CUDA_GENERATED_OUTPUT_DIR})
endif()
# Custom build rule to generate ptx files from cuda files
foreach(input ${NVCUDA_COMPILE_PTX_SOURCES})
get_filename_component(input_we ${input} NAME_WE)
# generate the *.ptx files inside "ptx" folder inside
# the executable's output directory.
set( output "${_OUTPUT_DIR}/${input_we}.ptx" )
list(APPEND PTX_FILES ${output})
add_custom_command(
OUTPUT ${output}
DEPENDS ${input}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMAND ${CUDA_NVCC_EXECUTABLE} ${MACHINE}
--ptx ${NVCUDA_COMPILE_PTX_NVCC_OPTIONS} ${input}
-o ${output}
)
endforeach()
set(${NVCUDA_COMPILE_PTX_GENERATED_FILES} ${PTX_FILES} PARENT_SCOPE)
endfunction()
+227
View File
@@ -0,0 +1,227 @@
#
# Create a "make doc" target using Doxygen
# Prototype:
# GENERATE_DOCUMENTATION(doxygen_config_file)
# Parameters:
# doxygen_config_file Doxygen configuration file (must in the
# root of the source directory)
include(MacroUtilities)
include(CMakeDependentOption)
#------------------------------------------------------------------------------#
# if BUILD_DOXYGEN_DOCS = ON, we want to build docs quietly
# else, don't build quietly
add_option(${PROJECT_NAME}_DOCS_QUIET "Suppress standard output when making the docs" ON)
mark_as_advanced(${PROJECT_NAME}_DOCS_QUIET)
if(${PROJECT_NAME}_DOCS_QUIET)
set(DOXYGEN_QUIET YES)
else()
set(DOXYGEN_QUIET NO)
endif()
# GraphViz dot program is used to build call graphs, caller graphs, class graphs
find_program(GRAPHVIZ_DOT_PATH dot)
mark_as_advanced(GRAPHVIZ_DOT_PATH)
if("${GRAPHVIZ_DOT_PATH}" STREQUAL "GRAPHVIZ_DOT_PATH-NOTFOUND")
set(DOXYGEN_DOT_FOUND NO)
set(GRAPHVIZ_DOT_PATH "")
else()
set(DOXYGEN_DOT_FOUND YES)
set(GRAPHVIZ_DOT_PATH ${GRAPHVIZ_DOT_PATH})
endif()
# available Doxygen doc formats
set(AVAILABLE_DOXYGEN_DOC_FORMATS HTML LATEX MAN XML RTF)
# we want HTML, LATEX, and MAN to be default
set(_default_on "MAN")
foreach(_doc_format ${AVAILABLE_DOXYGEN_DOC_FORMATS})
# find if we want it on
STRING(REGEX MATCH "${_doc_format}" SET_TO_ON "${_default_on}")
# if doc format is MAN and it is not a UNIX machine --> turn off
if("${_doc_format}" STREQUAL "MAN" AND NOT UNIX)
set(SET_TO_ON "")
endif()
# set ON/OFF
if("${SET_TO_ON}" STREQUAL "")
set(_default "OFF")
else()
set(_default "ON")
endif()
# add option
add_option(${PROJECT_NAME}_${_doc_format}_DOCS
"Build documentation with ${_doc_format} format" ${_default})
mark_as_advanced(${PROJECT_NAME}_${_doc_format}_DOCS)
endforeach()
# loop over doc formats and set GENERATE_DOXYGEN_${_doc_format}_DOC to
# YES/NO GENERATE_DOXYGEN_${_doc_format}_DOC is used in configure_file @ONLY
foreach(_doc_format ${AVAILABLE_DOXYGEN_DOC_FORMATS})
if(${PROJECT_NAME}_${_doc_format}_DOCS)
set(GENERATE_DOXYGEN_${_doc_format}_DOC YES)
else()
set(GENERATE_DOXYGEN_${_doc_format}_DOC NO)
endif()
endforeach()
if(DOXYGEN_DOT_FOUND)
set(DOXYGEN_DOT_GRAPH_TYPES CLASS CALL CALLER)
# options to turn generation of class, call, and caller graphs
foreach(_graph_type ${DOXYGEN_DOT_GRAPH_TYPES})
# create CMake doc string
string(TOLOWER _graph_type_desc ${_graph_type})
# add option
option(${PROJECT_NAME}_${_graph_type}_GRAPH "${_message}" ON)
mark_as_advanced(${PROJECT_NAME}_${_graph_type}_GRAPH)
# set GENERATE_DOXYGEN_${_graph_type}_GRAPH to YES/NO
# GENERATE_DOXYGEN_${_graph_type}_GRAPH is used in configure_file
# @ONLY
if(${PROJECT_NAME}_${_graph_type}_GRAPH)
set(GENERATE_DOXYGEN_${_graph_type}_GRAPH YES)
else()
set(GENERATE_DOXYGEN_${_graph_type}_GRAPH NO)
endif()
endforeach()
endif()
# get the documentation include directories
get_property(BUILDTREE_DIRS GLOBAL PROPERTY PTL_DOCUMENTATION_DIRS)
if("${BUILDTREE_DIRS}" STREQUAL "")
message(FATAL_ERROR "Property PTL_DOCUMENTATION_DIRS is empty")
endif()
LIST(REMOVE_DUPLICATES BUILDTREE_DIRS)
set(SOURCE_FILES)
set(EXTENSIONS h hh hpp c cc cpp icc tcc py)
foreach(_DIR ${BUILDTREE_DIRS})
foreach(_EXT ${EXTENSIONS})
file(GLOB _FILES "${_DIR}/*.${_EXT}")
if(_FILES)
list(APPEND SOURCE_FILES ${_FILES})
endif()
unset(_FILES CACHE)
endforeach()
endforeach()
#message(STATUS "source files: ${SOURCE_FILES}")
# Doxyfiles was spaces not semi-colon separated lists
STRING(REPLACE ";" " " BUILDTREE_DIRS "${BUILDTREE_DIRS}")
STRING(REPLACE ";" " " SOURCE_FILES "${SOURCE_FILES}")
#-----------------------------------------------------------------------
if(XCODE)
set(GENERATE_DOCSET_IF_XCODE YES)
else()
set(GENERATE_DOCSET_IF_XCODE NO)
endif()
#-----------------------------------------------------------------------
configure_file(${PROJECT_SOURCE_DIR}/cmake/Templates/Doxyfile.in
${PROJECT_BINARY_DIR}/doc/Doxyfile.${PROJECT_NAME}
@ONLY)
if(${PROJECT_NAME}_HTML_DOCS)
FILE(WRITE ${PROJECT_BINARY_DIR}/doc/${PROJECT_NAME}_Documentation.html
"<meta http-equiv=\"refresh\" content=\"1;url=html/index.html\">")
endif()
#------------------------------------------------------------------------------#
# Macro to generate documentation
# from:
# http://www.cmake.org/pipermail/cmake/2007-May/014174.html
MACRO(GENERATE_DOCUMENTATION DOXYGEN_CONFIG_FILE)
FIND_PACKAGE(Doxygen)
if(NOT Doxygen_FOUND)
message(STATUS "Doxygen executable cannot be found. Disable ${PROJECT_NAME}_DOXYGEN_DOCS")
return()
endif()
SET(DOXYFILE_FOUND false)
IF(EXISTS ${PROJECT_BINARY_DIR}/doc/${DOXYGEN_CONFIG_FILE})
SET(DOXYFILE_FOUND true)
ELSE(EXISTS ${PROJECT_BINARY_DIR}/doc/${DOXYGEN_CONFIG_FILE})
MESSAGE(STATUS "Doxygen config file was not found at ${PROJECT_BINARY_DIR}/doc/${DOXYGEN_CONFIG_FILE}")
ENDIF(EXISTS ${PROJECT_BINARY_DIR}/doc/${DOXYGEN_CONFIG_FILE})
IF( DOXYGEN_FOUND )
IF( DOXYFILE_FOUND )
# Add target
ADD_CUSTOM_TARGET(docs ${DOXYGEN_EXECUTABLE}
"${PROJECT_BINARY_DIR}/doc/${DOXYGEN_CONFIG_FILE}" )
install(DIRECTORY ${PROJECT_BINARY_DIR}/doc/man/
DESTINATION ${CMAKE_INSTALL_MANDIR}
OPTIONAL
COMPONENT documentation
)
install(DIRECTORY ${PROJECT_BINARY_DIR}/doc/html
DESTINATION ${CMAKE_INSTALL_DOCDIR}
OPTIONAL
COMPONENT documentation
)
install(DIRECTORY ${PROJECT_BINARY_DIR}/doc/latex
DESTINATION ${CMAKE_INSTALL_DOCDIR}
OPTIONAL
COMPONENT documentation
)
install(FILES ${PROJECT_BINARY_DIR}/doc/${PROJECT_NAME}_Documentation.html
DESTINATION ${CMAKE_INSTALL_DOCDIR}
OPTIONAL
COMPONENT documentation
)
ELSE( DOXYFILE_FOUND )
MESSAGE( STATUS "Doxygen configuration file not found - Documentation will not be generated" )
ENDIF( DOXYFILE_FOUND )
ELSE(DOXYGEN_FOUND)
MESSAGE(STATUS "Doxygen not found - Documentation will not be generated")
ENDIF(DOXYGEN_FOUND)
ENDMACRO(GENERATE_DOCUMENTATION)
#------------------------------------------------------------------------------#
# Macro to generate PDF manual from LaTeX using pdflatex
# assumes manual is in ${CMAKE_SOURCE_DIR}/doc
MACRO(GENERATE_MANUAL MANUAL_TEX MANUAL_BUILD_PATH EXTRA_FILES_TO_COPY)
find_program(PDFLATEX pdflatex)
if(PDFLATEX AND NOT "${PDFLATEX}" STREQUAL "PDFLATEX-NOTFOUND")
# name with no path is given
set(MANUAL_NAME ${MANUAL_TEX})
# set to full path
set(MANUAL_BUILD_PATH ${CMAKE_BINARY_DIR}/${MANUAL_BUILD_PATH})
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/doc/${MANUAL_TEX})
message(FATAL_ERROR "LaTeX of manual for ${PROJECT_NAME} is not in ${CMAKE_SOURCE_DIR}/doc")
endif()
configure_file(${CMAKE_SOURCE_DIR}/doc/${MANUAL_TEX}
${MANUAL_BUILD_PATH}/${MANUAL_NAME}
COPYONLY)
foreach(_file ${EXTRA_FILES_TO_COPY})
configure_file(${CMAKE_SOURCE_DIR}/doc/${_file}
${MANUAL_BUILD_PATH}/${_file}
COPYONLY)
endforeach()
add_custom_target(man
${PDFLATEX} "${MANUAL_NAME}"
WORKING_DIRECTORY
${MANUAL_BUILD_PATH}
)
endif()
ENDMACRO()
+350
View File
@@ -0,0 +1,350 @@
# Module for locating Intel's Threading Building Blocks (TBB).
#
# Customizable variables:
# TBB_ROOT_DIR
# Specifies TBB's root directory.
#
# Read-only variables:
# TBB_FOUND
# Indicates whether the library has been found.
#
# TBB_INCLUDE_DIRS
# Specifies TBB's include directory.
#
# TBB_LIBRARIES
# Specifies TBB libraries that should be passed to target_link_libararies.
#
# TBB_<COMPONENT>_LIBRARIES
# Specifies the libraries of a specific <COMPONENT>.
#
# TBB_<COMPONENT>_FOUND
# Indicates whether the specified <COMPONENT> was found.
#
#
# Copyright (c) 2012 Sergiu Dotenco
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTTBBLAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
INCLUDE (FindPackageHandleStandardArgs)
#------------------------------------------------------------------------------#
#
# Paths
#
#------------------------------------------------------------------------------#
IF(CMAKE_VERSION VERSION_GREATER 2.8.7)
SET(_TBB_CHECK_COMPONENTS ON)
ELSE()
SET(_TBB_CHECK_COMPONENTS OFF)
ENDIF()
FIND_PATH(TBB_ROOT_DIR
NAMES include/tbb/tbb.h
PATHS ENV TBBROOT
ENV TBB40_INSTALL_DIR
ENV TBB30_INSTALL_DIR
ENV TBB22_INSTALL_DIR
ENV TBB21_INSTALL_DIR
ENV TBB_ROOT_DIR
DOC "TBB root directory")
FIND_PATH(TBB_INCLUDE_DIR
NAMES tbb/tbb.h
HINTS ${TBB_ROOT_DIR}
PATH_SUFFIXES include
DOC "TBB include directory")
#------------------------------------------------------------------------------#
#
# Library suffixes
#
#------------------------------------------------------------------------------#
IF(MSVC11)
SET(_TBB_COMPILER vc11)
ELSEIF(MSVC10)
SET(_TBB_COMPILER vc10)
ELSEIF(MSVC90)
SET(_TBB_COMPILER vc9)
ELSEIF(MSVC80)
SET(_TBB_COMPILER vc8)
ELSEIF(WIN32)
SET(_TBB_COMPILER vc_mt)
ENDIF(MSVC11)
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
SET(_TBB_POSSIBLE_LIB_SUFFIXES lib/intel64/${_TBB_COMPILER})
SET(_TBB_POSSIBLE_BIN_SUFFIXES bin/intel64/${_TBB_COMPILER})
ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8)
SET(_TBB_POSSIBLE_LIB_SUFFIXES lib/ia32/${_TBB_COMPILER})
SET(_TBB_POSSIBLE_BIN_SUFFIXES bin/ia32/${_TBB_COMPILER})
ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8)
LIST(APPEND _TBB_POSSIBLE_LIB_SUFFIXES lib/$ENV{TBB_ARCH_PLATFORM})
IF(UNIX)
FOREACH(_VERSION 4.7 4.4 4.1)
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
LIST(APPEND _TBB_POSSIBLE_LIB_SUFFIXES lib/intel64/gcc${_VERSION})
ELSE()
LIST(APPEND _TBB_POSSIBLE_LIB_SUFFIXES lib/ia32/gcc${_VERSION})
ENDIF()
ENDFOREACH()
ENDIF()
#------------------------------------------------------------------------------#
#
# TBB library
#
#------------------------------------------------------------------------------#
FIND_LIBRARY(TBB_LIBRARY_RELEASE
NAMES tbb
HINTS ${TBB_ROOT_DIR}
PATH_SUFFIXES ${_TBB_POSSIBLE_LIB_SUFFIXES}
DOC "TBB release library")
FIND_LIBRARY(TBB_LIBRARY_DEBUG
NAMES tbb_debug
HINTS ${TBB_ROOT_DIR}
PATH_SUFFIXES ${_TBB_POSSIBLE_LIB_SUFFIXES}
DOC "TBB debug library")
IF(TBB_LIBRARY_RELEASE AND TBB_LIBRARY_DEBUG)
IF(NOT TBB_LIBRARY)
SET(TBB_LIBRARY optimized ${TBB_LIBRARY_RELEASE} debug ${TBB_LIBRARY_DEBUG}
CACHE DOC "TBB library" FORCE)
ENDIF(NOT TBB_LIBRARY)
LIST(APPEND _TBB_ALL_LIBS optimized ${TBB_LIBRARY_RELEASE} debug ${TBB_LIBRARY_DEBUG})
ELSE()
IF(TBB_LIBRARY_DEBUG)
IF(NOT TBB_LIBRARY)
SET(TBB_LIBRARY ${TBB_LIBRARY_DEBUG} CACHE FILEPATH "TBB library" FORCE)
ENDIF(NOT TBB_LIBRARY)
LIST(APPEND _TBB_ALL_LIBS ${TBB_LIBRARY_DEBUG})
ENDIF(TBB_LIBRARY_DEBUG)
IF(TBB_LIBRARY_RELEASE)
IF(NOT TBB_LIBRARY)
SET(TBB_LIBRARY ${TBB_LIBRARY_RELEASE} CACHE FILEPATH "TBB library" FORCE)
ENDIF(NOT TBB_LIBRARY)
LIST(APPEND _TBB_ALL_LIBS ${TBB_LIBRARY_RELEASE})
ENDIF()
ENDIF(TBB_LIBRARY_RELEASE AND TBB_LIBRARY_DEBUG)
#------------------------------------------------------------------------------#
#
# Components
#
#------------------------------------------------------------------------------#
SET(_TBB_POSSIBLE_COMPONENTS preview malloc malloc_proxy)
FOREACH(_TBB_COMPONENT ${TBB_FIND_COMPONENTS})
STRING(REPLACE "_debug" "" _TBB_COMPONENT ${_TBB_COMPONENT})
STRING(TOUPPER ${_TBB_COMPONENT} _TBB_COMPONENT_UPPER)
SET(_TBB_LIBRARY_BASE TBB_${_TBB_COMPONENT_UPPER}_LIBRARY)
IF(${_TBB_COMPONENT} MATCHES "malloc*")
SET(_TBB_LIBRARY_NAME tbb${_TBB_COMPONENT})
ELSE()
SET(_TBB_LIBRARY_NAME tbb_${_TBB_COMPONENT})
ENDIF()
FIND_LIBRARY(${_TBB_LIBRARY_BASE}_RELEASE
NAMES ${_TBB_LIBRARY_NAME}
HINTS ${TBB_ROOT_DIR}
PATH_SUFFIXES ${_TBB_POSSIBLE_LIB_SUFFIXES}
DOC "TBB ${_TBB_COMPONENT} release library")
FIND_LIBRARY(${_TBB_LIBRARY_BASE}_DEBUG
NAMES ${_TBB_LIBRARY_NAME}_debug
HINTS ${TBB_ROOT_DIR}
PATH_SUFFIXES ${_TBB_POSSIBLE_LIB_SUFFIXES}
DOC "TBB ${_TBB_COMPONENT} debug library")
MARK_AS_ADVANCED (${_TBB_LIBRARY_BASE} ${_TBB_LIBRARY_BASE}_DEBUG)
# default assumption
SET(TBB_${_TBB_COMPONENT_UPPER}_FOUND ON)
SET(TBB_${_TBB_COMPONENT}_FOUND ON)
IF(${_TBB_LIBRARY_BASE}_DEBUG AND ${_TBB_LIBRARY_BASE}_RELEASE)
SET(${_TBB_LIBRARY_BASE}
debug ${${_TBB_LIBRARY_BASE}_DEBUG}
optimized ${${_TBB_LIBRARY_BASE}_RELEASE} CACHE STRING
"TBB ${_TBB_COMPONENT} library")
LIST(APPEND _TBB_ALL_LIBS
optimized ${${_TBB_LIBRARY_BASE}_RELEASE}
debug ${${_TBB_LIBRARY_BASE}_DEBUG})
LIST(APPEND TBB_FOUND_COMPONENTS ${_TBB_LIBRARY_BASE} ${_TBB_LIBRARY_BASE}_debug)
SET(TBB_${_TBB_COMPONENT_UPPER}_LIBRARY ${${_TBB_LIBRARY_BASE}_RELEASE})
ELSEIF(${_TBB_LIBRARY_BASE}_DEBUG)
SET(${_TBB_LIBRARY_BASE} ${${_TBB_LIBRARY_BASE}_DEBUG})
LIST(APPEND _TBB_ALL_LIBS ${${_TBB_LIBRARY_BASE}_DEBUG})
LIST(APPEND TBB_FOUND_COMPONENTS ${_TBB_LIBRARY_BASE})
SET(TBB_${_TBB_COMPONENT_UPPER}_LIBRARY ${${_TBB_LIBRARY_BASE}_DEBUG})
ELSEIF(${_TBB_LIBRARY_BASE}_RELEASE)
SET(${_TBB_LIBRARY_BASE} ${${_TBB_LIBRARY_BASE}_RELEASE}
CACHE FILEPATH "TBB ${_TBB_COMPONENT} library")
LIST(APPEND _TBB_ALL_LIBS ${${_TBB_LIBRARY_BASE}_RELEASE})
LIST(APPEND TBB_FOUND_COMPONENTS ${_TBB_LIBRARY_BASE}_debug)
SET(TBB_${_TBB_COMPONENT_UPPER}_LIBRARY ${${_TBB_LIBRARY_BASE}_RELEASE})
ELSE()
# Component missing: record it for a later report
LIST(APPEND _TBB_MISSING_COMPONENTS ${_TBB_COMPONENT})
SET(TBB_${_TBB_COMPONENT}_FOUND OFF)
SET(TBB_${_TBB_COMPONENT_UPPER}_FOUND OFF)
ENDIF()
IF(${_TBB_LIBRARY_BASE})
# setup the TBB_<COMPONENT>_LIBRARIES variable
SET(TBB_${_TBB_COMPONENT_UPPER}_LIBRARIES ${${_TBB_LIBRARY_BASE}})
ELSE()
LIST(APPEND _TBB_MISSING_LIBRARIES ${_TBB_LIBRARY_BASE})
ENDIF()
ENDFOREACH(_TBB_COMPONENT ${TBB_FIND_COMPONENTS})
#------------------------------------------------------------------------------#
#
# Standard variables
#
#------------------------------------------------------------------------------#
IF(_TBB_ALL_LIBS)
SET(TBB_LIBRARIES ${_TBB_ALL_LIBS})
ENDIF()
IF(TBB_INCLUDE_DIR)
SET(TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR})
ENDIF()
IF(DEFINED _TBB_MISSING_COMPONENTS AND _TBB_CHECK_COMPONENTS)
IF(NOT TBB_FIND_QUIETLY)
MESSAGE(STATUS "One or more TBB components were not found:")
# Display missing components indented, each on a separate line
FOREACH(_TBB_MISSING_COMPONENT ${_TBB_MISSING_COMPONENTS})
MESSAGE(STATUS " " ${_TBB_MISSING_COMPONENT})
ENDFOREACH(_TBB_MISSING_COMPONENT ${_TBB_MISSING_COMPONENTS})
ENDIF(NOT TBB_FIND_QUIETLY)
ENDIF(DEFINED _TBB_MISSING_COMPONENTS AND _TBB_CHECK_COMPONENTS)
#------------------------------------------------------------------------------#
#
# Library's version
#
#------------------------------------------------------------------------------#
SET(_TBB_VERSION_HEADER ${TBB_INCLUDE_DIR}/tbb/tbb_stddef.h)
IF(EXISTS ${_TBB_VERSION_HEADER})
FILE(READ ${_TBB_VERSION_HEADER} _TBB_VERSION_CONTENTS)
STRING(REGEX REPLACE ".*#define TBB_VERSION_MAJOR[ \t]+([0-9]+).*" "\\1"
TBB_VERSION_MAJOR "${_TBB_VERSION_CONTENTS}")
STRING(REGEX REPLACE ".*#define TBB_VERSION_MINOR[ \t]+([0-9]+).*" "\\1"
TBB_VERSION_MINOR "${_TBB_VERSION_CONTENTS}")
SET(TBB_VERSION ${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR})
SET(TBB_VERSION_COMPONENTS 2)
ENDIF()
IF(WIN32)
FIND_PROGRAM(LIB_EXECUTABLE NAMES lib
HINTS
"$ENV{VS110COMNTOOLS}/../../VC/bin"
"$ENV{VS100COMNTOOLS}/../../VC/bin"
"$ENV{VS90COMNTOOLS}/../../VC/bin"
"$ENV{VS71COMNTOOLS}/../../VC/bin"
"$ENV{VS80COMNTOOLS}/../../VC/bin"
DOC "Library manager")
MARK_AS_ADVANCED (LIB_EXECUTABLE)
ENDIF()
MACRO(GET_LIB_REQUISITES LIB REQUISITES)
IF(LIB_EXECUTABLE)
GET_FILENAME_COMPONENT(_LIB_PATH ${LIB_EXECUTABLE} PATH)
IF(MSVC)
# Do not redirect the output
UNSET(ENV{VS_UNICODE_OUTPUT})
ENDIF()
EXECUTE_PROCESS(COMMAND ${LIB_EXECUTABLE} /nologo /list ${LIB}
WORKING_DIRECTORY ${_LIB_PATH}/../../Common7/IDE
OUTPUT_VARIABLE _LIB_OUTPUT ERROR_QUIET)
STRING(REPLACE "\n" ";" "${REQUISITES}" "${_LIB_OUTPUT}")
LIST(REMOVE_DUPLICATES ${REQUISITES})
ENDIF()
ENDMACRO()
IF(_TBB_ALL_LIBS)
# collect lib requisites using the lib tool
FOREACH(_TBB_COMPONENT ${_TBB_ALL_LIBS})
GET_LIB_REQUISITES(${_TBB_COMPONENT} _TBB_REQUISITES)
ENDFOREACH(_TBB_COMPONENT)
ENDIF()
IF(NOT TBB_BINARY_DIR)
SET(_TBB_UPDATE_BINARY_DIR ON)
ELSE()
SET(_TBB_UPDATE_BINARY_DIR OFF)
ENDIF()
SET(_TBB_BINARY_DIR_HINTS ${_TBB_POSSIBLE_BIN_SUFFIXES})
IF(_TBB_REQUISITES)
FIND_FILE(TBB_BINARY_DIR NAMES ${_TBB_REQUISITES}
HINTS ${TBB_ROOT_DIR}
PATH_SUFFIXES ${_TBB_BINARY_DIR_HINTS} NO_DEFAULT_PATH)
ENDIF()
IF(TBB_BINARY_DIR AND _TBB_UPDATE_BINARY_DIR)
SET(_TBB_BINARY_DIR ${TBB_BINARY_DIR})
UNSET(TBB_BINARY_DIR CACHE)
IF(_TBB_BINARY_DIR)
GET_FILENAME_COMPONENT(TBB_BINARY_DIR ${_TBB_BINARY_DIR} PATH)
ENDIF()
ENDIF()
SET(TBB_BINARY_DIR ${TBB_BINARY_DIR} CACHE PATH "TBB binary directory")
MARK_AS_ADVANCED(TBB_INCLUDE_DIR TBB_LIBRARY TBB_LIBRARY_RELEASE
TBB_LIBRARY_DEBUG TBB_BINARY_DIR)
IF(NOT _TBB_CHECK_COMPONENTS)
SET(_TBB_FPHSA_ADDITIONAL_ARGS HANDLE_COMPONENTS)
ENDIF()
LIST(APPEND _TBB_FPHSA_ADDITIONAL_ARGS VERSION_VAR TBB_VERSION)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(TBB
REQUIRED_VARS
TBB_ROOT_DIR
TBB_INCLUDE_DIR
TBB_LIBRARY
${_TBB_MISSING_LIBRARIES}
HANDLE_COMPONENTS
${_TBB_FPHSA_ADDITIONAL_ARGS})
+980
View File
@@ -0,0 +1,980 @@
# MacroUtilities - useful macros and functions for generic tasks
#
# CMake Extensions
# ----------------
# macro set_ifnot(<var> <value>)
# If variable var is not set, set its value to that provided
#
# function enum_option(<option>
# VALUES <value1> ... <valueN>
# TYPE <valuetype>
# DOC <docstring>
# [DEFAULT <elem>]
# [CASE_INSENSITIVE])
# Declare a cache variable <option> that can only take values
# listed in VALUES. TYPE may be FILEPATH, PATH or STRING.
# <docstring> should describe that option, and will appear in
# the interactive CMake interfaces. If DEFAULT is provided,
# <elem> will be taken as the zero-indexed element in VALUES
# to which the value of <option> should default to if not
# provided. Otherwise, the default is taken as the first
# entry in VALUES. If CASE_INSENSITIVE is present, then
# checks of the value of <option> against the allowed values
# will ignore the case when performing string comparison.
#
#
# General
# --------------
# function add_feature(<NAME> <DOCSTRING>)
# Add a feature, whose activation is specified by the
# existence of the variable <NAME>, to the list of enabled/disabled
# features, plus a docstring describing the feature
#
# function print_enabled_features()
# Print enabled features plus their docstrings.
#
#
cmake_policy(PUSH)
if(NOT CMAKE_VERSION VERSION_LESS 3.1)
cmake_policy(SET CMP0054 NEW)
endif()
include(CMakeDependentOption)
include(CMakeParseArguments)
#-----------------------------------------------------------------------
# CMAKE EXTENSIONS
#-----------------------------------------------------------------------
# macro set_ifnot(<var> <value>)
# If variable var is not set, set its value to that provided
#
macro(set_ifnot _var _value)
if(NOT DEFINED ${_var})
set(${_var} ${_value} ${ARGN})
endif()
endmacro()
#-----------------------------------------------------------------------
# macro safe_remove_duplicates(<list>)
# ensures remove_duplicates is only called if list has values
#
macro(safe_remove_duplicates _list)
if(NOT "${${_list}}" STREQUAL "")
list(REMOVE_DUPLICATES ${_list})
endif(NOT "${${_list}}" STREQUAL "")
endmacro()
#-----------------------------------------------------------------------
# function - capitalize - make a string capitalized (first letter is capital)
# usage:
# capitalize("SHARED" CShared)
# message(STATUS "-- CShared is \"${CShared}\"")
# $ -- CShared is "Shared"
function(capitalize str var)
# make string lower
string(TOLOWER "${str}" str)
string(SUBSTRING "${str}" 0 1 _first)
string(TOUPPER "${_first}" _first)
string(SUBSTRING "${str}" 1 -1 _remainder)
if(GOOD_CMAKE)
string(CONCAT str "${_first}" "${_remainder}")
else(GOOD_CMAKE)
set(str "${_first}${_remainder}")
endif(GOOD_CMAKE)
set(${var} "${str}" PARENT_SCOPE)
endfunction()
#-----------------------------------------------------------------------
# macro set_ifnot_match(<var> <value>)
# If variable var is not set, set its value to that provided
#
macro(SET_IFNOT_MATCH VAR APPEND)
if(NOT "${APPEND}" STREQUAL "")
STRING(REGEX MATCH "${APPEND}" _MATCH "${${VAR}}")
if(NOT "${_MATCH}" STREQUAL "")
SET(${VAR} "${${VAR}} ${APPEND}")
endif()
endif()
endmacro()
#-----------------------------------------------------------------------
# macro cache_ifnot(<var> <value>)
# If variable var is not set, set its value to that provided and cache it
#
macro(cache_ifnot _var _value _type _doc)
if(NOT ${_var} OR NOT ${CACHE_VARIABLES} MATCHES ${_var})
set(${_var} ${_value} CACHE ${_type} "${_doc}")
endif()
endmacro()
#-----------------------------------------------------------------------
# function enum_option(<option>
# VALUES <value1> ... <valueN>
# TYPE <valuetype>
# DOC <docstring>
# [DEFAULT <elem>]
# [CASE_INSENSITIVE])
# Declare a cache variable <option> that can only take values
# listed in VALUES. TYPE may be FILEPATH, PATH or STRING.
# <docstring> should describe that option, and will appear in
# the interactive CMake interfaces. If DEFAULT is provided,
# <elem> will be taken as the zero-indexed element in VALUES
# to which the value of <option> should default to if not
# provided. Otherwise, the default is taken as the first
# entry in VALUES. If CASE_INSENSITIVE is present, then
# checks of the value of <option> against the allowed values
# will ignore the case when performing string comparison.
#
function(enum_option _var)
set(options CASE_INSENSITIVE)
set(oneValueArgs DOC TYPE DEFAULT)
set(multiValueArgs VALUES)
cmake_parse_arguments(_ENUMOP "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# - Validation as needed arguments
if(NOT _ENUMOP_VALUES)
message(FATAL_ERROR "enum_option must be called with non-empty VALUES\n(Called for enum_option '${_var}')")
endif()
# - Set argument defaults as needed
if(_ENUMOP_CASE_INSENSITIVE)
set(_ci_values )
foreach(_elem ${_ENUMOP_VALUES})
string(TOLOWER "${_elem}" _ci_elem)
list(APPEND _ci_values "${_ci_elem}")
endforeach()
set(_ENUMOP_VALUES ${_ci_values})
endif()
set_ifnot(_ENUMOP_TYPE STRING)
set_ifnot(_ENUMOP_DEFAULT 0)
list(GET _ENUMOP_VALUES ${_ENUMOP_DEFAULT} _default)
if(NOT DEFINED ${_var})
set(${_var} ${_default} CACHE ${_ENUMOP_TYPE} "${_ENUMOP_DOC} (${_ENUMOP_VALUES})")
else()
set(_var_tmp ${${_var}})
if(_ENUMOP_CASE_INSENSITIVE)
string(TOLOWER ${_var_tmp} _var_tmp)
endif()
list(FIND _ENUMOP_VALUES ${_var_tmp} _elem)
if(_elem LESS 0)
message(FATAL_ERROR "Value '${${_var}}' for variable ${_var} is not allowed\nIt must be selected from the set: ${_ENUMOP_VALUES} (DEFAULT: ${_default})\n")
else()
# - convert to lowercase
if(_ENUMOP_CASE_INSENSITIVE)
set(${_var} ${_var_tmp} CACHE ${_ENUMOP_TYPE} "${_ENUMOP_DOC} (${_ENUMOP_VALUES})" FORCE)
endif()
endif()
endif()
endfunction()
#-----------------------------------------------------------------------
# from
# http://www.cmake.org/pipermail/cmake/2008-April/021345.html
#-----------------------------------------------------------------------
#
#
# Adds a file or directory to the FILES_TO_DELETE var so that it is removed
# when "make distclean" is run
#
# Prototype:
# ADD_TO_DISTCLEAN(file)
# Parameters:
# file A file or dir
#
MACRO(ADD_TO_DISTCLEAN TARGET_TO_DELETE)
SET( FILES_TO_DELETE ${FILES_TO_DELETE} ${TARGET_TO_DELETE} )
ENDMACRO(ADD_TO_DISTCLEAN)
#-----------------------------------------------------------------------
# from
# http://www.cmake.org/pipermail/cmake/2008-April/021345.html
#-----------------------------------------------------------------------
#
# Create a "make distclean" target
#
# Prototype:
# GENERATE_DISTCLEAN_TARGET()
# Parameters:
# (none)
#
MACRO(GENERATE_DISTCLEAN_TARGET)
IF( EXISTS ${PROJECT_BINARY_DIR}/distclean_manifest.txt )
FILE( REMOVE ${PROJECT_BINARY_DIR}/distclean_manifest.txt )
ENDIF( EXISTS ${PROJECT_BINARY_DIR}/distclean_manifest.txt )
IF(MSVC)
SET( FILES_TO_DELETE ${FILES_TO_DELETE} ${PROJECT_BINARY_DIR}/*.dir )
ADD_TO_DISTCLEAN( ${PROJECT_BINARY_DIR}/*.vcproj )
ADD_TO_DISTCLEAN( ${PROJECT_BINARY_DIR}/*.vcproj.cmake )
ADD_TO_DISTCLEAN( ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.sln )
ENDIF(MSVC)
ADD_TO_DISTCLEAN( ${PROJECT_BINARY_DIR}/distclean.dir )
ADD_TO_DISTCLEAN( ${PROJECT_BINARY_DIR}/CMakeCache.txt )
ADD_TO_DISTCLEAN( ${PROJECT_BINARY_DIR}/install_manifest.txt )
ADD_TO_DISTCLEAN( ${PROJECT_BINARY_DIR}/CPackConfig.cmake )
ADD_TO_DISTCLEAN( ${PROJECT_BINARY_DIR}/CPackSourceConfig.cmake )
ADD_TO_DISTCLEAN( ${PROJECT_BINARY_DIR}/_CPack_Packages )
ADD_TO_DISTCLEAN( ${PROJECT_BINARY_DIR}/PACKAGE.dir )
ADD_TO_DISTCLEAN( ${PROJECT_BINARY_DIR}/cmake_install.cmake )
# This code does not work yet. I don't know why CPACK_GENERATOR is null.
IF(CPACK_GENERATOR)
FOREACH(gen ${CPACK_GENERATOR})
MESSAGE("Adding file ${PROJECT_BINARY_DIR}/${CPACK_SOURCE_PACKAGE_FILE_NAME}.${gen} to distclean")
ADD_TO_DISTCLEAN(${PROJECT_BINARY_DIR}/${CPACK_SOURCE_PACKAGE_FILE_NAME}.${gen} )
ENDFOREACH(gen)
ELSE(CPACK_GENERATOR)
MESSAGE("CPACK_GENERATOR was not defined (value: ${CPACK_GENERATOR})")
ENDIF(CPACK_GENERATOR)
IF(EXECUTABLE_OUTPUT_PATH)
ADD_TO_DISTCLEAN( ${EXECUTABLE_OUTPUT_PATH} )
ENDIF(EXECUTABLE_OUTPUT_PATH)
IF(LIBRARY_OUTPUT_PATH)
ADD_TO_DISTCLEAN( ${LIBRARY_OUTPUT_PATH} )
ENDIF(LIBRARY_OUTPUT_PATH)
ADD_TO_DISTCLEAN( ${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY} )
ADD_TO_DISTCLEAN( ${PROJECT_BINARY_DIR}/Makefile )
ADD_TO_DISTCLEAN( ${PROJECT_BINARY_DIR}/distclean_manifest.txt )
FOREACH(f ${FILES_TO_DELETE})
FILE(TO_NATIVE_PATH ${f} ff)
FILE(APPEND ${PROJECT_BINARY_DIR}/distclean_manifest.txt ${ff})
FILE(APPEND ${PROJECT_BINARY_DIR}/distclean_manifest.txt "\n")
ENDFOREACH(f)
IF(WIN32)
FILE( TO_NATIVE_PATH ${PROJECT_BINARY_DIR} PROJECT_BINARY_DIR_WIN32 )
ADD_CUSTOM_TARGET(distclean
FOR /F \"tokens=1* delims= \" %%f IN
\(${PROJECT_BINARY_DIR_WIN32}\\distclean_manifest.txt\) DO \(
IF EXIST %%f\\nul \(
rd /q /s %%f
\) ELSE \(
IF EXIST %%f \(
del /q /f %%f
\) ELSE \(
echo Warning: Problem when removing %%f. - Probable causes: File already removed or not enough permissions
\)
\)
\)
)
ELSE(WIN32)
# Unix
ADD_CUSTOM_TARGET(distclean cat
"${PROJECT_BINARY_DIR}/distclean_manifest.txt" | while read f \; do if
\[ -e \"\$\${f}\" \]; then rm -rf \"\$\${f}\" \; else echo \"Warning:
Problem when removing \"\$\${f}\" - Probable causes: File already
removed or not enough permissions\" \; fi\; done COMMENT Cleaning all
generated files...
)
ENDIF(WIN32)
ENDMACRO(GENERATE_DISTCLEAN_TARGET)
#-----------------------------------------------------------------------
# Determine if two paths are the same
#
#-----------------------------------------------------------------------
function(equal_paths VAR PATH1 PATH2)
get_filename_component(PATH1 ${PATH1} ABSOLUTE)
get_filename_component(PATH2 ${PATH2} ABSOLUTE)
if ("${PATH1}" STREQUAL "${PATH2}")
set(${VAR} ON PARENT_SCOPE)
else()
set(${VAR} OFF PARENT_SCOPE)
endif()
endfunction(equal_paths VAR PATH1 PATH2)
#-----------------------------------------------------------------------
# Resolve symbolic links, remove duplicates, and remove system paths
# in CMAKE_PREFIX_PATH
#
#-----------------------------------------------------------------------
function(clean_prefix_path)
set(_prefix_path )
foreach(_path ${CMAKE_PREFIX_PATH})
get_filename_component(_path ${_path} REALPATH)
set(IS_SYS_PATH OFF)
# loop over system path types
foreach(_type PREFIX INCLUDE LIBRARY APPBUNDLE FRAMEWORK PROGRAM)
# loop over system paths
foreach(_syspath ${CMAKE_SYSTEM_${_type}_PATH})
# check if equal
equal_paths(IS_SYS_PATH ${_path} ${_syspath})
# exit loop if equal
if(IS_SYS_PATH)
break()
endif(IS_SYS_PATH)
endforeach(_syspath ${CMAKE_SYSTEM_${_type}_PATH})
# exit loop if equal
if(IS_SYS_PATH)
break()
endif(IS_SYS_PATH)
endforeach(_type PREFIX INCLUDE LIBRARY APPBUNDLE FRAMEWORK PROGRAM)
# if not a system path
if(NOT IS_SYS_PATH)
list(APPEND _prefix_path ${_path})
endif(NOT IS_SYS_PATH)
endforeach()
# remove any duplicates
if(NOT "${_prefix_path}" STREQUAL "")
list(REMOVE_DUPLICATES _prefix_path)
endif()
# force the new prefix path
set(CMAKE_PREFIX_PATH ${_prefix_path} CACHE PATH
"Prefix path for finding packages" FORCE)
endfunction()
#-----------------------------------------------------------------------
# Add a defined ${PACKAGE_NAME}_ROOT variable defined via CMake or in
# environment to the CMAKE_PREFIX_PATH
#
# This macro should NOT be called directly, instead call
# ConfigureRootSearchPath (i.e. with "_" prefix)
#-----------------------------------------------------------------------
function(subConfigureRootSearchPath _package_name _search_other)
mark_as_advanced(PREVIOUS_${_package_name}_ROOT)
# if ROOT not already defined and ENV variable defines it
if(NOT ${_package_name}_ROOT_DIR AND
NOT "$ENV{${_package_name}_ROOT}" STREQUAL "")
cache_ifnot(${_package_name}_ROOT_DIR $ENV{${_package_name}_ROOT}
FILEPATH "ROOT search path for ${_package_name}")
endif()
# if ROOT is still not defined, check upper case and capitalize version
# and return
if(NOT ${_package_name}_ROOT_DIR)
if(_search_other)
string(TOUPPER "${_package_name}" ALT_PACKAGE_NAME)
if("${ALT_PACKAGE_NAME}" STREQUAL "${_package_name}")
capitalize("${_package_name}" ALT_PACKAGE_NAME)
subConfigureRootSearchPath(${ALT_PACKAGE_NAME} OFF)
else()
subConfigureRootSearchPath(${ALT_PACKAGE_NAME} OFF)
endif()
endif()
return()
endif()
# if ROOT is defined and has changed
if(${_package_name}_ROOT_DIR AND PREVIOUS_${_package_name}_ROOT_DIR AND
NOT "${PREVIOUS_${_package_name}_ROOT_DIR}" STREQUAL "${${_package_name}_ROOT_DIR}")
if(NOT "${PREVIOUS_${_package_name}_ROOT_DIR}" STREQUAL "${${_package_name}_ROOT_DIR}")
set(UNCACHE_PACKAGE_VARS ON)
endif()
# make sure it exists and is a directory
if(EXISTS "${${_package_name}_ROOT_DIR}" AND
IS_DIRECTORY "${${_package_name}_ROOT_DIR}")
set(CMAKE_PREFIX_PATH ${${_package_name}_ROOT_DIR} ${CMAKE_PREFIX_PATH}
CACHE PATH "CMake prefix paths" FORCE)
# store previous root to see if it changed
set(PREVIOUS_${_package_name}_ROOT_DIR ${${_package_name}_ROOT_DIR}
CACHE FILEPATH "Previous root search path for ${_package_name}" FORCE)
clean_prefix_path()
else()
message(WARNING "${_package_name}_ROOT_DIR specified an invalid directory")
unset(${_package_name}_ROOT_DIR CACHE)
endif()
# root changed so we want to refind the package
if(UNCACHE_PACKAGE_VARS)
string(TOLOWER "${_package_name}" CAP_PACKAGE_NAME)
string(SUBSTRING "${CAP_PACKAGE_NAME}" 0 1 FIRST)
string(SUBSTRING "${CAP_PACKAGE_NAME}" 1 -1 REST)
string(TOUPPER "${FIRST}" FIRST)
string(CONCAT CAP_PACKAGE_NAME "${FIRST}" "${REST}")
string(TOUPPER "${_package_name}" UPP_PACKAGE_NAME)
string(TOLOWER "${_package_name}" LOW_PACKAGE_NAME)
get_cmake_property(CACHED_VARIABLES CACHE_VARIABLES)
foreach(_name ${_package_name} ${CAP_PACKAGE_NAME}
${UPP_PACKAGE_NAME} ${LOW_PACKAGE_NAME})
# skip maintain
if(NOT MAINTAIN_${_name}_CACHE)
# loop over cached variables
foreach(_var ${CACHED_VARIABLES})
if("${_var}_" MATCHES "${_name}")
if(NOT "${_var}" STREQUAL "${_name}_ROOT_DIR" AND
NOT "${_var}" STREQUAL "PREVIOUS_${_name}_ROOT_DIR" AND
NOT "${_var}" STREQUAL "MAINTAIN_${_name}_CACHE")
unset(${_var} CACHE)
list(REMOVE_ITEM CACHED_VARIABLES "${_var}")
endif()
endif()
endforeach(_var ${CACHE_VARIABLES})
endif()
endforeach()
endif()
else()
if(EXISTS "${${_package_name}_ROOT_DIR}" AND
IS_DIRECTORY "${${_package_name}_ROOT_DIR}")
set(_add ON)
foreach(_path ${CMAKE_PREFIX_PATH})
if("${_path}" STREQUAL "${${_package_name}_ROOT_DIR}")
set(_add OFF)
break()
endif()
endforeach()
if(_add)
set(CMAKE_PREFIX_PATH ${${_package_name}_ROOT_DIR} ${CMAKE_PREFIX_PATH}
CACHE PATH "CMake prefix paths" FORCE)
clean_prefix_path()
endif()
# store previous root to see if it changed
set(PREVIOUS_${_package_name}_ROOT_DIR ${${_package_name}_ROOT_DIR}
CACHE FILEPATH "Previous root search path for ${_package_name}" FORCE)
else()
message(WARNING "${_package_name}_ROOT_DIR specified an invalid directory")
unset(${_package_name}_ROOT_DIR CACHE)
endif()
endif()
endfunction()
#-----------------------------------------------------------------------
# Add a defined ${PACKAGE_NAME}_ROOT_DIR variable defined via CMake or in
# environment to the CMAKE_PREFIX_PATH
#
# Different from _ConfigureRootSearchPath
#-----------------------------------------------------------------------
macro(ConfigureRootSearchPath)
foreach(_package_name ${ARGN})
subConfigureRootSearchPath(${_package_name} ON)
if($ENV{${_package_name}_DIR})
set(${_package_name}_DIR "$ENV{${_package_name}_DIR}" CACHE PATH
"CMake config directory for ${_package_name}")
endif()
endforeach()
endmacro()
#-----------------------------------------------------------------------
# GENERAL
#-----------------------------------------------------------------------
# function add_feature(<NAME> <DOCSTRING>)
# Add a project feature, whose activation is specified by the
# existence of the variable <NAME>, to the list of enabled/disabled
# features, plus a docstring describing the feature
#
FUNCTION(ADD_FEATURE _var _description)
set(EXTRA_DESC "")
foreach(currentArg ${ARGN})
if(NOT "${currentArg}" STREQUAL "${_var}" AND
NOT "${currentArg}" STREQUAL "${_description}")
set(EXTRA_DESC "${EXTA_DESC}${currentArg}")
endif()
endforeach()
set_property(GLOBAL APPEND PROPERTY PROJECT_FEATURES ${_var})
#set(${_var} ${${_var}} CACHE INTERNAL "${_description}${EXTRA_DESC}")
set_property(GLOBAL PROPERTY ${_var}_DESCRIPTION "${_description}${EXTRA_DESC}")
ENDFUNCTION()
#------------------------------------------------------------------------------#
# function add_subfeature(<ROOT_OPTION> <NAME> <DOCSTRING>)
# Add a subfeature, whose activation is specified by the
# existence of the variable <NAME>, to the list of enabled/disabled
# features, plus a docstring describing the feature
#
FUNCTION(ADD_SUBFEATURE _root _var _description)
set(EXTRA_DESC "")
foreach(currentArg ${ARGN})
if(NOT "${currentArg}" STREQUAL "${_var}" AND
NOT "${currentArg}" STREQUAL "${_description}")
set(EXTRA_DESC "${EXTA_DESC}${currentArg}")
endif()
endforeach()
set_property(GLOBAL APPEND PROPERTY ${_root}_FEATURES ${_var})
set_property(GLOBAL PROPERTY ${_root}_${_var}_DESCRIPTION "${_description}${EXTRA_DESC}")
ENDFUNCTION()
#------------------------------------------------------------------------------#
# function add_option(<OPTION_NAME> <DOCSRING> <DEFAULT_SETTING> [NO_FEATURE])
# Add an option and add as a feature if NO_FEATURE is not provided
#
FUNCTION(ADD_OPTION _NAME _MESSAGE _DEFAULT)
SET(__FEATURE ${ARGN})
OPTION(${_NAME} "${_MESSAGE}" ${_DEFAULT})
IF(NOT "${__FEATURE}" STREQUAL "NO_FEATURE")
ADD_FEATURE(${_NAME} "${_MESSAGE}")
ELSE()
MARK_AS_ADVANCED(${_NAME})
ENDIF()
ENDFUNCTION(ADD_OPTION _NAME _MESSAGE _DEFAULT)
#------------------------------------------------------------------------------#
# function add_dependent_option(<OPTION_NAME> <DOCSRING>
# <CONDITION_TRUE_SETTING> <CONDITION>
# <DEFAULT_SETTING> [NO_FEATURE])
# Add an option and add as a feature if NO_FEATURE is not provided
#
FUNCTION(ADD_DEPENDENT_OPTION _NAME _MESSAGE _COND_SETTING _COND _DEFAULT)
SET(_FEATURE ${ARGN})
IF(DEFINED ${_NAME} AND NOT ${_COND})
OPTION(${_NAME} "${_MESSAGE}" ${_DEFAULT})
ELSE(DEFINED ${_NAME} AND NOT ${_COND})
CMAKE_DEPENDENT_OPTION(${_NAME} "${_MESSAGE}" ${_COND_SETTING}
"${_COND}" ${_DEFAULT})
ENDIF(DEFINED ${_NAME} AND NOT ${_COND})
IF(NOT "${_FEATURE}" STREQUAL "NO_FEATURE")
ADD_FEATURE(${_NAME} "${_MESSAGE}")
ELSE()
MARK_AS_ADVANCED(${_NAME})
ENDIF()
ENDFUNCTION(ADD_DEPENDENT_OPTION _NAME _MESSAGE _DEFAULT _COND _COND_SETTING)
#------------------------------------------------------------------------------#
# function print_enabled_features()
# Print enabled features plus their docstrings.
#
function(print_enabled_features)
set(_basemsg "The following features are defined/enabled (+):")
set(_currentFeatureText "${_basemsg}")
get_property(_features GLOBAL PROPERTY PROJECT_FEATURES)
if(NOT "${_features}" STREQUAL "")
list(REMOVE_DUPLICATES _features)
list(SORT _features)
endif()
foreach(_feature ${_features})
if(${_feature})
# add feature to text
set(_currentFeatureText "${_currentFeatureText}\n ${_feature}")
# get description
get_property(_desc GLOBAL PROPERTY ${_feature}_DESCRIPTION)
# print description, if not standard ON/OFF, print what is set to
if(_desc)
if(NOT "${${_feature}}" STREQUAL "ON" AND
NOT "${${_feature}}" STREQUAL "TRUE")
set(_currentFeatureText "${_currentFeatureText}: ${_desc} -- [\"${${_feature}}\"]")
else()
string(REGEX REPLACE "^USE_" "" _feature_tmp "${_feature}")
string(TOLOWER "${_feature_tmp}" _feature_tmp_l)
capitalize("${_feature_tmp}" _feature_tmp_c)
foreach(_var _feature_tmp _feature_tmp_l _feature_tmp_c)
set(_ver "${${${_var}}_VERSION}")
if(NOT "${_ver}" STREQUAL "")
set(_desc "${_desc} -- [found version ${_ver}]")
break()
endif()
unset(_ver)
endforeach(_var _feature_tmp _feature_tmp_l _feature_tmp_c)
set(_currentFeatureText "${_currentFeatureText}: ${_desc}")
endif()
set(_desc NOTFOUND)
endif(_desc)
# check for subfeatures
get_property(_subfeatures GLOBAL PROPERTY ${_feature}_FEATURES)
# remove duplicates and sort if subfeatures exist
if(NOT "${_subfeatures}" STREQUAL "")
list(REMOVE_DUPLICATES _subfeatures)
list(SORT _subfeatures)
endif()
# sort enabled and disabled features into lists
set(_enabled_subfeatures )
set(_disabled_subfeatures )
foreach(_subfeature ${_subfeatures})
if(${_subfeature})
list(APPEND _enabled_subfeatures ${_subfeature})
else()
list(APPEND _disabled_subfeatures ${_subfeature})
endif()
endforeach()
# loop over enabled subfeatures
foreach(_subfeature ${_enabled_subfeatures})
# add subfeature to text
set(_currentFeatureText "${_currentFeatureText}\n + ${_subfeature}")
# get subfeature description
get_property(_subdesc GLOBAL PROPERTY ${_feature}_${_subfeature}_DESCRIPTION)
# print subfeature description. If not standard ON/OFF, print
# what is set to
if(_subdesc)
if(NOT "${${_subfeature}}" STREQUAL "ON" AND
NOT "${${_subfeature}}" STREQUAL "TRUE")
set(_currentFeatureText "${_currentFeatureText}: ${_subdesc} -- [\"${${_subfeature}}\"]")
else()
set(_currentFeatureText "${_currentFeatureText}: ${_subdesc}")
endif()
set(_subdesc NOTFOUND)
endif(_subdesc)
endforeach(_subfeature)
# loop over disabled subfeatures
foreach(_subfeature ${_disabled_subfeatures})
# add subfeature to text
set(_currentFeatureText "${_currentFeatureText}\n - ${_subfeature}")
# get subfeature description
get_property(_subdesc GLOBAL PROPERTY ${_feature}_${_subfeature}_DESCRIPTION)
# print subfeature description.
if(_subdesc)
set(_currentFeatureText "${_currentFeatureText}: ${_subdesc}")
set(_subdesc NOTFOUND)
endif(_subdesc)
endforeach(_subfeature)
endif(${_feature})
endforeach(_feature)
if(NOT "${_currentFeatureText}" STREQUAL "${_basemsg}")
message(STATUS "${_currentFeatureText}\n")
endif()
endfunction()
#------------------------------------------------------------------------------#
# function print_disabled_features()
# Print disabled features plus their docstrings.
#
function(print_disabled_features)
set(_basemsg "The following features are NOT defined/enabled (-):")
set(_currentFeatureText "${_basemsg}")
get_property(_features GLOBAL PROPERTY PROJECT_FEATURES)
if(NOT "${_features}" STREQUAL "")
list(REMOVE_DUPLICATES _features)
list(SORT _features)
endif()
foreach(_feature ${_features})
if(NOT ${_feature})
set(_currentFeatureText "${_currentFeatureText}\n ${_feature}")
get_property(_desc GLOBAL PROPERTY ${_feature}_DESCRIPTION)
if(_desc)
set(_currentFeatureText "${_currentFeatureText}: ${_desc}")
set(_desc NOTFOUND)
endif(_desc)
endif()
endforeach(_feature)
if(NOT "${_currentFeatureText}" STREQUAL "${_basemsg}")
message(STATUS "${_currentFeatureText}\n")
endif()
endfunction()
#------------------------------------------------------------------------------#
# function print_features()
# Print all features plus their docstrings.
#
function(print_features)
message(STATUS "")
print_enabled_features()
print_disabled_features()
endfunction()
#------------------------------------------------------------------------------#
macro(DETERMINE_LIBDIR_DEFAULT VAR)
set(_LIBDIR_DEFAULT "lib")
# Override this default 'lib' with 'lib64' iff:
# - we are on Linux system but NOT cross-compiling
# - we are NOT on debian
# - we are on a 64 bits system
# reason is: amd64 ABI: https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI
# For Debian with multiarch, use 'lib/${CMAKE_LIBRARY_ARCHITECTURE}' if
# CMAKE_LIBRARY_ARCHITECTURE is set (which contains e.g. "i386-linux-gnu"
# and CMAKE_INSTALL_PREFIX is "/usr"
# See http://wiki.debian.org/Multiarch
if(DEFINED _GNUInstallDirs_LAST_CMAKE_INSTALL_PREFIX)
set(__LAST_LIBDIR_DEFAULT "lib")
# __LAST_LIBDIR_DEFAULT is the default value that we compute from
# _GNUInstallDirs_LAST_CMAKE_INSTALL_PREFIX, not a cache entry for
# the value that was last used as the default.
# This value is used to figure out whether the user changed the
# LIBDIR_DEFAULT value manually, or if the value was the
# default one. When CMAKE_INSTALL_PREFIX changes, the value is
# updated to the new default, unless the user explicitly changed it.
endif()
if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU)$"
AND NOT CMAKE_CROSSCOMPILING)
if (EXISTS "/etc/debian_version") # is this a debian system ?
if(CMAKE_LIBRARY_ARCHITECTURE)
if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/?$")
set(_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}")
endif()
if(DEFINED _GNUInstallDirs_LAST_CMAKE_INSTALL_PREFIX
AND "${_GNUInstallDirs_LAST_CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/?$")
set(__LAST_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}")
endif()
endif()
else() # not debian, rely on CMAKE_SIZEOF_VOID_P:
if(NOT DEFINED CMAKE_SIZEOF_VOID_P)
message(AUTHOR_WARNING
"Unable to determine default LIBDIR_DEFAULT directory "
"because no target architecture is known. "
"Please enable at least one language before including GNUInstallDirs.")
else()
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(_LIBDIR_DEFAULT "lib64")
if(DEFINED _GNUInstallDirs_LAST_CMAKE_INSTALL_PREFIX)
set(__LAST_LIBDIR_DEFAULT "lib64")
endif()
endif()
endif()
endif()
endif()
# if assign to another variable
if(NOT "${VAR}" STREQUAL "LIBDIR_DEFAULT")
set(${VAR} "${_LIBDIR_DEFAULT}")
endif(NOT "${VAR}" STREQUAL "LIBDIR_DEFAULT")
# cache the value
if(NOT DEFINED LIBDIR_DEFAULT)
set(LIBDIR_DEFAULT "${_LIBDIR_DEFAULT}" CACHE PATH "Object code libraries (${_LIBDIR_DEFAULT})" FORCE)
elseif(DEFINED __LAST_LIBDIR_DEFAULT
AND "${__LAST_LIBDIR_DEFAULT}" STREQUAL "${LIBDIR_DEFAULT}")
set_property(CACHE LIBDIR_DEFAULT PROPERTY VALUE "${_LIBDIR_DEFAULT}")
endif()
endmacro()
#------------------------------------------------------------------------------#
# macro CACHE_VARIABLES_FOR_REFERENCE(...)
# Provide a list of variables that will be stored in the cache
# as {variable_name}_REF for later reference
# This version does not force the cache to be updated
macro(CACHE_VARIABLES_FOR_REFERENCE)
foreach(_var ${ARGN})
set(${_var}_REF ${${_var}} CACHE STRING
"Cached reference of ${_var} under ${_var}_REF for later comparison")
endforeach()
endmacro()
#------------------------------------------------------------------------------#
# macro CACHE_VARIABLES_FOR_REFERENCE(...)
# Provide a list of variables that will be stored in the cache
# as {variable_name}_REF for later reference
# This version forces the cache to be updated
macro(UPDATE_REFERENCE_CACHE_VARIABLES)
foreach(_var ${ARGN})
set(${_var}_REF ${${_var}} CACHE STRING
"Cached reference of ${_var} under ${_var}_REF for later comparison"
FORCE)
endforeach()
endmacro()
#------------------------------------------------------------------------------#
# macro GET_HOSTNAME(<VAR>)
#
function(GET_HOSTNAME VAR)
find_program(HOSTNAME_CMD hostname)
execute_process(COMMAND ${HOSTNAME_CMD}
OUTPUT_VARIABLE _HOSTNAME
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(${VAR} "${_HOSTNAME}" PARENT_SCOPE)
endfunction(GET_HOSTNAME VAR)
#------------------------------------------------------------------------------#
# macro CHECKOUT_GIT_SUBMODULE()
#
# Run "git submodule update" if a file in a submodule does not exist
#
# ARGS:
# RECURSIVE (option) -- add "--recursive" flag
# RELATIVE_PATH (one value) -- typically the relative path to submodule
# from PROJECT_SOURCE_DIR
# WORKING_DIRECTORY (one value) -- (default: PROJECT_SOURCE_DIR)
# TEST_FILE (one value) -- file to check for (default: CMakeLists.txt)
# ADDITIONAL_CMDS (many value) -- any addition commands to pass
#
macro(CHECKOUT_GIT_SUBMODULE)
# parse args
cmake_parse_arguments(
CHECKOUT
"RECURSIVE"
"RELATIVE_PATH;WORKING_DIRECTORY;TEST_FILE"
"ADDITIONAL_CMDS"
${ARGN})
if(NOT CHECKOUT_WORKING_DIRECTORY)
set(CHECKOUT_WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
endif(NOT CHECKOUT_WORKING_DIRECTORY)
if(NOT CHECKOUT_TEST_FILE)
set(CHECKOUT_TEST_FILE "CMakeLists.txt")
endif(NOT CHECKOUT_TEST_FILE)
set(_DIR "${CHECKOUT_WORKING_DIRECTORY}/${CHECKOUT_RELATIVE_PATH}")
# ensure the (possibly empty) directory exists
if(NOT EXISTS "${_DIR}")
message(FATAL_ERROR "submodule directory does not exist")
endif(NOT EXISTS "${_DIR}")
# if this file exists --> project has been checked out
# if not exists --> not been checked out
set(_TEST_FILE "${_DIR}/${CHECKOUT_TEST_FILE}")
# if the module has not been checked out
if(NOT EXISTS "${_TEST_FILE}")
find_package(Git REQUIRED)
set(_RECURSE )
if(CHECKOUT_RECURSIVE)
set(_RECURSE --recursive)
endif(CHECKOUT_RECURSIVE)
# perform the checkout
execute_process(
COMMAND
${GIT_EXECUTABLE} submodule update --init ${_RECURSE}
${CHECKOUT_ADDITIONAL_CMDS} ${CHECKOUT_RELATIVE_PATH}
WORKING_DIRECTORY
${CHECKOUT_WORKING_DIRECTORY}
RESULT_VARIABLE RET)
# check the return code
if(RET GREATER 0)
set(_CMD "${GIT_EXECUTABLE} submodule update --init ${_RECURSE}
${CHECKOUT_ADDITIONAL_CMDS} ${CHECKOUT_RELATIVE_PATH}")
message(STATUS "macro(CHECKOUT_SUBMODULE) failed.")
message(FATAL_ERROR "Command: \"${_CMD}\"")
endif(RET GREATER 0)
endif(NOT EXISTS "${_TEST_FILE}")
endmacro(CHECKOUT_GIT_SUBMODULE)
#----------------------------------------------------------------------------
# Macro for building library
#
macro(BUILD_LIBRARY)
cmake_parse_arguments(LIB
"VERSION" "TYPE;TARGET_NAME;OUTPUT_NAME;EXTENSION" "LINK_LIBRARIES;SOURCES;EXTRA_ARGS"
${ARGN})
macro(setifnot VAR)
if(NOT ${VAR} OR "${${VAR}}" STREQUAL "")
set(${VAR} ${ARGN})
endif()
endmacro()
setifnot(LIB_EXTENSION ${PYTHON_MODULE_EXTENSION})
setifnot(LIB_OUTPUT_NAME ${LIB_TARGET_NAME})
add_library(${LIB_TARGET_NAME} ${LIB_TYPE} ${LIB_SOURCES})
if(LIB_VERSION)
list(APPEND LIB_EXTRA_ARGS VERSION ${${PROJECT_NAME}_VERSION}
SOVERSION ${${PROJECT_NAME}_VERSION_MAJOR})
endif()
target_link_libraries(${LIB_TARGET_NAME} PUBLIC
${EXTERNAL_LIBRARIES} ${LIB_LINK_LIBRARIES}
PRIVATE ${PRIVATE_EXTERNAL_LIBRARIES})
set_target_properties(${LIB_TARGET_NAME}
PROPERTIES
OUTPUT_NAME ${LIB_OUTPUT_NAME}
LANGUAGE CXX
LINKER_LANGUAGE CXX
POSITION_INDEPENDENT_CODE ON
${LIB_EXTRA_ARGS})
target_compile_definitions(${LIB_TARGET_NAME} PUBLIC
${${PROJECT_NAME}_DEFINITIONS})
target_compile_features(${LIB_TARGET_NAME}
PUBLIC cxx_std_${GEANT4_BUILD_CXXSTD})
get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES)
if("CUDA" IN_LIST languages)
target_compile_options(${LIB_TARGET_NAME} PUBLIC
$<$<COMPILE_LANGUAGE:C>:${${PROJECT_NAME}_C_FLAGS} ${LIB_CFLAGS}>
$<$<COMPILE_LANGUAGE:CXX>:${${PROJECT_NAME}_CXX_FLAGS} ${LIB_CXXFLAGS}>
$<$<COMPILE_LANGUAGE:CUDA>:${${PROJECT_NAME}_CUDA_FLAGS} ${LIB_CUDAFLAGS}>)
else()
target_compile_options(${LIB_TARGET_NAME} PUBLIC
$<$<COMPILE_LANGUAGE:C>:${${PROJECT_NAME}_C_FLAGS} ${LIB_CFLAGS}>
$<$<COMPILE_LANGUAGE:CXX>:${${PROJECT_NAME}_CXX_FLAGS} ${LIB_CXXFLAGS}>)
endif()
list(APPEND INSTALL_LIBRARIES ${TARGET_NAME})
endmacro(BUILD_LIBRARY)
#-----------------------------------------------------------------------
# function add_enabled_interface(<NAME>)
# Mark an interface library as enabled
#
FUNCTION(PTL_ADD_ENABLED_INTERFACE _var)
set_property(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_ENABLED_INTERFACES ${_var})
ENDFUNCTION()
#-----------------------------------------------------------------------
# function add_disabled_interface(<NAME>)
# Mark an interface as disabled
#
FUNCTION(PTL_ADD_DISABLED_INTERFACE _var)
get_property(_DISABLED GLOBAL PROPERTY ${PROJECT_NAME}_DISABLED_INTERFACES)
if(NOT ${_var} IN_LIST _DISABLED)
set_property(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_DISABLED_INTERFACES ${_var})
endif()
ENDFUNCTION()
#----------------------------------------------------------------------------------------#
# macro to add an interface lib
#
FUNCTION(PTL_ADD_INTERFACE_LIBRARY _TARGET)
if(NOT TARGET ${_TARGET})
add_library(${_TARGET} INTERFACE ${ARGN})
set_property(GLOBAL APPEND PROPERTY ${PROJECT_NAME}_INTERFACE_LIBRARIES ${_TARGET})
ptl_add_enabled_interface(${_TARGET})
install(TARGETS ${_TARGET}
DESTINATION ${CMAKE_INSTALL_LIBDIR}
EXPORT ${PROJECT_NAME}Targets
COMPONENT development)
endif()
ENDFUNCTION()
#----------------------------------------------------------------------------------------#
# get the list of interface libraries
#
FUNCTION(PTL_GET_INTERFACE_LIBRARIES _VAR)
get_property(_LIBS GLOBAL PROPERTY ${PROJECT_NAME}_INTERFACE_LIBRARIES)
set(${_VAR} ${_LIBS} PARENT_SCOPE)
ENDFUNCTION()
#------------------------------------------------------------------------------#
# always determine the default lib directory
DETERMINE_LIBDIR_DEFAULT(LIBDIR_DEFAULT)
#------------------------------------------------------------------------------#
# get the hostname
get_hostname(HOSTNAME)
string(REPLACE ".local" "" HOSTNAME "${HOSTNAME}")
cmake_policy(POP)
+107
View File
@@ -0,0 +1,107 @@
################################################################################
#
# PTL Options
#
################################################################################
include(MacroUtilities)
set(_FEATURE )
if(NOT PTL_MASTER_PROJECT)
set(_FEATURE NO_FEATURE)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Require the C++ standard" FORCE)
set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Disable GNU extensions")
# features
add_feature(CMAKE_BUILD_TYPE "Build type (Debug, Release, RelWithDebInfo, MinSizeRel)")
add_feature(CMAKE_INSTALL_PREFIX "Installation prefix")
add_feature(CMAKE_CXX_STANDARD "C++11 STL standard")
add_feature(${PROJECT_NAME}_C_FLAGS "C flags for project")
add_feature(${PROJECT_NAME}_CXX_FLAGS "C++ flags for project")
# options (always available)
add_option(BUILD_STATIC_LIBS "Build static library" ON ${_FEATURE})
add_option(BUILD_SHARED_LIBS "Build shared library" ON ${_FEATURE})
add_option(PTL_BUILD_EXAMPLES "Build examples" OFF ${_FEATURE})
add_option(PTL_BUILD_DOCS "Build documentation with Doxygen" OFF ${_FEATURE})
add_option(PTL_DEVELOPER_INSTALL "Install headers, cmake export, and shared libs" ON ${_FEATURE})
add_option(PTL_USE_TBB "Enable TBB" ON ${_FEATURE})
add_option(PTL_USE_GPU "Enable GPU preprocessor" OFF ${_FEATURE})
add_option(PTL_USE_SANITIZER "Enable -fsanitize=<type>" OFF ${_FEATURE})
add_option(PTL_USE_CLANG_TIDY "Enable running clang-tidy on" OFF ${_FEATURE})
add_option(PTL_USE_COVERAGE "Enable code coverage" OFF ${_FEATURE})
add_option(PTL_USE_PROFILE "Enable profiling" OFF ${_FEATURE})
if(PTL_USE_ARCH)
add_option(PTL_USE_AVX512 "Enable AVX-512 flags (if available)" OFF ${_FEATURE})
endif()
if(PTL_USE_SANITIZER)
add_feature(PTL_SANITIZER_TYPE "Sanitizer type (-fsanitize=<type>)")
set(PTL_SANITIZER_TYPE leak CACHE STRING "Sanitizer type (-fsanitize=<type>)")
endif()
if(PTL_USE_GPU)
# Check if CUDA can be enabled
find_package(CUDA QUIET)
if(CUDA_FOUND)
check_language(CUDA)
if(CMAKE_CUDA_COMPILER)
enable_language(CUDA)
else()
message(STATUS "No CUDA support")
set(_USE_CUDA OFF)
endif()
else()
set(_USE_CUDA OFF)
set(PTL_USE_GPU OFF)
endif()
if(_USE_CUDA)
list(APPEND ${PROJECT_NAME}_DEFINITIONS PTL_USE_GPU)
add_option(PTL_USE_CUDA "Enable CUDA option for GPU execution" ${_USE_CUDA} ${_FEATURE})
add_option(PTL_USE_NVTX "Enable NVTX for Nsight" ${_USE_CUDA} ${_FEATURE})
set(CUDA_ARCH "sm_35" CACHE STRING "CUDA architecture flag")
if(_FEATURE)
add_feature(CUDA_ARCH "CUDA architecture (e.g. sm_35)")
add_feature(CUDA_GENERATED_OUTPUT_DIR "CUDA output directory for generated files")
endif()
endif()
endif()
# RPATH settings
set(_RPATH_LINK OFF)
if(APPLE)
set(_RPATH_LINK ON)
endif()
add_option(CMAKE_INSTALL_RPATH_USE_LINK_PATH "Hardcode installation rpath based on link path" ${_RPATH_LINK} ${_FEATURE})
unset(_RPATH_LINK)
# clang-tidy
if(PTL_USE_CLANG_TIDY)
find_program(CLANG_TIDY_COMMAND NAMES clang-tidy)
add_feature(CLANG_TIDY_COMMAND "Path to clang-tidy command")
if(NOT CLANG_TIDY_COMMAND)
message(WARNING "PTL_USE_CLANG_TIDY is ON but clang-tidy is not found!")
set(PTL_USE_CLANG_TIDY OFF)
else()
set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}")
# Create a preprocessor definition that depends on .clang-tidy content so
# the compile command will change when .clang-tidy changes. This ensures
# that a subsequent build re-runs clang-tidy on all sources even if they
# do not otherwise need to be recompiled. Nothing actually uses this
# definition. We add it to targets on which we run clang-tidy just to
# get the build dependency on the .clang-tidy file.
file(SHA1 ${PROJECT_SOURCE_DIR}/.clang-tidy clang_tidy_sha1)
set(CLANG_TIDY_DEFINITIONS "CLANG_TIDY_SHA1=${clang_tidy_sha1}")
unset(clang_tidy_sha1)
endif()
configure_file(${PROJECT_SOURCE_DIR}/.clang-tidy ${PROJECT_SOURCE_DIR}/.clang-tidy COPYONLY)
endif()
+46
View File
@@ -0,0 +1,46 @@
#
# Find packages
#
include(FindPackageHandleStandardArgs)
include(MacroUtilities)
ptl_add_interface_library(ptl-external-packages)
ptl_add_interface_library(ptl-threads)
ptl_add_interface_library(ptl-tbb)
################################################################################
#
# Threads
#
################################################################################
if(NOT WIN32)
set(CMAKE_THREAD_PREFER_PTHREAD ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)
endif()
find_package(Threads)
if(Threads_FOUND)
target_link_libraries(ptl-threads INTERFACE Threads::Threads)
target_link_libraries(ptl-external-packages INTERFACE ptl-threads)
endif()
################################################################################
#
# TBB
#
################################################################################
if(PTL_USE_TBB)
find_package(TBB)
if(TBB_FOUND)
target_compile_definitions(ptl-tbb INTERFACE PTL_USE_TBB)
target_include_directories(ptl-tbb SYSTEM INTERFACE ${TBB_INCLUDE_DIRS})
target_link_libraries(ptl-tbb INTERFACE ${TBB_LIBRARIES})
target_link_libraries(ptl-external-packages INTERFACE ptl-tbb)
endif()
endif()
@@ -0,0 +1,34 @@
################################################################################
#
# Project settings
#
################################################################################
string(TOUPPER "${CMAKE_BUILD_TYPE}" _CONFIG)
add_feature(CMAKE_C_FLAGS_${_CONFIG} "C compiler build-specific flags")
add_feature(CMAKE_CXX_FLAGS_${_CONFIG} "C++ compiler build-specific flags")
################################################################################
#
# installation directories
#
################################################################################
# cmake installation folder
set(CMAKE_INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME})
# create the full path version and generic path versions
foreach(_TYPE in DATAROOT CMAKE INCLUDE LIB BIN MAN DOC)
# set the absolute versions
if(NOT IS_ABSOLUTE "${CMAKE_INSTALL_${_TYPE}DIR}")
set(CMAKE_INSTALL_FULL_${_TYPE}DIR ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${_TYPE}DIR})
else(NOT IS_ABSOLUTE "${CMAKE_INSTALL_${_TYPE}DIR}")
set(CMAKE_INSTALL_FULL_${_TYPE}DIR ${CMAKE_INSTALL_${_TYPE}DIR})
endif(NOT IS_ABSOLUTE "${CMAKE_INSTALL_${_TYPE}DIR}")
# generic "PROJECT_INSTALL_" variables (used by documentation)"
set(PROJECT_INSTALL_${_TYPE}DIR ${CMAKE_INSTALL_${TYPE}DIR})
set(PROJECT_INSTALL_FULL_${_TYPE}DIR ${CMAKE_INSTALL_FULL_${TYPE}DIR})
endforeach(_TYPE in DATAROOT CMAKE INCLUDE LIB BIN MAN DOC)
File diff suppressed because it is too large Load Diff
+148
View File
@@ -0,0 +1,148 @@
#------------------------------------------------------------------------------#
#
#
cmake_policy(SET CMP0012 NEW)
get_filename_component(PTL_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@ConfigVersion.cmake)
include(CMakeFindDependencyMacro)
set(_PTL_AVAILABLE_COMPONENTS )
if(@BUILD_SHARED_LIBS@)
list(APPEND _PTL_AVAILABLE_COMPONENTS shared)
endif()
if(@BUILD_STATIC_LIBS@)
list(APPEND _PTL_AVAILABLE_COMPONENTS static)
endif()
#------------------------------------------------------------------------------#
# package initialization
#
@PACKAGE_INIT@
#------------------------------------------------------------------------------#
# install or build tree
#
set(_PTL_BUILD_TREE @BUILD_TREE@)
if(NOT _PTL_BUILD_TREE)
set(_PTL_INSTALL_TREE ON)
endif()
#------------------------------------------------------------------------------#
# configure lib and include
#
if(_PTL_INSTALL_TREE)
set_and_check(@PROJECT_NAME@_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
set_and_check(@PROJECT_NAME@_LIB_DIR "@PACKAGE_LIB_INSTALL_DIR@")
include(${PTL_CMAKE_DIR}/@PROJECT_NAME@Targets.cmake)
check_required_components(@PROJECT_NAME@)
# get target
foreach(_COMPONENT ${_PTL_AVAILABLE_COMPONENTS})
# set the library
if(NOT @PROJECT_NAME@_LIBRARY)
set(@PROJECT_NAME@_LIBRARY ${_${_COMPONENT}_LOCATION} CACHE INTERNAL
"@PROJECT_NAME@ library")
endif()
endforeach()
if(@Threads_FOUND@)
set(CMAKE_THREAD_PREFER_PTHREAD @CMAKE_THREAD_PREFER_PTHREAD@)
set(THREADS_PREFER_PTHREAD_FLAG @THREADS_PREFER_PTHREAD_FLAG@)
find_package(Threads REQUIRED)
endif()
if(@TBB_FOUND@)
set(TBB_ROOT_DIR @TBB_ROOT_DIR@)
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/PTL/Modules ${CMAKE_MODULE_PATH})
find_package(TBB @TBB_VERSION@ REQUIRED)
endif()
else()
set(@PROJECT_NAME@_INCLUDE_DIR "@PROJECT_SOURCE_DIR@")
set(@PROJECT_NAME@_LIB_DIR "@PROJECT_BINARY_DIR@")
set(@PROJECT_NAME@_Threads_FOUND @Threads_FOUND@)
set(@PROJECT_NAME@_TBB_FOUND @TBB_FOUND@)
if(NOT Threads_FOUND AND @PROJECT_NAME@_Threads_FOUND)
set(CMAKE_THREAD_PREFER_PTHREAD @CMAKE_THREAD_PREFER_PTHREAD@)
set(THREADS_PREFER_PTHREAD_FLAG @THREADS_PREFER_PTHREAD_FLAG@)
find_package(Threads REQUIRED)
endif()
if(NOT TBB_FOUND AND @PROJECT_NAME@_TBB_FOUND)
set(TBB_ROOT_DIR @TBB_ROOT_DIR@)
set(CMAKE_MODULE_PATH @PROJECT_SOURCE_DIR@/cmake/Modules ${CMAKE_MODULE_PATH})
find_package(TBB @TBB_VERSION@ REQUIRED)
endif()
foreach(_COMPONENT ${_PTL_AVAILABLE_COMPONENTS})
if(NOT TARGET @LIBNAME@-${_COMPONENT})
include("@PROJECT_BINARY_DIR@/@PROJECT_NAME@-${_COMPONENT}.cmake")
endif()
# set the library
if(NOT @PROJECT_NAME@_LIBRARY)
set(@PROJECT_NAME@_LIBRARY @LIBNAME@-${_COMPONENT})
endif()
endforeach()
set(PROJECT_DEPENDS ${@PROJECT_NAME@_LIBRARY})
endif()
#------------------------------------------------------------------------------#
# internal PTL include directory
#
set(@PROJECT_NAME@_INCLUDE_DIRS ${@PROJECT_NAME@_INCLUDE_DIR})
#------------------------------------------------------------------------------#
# CXX standard
#
if(DEFINED CMAKE_CXX_STANDARD)
if("${CMAKE_CXX_STANDARD}" VERSION_LESS 11)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
else(DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD @CMAKE_CXX_STANDARD@)
endif(DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
#------------------------------------------------------------------------------#
# Linking type
#
if(WIN32)
add_definitions(-D_PTL_ARCHIVE)
endif()
#------------------------------------------------------------------------------#
# link target
#
set(@PROJECT_NAME@_LIBRARIES ${@PROJECT_NAME@_LIBRARY})
#------------------------------------------------------------------------------#
# Definitions
#
set(PTL_USE_TBB @PTL_USE_TBB@)
set(PTL_USE_GPU @PTL_USE_GPU@)
set(PTL_USE_CUDA @PTL_USE_CUDA@)
if(NOT _PTL_INSTALL_TREE)
list(APPEND @PROJECT_NAME@_LIBRARIES "@EXTERNAL_LIBRARIES@")
endif()
+491
View File
@@ -0,0 +1,491 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
// ---------------------------------------------------------------
// Tasking class header file
//
/// Class Description:
///
/// This class provides a mechanism to create a mutex and locks/unlocks it.
/// Can be used by applications to implement in a portable way a mutexing logic.
/// Usage Example:
///
/// #include "Threading.hh"
/// #include "AutoLock.hh"
///
/// /// defined somewhere -- static so all threads see the same mutex
/// static Mutex aMutex;
///
/// /// somewhere else:
/// /// The AutoLock instance will automatically unlock the mutex when it
/// /// goes out of scope. One typically defines the scope within { } if
/// /// there is thread-safe code following the auto-lock
///
/// {
/// AutoLock l(&aMutex);
/// ProtectedCode();
/// }
///
/// UnprotectedCode();
///
/// /// When ProtectedCode() is calling a function that also tries to lock
/// /// a normal AutoLock + Mutex will "deadlock". In other words, the
/// /// the mutex in the ProtectedCode() function will wait forever to
/// /// acquire the lock that is being held by the function that called
/// /// ProtectedCode(). In this situation, use a RecursiveAutoLock +
/// /// RecursiveMutex, e.g.
///
/// /// defined somewhere -- static so all threads see the same mutex
/// static RecursiveMutex aRecursiveMutex;
///
/// /// this function is sometimes called directly and sometimes called
/// /// from SomeFunction_B(), which also locks the mutex
/// void SomeFunction_A()
/// {
/// /// when called from SomeFunction_B(), a Mutex + AutoLock will
/// /// deadlock
/// RecursiveAutoLock l(&aRecursiveMutex);
/// /// do something
/// }
///
/// void SomeFunction_B()
/// {
///
/// {
/// RecursiveAutoLock l(&aRecursiveMutex);
/// SomeFunction_A();
/// }
///
/// UnprotectedCode();
/// }
///
///
/// ---------------------------------------------------------------
/// Author: Andrea Dotti (15 Feb 2013): First Implementation
///
/// Update: Jonathan Madsen (9 Feb 2018): Replaced custom implementation
/// with inheritance from C++11 unique_lock, which inherits the
/// following member functions:
///
/// - unique_lock(unique_lock&& other) noexcept;
/// - explicit unique_lock(mutex_type& m);
/// - unique_lock(mutex_type& m, std::defer_lock_t t) noexcept;
/// - unique_lock(mutex_type& m, std::try_to_lock_t t);
/// - unique_lock(mutex_type& m, std::adopt_lock_t t);
///
/// - template <typename Rep, typename Period>
/// unique_lock(mutex_type& m,
/// const std::chrono::duration<Rep,Period>&
/// timeout_duration);
///
/// - template<typename Clock, typename Duration>
/// unique_lock(mutex_type& m,
/// const std::chrono::time_point<Clock,Duration>& timeout_time);
///
/// - void lock();
/// - void unlock();
/// - bool try_lock();
///
/// - template <typename Rep, typename Period>
/// bool try_lock_for(const std::chrono::duration<Rep,Period>&);
///
/// - template <typename Rep, typename Period>
/// bool try_lock_until(const std::chrono::time_point<Clock,Duration>&);
///
/// - void swap(unique_lock& other) noexcept;
/// - mutex_type* release() noexcept;
/// - mutex_type* mutex() const noexcept;
/// - bool owns_lock() const noexcept;
/// - explicit operator bool() const noexcept;
/// - unique_lock& operator=(unique_lock&& other);
///
/// ---------------------------------------------------------------
///
/// Note that AutoLock is defined also for a sequential Tasking build but below
/// regarding implementation (also found in Threading.hh)
///
///
/// NOTE ON Tasking SERIAL BUILDS AND MUTEX/UNIQUE_LOCK
/// ==================================================
///
/// Mutex and RecursiveMutex are always C++11 std::mutex types
/// however, in serial mode, using MUTEXLOCK and MUTEXUNLOCK on these
/// types has no effect -- i.e. the mutexes are not actually locked or unlocked
///
/// Additionally, when a Mutex or RecursiveMutex is used with AutoLock
/// and RecursiveAutoLock, respectively, these classes also suppressing
/// the locking and unlocking of the mutex. Regardless of the build type,
/// AutoLock and RecursiveAutoLock inherit from std::unique_lock<std::mutex>
/// and std::unique_lock<std::recursive_mutex>, respectively. This means
/// that in situations (such as is needed by the analysis category), the
/// AutoLock and RecursiveAutoLock can be passed to functions requesting
/// a std::unique_lock. Within these functions, since std::unique_lock
/// member functions are not virtual, they will not retain the dummy locking
/// and unlocking behavior
/// --> An example of this behavior can be found below
///
/// Jonathan R. Madsen (February 21, 2018)
///
/***
//======================================================================================//
typedef std::unique_lock<std::mutex> unique_lock_t;
// functions for casting AutoLock to std::unique_lock to demonstrate
// that AutoLock is NOT polymorphic
void as_unique_lock(unique_lock_t* lock) { lock->lock(); }
void as_unique_unlock(unique_lock_t* lock) { lock->unlock(); }
//======================================================================================//
void run(const uint64_t& n)
{
// sync the threads a bit
std::this_thread::sleep_for(std::chrono::milliseconds(10));
// get two mutexes to avoid deadlock when l32 actually locks
AutoLock l32(TypeMutex<int32_t>(), std::defer_lock);
AutoLock l64(TypeMutex<int64_t>(), std::defer_lock);
// when serial: will not execute std::unique_lock::lock() because
// it overrides the member function
l32.lock();
// regardless of serial or MT: will execute std::unique_lock::lock()
// because std::unique_lock::lock() is not virtual
as_unique_lock(&l64);
std::cout << "Running iteration " << n << "..." << std::endl;
}
//======================================================================================//
// execute some work
template <typename thread_type = std::thread>
void exec(uint64_t n)
{
// get two mutexes to avoid deadlock when l32 actually locks
AutoLock l32(TypeMutex<int32_t>(), std::defer_lock);
AutoLock l64(TypeMutex<int64_t>(), std::defer_lock);
std::vector<thread_type*> threads(n, nullptr);
for(uint64_t i = 0; i < n; ++i)
{
threads[i] = new thread_type();
*(threads[i]) = std::move(thread_type(run, i));
}
// when serial: will not execute std::unique_lock::lock() because
// it overrides the member function
l32.lock();
// regardless of serial or MT: will execute std::unique_lock::lock()
// because std::unique_lock::lock() is not virtual
as_unique_lock(&l64);
std::cout << "Joining..." << std::endl;
// when serial: will not execute std::unique_lock::unlock() because
// it overrides the member function
l32.unlock();
// regardless of serial or MT: will execute std::unique_lock::unlock()
// because std::unique_lock::unlock() is not virtual
as_unique_unlock(&l64);
// NOTE ABOUT UNLOCKS:
// in MT, commenting out either
// l32.unlock();
// or
// as_unique_unlock(&l64);
// creates a deadlock; in serial, commenting out
// as_unique_unlock(&l64);
// creates a deadlock but commenting out
// l32.unlock();
// does not
// clean up and join
for(uint64_t i = 0; i < n; ++i)
{
threads[i]->join();
delete threads[i];
}
threads.clear();
}
//======================================================================================//
int main()
{
print_threading();
uint64_t n = 30;
std::cout << "\nRunning with real threads...\n" << std::endl;
exec<std::thread>(n);
std::cout << "\nRunning with fake threads...\n" << std::endl;
exec<DummyThread>(n);
}
***/
#pragma once
#include "PTL/Threading.hh"
#include <chrono>
#include <iostream>
#include <mutex>
#include <system_error>
namespace PTL
{
// Note: Note that TemplateAutoLock by itself is not thread-safe and
// cannot be shared among threads due to the locked switch
//
template <typename _Mutex_t>
class TemplateAutoLock : public std::unique_lock<_Mutex_t>
{
public:
//------------------------------------------------------------------------//
// Some useful typedefs
//------------------------------------------------------------------------//
typedef std::unique_lock<_Mutex_t> unique_lock_t;
typedef TemplateAutoLock<_Mutex_t> this_type;
typedef typename unique_lock_t::mutex_type mutex_type;
public:
//------------------------------------------------------------------------//
// STL-consistent reference form constructors
//------------------------------------------------------------------------//
// reference form is consistent with STL lock_guard types
// Locks the associated mutex by calling m.lock(). The behavior is
// undefined if the current thread already owns the mutex except when
// the mutex is recursive
explicit TemplateAutoLock(mutex_type& _mutex)
: unique_lock_t(_mutex, std::defer_lock)
{
// call termination-safe locking. if serial, this call has no effect
_lock_deferred();
}
// Tries to lock the associated mutex by calling
// m.try_lock_for(_timeout_duration). Blocks until specified
// _timeout_duration has elapsed or the lock is acquired, whichever comes
// first. May block for longer than _timeout_duration.
template <typename Rep, typename Period>
TemplateAutoLock(mutex_type& _mutex,
const std::chrono::duration<Rep, Period>& _timeout_duration)
: unique_lock_t(_mutex, std::defer_lock)
{
// call termination-safe locking. if serial, this call has no effect
_lock_deferred(_timeout_duration);
}
// Tries to lock the associated mutex by calling
// m.try_lock_until(_timeout_time). Blocks until specified _timeout_time has
// been reached or the lock is acquired, whichever comes first. May block
// for longer than until _timeout_time has been reached.
template <typename Clock, typename Duration>
TemplateAutoLock(mutex_type& _mutex,
const std::chrono::time_point<Clock, Duration>& _timeout_time)
: unique_lock_t(_mutex, std::defer_lock)
{
// call termination-safe locking. if serial, this call has no effect
_lock_deferred(_timeout_time);
}
// Does not lock the associated mutex.
TemplateAutoLock(mutex_type& _mutex, std::defer_lock_t _lock) noexcept
: unique_lock_t(_mutex, _lock)
{}
// Tries to lock the associated mutex without blocking by calling
// m.try_lock(). The behavior is undefined if the current thread already
// owns the mutex except when the mutex is recursive.
TemplateAutoLock(mutex_type& _mutex, std::try_to_lock_t _lock)
: unique_lock_t(_mutex, _lock)
{}
// Assumes the calling thread already owns m
TemplateAutoLock(mutex_type& _mutex, std::adopt_lock_t _lock)
: unique_lock_t(_mutex, _lock)
{}
public:
//------------------------------------------------------------------------//
// Backwards compatibility versions (constructor with pointer to mutex)
//------------------------------------------------------------------------//
TemplateAutoLock(mutex_type* _mutex)
: unique_lock_t(*_mutex, std::defer_lock)
{
// call termination-safe locking. if serial, this call has no effect
_lock_deferred();
}
TemplateAutoLock(mutex_type* _mutex, std::defer_lock_t _lock) noexcept
: unique_lock_t(*_mutex, _lock)
{}
TemplateAutoLock(mutex_type* _mutex, std::try_to_lock_t _lock)
: unique_lock_t(*_mutex, _lock)
{}
TemplateAutoLock(mutex_type* _mutex, std::adopt_lock_t _lock)
: unique_lock_t(*_mutex, _lock)
{}
private:
// helpful macros
#define _is_stand_mutex(_Tp) (std::is_same<_Tp, Mutex>::value)
#define _is_recur_mutex(_Tp) (std::is_same<_Tp, RecursiveMutex>::value)
#define _is_other_mutex(_Tp) (!_is_stand_mutex(_Tp) && !_is_recur_mutex(_Tp))
template <typename _Tp = _Mutex_t,
typename std::enable_if<_is_stand_mutex(_Tp), int>::type = 0>
std::string GetTypeString()
{
return "AutoLock<Mutex>";
}
template <typename _Tp = _Mutex_t,
typename std::enable_if<_is_recur_mutex(_Tp), int>::type = 0>
std::string GetTypeString()
{
return "AutoLock<RecursiveMutex>";
}
template <typename _Tp = _Mutex_t,
typename std::enable_if<_is_other_mutex(_Tp), int>::type = 0>
std::string GetTypeString()
{
return "AutoLock<UNKNOWN_MUTEX>";
}
// pollution is bad
#undef _is_stand_mutex
#undef _is_recur_mutex
#undef _is_other_mutex
// used in _lock_deferred chrono variants to avoid ununsed-variable warning
template <typename _Tp>
void suppress_unused_variable(const _Tp&)
{}
//========================================================================//
// NOTE on _lock_deferred(...) variants:
// a system_error in lock means that the mutex is unavailable
// we want to throw the error that comes from locking an unavailable
// mutex so that we know there is a memory leak
// if the mutex is valid, this will hold until the other thread
// finishes
// sometimes certain destructors use locks, this isn't an issue unless
// the object is leaked. When this occurs, the application finalization
// (i.e. the real or implied "return 0" part of main) will call destructors
// on Tasking object after some static mutex variables are deleted, leading
// to the error code (typically on Clang compilers):
// libc++abi.dylib: terminating with uncaught exception of type
// std::__1::system_error: mutex lock failed: Invalid argument
// this function protects against this failure until such a time that
// these issues have been resolved
//========================================================================//
// standard locking
inline void _lock_deferred()
{
try
{
this->unique_lock_t::lock();
} catch(std::system_error& e)
{
PrintLockErrorMessage(e);
}
}
//========================================================================//
// Tries to lock the associated mutex by calling
// m.try_lock_for(_timeout_duration). Blocks until specified
// _timeout_duration has elapsed or the lock is acquired, whichever comes
// first. May block for longer than _timeout_duration.
template <typename Rep, typename Period>
void _lock_deferred(const std::chrono::duration<Rep, Period>& _timeout_duration)
{
try
{
this->unique_lock_t::try_lock_for(_timeout_duration);
} catch(std::system_error& e)
{
PrintLockErrorMessage(e);
}
}
//========================================================================//
// Tries to lock the associated mutex by calling
// m.try_lock_until(_timeout_time). Blocks until specified _timeout_time has
// been reached or the lock is acquired, whichever comes first. May block
// for longer than until _timeout_time has been reached.
template <typename Clock, typename Duration>
void _lock_deferred(const std::chrono::time_point<Clock, Duration>& _timeout_time)
{
try
{
this->unique_lock_t::try_lock_until(_timeout_time);
} catch(std::system_error& e)
{
PrintLockErrorMessage(e);
}
}
//========================================================================//
// the message for what mutex lock fails due to deleted static mutex
// at termination
void PrintLockErrorMessage(std::system_error& e)
{
// use std::cout/std::endl to avoid include dependencies
using std::cout;
using std::endl;
// the error that comes from locking an unavailable mutex
#if defined(VERBOSE)
cout << "Non-critical error: mutex lock failure in "
<< GetTypeString<mutex_type>() << ". "
<< "If the app is terminating, Tasking failed to "
<< "delete an allocated resource and a Tasking destructor is "
<< "being called after the statics were destroyed. \n\t--> "
<< "Exception: [code: " << e.code() << "] caught: " << e.what() << std::endl;
#else
suppress_unused_variable(e);
#endif
}
};
// -------------------------------------------------------------------------- //
//
// Use the non-template types below:
// - AutoLock with Mutex
// - RecursiveAutoLock with RecursiveMutex
//
// -------------------------------------------------------------------------- //
typedef TemplateAutoLock<Mutex> AutoLock;
typedef TemplateAutoLock<RecursiveMutex> RecursiveAutoLock;
// provide abbriviated type if another mutex type is desired to be used
// aside from above
template <typename _Tp>
using TAutoLock = TemplateAutoLock<_Tp>;
} // namespace PTL
+200
View File
@@ -0,0 +1,200 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#pragma once
#ifndef FALSE
# define FALSE 0
#endif
#ifndef TRUE
# define TRUE 1
#endif
#include <algorithm> // Retrieve definitions of min/max
// Include base types
#include "PTL/Types.hh"
// Global utility functions
#include "PTL/Utility.hh"
#include <initializer_list>
#include <tuple>
#include <type_traits>
#include <utility>
#if !defined(PTL_FOLD_EXPRESSION)
# define PTL_FOLD_EXPRESSION(...) \
::PTL::details::consume_parameters( \
::std::initializer_list<int>{ (__VA_ARGS__, 0)... })
#endif
namespace PTL
{
template <typename T>
using decay_t = typename std::decay<T>::type;
template <bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
// for pre-C++14 tuple expansion to arguments
namespace details
{
//--------------------------------------------------------------------------------------//
template <typename... Args>
void
consume_parameters(Args&&...)
{}
//--------------------------------------------------------------------------------------//
namespace impl
{
//--------------------------------------------------------------------------------------//
// Stores a tuple of indices. Used by tuple and pair, and by bind() to
// extract the elements in a tuple.
template <size_t... _Indexes>
struct _Index_tuple
{};
// Concatenates two _Index_tuples.
template <typename _Itup1, typename _Itup2>
struct _Itup_cat;
template <size_t... _Ind1, size_t... _Ind2>
struct _Itup_cat<_Index_tuple<_Ind1...>, _Index_tuple<_Ind2...>>
{
using __type = _Index_tuple<_Ind1..., (_Ind2 + sizeof...(_Ind1))...>;
};
// Builds an _Index_tuple<0, 1, 2, ..., _Num-1>.
template <size_t _Num>
struct _Build_index_tuple
: _Itup_cat<typename _Build_index_tuple<_Num / 2>::__type,
typename _Build_index_tuple<_Num - _Num / 2>::__type>
{};
template <>
struct _Build_index_tuple<1>
{
typedef _Index_tuple<0> __type;
};
template <>
struct _Build_index_tuple<0>
{
typedef _Index_tuple<> __type;
};
/// Class template integer_sequence
template <typename _Tp, _Tp... _Idx>
struct integer_sequence
{
typedef _Tp value_type;
static constexpr size_t size() noexcept { return sizeof...(_Idx); }
};
template <typename _Tp, _Tp _Num,
typename _ISeq = typename _Build_index_tuple<_Num>::__type>
struct _Make_integer_sequence;
template <typename _Tp, _Tp _Num, size_t... _Idx>
struct _Make_integer_sequence<_Tp, _Num, _Index_tuple<_Idx...>>
{
static_assert(_Num >= 0, "Cannot make integer sequence of negative length");
typedef integer_sequence<_Tp, static_cast<_Tp>(_Idx)...> __type;
};
/// Alias template make_integer_sequence
template <typename _Tp, _Tp _Num>
using make_integer_sequence = typename _Make_integer_sequence<_Tp, _Num>::__type;
/// Alias template index_sequence
template <size_t... _Idx>
using index_sequence = integer_sequence<size_t, _Idx...>;
/// Alias template make_index_sequence
template <size_t _Num>
using make_index_sequence = make_integer_sequence<size_t, _Num>;
/// Alias template index_sequence_for
template <typename... _Types>
using index_sequence_for = make_index_sequence<sizeof...(_Types)>;
template <size_t I, typename Tup>
using index_type_t = decay_t<decltype(std::get<I>(std::declval<Tup>()))>;
template <typename _Fn, typename _Tuple, size_t... _Idx>
static inline void
apply(_Fn&& __f, _Tuple&& __t, impl::index_sequence<_Idx...>)
{
std::forward<_Fn>(__f)(std::get<_Idx>(std::forward<_Tuple>(__t))...);
// __f(std::get<_Idx>(std::forward<_Tuple>(__t))...);
}
//--------------------------------------------------------------------------------------//
} // namespace impl
//--------------------------------------------------------------------------------------//
/// Alias template index_sequence
template <size_t... _Idx>
using index_sequence = impl::integer_sequence<size_t, _Idx...>;
/// Alias template make_index_sequence
template <size_t _Num>
using make_index_sequence = impl::make_integer_sequence<size_t, _Num>;
/// Alias template index_sequence_for
template <typename... _Types>
using index_sequence_for = impl::make_index_sequence<sizeof...(_Types)>;
template <typename _Fn, typename _Tuple>
static inline void
apply(_Fn&& __f, _Tuple&& __t)
{
constexpr auto _N = std::tuple_size<_Tuple>::value;
impl::apply(std::forward<_Fn>(__f), std::forward<_Tuple>(__t),
impl::make_index_sequence<_N>{});
}
//--------------------------------------------------------------------------------------//
} // namespace details
namespace thread_pool
{
namespace state
{
static const short STARTED = 0;
static const short PARTIAL = 1;
static const short STOPPED = 2;
static const short NONINIT = 3;
} // namespace state
} // namespace thread_pool
//--------------------------------------------------------------------------------------//
} // namespace PTL
+405
View File
@@ -0,0 +1,405 @@
// MIT License
//
// Copyright (c) 2020 Jonathan R. Madsen
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//
#pragma once
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <set>
#include <thread>
#include <type_traits>
namespace PTL
{
/// \class PTL::Singleton
/// \brief Singleton object that allows a deleter class to be specified
///
template <typename Type,
typename PointerT = std::unique_ptr<Type, std::default_delete<Type>>>
class Singleton
{
public:
using this_type = Singleton<Type, PointerT>;
using thread_id_t = std::thread::id;
using mutex_t = std::recursive_mutex;
using auto_lock_t = std::unique_lock<mutex_t>;
using pointer = Type*;
using list_t = std::set<pointer>;
using smart_pointer = PointerT;
using deleter_t = std::function<void(PointerT&)>;
template <bool B, typename T = int>
using enable_if_t = typename std::enable_if<B, T>::type;
public:
// Constructor and Destructors
Singleton();
Singleton(pointer);
~Singleton();
Singleton(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton& operator=(Singleton&&) = delete;
public:
// public static functions
static pointer GetInstance();
static pointer GetMasterInstance();
static thread_id_t GetMasterThreadID() { return f_master_thread(); }
static list_t Children() { return f_children(); }
static bool IsMaster(pointer ptr) { return ptr == GetRawMasterInstance(); }
static bool IsMasterThread();
static void Insert(pointer);
static void Remove(pointer);
static mutex_t& GetMutex() { return f_mutex(); }
public:
// public member function
void Initialize();
void Initialize(pointer);
void Destroy();
void Reset(pointer);
void Reset();
// since we are overloading delete we overload new
void* operator new(size_t)
{
this_type* ptr = ::new this_type();
return static_cast<void*>(ptr);
}
// overload delete so that f_master_instance is guaranteed to be
// a nullptr after deletion
void operator delete(void* ptr)
{
this_type* _instance = (this_type*) (ptr);
::delete _instance;
if(std::this_thread::get_id() == f_master_thread())
f_master_instance() = nullptr;
}
protected:
friend class Type;
// instance functions that do not Initialize
smart_pointer& GetSmartInstance() { return _local_instance(); }
static smart_pointer& GetSmartMasterInstance() { return _master_instance(); }
// for checking but not allocating
pointer GetRawInstance()
{
return IsMasterThread() ? f_master_instance() : _local_instance().get();
}
static pointer GetRawMasterInstance() { return f_master_instance(); }
private:
// Private functions
static smart_pointer& _local_instance()
{
static thread_local smart_pointer _instance = smart_pointer();
return _instance;
}
static smart_pointer& _master_instance()
{
static smart_pointer _instance = smart_pointer();
return _instance;
}
void* operator new[](std::size_t) noexcept { return nullptr; }
void operator delete[](void*) noexcept {}
template <typename Tp = Type, typename PtrT = PointerT,
enable_if_t<(std::is_same<PtrT, std::shared_ptr<Tp>>::value)> = 0>
deleter_t& GetDeleter()
{
static deleter_t _instance = [](PointerT&) {};
return _instance;
}
template <typename Tp = Type, typename PtrT = PointerT,
enable_if_t<!(std::is_same<PtrT, std::shared_ptr<Tp>>::value)> = 0>
deleter_t& GetDeleter()
{
static deleter_t _instance = [](PointerT& _master) {
auto& del = _master.get_deleter();
del(_master.get());
_master.reset(nullptr);
};
return _instance;
}
private:
// Private variables
struct persistent_data
{
thread_id_t m_master_thread = std::this_thread::get_id();
mutex_t m_mutex;
pointer m_master_instance = nullptr;
list_t m_children = {};
persistent_data() = default;
~persistent_data() = default;
persistent_data(const persistent_data&) = delete;
persistent_data(persistent_data&&) = delete;
persistent_data& operator=(const persistent_data&) = delete;
persistent_data& operator=(persistent_data&&) = delete;
persistent_data(pointer _master, std::thread::id _tid)
: m_master_thread(_tid)
, m_master_instance(_master)
{}
void reset()
{
m_master_instance = nullptr;
m_children.clear();
}
};
bool m_IsMaster = false;
static thread_id_t& f_master_thread();
static mutex_t& f_mutex();
static pointer& f_master_instance();
static list_t& f_children();
static persistent_data& f_persistent_data()
{
static persistent_data _instance;
return _instance;
}
};
//======================================================================================//
template <typename Type, typename PointerT>
typename Singleton<Type, PointerT>::thread_id_t&
Singleton<Type, PointerT>::f_master_thread()
{
return f_persistent_data().m_master_thread;
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
typename Singleton<Type, PointerT>::pointer&
Singleton<Type, PointerT>::f_master_instance()
{
return f_persistent_data().m_master_instance;
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
typename Singleton<Type, PointerT>::mutex_t&
Singleton<Type, PointerT>::f_mutex()
{
return f_persistent_data().m_mutex;
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
typename Singleton<Type, PointerT>::list_t&
Singleton<Type, PointerT>::f_children()
{
return f_persistent_data().m_children;
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
Singleton<Type, PointerT>::Singleton()
{
Initialize();
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
Singleton<Type, PointerT>::Singleton(pointer ptr)
{
Initialize(ptr);
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
Singleton<Type, PointerT>::~Singleton()
{
auto& del = GetDeleter();
del(_master_instance());
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
void
Singleton<Type, PointerT>::Initialize()
{
if(!f_master_instance())
{
f_master_thread() = std::this_thread::get_id();
f_master_instance() = new Type();
}
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
void
Singleton<Type, PointerT>::Initialize(pointer ptr)
{
if(!f_master_instance())
{
f_master_thread() = std::this_thread::get_id();
f_master_instance() = ptr;
}
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
void
Singleton<Type, PointerT>::Destroy()
{
if(std::this_thread::get_id() == f_master_thread() && f_master_instance())
{
delete f_master_instance();
f_master_instance() = nullptr;
}
else
{
remove(_local_instance().get());
}
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
typename Singleton<Type, PointerT>::pointer
Singleton<Type, PointerT>::GetInstance()
{
if(std::this_thread::get_id() == f_master_thread())
return GetMasterInstance();
else if(!_local_instance().get())
{
_local_instance().reset(new Type());
Insert(_local_instance().get());
}
return _local_instance().get();
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
typename Singleton<Type, PointerT>::pointer
Singleton<Type, PointerT>::GetMasterInstance()
{
if(!f_master_instance())
{
f_master_thread() = std::this_thread::get_id();
f_master_instance() = new Type();
}
return f_master_instance();
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
void
Singleton<Type, PointerT>::Reset(pointer ptr)
{
if(IsMaster(ptr))
{
if(_master_instance().get())
_master_instance().reset();
else if(f_master_instance())
{
auto& del = GetDeleter();
del(_master_instance());
f_master_instance() = nullptr;
}
f_persistent_data().reset();
}
else
{
_local_instance().reset();
}
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
void
Singleton<Type, PointerT>::Reset()
{
if(IsMasterThread())
_master_instance().reset();
_local_instance().reset();
f_persistent_data().reset();
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
bool
Singleton<Type, PointerT>::IsMasterThread()
{
return std::this_thread::get_id() == f_master_thread();
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
void
Singleton<Type, PointerT>::Insert(pointer itr)
{
auto_lock_t l(f_mutex());
f_children().insert(itr);
}
//--------------------------------------------------------------------------------------//
template <typename Type, typename PointerT>
void
Singleton<Type, PointerT>::Remove(pointer itr)
{
auto_lock_t l(f_mutex());
for(auto litr = f_children().begin(); litr != f_children().end(); ++litr)
{
if(*litr == itr)
{
f_children().erase(litr);
break;
}
}
}
//--------------------------------------------------------------------------------------//
} // namespace PTL
+220
View File
@@ -0,0 +1,220 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class header file
//
// Class Description:
//
// This file wraps a TBB task_group into a TaskGroup
//
// ---------------------------------------------------------------
// Author: Jonathan Madsen (Jun 21st 2018)
// ---------------------------------------------------------------
#pragma once
#include "PTL/TaskGroup.hh"
#include <functional>
#include <memory>
#if defined(PTL_USE_TBB)
# include <tbb/tbb.h>
#endif
namespace PTL
{
class ThreadPool;
//--------------------------------------------------------------------------------------//
#if defined(PTL_USE_TBB)
class ThreadPool;
namespace
{
typedef ::tbb::task_group tbb_task_group_t;
}
//--------------------------------------------------------------------------------------//
template <typename Tp, typename Arg = Tp>
class TBBTaskGroup : public TaskGroup<Tp, Arg>
{
public:
//------------------------------------------------------------------------//
typedef decay_t<Arg> ArgTp;
typedef TBBTaskGroup<Tp, Arg> this_type;
typedef TaskGroup<Tp, Arg> base_type;
typedef typename base_type::result_type result_type;
typedef typename base_type::packaged_task_type packaged_task_type;
typedef typename base_type::future_type future_type;
typedef typename base_type::promise_type promise_type;
typedef typename base_type::template JoinFunction<Tp, Arg>::Type join_type;
typedef tbb::task_group tbb_task_group_t;
//------------------------------------------------------------------------//
template <typename... Args>
using task_type = Task<ArgTp, Args...>;
//------------------------------------------------------------------------//
public:
// Constructor
template <typename Func>
TBBTaskGroup(Func&& _join, ThreadPool* _tp = nullptr)
: base_type(std::forward<Func>(_join), _tp)
, m_tbb_task_group(new tbb_task_group_t)
{}
template <typename Up = Tp, enable_if_t<std::is_same<Up, void>::value, int> = 0>
TBBTaskGroup(ThreadPool* _tp = nullptr)
: base_type(_tp)
, m_tbb_task_group(new tbb_task_group_t)
{}
// Destructor
virtual ~TBBTaskGroup()
{
delete m_tbb_task_group;
this->clear();
}
// delete copy-construct
TBBTaskGroup(const this_type&) = delete;
// define move-construct
TBBTaskGroup(this_type&& rhs) = default;
// delete copy-assign
this_type& operator=(const this_type& rhs) = delete;
// define move-assign
this_type& operator=(this_type&& rhs) = default;
public:
//------------------------------------------------------------------------//
template <typename Up>
Up* operator+=(Up* _task)
{
// store in list
vtask_list.push_back(_task);
// thread-safe increment of tasks in task group
operator++();
// add the future
m_task_set.push_back(std::move(_task->get_future()));
// return
return _task;
}
public:
//------------------------------------------------------------------------//
template <typename Func, typename... Args>
task_type<Args...>* wrap(Func&& func, Args&&... args)
{
return operator+=(new task_type<Args...>(this, std::forward<Func>(func),
std::forward<Args>(args)...));
}
public:
//------------------------------------------------------------------------//
template <typename Func, typename... Args>
void run(Func&& func, Args&&... args)
{
auto _task = wrap(std::forward<Func>(func), std::forward<Args>(args)...);
auto _lamb = [=]() { (*_task)(); };
m_tbb_task_group->run(_lamb);
}
//------------------------------------------------------------------------//
template <typename Func, typename... Args>
void exec(Func&& func, Args&&... args)
{
run(std::forward<Func>(func), std::forward<Args>(args)...);
}
//------------------------------------------------------------------------//
template <typename Func, typename... Args, typename Up = Tp,
enable_if_t<std::is_same<Up, void>::value, int> = 0>
void parallel_for(uintmax_t nitr, uintmax_t, Func&& func, Args&&... args)
{
tbb::parallel_for(tbb::blocked_range<size_t>(0, nitr),
[&](const tbb::blocked_range<size_t>& range) {
for(size_t i = range.begin(); i != range.end(); ++i)
func(std::forward<Args>(args)...);
});
}
public:
//------------------------------------------------------------------------//
// this is not a native Tasking task group
virtual bool is_native_task_group() const override { return false; }
//------------------------------------------------------------------------//
// wait on tbb::task_group, not internal thread-pool
virtual void wait() override
{
base_type::wait();
m_tbb_task_group->wait();
}
public:
using base_type::begin;
using base_type::cbegin;
using base_type::cend;
using base_type::clear;
using base_type::end;
using base_type::get_tasks;
//------------------------------------------------------------------------//
// wait to finish
template <typename Up = Tp, enable_if_t<!std::is_same<Up, void>::value, int> = 0>
inline Up join(Up accum = {})
{
this->wait();
for(auto& itr : m_task_set)
accum = m_join(std::ref(accum), std::forward<ArgTp>(itr.get()));
this->clear();
return accum;
}
//------------------------------------------------------------------------//
// wait to finish
template <typename Up = Tp, enable_if_t<std::is_same<Up, void>::value, int> = 0>
inline void join()
{
this->wait();
for(auto& itr : m_task_set)
itr.get();
m_join();
this->clear();
}
protected:
// Protected variables
tbb_task_group_t* m_tbb_task_group;
using base_type:: operator++;
using base_type:: operator--;
using base_type::m_join;
using base_type::m_task_set;
using base_type::vtask_list;
};
//--------------------------------------------------------------------------------------//
#else
//--------------------------------------------------------------------------------------//
template <typename Tp, typename Arg = Tp>
using TBBTaskGroup = TaskGroup<Tp, Arg>;
//--------------------------------------------------------------------------------------//
#endif
//--------------------------------------------------------------------------------------//
} // namespace PTL
+271
View File
@@ -0,0 +1,271 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
// ---------------------------------------------------------------
// Tasking class header file
//
// Class Description:
//
// This file defines the task types for TaskManager and ThreadPool
//
// ---------------------------------------------------------------
// Author: Jonathan Madsen (Feb 13th 2018)
// ---------------------------------------------------------------
#pragma once
#include "Globals.hh"
#include "TaskAllocator.hh"
#include "VTask.hh"
#include <cstdint>
#include <functional>
#include <stdexcept>
namespace PTL
{
class VTaskGroup;
class ThreadPool;
//======================================================================================//
/// \brief The task class is supplied to thread_pool.
template <typename _Ret, typename... _Args>
class PackagedTask : public VTask
{
public:
typedef PackagedTask<_Ret, _Args...> this_type;
typedef std::promise<_Ret> promise_type;
typedef std::future<_Ret> future_type;
typedef std::packaged_task<_Ret(_Args...)> packaged_task_type;
typedef _Ret result_type;
typedef std::tuple<_Args...> tuple_type;
public:
// pass a free function pointer
template <typename _Func>
PackagedTask(_Func&& func, _Args&&... args)
: VTask()
, m_ptask(std::forward<_Func>(func))
, m_args(std::forward<_Args>(args)...)
{}
template <typename _Func>
PackagedTask(VTaskGroup* tg, _Func&& func, _Args&&... args)
: VTask(tg)
, m_ptask(std::forward<_Func>(func))
, m_args(std::forward<_Args>(args)...)
{}
template <typename _Func>
PackagedTask(ThreadPool* _pool, _Func&& func, _Args&&... args)
: VTask(_pool)
, m_ptask(std::forward<_Func>(func))
, m_args(std::forward<_Args>(args)...)
{}
virtual ~PackagedTask() {}
public:
// execution operator
virtual void operator()() override
{
details::apply(std::forward<packaged_task_type>(m_ptask),
std::forward<tuple_type>(m_args));
}
future_type get_future() { return m_ptask.get_future(); }
virtual bool is_native_task() const override { return true; }
private:
packaged_task_type m_ptask;
tuple_type m_args;
};
//======================================================================================//
/// \brief The task class is supplied to thread_pool.
template <typename _Ret, typename... _Args>
class Task : public VTask
{
public:
typedef Task<_Ret, _Args...> this_type;
typedef std::promise<_Ret> promise_type;
typedef std::future<_Ret> future_type;
typedef std::packaged_task<_Ret(_Args...)> packaged_task_type;
typedef _Ret result_type;
typedef std::tuple<_Args...> tuple_type;
public:
template <typename _Func>
Task(_Func&& func, _Args&&... args)
: VTask()
, m_ptask(std::forward<_Func>(func))
, m_args(std::forward<_Args>(args)...)
{}
template <typename _Func>
Task(VTaskGroup* tg, _Func&& func, _Args&&... args)
: VTask(tg)
, m_ptask(std::forward<_Func>(func))
, m_args(std::forward<_Args>(args)...)
{}
template <typename _Func>
Task(ThreadPool* tp, _Func&& func, _Args&&... args)
: VTask(tp)
, m_ptask(std::forward<_Func>(func))
, m_args(std::forward<_Args>(args)...)
{}
virtual ~Task() {}
public:
// execution operator
virtual void operator()() final
{
details::apply(std::forward<packaged_task_type>(m_ptask),
std::forward<tuple_type>(m_args));
// decrements the task-group counter on active tasks
// when the counter is < 2, if the thread owning the task group is
// sleeping at the TaskGroup::wait(), it signals the thread to wake
// up and check if all tasks are finished, proceeding if this
// check returns as true
this_type::operator--();
}
virtual bool is_native_task() const override { return true; }
future_type get_future() { return m_ptask.get_future(); }
private:
packaged_task_type m_ptask;
tuple_type m_args;
};
//======================================================================================//
/// \brief The task class is supplied to thread_pool.
template <typename _Ret>
class Task<_Ret, void> : public VTask
{
public:
typedef Task<_Ret> this_type;
typedef std::promise<_Ret> promise_type;
typedef std::future<_Ret> future_type;
typedef std::packaged_task<_Ret()> packaged_task_type;
typedef _Ret result_type;
public:
template <typename _Func>
Task(_Func&& func)
: VTask()
, m_ptask(std::forward<_Func>(func))
{}
template <typename _Func>
Task(VTaskGroup* tg, _Func&& func)
: VTask(tg)
, m_ptask(std::forward<_Func>(func))
{}
template <typename _Func>
Task(ThreadPool* tp, _Func&& func)
: VTask(tp)
, m_ptask(std::forward<_Func>(func))
{}
virtual ~Task() {}
public:
// execution operator
virtual void operator()() final
{
m_ptask();
// decrements the task-group counter on active tasks
// when the counter is < 2, if the thread owning the task group is
// sleeping at the TaskGroup::wait(), it signals the thread to wake
// up and check if all tasks are finished, proceeding if this
// check returns as true
this_type::operator--();
}
virtual bool is_native_task() const override { return true; }
future_type get_future() { return m_ptask.get_future(); }
private:
packaged_task_type m_ptask;
};
//======================================================================================//
/// \brief The task class is supplied to thread_pool.
template <>
class Task<void, void> : public VTask
{
public:
typedef void _Ret;
typedef Task<void, void> this_type;
typedef std::promise<_Ret> promise_type;
typedef std::future<_Ret> future_type;
typedef std::packaged_task<_Ret()> packaged_task_type;
typedef _Ret result_type;
public:
template <typename _Func>
explicit Task(_Func&& func)
: VTask()
, m_ptask(std::forward<_Func>(func))
{}
template <typename _Func>
Task(VTaskGroup* tg, _Func&& func)
: VTask(tg)
, m_ptask(std::forward<_Func>(func))
{}
template <typename _Func>
Task(ThreadPool* tp, _Func&& func)
: VTask(tp)
, m_ptask(std::forward<_Func>(func))
{}
virtual ~Task() {}
public:
// execution operator
virtual void operator()() final
{
m_ptask();
// decrements the task-group counter on active tasks
// when the counter is < 2, if the thread owning the task group is
// sleeping at the TaskGroup::wait(), it signals the thread to wake
// up and check if all tasks are finished, proceeding if this
// check returns as true
this_type::operator--();
}
virtual bool is_native_task() const override { return true; }
future_type get_future() { return m_ptask.get_future(); }
private:
packaged_task_type m_ptask;
};
//======================================================================================//
} // namespace PTL
+339
View File
@@ -0,0 +1,339 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
// ------------------------------------------------------------
// Tasking class header file
//
// Class Description:
//
// A class for fast allocation of objects to the heap through a pool of
// chunks organised as linked list. It's meant to be used by associating
// it to the object to be allocated and defining for it new and delete
// operators via MallocSingle() and FreeSingle() methods.
// ---------------- TaskAllocator ----------------
//
// ------------------------------------------------------------
#pragma once
#include <cstddef>
#include <typeinfo>
#include "PTL/TaskAllocatorPool.hh"
#include "PTL/Threading.hh"
namespace PTL
{
//--------------------------------------------------------------------------------------//
class TaskAllocatorBase
{
public:
TaskAllocatorBase();
virtual ~TaskAllocatorBase();
virtual void ResetStorage() = 0;
virtual size_t GetAllocatedSize() const = 0;
virtual int GetNoPages() const = 0;
virtual size_t GetPageSize() const = 0;
virtual void IncreasePageSize(unsigned int sz) = 0;
virtual const char* GetPoolType() const = 0;
};
//--------------------------------------------------------------------------------------//
template <class Type>
class TaskAllocatorImpl : public TaskAllocatorBase
{
public: // with description
TaskAllocatorImpl();
~TaskAllocatorImpl();
// Constructor & destructor
inline Type* MallocSingle();
inline void FreeSingle(Type* anElement);
// Malloc and Free methods to be used when overloading
// new and delete operators in the client <Type> object
inline void ResetStorage() override;
// Returns allocated storage to the free store, resets allocator.
// Note: contents in memory are lost using this call !
inline size_t GetAllocatedSize() const override;
// Returns the size of the total memory allocated
inline int GetNoPages() const override;
// Returns the total number of allocated pages
inline size_t GetPageSize() const override;
// Returns the current size of a page
inline void IncreasePageSize(unsigned int sz) override;
// Resets allocator and increases default page size of a given factor
inline const char* GetPoolType() const override;
// Returns the type_info Id of the allocated type in the pool
public: // without description
// This public section includes standard methods and types
// required if the allocator is to be used as alternative
// allocator for STL containers.
// NOTE: the code below is a trivial implementation to make
// this class an STL compliant allocator.
// It is anyhow NOT recommended to use this class as
// alternative allocator for STL containers !
typedef Type value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef Type* pointer;
typedef const Type* const_pointer;
typedef Type& reference;
typedef const Type& const_reference;
template <class U>
TaskAllocatorImpl(const TaskAllocatorImpl<U>& right) throw()
: mem(right.mem)
, tname(right.name())
{}
// Copy constructor
pointer address(reference r) const { return &r; }
const_pointer address(const_reference r) const { return &r; }
// Returns the address of values
pointer allocate(size_type n, void* = 0)
{
// Allocates space for n elements of type Type, but does not initialise
//
Type* mem_alloc = 0;
if(n == 1)
mem_alloc = MallocSingle();
else
mem_alloc = static_cast<Type*>(::operator new(n * sizeof(Type)));
return mem_alloc;
}
void deallocate(pointer p, size_type n)
{
// Deallocates n elements of type Type, but doesn't destroy
//
if(n == 1)
FreeSingle(p);
else
::operator delete((void*) p);
return;
}
void construct(pointer p, const Type& val) { new((void*) p) Type(val); }
// Initialises *p by val
void destroy(pointer p) { p->~Type(); }
// Destroy *p but doesn't deallocate
size_type max_size() const throw()
{
// Returns the maximum number of elements that can be allocated
//
return 2147483647 / sizeof(Type);
}
template <class U>
struct rebind
{
typedef TaskAllocatorImpl<U> other;
};
// Rebind allocator to type U
TaskAllocatorPool mem;
// Pool of elements of sizeof(Type)
private:
const char* tname;
// Type name identifier
};
//--------------------------------------------------------------------------------------//
//
// Inherit from this class, e.g. MyClass : public TaskAllocator<MyClass>
//
//--------------------------------------------------------------------------------------//
template <typename Type>
class TaskAllocator : public TaskAllocatorImpl<Type>
{
public:
typedef Type value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef Type* pointer;
typedef const Type* const_pointer;
typedef Type& reference;
typedef const Type& const_reference;
typedef TaskAllocatorImpl<Type> allocator_type;
public:
// define the new operator
void* operator new(size_type)
{
return static_cast<void*>(get_allocator()->MallocSingle());
}
// define the delete operator
void operator delete(void* ptr)
{
get_allocator()->FreeSingle(static_cast<pointer>(ptr));
}
private:
// currently disabled due to memory leak found via -fsanitize=leak
// static function to get allocator
static allocator_type* get_allocator()
{
typedef std::unique_ptr<allocator_type> allocator_ptr;
static thread_local allocator_ptr _allocator = allocator_ptr(new allocator_type);
return _allocator.get();
}
};
//--------------------------------------------------------------------------------------//
// Inline implementation
template <class Type>
TaskAllocatorImpl<Type>::TaskAllocatorImpl()
: mem(sizeof(Type))
, tname(typeid(Type).name())
{}
// ************************************************************
// TaskAllocatorImpl destructor
// ************************************************************
//
template <class Type>
TaskAllocatorImpl<Type>::~TaskAllocatorImpl()
{}
// ************************************************************
// MallocSingle
// ************************************************************
//
template <class Type>
Type*
TaskAllocatorImpl<Type>::MallocSingle()
{
return static_cast<Type*>(mem.Alloc());
}
// ************************************************************
// FreeSingle
// ************************************************************
//
template <class Type>
void
TaskAllocatorImpl<Type>::FreeSingle(Type* anElement)
{
mem.Free(anElement);
return;
}
// ************************************************************
// ResetStorage
// ************************************************************
//
template <class Type>
void
TaskAllocatorImpl<Type>::ResetStorage()
{
// Clear all allocated storage and return it to the free store
//
mem.Reset();
return;
}
// ************************************************************
// GetAllocatedSize
// ************************************************************
//
template <class Type>
size_t
TaskAllocatorImpl<Type>::GetAllocatedSize() const
{
return mem.Size();
}
// ************************************************************
// GetNoPages
// ************************************************************
//
template <class Type>
int
TaskAllocatorImpl<Type>::GetNoPages() const
{
return mem.GetNoPages();
}
// ************************************************************
// GetPageSize
// ************************************************************
//
template <class Type>
size_t
TaskAllocatorImpl<Type>::GetPageSize() const
{
return mem.GetPageSize();
}
// ************************************************************
// IncreasePageSize
// ************************************************************
//
template <class Type>
void
TaskAllocatorImpl<Type>::IncreasePageSize(unsigned int sz)
{
ResetStorage();
mem.GrowPageSize(sz);
}
// ************************************************************
// GetPoolType
// ************************************************************
//
template <class Type>
const char*
TaskAllocatorImpl<Type>::GetPoolType() const
{
return tname;
}
// ************************************************************
// operator==
// ************************************************************
//
template <class T1, class T2>
bool
operator==(const TaskAllocatorImpl<T1>&, const TaskAllocatorImpl<T2>&) throw()
{
return true;
}
// ************************************************************
// operator!=
// ************************************************************
//
template <class T1, class T2>
bool
operator!=(const TaskAllocatorImpl<T1>&, const TaskAllocatorImpl<T2>&) throw()
{
return false;
}
} // namespace PTL
+58
View File
@@ -0,0 +1,58 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ------------------------------------------------------------
// Tasking class header file
//
// Class Description:
//
// A class to store all TaskAllocator objects in a thread for the sake
// of cleanly deleting them.
//
// ------------------------------------------------------------
#pragma once
#include "PTL/Globals.hh"
#include <vector>
namespace PTL
{
class TaskAllocatorBase;
class TaskAllocatorList
{
public: // with description
static TaskAllocatorList* GetAllocatorList();
static TaskAllocatorList* GetAllocatorListIfExist();
public:
~TaskAllocatorList();
void Register(TaskAllocatorBase*);
void Destroy(int nStat = 0, int verboseLevel = 0);
int Size() const;
private:
TaskAllocatorList();
private:
static TaskAllocatorList*& fAllocatorList();
std::vector<TaskAllocatorBase*> fList;
};
} // namespace PTL
+173
View File
@@ -0,0 +1,173 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
// -------------------------------------------------------------------
// Tasking class header file
//
// Class description:
//
// Class implementing a memory pool for fast allocation and deallocation
// of memory chunks. The size of the chunks for small allocated objects
// is fixed to 1Kb and takes into account of memory alignment; for large
// objects it is set to 10 times the object's size.
// The implementation is derived from: B.Stroustrup, The C++ Programming
// Language, Third Edition.
// -------------- TaskAllocatorPool ----------------
//
// Author: G.Cosmo (CERN), November 2000
// -------------------------------------------------------------------
#pragma once
namespace PTL
{
class TaskAllocatorPool
{
public:
explicit TaskAllocatorPool(unsigned int n = 0);
// Create a pool of elements of size n
~TaskAllocatorPool();
// Destructor. Return storage to the free store
inline void* Alloc();
// Allocate one element
inline void Free(void* b);
// Return an element back to the pool
inline unsigned int Size() const;
// Return storage size
void Reset();
// Return storage to the free store
inline int GetNoPages() const;
// Return the total number of allocated pages
inline unsigned int GetPageSize() const;
// Accessor for default page size
inline void GrowPageSize(unsigned int factor);
// Increase default page size by a given factor
private:
TaskAllocatorPool(const TaskAllocatorPool& right);
// Provate copy constructor
TaskAllocatorPool& operator=(const TaskAllocatorPool& right);
// Private equality operator
struct PoolLink
{
PoolLink* next;
};
class PoolChunk
{
public:
explicit PoolChunk(unsigned int sz)
: size(sz)
, mem(new char[size])
, next(0)
{
;
}
~PoolChunk() { delete[] mem; }
const unsigned int size;
char* mem;
PoolChunk* next;
};
void Grow();
// Make pool larger
private:
const unsigned int esize;
unsigned int csize;
PoolChunk* chunks;
PoolLink* head;
int nchunks;
};
//--------------------------------------------------------------------------------------//
// Inline implementation
// ************************************************************
// Alloc
// ************************************************************
//
inline void*
TaskAllocatorPool::Alloc()
{
if(head == nullptr)
Grow();
PoolLink* p = head; // return first element
head = p->next;
return p;
}
// ************************************************************
// Free
// ************************************************************
//
inline void
TaskAllocatorPool::Free(void* b)
{
PoolLink* p = static_cast<PoolLink*>(b);
p->next = head; // put b back as first element
head = p;
}
// ************************************************************
// Size
// ************************************************************
//
inline unsigned int
TaskAllocatorPool::Size() const
{
return nchunks * csize;
}
// ************************************************************
// GetNoPages
// ************************************************************
//
inline int
TaskAllocatorPool::GetNoPages() const
{
return nchunks;
}
// ************************************************************
// GetPageSize
// ************************************************************
//
inline unsigned int
TaskAllocatorPool::GetPageSize() const
{
return csize;
}
// ************************************************************
// GrowPageSize
// ************************************************************
//
inline void
TaskAllocatorPool::GrowPageSize(unsigned int sz)
{
csize = (sz) ? sz * csize : csize;
}
} // namespace PTL
+284
View File
@@ -0,0 +1,284 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
// ---------------------------------------------------------------
// Tasking class header file
//
// Class Description:
//
// This file creates the a class for handling a group of tasks that
// can be independently joined
//
// ---------------------------------------------------------------
// Author: Jonathan Madsen (Feb 13th 2018)
// ---------------------------------------------------------------
#pragma once
#include "PTL/Task.hh"
#include "PTL/ThreadPool.hh"
#include "PTL/VTaskGroup.hh"
#include <cstdint>
#include <deque>
#include <future>
#include <list>
#include <vector>
#ifdef PTL_USE_TBB
# include <tbb/tbb.h>
#endif
namespace PTL
{
class ThreadPool;
//--------------------------------------------------------------------------------------//
template <typename Tp, typename Arg = Tp>
class TaskGroup
: public VTaskGroup
, public TaskAllocator<TaskGroup<Tp, Arg>>
{
protected:
//----------------------------------------------------------------------------------//
template <typename JoinT, typename JoinArg>
struct JoinFunction
{
public:
typedef std::function<JoinT(JoinT&, JoinArg&&)> Type;
public:
JoinFunction() = default;
~JoinFunction() = default;
JoinFunction(const JoinFunction&) = default;
JoinFunction(JoinFunction&&) = default;
JoinFunction& operator=(const JoinFunction&) = default;
JoinFunction& operator=(JoinFunction&&) = default;
template <typename Func>
JoinFunction(Func&& func)
: m_func(std::forward<Func>(func))
{}
template <typename... Args>
JoinT& operator()(Args&&... args)
{
return std::move(m_func(std::forward<Args>(args)...));
}
private:
Type m_func = [](JoinT& lhs, JoinArg&&) { return lhs; };
};
//----------------------------------------------------------------------------------//
template <typename JoinArg>
struct JoinFunction<void, JoinArg>
{
public:
typedef std::function<void()> Type;
public:
JoinFunction() = default;
~JoinFunction() = default;
JoinFunction(const JoinFunction&) = default;
JoinFunction(JoinFunction&&) = default;
JoinFunction& operator=(const JoinFunction&) = default;
JoinFunction& operator=(JoinFunction&&) = default;
template <typename Func>
JoinFunction(Func&& func)
: m_func(std::forward<Func>(func))
{}
void operator()() { m_func(); }
private:
Type m_func = []() {};
};
//----------------------------------------------------------------------------------//
public:
//------------------------------------------------------------------------//
typedef decay_t<Arg> ArgTp;
typedef Tp result_type;
typedef TaskGroup<Tp, Arg> this_type;
typedef std::promise<ArgTp> promise_type;
typedef std::future<ArgTp> future_type;
typedef std::packaged_task<ArgTp()> packaged_task_type;
typedef list_type<future_type> task_list_t;
typedef typename JoinFunction<Tp, Arg>::Type join_type;
typedef typename task_list_t::iterator iterator;
typedef typename task_list_t::reverse_iterator reverse_iterator;
typedef typename task_list_t::const_iterator const_iterator;
typedef typename task_list_t::const_reverse_iterator const_reverse_iterator;
//------------------------------------------------------------------------//
template <typename... Args>
using task_type = Task<ArgTp, Args...>;
//------------------------------------------------------------------------//
public:
// Constructor
template <typename Func>
TaskGroup(Func&& _join, ThreadPool* _tp = nullptr)
: VTaskGroup(_tp)
, m_join(std::forward<Func>(_join))
{}
template <typename Up = Tp, enable_if_t<std::is_same<Up, void>::value, int> = 0>
explicit TaskGroup(ThreadPool* _tp = nullptr)
: VTaskGroup(_tp)
, m_join([]() {})
{}
// Destructor
virtual ~TaskGroup() { this->clear(); }
// delete copy-construct
TaskGroup(const this_type&) = delete;
// define move-construct
TaskGroup(this_type&& rhs) = default;
// delete copy-assign
this_type& operator=(const this_type& rhs) = delete;
// define move-assign
this_type& operator=(this_type&& rhs) = default;
public:
//------------------------------------------------------------------------//
template <typename Up>
Up* operator+=(Up* _task)
{
// store in list
vtask_list.push_back(_task);
// thread-safe increment of tasks in task group
operator++();
// add the future
m_task_set.push_back(std::move(_task->get_future()));
// return
return _task;
}
public:
//------------------------------------------------------------------------//
template <typename Func, typename... Args>
task_type<Args...>* wrap(Func&& func, Args&&... args)
{
return operator+=(new task_type<Args...>(this, std::forward<Func>(func),
std::forward<Args>(args)...));
}
public:
//------------------------------------------------------------------------//
template <typename Func, typename... Args>
void exec(Func&& func, Args&&... args)
{
m_pool->add_task(wrap(std::forward<Func>(func), std::forward<Args>(args)...));
}
//------------------------------------------------------------------------//
template <typename Func, typename... Args>
void run(Func&& func, Args&&... args)
{
m_pool->add_task(wrap(std::forward<Func>(func), std::forward<Args>(args)...));
}
//------------------------------------------------------------------------//
template <typename Func, typename... Args>
void parallel_for(const intmax_t& nitr, const intmax_t& chunks, Func&& func,
Args&&... args)
{
auto nsplit = nitr / chunks;
auto nmod = nitr % chunks;
if(nsplit < 1)
nsplit = 1;
for(intmax_t n = 0; n < nsplit; ++n)
{
auto _beg = n * chunks;
auto _end = (n + 1) * chunks + ((n + 1 == nsplit) ? nmod : 0);
run(std::forward<Func>(func), std::move(_beg), std::move(_end),
std::forward<Args>(args)...);
}
}
protected:
//------------------------------------------------------------------------//
// shorter typedefs
typedef iterator itr_t;
typedef const_iterator citr_t;
typedef reverse_iterator ritr_t;
typedef const_reverse_iterator critr_t;
public:
//------------------------------------------------------------------------//
// Get tasks with non-void return types
//
task_list_t& get_tasks() { return m_task_set; }
const task_list_t& get_tasks() const { return m_task_set; }
//------------------------------------------------------------------------//
// iterate over tasks with return type
//
itr_t begin() { return m_task_set.begin(); }
itr_t end() { return m_task_set.end(); }
citr_t begin() const { return m_task_set.begin(); }
citr_t end() const { return m_task_set.end(); }
citr_t cbegin() const { return m_task_set.begin(); }
citr_t cend() const { return m_task_set.end(); }
ritr_t rbegin() { return m_task_set.rbegin(); }
ritr_t rend() { return m_task_set.rend(); }
critr_t rbegin() const { return m_task_set.rbegin(); }
critr_t rend() const { return m_task_set.rend(); }
//------------------------------------------------------------------------//
// wait to finish
template <typename Up = Tp, enable_if_t<!std::is_same<Up, void>::value, int> = 0>
inline Up join(Up accum = {})
{
this->wait();
for(auto& itr : m_task_set)
{
using RetType = decltype(itr.get());
accum = std::move(m_join(std::ref(accum), std::forward<RetType>(itr.get())));
}
this->clear();
return accum;
}
//------------------------------------------------------------------------//
// wait to finish
template <typename Up = Tp, enable_if_t<std::is_same<Up, void>::value, int> = 0>
inline void join()
{
this->wait();
for(auto& itr : m_task_set)
itr.get();
m_join();
this->clear();
}
//------------------------------------------------------------------------//
// clear the task result history
void clear()
{
m_task_set.clear();
VTaskGroup::clear();
}
protected:
// Protected variables
task_list_t m_task_set;
join_type m_join;
};
} // namespace PTL
+297
View File
@@ -0,0 +1,297 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class header file
//
// Class Description:
//
// This file creates a class for handling the wrapping of functions
// into task objects and submitting to thread pool
//
// ---------------------------------------------------------------
// Author: Jonathan Madsen (Feb 13th 2018)
// ---------------------------------------------------------------
#pragma once
#include "PTL/TBBTaskGroup.hh"
#include "PTL/Task.hh"
#include "PTL/TaskGroup.hh"
#include "PTL/ThreadPool.hh"
#include "PTL/Threading.hh"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <iomanip>
namespace PTL
{
//======================================================================================//
class TaskManager
{
public:
typedef TaskManager this_type;
typedef ThreadPool::size_type size_type;
template <bool _Bp, typename _Tp = void>
using enable_if_t = typename std::enable_if<_Bp, _Tp>::type;
public:
// Constructor and Destructors
explicit TaskManager(ThreadPool*);
virtual ~TaskManager();
TaskManager(const this_type&) = delete;
TaskManager(this_type&&) = default;
this_type& operator=(const this_type&) = delete;
this_type& operator=(this_type&&) = default;
public:
/// get the singleton pointer
static TaskManager* GetInstance();
static TaskManager* GetInstanceIfExists();
static unsigned ncores() { return std::thread::hardware_concurrency(); }
public:
//------------------------------------------------------------------------//
// return the thread pool
inline ThreadPool* thread_pool() const { return m_pool; }
//------------------------------------------------------------------------//
// return the number of threads in the thread pool
inline size_type size() const { return m_pool->size(); }
//------------------------------------------------------------------------//
// kill all the threads
inline void finalize() { m_pool->destroy_threadpool(); }
//------------------------------------------------------------------------//
public:
//------------------------------------------------------------------------//
// direct insertion of a task
//------------------------------------------------------------------------//
template <typename... _Args>
void exec(Task<_Args...>* _task)
{
m_pool->add_task(_task);
}
//------------------------------------------------------------------------//
// direct insertion of a packaged_task
//------------------------------------------------------------------------//
template <typename _Ret, typename _Func, typename... _Args>
std::future<_Ret> async(_Func&& func, _Args&&... args)
{
typedef PackagedTask<_Ret, _Args...> task_type;
typedef task_type* task_pointer;
task_pointer _ptask =
new task_type(std::forward<_Func>(func), std::forward<_Args>(args)...);
std::future<_Ret> _f = _ptask->get_future();
m_pool->add_task(_ptask);
return _f;
}
//------------------------------------------------------------------------//
template <typename _Ret, typename _Func>
std::future<_Ret> async(_Func&& func)
{
typedef PackagedTask<_Ret> task_type;
typedef task_type* task_pointer;
task_pointer _ptask = new task_type(std::forward<_Func>(func));
std::future<_Ret> _f = _ptask->get_future();
m_pool->add_task(_ptask);
return _f;
}
//------------------------------------------------------------------------//
template <typename _Func, typename... _Args,
typename _Ret = typename std::result_of<_Func(_Args&&...)>::type>
auto async(_Func&& func, _Args&&... args) -> std::future<_Ret>
{
typedef PackagedTask<_Ret, _Args...> task_type;
auto _ptask =
new task_type(std::forward<_Func>(func), std::forward<_Args>(args)...);
auto _f = _ptask->get_future();
m_pool->add_task(_ptask);
return _f;
}
//------------------------------------------------------------------------//
public:
//------------------------------------------------------------------------//
// public wrap functions
//------------------------------------------------------------------------//
template <typename _Ret, typename _Arg, typename _Func, typename... _Args>
Task<_Ret, _Arg, _Args...>* wrap(TaskGroup<_Ret, _Arg>& tg, _Func&& func,
_Args&&... args)
{
return tg.wrap(std::forward<_Func>(func), std::forward<_Args>(args)...);
}
//------------------------------------------------------------------------//
template <typename _Ret, typename _Arg, typename _Func>
Task<_Ret, _Arg>* wrap(TaskGroup<_Ret, _Arg>& tg, _Func&& func)
{
return tg.wrap(std::forward<_Func>(func));
}
public:
//------------------------------------------------------------------------//
// public exec functions
//------------------------------------------------------------------------//
template <typename _Ret, typename _Arg, typename _Func, typename... _Args>
void exec(TaskGroup<_Ret, _Arg>& tg, _Func&& func, _Args&&... args)
{
tg.exec(std::forward<_Func>(func), std::forward<_Args>(args)...);
}
//------------------------------------------------------------------------//
template <typename _Ret, typename _Arg, typename _Func>
void exec(TaskGroup<_Ret, _Arg>& tg, _Func&& func)
{
tg.exec(std::forward<_Func>(func));
}
//------------------------------------------------------------------------//
template <typename _Ret, typename _Arg, typename _Func, typename... _Args>
void rexec(TaskGroup<_Ret, _Arg>& tg, _Func&& func, _Args&&... args)
{
tg.exec(std::forward<_Func>(func), std::forward<_Args>(args)...);
}
//------------------------------------------------------------------------//
template <typename _Ret, typename _Arg, typename _Func>
void rexec(TaskGroup<_Ret, _Arg>& tg, _Func&& func)
{
tg.exec(std::forward<_Func>(func));
}
//------------------------------------------------------------------------//
// public exec functions (void specializations)
//------------------------------------------------------------------------//
template <typename _Func, typename... _Args>
void rexec(TaskGroup<void, void>& tg, _Func&& func, _Args&&... args)
{
tg.exec(std::forward<_Func>(func), std::forward<_Args>(args)...);
}
//------------------------------------------------------------------------//
template <typename _Func>
void rexec(TaskGroup<void, void>& tg, _Func&& func)
{
tg.exec(std::forward<_Func>(func));
}
//------------------------------------------------------------------------//
#if defined(PTL_USE_TBB)
//------------------------------------------------------------------------//
// public wrap functions using TBB tasks
//------------------------------------------------------------------------//
template <typename _Ret, typename _Arg, typename _Func, typename... _Args>
Task<_Ret, _Arg, _Args...>* wrap(TBBTaskGroup<_Ret, _Arg>& tg, _Func&& func,
_Args&&... args)
{
return tg.wrap(std::forward<_Func>(func), std::forward<_Args>(args)...);
}
//------------------------------------------------------------------------//
template <typename _Ret, typename _Arg, typename _Func>
Task<_Ret, _Arg>* wrap(TBBTaskGroup<_Ret, _Arg>& tg, _Func&& func)
{
return tg.wrap(std::forward<_Func>(func));
}
//------------------------------------------------------------------------//
// public exec functions using TBB tasks
//------------------------------------------------------------------------//
template <typename _Ret, typename _Arg, typename _Func, typename... _Args>
void exec(TBBTaskGroup<_Ret, _Arg>& tg, _Func&& func, _Args&&... args)
{
tg.exec(std::forward<_Func>(func), std::forward<_Args>(args)...);
}
//------------------------------------------------------------------------//
template <typename _Ret, typename _Arg, typename _Func>
void exec(TBBTaskGroup<_Ret, _Arg>& tg, _Func&& func)
{
tg.exec(std::forward<_Func>(func));
}
//------------------------------------------------------------------------//
#endif
protected:
// Protected variables
ThreadPool* m_pool;
private:
static TaskManager*& fgInstance();
};
} // namespace PTL
//======================================================================================//
#include "TaskRunManager.hh"
//--------------------------------------------------------------------------------------//
inline PTL::TaskManager*&
PTL::TaskManager::fgInstance()
{
static thread_local TaskManager* _instance = nullptr;
return _instance;
}
//--------------------------------------------------------------------------------------//
inline PTL::TaskManager*
PTL::TaskManager::GetInstance()
{
if(!fgInstance())
{
auto nthreads = std::thread::hardware_concurrency();
std::cout << "Allocating mad::TaskManager with " << nthreads << " thread(s)..."
<< std::endl;
new TaskManager(TaskRunManager::GetMasterRunManager()->GetThreadPool());
}
return fgInstance();
}
//--------------------------------------------------------------------------------------//
inline PTL::TaskManager*
PTL::TaskManager::GetInstanceIfExists()
{
return fgInstance();
}
//--------------------------------------------------------------------------------------//
inline PTL::TaskManager::TaskManager(ThreadPool* _pool)
: m_pool(_pool)
{
if(!fgInstance())
fgInstance() = this;
}
//--------------------------------------------------------------------------------------//
inline PTL::TaskManager::~TaskManager()
{
finalize();
if(fgInstance() == this)
fgInstance() = nullptr;
}
//======================================================================================//
+97
View File
@@ -0,0 +1,97 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// class description:
// This is a class for run control in Tasking for multi-threaded runs
// It extends RunManager re-implementing multi-threaded behavior in
// key methods. See documentation for RunManager
// Users initializes an instance of this class instead of RunManager
// to start a multi-threaded simulation.
#pragma once
#include "PTL/TBBTaskGroup.hh"
#include "PTL/TaskGroup.hh"
#include "PTL/ThreadPool.hh"
#include "PTL/Threading.hh"
#include "PTL/VUserTaskQueue.hh"
#include <list>
#include <map>
namespace PTL
{
class ThreadPool;
class TaskManager;
//======================================================================================//
class TaskRunManager
{
public:
typedef TaskGroup<void> RunTaskGroup;
typedef TBBTaskGroup<void> RunTaskGroupTBB;
typedef TaskRunManager* pointer;
public:
// Parameters:
// m_task_queue: provide a custom task queue
// useTBB: only relevant if PTL_USE_TBB defined
// grainsize: 0 = auto
explicit TaskRunManager(bool useTBB = false);
virtual ~TaskRunManager();
public:
virtual int GetNumberOfThreads() const
{
return (m_thread_pool) ? m_thread_pool->size() : 0;
}
virtual size_t GetNumberActiveThreads() const
{
return (m_thread_pool) ? m_thread_pool->size() : 0;
}
public:
// Inherited methods to re-implement for MT case
virtual void Initialize(uint64_t n = std::thread::hardware_concurrency());
virtual void Terminate();
ThreadPool* GetThreadPool() const { return m_thread_pool; }
TaskManager* GetTaskManager() const { return m_task_manager; }
bool IsInitialized() const { return m_is_initialized; }
int GetVerbose() const { return m_verbose; }
void SetVerbose(int val) { m_verbose = val; }
public: // with description
// Singleton implementing master thread behavior
static TaskRunManager* GetInstance(bool useTBB = false);
static TaskRunManager* GetMasterRunManager(bool useTBB = false);
private:
static pointer& GetPrivateMasterRunManager(bool init, bool useTBB = false);
protected:
// Barriers: synch points between master and workers
bool m_is_initialized = false;
int m_verbose = 0;
uint64_t m_workers = 0;
VUserTaskQueue* m_task_queue = nullptr;
ThreadPool* m_thread_pool = nullptr;
TaskManager* m_task_manager = nullptr;
};
} // namespace PTL
+125
View File
@@ -0,0 +1,125 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class header
// Class Description:
// ---------------------------------------------------------------
// Author: Jonathan Madsen
// ---------------------------------------------------------------
#pragma once
#include <cstddef>
#include <cstdint>
#include <deque>
#if defined(PTL_USE_TBB)
# include <tbb/global_control.h>
# include <tbb/task_group.h>
# include <tbb/task_scheduler_init.h>
#endif
namespace PTL
{
//--------------------------------------------------------------------------------------//
#if defined(PTL_USE_TBB)
using tbb_global_control_t = ::tbb::global_control;
using tbb_task_group_t = ::tbb::task_group;
#else
namespace tbb
{
class task_group
{
public:
// dummy constructor
task_group() {}
// dummy wait
inline void wait() {}
// run function
template <typename _Func>
inline void run(_Func f)
{
f();
}
// run and wait
template <typename _Func>
inline void run_and_wait(_Func f)
{
f();
}
};
class global_control
{
public:
enum parameter
{
max_allowed_parallelism,
thread_stack_size
};
global_control(parameter p, size_t value);
~global_control();
static size_t active_value(parameter param);
};
} // namespace tbb
using tbb_global_control_t = tbb::global_control;
using tbb_task_group_t = tbb::task_group;
#endif
//--------------------------------------------------------------------------------------//
class ThreadPool;
class VUserTaskQueue;
//--------------------------------------------------------------------------------------//
class ThreadData
{
public:
template <typename _Tp>
using TaskStack = std::deque<_Tp>;
ThreadData(ThreadPool* tp);
~ThreadData();
void update();
public:
bool is_master;
bool within_task;
intmax_t task_depth;
ThreadPool* thread_pool;
VUserTaskQueue* current_queue;
TaskStack<VUserTaskQueue*> queue_stack;
public:
// Public functions
static ThreadData*& GetInstance();
};
//--------------------------------------------------------------------------------------//
} // namespace PTL
+368
View File
@@ -0,0 +1,368 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class header file
//
// Class Description:
//
// This file creates a class for an efficient thread-pool that
// accepts work in the form of tasks.
//
// ---------------------------------------------------------------
// Author: Jonathan Madsen (Feb 13th 2018)
// ---------------------------------------------------------------
#pragma once
#include "PTL/AutoLock.hh"
#include "PTL/ThreadData.hh"
#include "PTL/Threading.hh"
#include "PTL/VTask.hh"
#include "PTL/VTaskGroup.hh"
#include "PTL/VUserTaskQueue.hh"
#ifdef PTL_USE_TBB
# include <tbb/global_control.h>
# include <tbb/tbb.h>
#endif
// C
#include <cstdint>
#include <cstdlib>
#include <cstring>
// C++
#include <atomic>
#include <deque>
#include <iostream>
#include <map>
#include <memory>
#include <queue>
#include <stack>
#include <unordered_map>
#include <vector>
namespace PTL
{
class ThreadPool
{
public:
template <typename _KeyType, typename _MappedType, typename _HashType = _KeyType>
using uomap = std::unordered_map<_KeyType, _MappedType, std::hash<_HashType>>;
// pod-types
using size_type = size_t;
using task_count_type = std::shared_ptr<std::atomic_uintmax_t>;
using atomic_int_type = std::shared_ptr<std::atomic_uintmax_t>;
using pool_state_type = std::shared_ptr<std::atomic_short>;
using atomic_bool_type = std::shared_ptr<std::atomic_bool>;
// objects
using task_type = VTask;
using lock_t = std::shared_ptr<Mutex>;
using condition_t = std::shared_ptr<Condition>;
using task_pointer = task_type*;
using task_queue_t = VUserTaskQueue;
// containers
typedef std::deque<ThreadId> thread_list_t;
typedef std::vector<bool> bool_list_t;
typedef std::map<ThreadId, uintmax_t> thread_id_map_t;
typedef std::map<uintmax_t, ThreadId> thread_index_map_t;
typedef std::function<void()> initialize_func_t;
// functions
typedef std::function<intmax_t(intmax_t)> affinity_func_t;
public:
// Constructor and Destructors
ThreadPool(
const size_type& pool_size, VUserTaskQueue* task_queue = nullptr,
bool _use_affinity = GetEnv<bool>("PTL_CPU_AFFINITY", false),
const affinity_func_t& = [](intmax_t) {
static std::atomic<intmax_t> assigned;
intmax_t _assign = assigned++;
return _assign % Thread::hardware_concurrency();
});
// Virtual destructors are required by abstract classes
// so add it by default, just in case
virtual ~ThreadPool();
ThreadPool(const ThreadPool&) = delete;
ThreadPool(ThreadPool&&) = default;
ThreadPool& operator=(const ThreadPool&) = delete;
ThreadPool& operator=(ThreadPool&&) = default;
public:
// Public functions
size_type initialize_threadpool(size_type); // start the threads
size_type destroy_threadpool(); // destroy the threads
size_type stop_thread();
public:
// Public functions related to TBB
static bool using_tbb();
// enable using TBB if available
static void set_use_tbb(bool val);
public:
// add tasks for threads to process
size_type add_task(task_pointer task, int bin = -1);
// size_type add_thread_task(ThreadId id, task_pointer&& task);
// add a generic container with iterator
template <typename _List_t>
size_type add_tasks(_List_t&);
Thread* get_thread(size_type _n) const;
Thread* get_thread(std::thread::id id) const;
task_queue_t* get_queue() const { return m_task_queue; }
// only relevant when compiled with PTL_USE_TBB
static tbb_global_control_t*& tbb_global_control();
void set_initialization(initialize_func_t f) { m_init_func = f; }
void reset_initialization()
{
auto f = []() {};
m_init_func = f;
}
public:
// get the pool state
const pool_state_type& state() const { return m_pool_state; }
// see how many main task threads there are
size_type size() const { return m_pool_size; }
// set the thread pool size
void resize(size_type _n);
// affinity assigns threads to cores, assignment at constructor
bool using_affinity() const { return m_use_affinity; }
bool is_alive() { return m_alive_flag->load(); }
void notify();
void notify_all();
void notify(size_type);
bool is_initialized() const;
int get_active_threads_count() const
{
return (m_thread_awake) ? m_thread_awake->load() : 0;
}
void set_affinity(affinity_func_t f) { m_affinity_func = f; }
void set_affinity(intmax_t i, Thread&);
void SetVerbose(int n) { m_verbose = n; }
int GetVerbose() const { return m_verbose; }
bool is_master() const { return ThisThread::get_id() == m_master_tid; }
public:
// read FORCE_NUM_THREADS environment variable
static const thread_id_map_t& GetThreadIDs();
static uintmax_t GetThisThreadID();
protected:
void execute_thread(VUserTaskQueue*); // function thread sits in
int insert(const task_pointer&, int = -1);
int run_on_this(task_pointer);
protected:
// called in THREAD INIT
static void start_thread(ThreadPool*, intmax_t = -1);
void record_entry()
{
if(m_thread_active)
++(*m_thread_active);
}
void record_exit()
{
if(m_thread_active)
--(*m_thread_active);
}
private:
// Private variables
// random
bool m_use_affinity;
bool m_tbb_tp;
int m_verbose = 0;
size_type m_pool_size = 0;
ThreadId m_master_tid;
atomic_bool_type m_alive_flag = std::make_shared<std::atomic_bool>(false);
pool_state_type m_pool_state = std::make_shared<std::atomic_short>(0);
atomic_int_type m_thread_awake = std::make_shared<std::atomic_uintmax_t>();
atomic_int_type m_thread_active = std::make_shared<std::atomic_uintmax_t>();
// locks
lock_t m_task_lock = std::make_shared<Mutex>();
// conditions
condition_t m_task_cond = std::make_shared<Condition>();
// containers
bool_list_t m_is_joined; // join list
bool_list_t m_is_stopped; // lets thread know to stop
thread_list_t m_main_threads; // storage for active threads
thread_list_t m_stop_threads; // storage for stopped threads
// task queue
task_queue_t* m_task_queue;
tbb_task_group_t* m_tbb_task_group;
// functions
initialize_func_t m_init_func;
affinity_func_t m_affinity_func;
private:
// Private static variables
PTL_DLL static thread_id_map_t f_thread_ids;
PTL_DLL static bool f_use_tbb;
};
//--------------------------------------------------------------------------------------//
inline void
ThreadPool::notify()
{
// wake up one thread that is waiting for a task to be available
if(m_thread_awake && m_thread_awake->load() < m_pool_size)
{
AutoLock l(*m_task_lock);
m_task_cond->notify_one();
}
}
//--------------------------------------------------------------------------------------//
inline void
ThreadPool::notify_all()
{
// wake all threads
AutoLock l(*m_task_lock);
m_task_cond->notify_all();
}
//--------------------------------------------------------------------------------------//
inline void
ThreadPool::notify(size_type ntasks)
{
if(ntasks == 0)
return;
// wake up as many threads that tasks just added
if(m_thread_awake && m_thread_awake->load() < m_pool_size)
{
AutoLock l(*m_task_lock);
if(ntasks < this->size())
{
for(size_type i = 0; i < ntasks; ++i)
m_task_cond->notify_one();
}
else
m_task_cond->notify_all();
}
}
//--------------------------------------------------------------------------------------//
// local function for getting the tbb task scheduler
inline tbb_global_control_t*&
ThreadPool::tbb_global_control()
{
static thread_local tbb_global_control_t* _instance = nullptr;
return _instance;
}
//--------------------------------------------------------------------------------------//
inline void
ThreadPool::resize(size_type _n)
{
if(_n == m_pool_size)
return;
initialize_threadpool(_n);
m_task_queue->resize(static_cast<intmax_t>(_n));
}
//--------------------------------------------------------------------------------------//
inline int
ThreadPool::run_on_this(task_pointer task)
{
auto _func = [=]() {
(*task)();
if(!task->group())
delete task;
};
if(m_tbb_tp && m_tbb_task_group)
{
m_tbb_task_group->run(_func);
}
else
{
_func();
}
// return the number of tasks added to task-list
return 0;
}
//--------------------------------------------------------------------------------------//
inline int
ThreadPool::insert(const task_pointer& task, int bin)
{
static thread_local ThreadData* _data = ThreadData::GetInstance();
// pass the task to the queue
auto ibin = m_task_queue->InsertTask(task, _data, bin);
notify();
return ibin;
}
//--------------------------------------------------------------------------------------//
inline ThreadPool::size_type
ThreadPool::add_task(task_pointer task, int bin)
{
// if not native (i.e. TBB) then return
if(!task->is_native_task())
return 0;
// if we haven't built thread-pool, just execute
if(!m_alive_flag->load())
return static_cast<size_type>(run_on_this(task));
return static_cast<size_type>(insert(task, bin));
}
//--------------------------------------------------------------------------------------//
template <typename _List_t>
inline ThreadPool::size_type
ThreadPool::add_tasks(_List_t& c)
{
if(!m_alive_flag) // if we haven't built thread-pool, just execute
{
for(auto& itr : c)
run(itr);
c.clear();
return 0;
}
// TODO: put a limit on how many tasks can be added at most
auto c_size = c.size();
for(auto& itr : c)
{
if(!itr->is_native_task())
--c_size;
else
{
//++(m_task_queue);
m_task_queue->InsertTask(itr);
}
}
c.clear();
// notify sleeping threads
notify(c_size);
return c_size;
}
//======================================================================================//
} // namespace PTL
+237
View File
@@ -0,0 +1,237 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class header file
//
// Class Description:
//
// This file defines types and macros used to expose Tasking threading model.
#pragma once
#include "PTL/Globals.hh"
#include "PTL/Types.hh"
#include <chrono>
#include <condition_variable>
#include <future>
#include <mutex>
#include <thread>
#include <vector>
namespace PTL
{
// Macro to put current thread to sleep
//
#define THREADSLEEP(tick) std::this_thread::sleep_for(std::chrono::seconds(tick))
// will be used in the future when migrating threading to task-based style
template <typename _Tp>
using Future = std::future<_Tp>;
template <typename _Tp>
using SharedFuture = std::shared_future<_Tp>;
template <typename _Tp>
using Promise = std::promise<_Tp>;
//
// NOTE ON Tasking SERIAL BUILDS AND MUTEX/UNIQUE_LOCK
// ==================================================
//
// Mutex and RecursiveMutex are always C++11 std::mutex types
// however, in serial mode, using MUTEXLOCK and MUTEXUNLOCK on these
// types has no effect -- i.e. the mutexes are not actually locked or unlocked
//
// Additionally, when a Mutex or RecursiveMutex is used with AutoLock
// and RecursiveAutoLock, respectively, these classes also suppressing
// the locking and unlocking of the mutex. Regardless of the build type,
// AutoLock and RecursiveAutoLock inherit from std::unique_lock<std::mutex>
// and std::unique_lock<std::recursive_mutex>, respectively. This means
// that in situations (such as is needed by the analysis category), the
// AutoLock and RecursiveAutoLock can be passed to functions requesting
// a std::unique_lock. Within these functions, since std::unique_lock
// member functions are not virtual, they will not retain the dummy locking
// and unlocking behavior
// --> An example of this behavior can be found in AutoLock.hh
//
// Jonathan R. Madsen (February 21, 2018)
//
// global mutex types
typedef std::mutex Mutex;
typedef std::recursive_mutex RecursiveMutex;
// mutex macros
#define MUTEX_INITIALIZER \
{}
#define MUTEXINIT(mutex) \
; \
;
#define MUTEXDESTROY(mutex) \
; \
;
// static functions: get_id(), sleep_for(...), sleep_until(...), yield(),
namespace ThisThread
{
using namespace std::this_thread;
}
// will be used in the future when migrating threading to task-based style
// and are currently used in unit tests
template <typename _Tp>
using Promise = std::promise<_Tp>;
template <typename _Tp>
using Future = std::future<_Tp>;
template <typename _Tp>
using SharedFuture = std::shared_future<_Tp>;
// Some useful types
typedef void* ThreadFunReturnType;
typedef void* ThreadFunArgType;
typedef int (*thread_lock)(Mutex*);
typedef int (*thread_unlock)(Mutex*);
// Helper function for getting a unique static mutex for a specific
// class or type
// Usage example:
// a template class "Cache<T>" that required a static
// mutex for specific to type T:
// AutoLock l(TypeMutex<Cache<T>>());
template <typename _Tp>
Mutex&
TypeMutex(const unsigned int& _n = 0)
{
static Mutex* _mutex = new Mutex();
if(_n == 0)
return *_mutex;
static std::vector<Mutex*> _mutexes;
if(_n > _mutexes.size())
_mutexes.resize(_n, nullptr);
if(!_mutexes[_n])
_mutexes[_n] = new Mutex();
return *(_mutexes[_n - 1]);
}
// Helper function for getting a unique static recursive_mutex for a
// specific class or type
// Usage example:
// a template class "Cache<T>" that required a static
// recursive_mutex for specific to type T:
// RecursiveAutoLock l(TypeRecursiveMutex<Cache<T>>());
template <typename _Tp>
RecursiveMutex&
TypeRecursiveMutex(const unsigned int& _n = 0)
{
static RecursiveMutex* _mutex = new RecursiveMutex();
if(_n == 0)
return *(_mutex);
static std::vector<RecursiveMutex*> _mutexes;
if(_n > _mutexes.size())
_mutexes.resize(_n, nullptr);
if(!_mutexes[_n])
_mutexes[_n] = new RecursiveMutex();
return *(_mutexes[_n - 1]);
}
//======================================================================================//
// global thread types
typedef std::thread Thread;
typedef std::thread::native_handle_type NativeThread;
// mutex macros
#define MUTEXLOCK(mutex) \
{ \
(mutex)->lock(); \
}
#define MUTEXUNLOCK(mutex) \
{ \
(mutex)->unlock(); \
}
// Macro to join thread
#define THREADJOIN(worker) (worker).join()
// std::thread::id does not cast to integer
typedef std::thread::id Pid_t;
// Instead of previous macro taking one argument, define function taking
// unlimited arguments
template <typename _Worker, typename _Func, typename... _Args>
void
THREADCREATE(_Worker*& worker, _Func func, _Args... args)
{
*worker = Thread(func, std::forward<_Args>(args)...);
}
// Conditions
//
// See MTRunManager for example on how to use these
//
typedef std::condition_variable Condition;
#define CONDITION_INITIALIZER \
{}
#define CONDITIONWAIT(cond, lock) (cond)->wait(*lock);
#define CONDITIONWAITLAMBDA(cond, lock, lambda) (cond)->wait(*lock, lambda);
#define CONDITIONNOTIFY(cond) (cond)->notify_one();
#define CONDITIONBROADCAST(cond) (cond)->notify_all();
//
//======================================================================================//
// Define here after Thread has been typedef
typedef Thread::id ThreadId;
//======================================================================================//
namespace Threading
{
enum
{
SEQUENTIAL_ID = -2,
MASTER_ID = -1,
WORKER_ID = 0,
GENERICTHREAD_ID = -1000
};
Pid_t
GetPidId();
unsigned
GetNumberOfCores();
int
GetThreadId();
bool
IsWorkerThread();
bool
IsMasterThread();
void
SetThreadId(int aNewValue);
bool
SetPinAffinity(int idx, NativeThread& at);
int
WorkerThreadLeavesPool();
int
WorkerThreadJoinsPool();
int
GetNumberOfRunningWorkerThreads();
} // namespace Threading
} // namespace PTL
+443
View File
@@ -0,0 +1,443 @@
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <cstring>
#include <functional>
#include <tuple>
#include <type_traits>
namespace PTL
{
template <typename _Tp, typename _Up>
struct SmallerThanT
{
static constexpr bool value = sizeof(_Tp) < sizeof(_Up);
};
//======================================================================================//
// CTValue == compile-time value
//
// useful to work with sequences of compile-time values, such as the bounds of a
// multidimensional array or indices into another typelist.
template <typename _Tp, _Tp Value>
struct CTValue
{
static constexpr _Tp value = Value;
};
//======================================================================================//
template <std::size_t Height, typename _Tp,
bool = std::is_class<_Tp>::value && !std::is_final<_Tp>::value>
class TupleElt;
//--------------------------------------------------------------------------------------//
// specialization that does not satisfy is_class<> and not is_final<>
//
template <std::size_t Height, typename _Tp>
class TupleElt<Height, _Tp, false>
{
_Tp value;
public:
TupleElt() = default;
template <typename _Up>
TupleElt(_Up&& other)
: value(std::forward<_Up>(other))
{}
_Tp& get() { return value; }
_Tp const& get() const { return value; }
};
//--------------------------------------------------------------------------------------//
// specialization that does satisfy is_class<> and not is_final<>
//
template <std::size_t Height, typename _Tp>
class TupleElt<Height, _Tp, true> : private _Tp
{
public:
TupleElt() = default;
template <typename _Up>
explicit TupleElt(_Up&& other)
: _Tp(std::forward<_Up>(other))
{}
_Tp& get() { return *this; }
const _Tp& get() const { return *this; }
};
//======================================================================================//
template <unsigned H, typename T>
T&
get_height(TupleElt<H, T>& te)
{
return te.get();
}
//======================================================================================//
template <typename... Types>
class Tuple;
//--------------------------------------------------------------------------------------//
// recursive case:
template <typename Head, typename... Tail>
class Tuple<Head, Tail...>
: private TupleElt<sizeof...(Tail), Head>
, private Tuple<Tail...>
{
template <unsigned I, typename... Elements>
friend auto get(Tuple<Elements...>& t)
-> decltype(get_height<sizeof...(Elements) - I - 1>(t));
public:
Head& head() { return static_cast<HeadElt*>(this)->get(); }
const Head& head() const { return static_cast<HeadElt const*>(this)->get(); }
Tuple<Tail...>& tail() { return *this; }
const Tuple<Tail...>& tail() const { return *this; }
private:
using HeadElt = TupleElt<sizeof...(Tail), Head>;
};
//--------------------------------------------------------------------------------------//
// basis case:
template <>
class Tuple<>
{
// no storage required
};
//--------------------------------------------------------------------------------------//
template <unsigned I, typename... Elements>
auto
get(Tuple<Elements...>& t) -> decltype(get_height<sizeof...(Elements) - I - 1>(t))
{
return get_height<sizeof...(Elements) - I - 1>(t);
}
template <typename... _Tp>
using TypeList = Tuple<_Tp...>;
//======================================================================================//
template <typename List>
class IsEmpty
{
public:
static constexpr bool value = false;
};
template <>
class IsEmpty<Tuple<>>
{
public:
static constexpr bool value = true;
};
template <typename List, typename NewElement>
class PushBackT;
template <typename... Elements, typename NewElement>
class PushBackT<Tuple<Elements...>, NewElement>
{
public:
using Type = Tuple<Elements..., NewElement>;
};
template <typename List, typename NewElement>
using PushBack = typename PushBackT<List, NewElement>::Type;
template <typename List>
class PopFrontT;
template <typename Head, typename... Tail>
class PopFrontT<std::tuple<Head, Tail...>>
{
public:
using Type = std::tuple<Tail...>;
};
template <typename List>
using PopFront = typename PopFrontT<List>::Type;
template <typename List, typename Element>
class PushFrontT;
template <typename... Types, typename Element>
class PushFrontT<std::tuple<Types...>, Element>
{
public:
using Type = std::tuple<Element, Types...>;
};
template <typename List, typename NewElement>
using PushFront = typename PushFrontT<List, NewElement>::Type;
template <typename... Types, typename V>
PushFront<std::tuple<Types...>, V>
pushFront(std::tuple<Types...> const& tuple, V const& value)
{
return PushFront<std::tuple<Types...>, V>(value, tuple);
}
template <typename T, T... Values>
struct Valuelist
{};
template <typename... Types>
struct Front;
template <typename FrontT, typename... Types>
struct Front<FrontT, Types...>
{
using Type = FrontT;
};
template <typename... Types>
struct Back;
template <typename BackT, typename... Types>
struct Back<Types..., BackT>
{ // ERROR: pack expansion not at the end of
using Type = BackT; // template argument list
};
//======================================================================================//
template <typename List, template <typename T> class MetaFun,
bool Empty = IsEmpty<List>::value>
class TransformT;
// recursive case:
template <typename List, template <typename T> class MetaFun>
class TransformT<List, MetaFun, false>
: public PushFrontT<typename TransformT<PopFront<List>, MetaFun>::Type,
typename MetaFun<Front<List>>::Type>
{};
// basis case:
template <typename List, template <typename T> class MetaFun>
class TransformT<List, MetaFun, true>
{
public:
using Type = List;
};
template <typename List, template <typename T> class MetaFun>
using Transform = typename TransformT<List, MetaFun>::Type;
template <typename... Elements, template <typename T> class MetaFun>
class TransformT<Tuple<Elements...>, MetaFun, false>
{
public:
using Type = Tuple<typename MetaFun<Elements>::Type...>;
};
template <size_t N>
struct ForwardTupleAsArgs
{
template <typename Func, typename Head, typename... Tail>
static inline auto forward(Func&& func, Head&& head, Tail&&... tail)
-> decltype(ForwardTupleAsArgs<N - 1>::forward(
std::forward<Func>(func), std::forward<Head>(head),
std::get<N - 1>(std::forward<Head>(head)), std::forward<Tail>(tail)...))
{
return ForwardTupleAsArgs<N - 1>::forward(
std::forward<Func>(func), std::forward<Head>(head),
std::get<N - 1>(std::forward<Head>(head)), std::forward<Tail>(tail)...);
}
};
//======================================================================================//
template <>
struct ForwardTupleAsArgs<0>
{
template <typename Func, typename Head, typename... Tail>
static inline auto forward(Func&& func, Head&&, Tail&&... tail)
-> decltype(std::forward<Func>(func)(std::forward<Tail>(tail)...))
{
return std::forward<Func>(func)(std::forward<Tail>(tail)...);
}
};
//======================================================================================//
template <size_t N>
struct ForEachTupleArg
{
template <typename Func, typename Head>
static inline auto apply(Func&& func, Head&& head)
{
std::forward<Func>(func)(std::forward<Head>(head));
}
template <typename Func, typename Head, typename... Tail>
static inline void apply(Func&& func, Head&& head, Tail&&... tail)
{
std::forward<Func>(func)(std::forward<Head>(head));
ForEachTupleArg<N - 1>::apply(std::forward<Func>(func),
std::forward<Tail>(tail)...);
}
};
//======================================================================================//
template <typename Func, typename Tuple>
void
for_each_tuple_arg(Func&& func, Tuple&& _tuple)
{
ForEachTupleArg<std::tuple_size<Tuple>::value>::apply(
std::forward<Func>(func), std::forward<std::tuple>(_tuple));
}
//======================================================================================//
template <typename Func, typename Tuple, std::size_t Head>
inline auto
InvokeSequence_impl(const Func& func, const Tuple& data)
{
func(std::get<Head>(data));
}
//======================================================================================//
template <typename Func, typename Tuple, std::size_t Head, std::size_t... Tail>
inline auto
InvokeSequence_impl(const Func& func, const Tuple& data)
{
func(std::get<Head>(data));
InvokeSequence_impl<Func, Tuple, Tail...>(func, data);
}
//======================================================================================//
template <typename Func, typename Tuple, std::size_t N = std::tuple_size<Tuple>::value,
typename Indices = std::make_index_sequence<N>>
inline auto
InvokeSequence(const Func& func, const Tuple& data)
{
return InvokeSequence_impl(func, data);
}
//======================================================================================//
// Convert array into a tuple
template <typename Container, std::size_t... N>
inline auto
ContainerToTuple_impl(const Container& tasks, std::index_sequence<N...>)
{
return std::make_tuple(tasks[N]...);
}
//======================================================================================//
template <std::size_t N, typename Container,
typename Indices = std::make_index_sequence<N>>
inline auto
ContainerToTuple(const Container& tasks)
{
return ContainerToTuple_impl(tasks, Indices{});
}
//======================================================================================//
template <typename... Args>
struct tuple_subset
{
static std::tuple<> get(const std::tuple<>&) { return std::tuple<>{}; }
template <typename... SubArgs>
static std::tuple<SubArgs...> get(const std::tuple<Args...>& t)
{
return std::tuple<SubArgs...>{ std::get<SubArgs>(t)... };
}
};
template <typename Head>
inline void
tuple_transform(const std::function<void(const Head&)>& pred,
const std::tuple<Head>& data)
{
pred(std::get<0>(data));
}
template <typename Head, typename... Tail>
inline void
tuple_transform(const std::function<void(const Head&)>& pred,
const std::tuple<Head, Tail...>& data)
{
pred(std::get<0>(data));
auto subset = tuple_subset<Head, Tail...>::template get<Tail...>(data);
tuple_transform<Tail...>(pred, std::forward<decltype(subset)>(subset));
}
template <typename Head, typename... Tail>
struct transform_tuple
{
using Function = std::function<void(Head)>;
template <typename TupleType>
static void apply(const Function& func, const TupleType& t)
{
func(std::get<0>(t));
PopFront<TupleType> nt = std::tuple<Tail...>{ std::get<Tail>(t)... };
transform_tuple<Tail...>::apply(func, nt);
}
};
template <typename Head>
struct transform_tuple<Head>
{
using Function = std::function<void(Head)>;
template <typename TupleType>
static void apply(const Function& func, const TupleType& t)
{
func(std::get<0>(t));
}
};
//======================================================================================//
template <typename Func, typename... Elements, unsigned... Indices>
auto
applyImpl(Func func, std::tuple<Elements...> const& t, Valuelist<unsigned, Indices...>)
-> decltype(func(std::get<Indices>(t)...))
{
return func(std::get<Indices>(t)...);
}
template <typename Func, typename... Elements, unsigned N = sizeof...(Elements)>
auto
apply(Func func, std::tuple<Elements...> const& t)
-> decltype(applyImpl(func, t, std::make_index_sequence<N>()))
{
return applyImpl(func, t, std::make_index_sequence<N>());
}
} // namespace PTL
+210
View File
@@ -0,0 +1,210 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Tasking native types
//
#pragma once
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
// Disable warning C4786 on WIN32 architectures:
// identifier was truncated to '255' characters
// in the debug information
//
# pragma warning(disable : 4786)
//
// Define DLL export macro for WIN32 systems for
// importing/exporting external symbols to DLLs
//
# if defined G4LIB_BUILD_DLL
# define DLLEXPORT __declspec(dllexport)
# define DLLIMPORT __declspec(dllimport)
# else
# define DLLEXPORT
# define DLLIMPORT
# endif
//
// Unique identifier for global module
//
# if defined PTL_ALLOC_EXPORT
# define PTL_DLL DLLEXPORT
# else
# define PTL_DLL DLLIMPORT
# endif
#else
# define DLLEXPORT
# define DLLIMPORT
# define PTL_DLL
#endif
#include <atomic>
#include <complex>
#include <limits>
#include <memory>
namespace PTL
{
//--------------------------------------------------------------------------------------//
//
namespace api
{
struct native
{};
struct tbb
{};
} // namespace api
//
//--------------------------------------------------------------------------------------//
//
template <typename Tp, typename Tag = api::native, typename Ptr = std::shared_ptr<Tp>,
typename Pair = std::pair<Ptr, Ptr>>
Pair&
GetSharedPointerPair()
{
static auto _master = std::make_shared<Tp>();
static std::atomic<int64_t> _count(0);
static thread_local auto _inst =
Pair(_master, Ptr((_count++ == 0) ? nullptr : new Tp()));
return _inst;
}
//
//--------------------------------------------------------------------------------------//
//
template <typename Tp, typename Tag = api::native, typename Ptr = std::shared_ptr<Tp>,
typename Pair = std::pair<Ptr, Ptr>>
Ptr
GetSharedPointerPairInstance()
{
static thread_local auto& _pinst = GetSharedPointerPair<Tp, Tag>();
static thread_local auto& _inst = _pinst.second.get() ? _pinst.second : _pinst.first;
return _inst;
}
//
//--------------------------------------------------------------------------------------//
//
template <typename Tp, typename Tag = api::native, typename Ptr = std::shared_ptr<Tp>,
typename Pair = std::pair<Ptr, Ptr>>
Ptr
GetSharedPointerPairMasterInstance()
{
static auto& _pinst = GetSharedPointerPair<Tp, Tag>();
static auto _inst = _pinst.first;
return _inst;
}
//--------------------------------------------------------------------------------------//
template <typename CountedType>
class CountedObject
{
public:
typedef CountedObject<CountedType> this_type;
typedef CountedObject<void> void_type;
public:
// return number of existing objects:
static int64_t live() { return count(); }
static constexpr int64_t zero() { return static_cast<int64_t>(0); }
static int64_t max_depth() { return fmax_depth; }
static void enable(const bool& val) { fenabled = val; }
static void set_max_depth(const int64_t& val) { fmax_depth = val; }
static bool is_enabled() { return fenabled; }
template <typename _Tp = CountedType,
typename std::enable_if<std::is_same<_Tp, void>::value>::type* = nullptr>
static bool enable()
{
return fenabled && fmax_depth > count();
}
// the void type is consider the global setting
template <typename _Tp = CountedType,
typename std::enable_if<!std::is_same<_Tp, void>::value>::type* = nullptr>
static bool enable()
{
return void_type::is_enabled() && void_type::max_depth() > count() && fenabled &&
fmax_depth > count();
}
protected:
// default constructor
CountedObject() { ++count(); }
~CountedObject() { --count(); }
CountedObject(const this_type&) { ++count(); }
explicit CountedObject(this_type&&) { ++count(); }
private:
// number of existing objects
static int64_t& thread_number();
static int64_t& master_count();
static int64_t& count();
static int64_t fmax_depth;
static bool fenabled;
};
//--------------------------------------------------------------------------------------//
template <typename CountedType>
int64_t&
CountedObject<CountedType>::thread_number()
{
static std::atomic<int64_t> _all_instance;
static thread_local int64_t _instance = _all_instance++;
return _instance;
}
//--------------------------------------------------------------------------------------//
template <typename CountedType>
int64_t&
CountedObject<CountedType>::master_count()
{
static int64_t _instance = 0;
return _instance;
}
//--------------------------------------------------------------------------------------//
template <typename CountedType>
int64_t&
CountedObject<CountedType>::count()
{
if(thread_number() == 0)
return master_count();
static thread_local int64_t _instance = master_count();
return _instance;
}
//--------------------------------------------------------------------------------------//
template <typename CountedType>
int64_t CountedObject<CountedType>::fmax_depth = std::numeric_limits<int64_t>::max();
//--------------------------------------------------------------------------------------//
template <typename CountedType>
bool CountedObject<CountedType>::fenabled = true;
//======================================================================================//
} // namespace PTL
// Forward declation of void type argument for usage in direct object
// persistency to define fake default constructors
//
class __void__;
+173
View File
@@ -0,0 +1,173 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class header
// Class Description:
// ---------------------------------------------------------------
// Author: Jonathan Madsen
// ---------------------------------------------------------------
#pragma once
#include "PTL/Globals.hh"
#include "PTL/Threading.hh"
#include "PTL/Types.hh"
#include "PTL/VUserTaskQueue.hh"
#include <atomic>
#include <deque>
#include <list>
#include <queue>
#include <random>
#include <set>
#include <stack>
namespace PTL
{
class VTask;
class VTaskGroup;
class TaskSubQueue; // definition in UserTaskQueue.icc
class UserTaskQueue : public VUserTaskQueue
{
public:
typedef VTask* task_pointer;
typedef std::vector<TaskSubQueue*> TaskSubQueueContainer;
typedef std::default_random_engine random_engine_t;
typedef std::uniform_int_distribution<int> int_dist_t;
public:
// Constructor and Destructors
UserTaskQueue(intmax_t nworkers = -1, UserTaskQueue* = nullptr);
// Virtual destructors are required by abstract classes
// so add it by default, just in case
virtual ~UserTaskQueue() override;
public:
// Virtual function for getting a task from the queue
virtual task_pointer GetTask(intmax_t subq = -1, intmax_t nitr = -1) override;
// Virtual function for inserting a task into the queue
virtual intmax_t InsertTask(task_pointer, ThreadData* = nullptr,
intmax_t subq = -1) override;
// if executing only tasks in threads bin
task_pointer GetThreadBinTask();
// Overload this function to hold threads
virtual void Wait() override {}
virtual void resize(intmax_t) override;
virtual bool empty() const override;
virtual size_type size() const override;
virtual size_type bin_size(size_type bin) const override;
virtual bool bin_empty(size_type bin) const override;
inline bool true_empty() const override;
inline size_type true_size() const override;
virtual void ExecuteOnAllThreads(ThreadPool* tp, function_type f) override;
virtual void ExecuteOnSpecificThreads(ThreadIdSet tid_set, ThreadPool* tp,
function_type f) override;
virtual VUserTaskQueue* clone() override;
virtual intmax_t GetThreadBin() const override;
protected:
intmax_t GetInsertBin() const;
private:
void AcquireHold();
void ReleaseHold();
private:
bool m_is_clone;
intmax_t m_thread_bin;
mutable intmax_t m_insert_bin;
std::atomic_bool* m_hold;
std::atomic_uintmax_t* m_ntasks;
Mutex* m_mutex;
TaskSubQueueContainer* m_subqueues;
std::vector<int> m_rand_list;
std::vector<int>::iterator m_rand_itr;
};
} // namespace PTL
//======================================================================================//
#include "PTL/UserTaskQueue.icc"
//======================================================================================//
inline bool
PTL::UserTaskQueue::empty() const
{
return (m_ntasks->load(std::memory_order_relaxed) == 0);
}
//======================================================================================//
inline PTL::UserTaskQueue::size_type
PTL::UserTaskQueue::size() const
{
return m_ntasks->load(std::memory_order_relaxed);
}
//======================================================================================//
inline PTL::UserTaskQueue::size_type
PTL::UserTaskQueue::bin_size(size_type bin) const
{
return (*m_subqueues)[bin]->size();
}
//======================================================================================//
inline bool
PTL::UserTaskQueue::bin_empty(size_type bin) const
{
return (*m_subqueues)[bin]->empty();
}
//======================================================================================//
inline bool
PTL::UserTaskQueue::true_empty() const
{
for(const auto& itr : *m_subqueues)
if(!itr->empty())
return false;
return true;
}
//======================================================================================//
inline PTL::UserTaskQueue::size_type
PTL::UserTaskQueue::true_size() const
{
size_type _n = 0;
for(const auto& itr : *m_subqueues)
_n += itr->size();
return _n;
}
//======================================================================================//
+184
View File
@@ -0,0 +1,184 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class header
// Class Description:
// ---------------------------------------------------------------
// Author: Jonathan Madsen
// ---------------------------------------------------------------
#include "PTL/AutoLock.hh"
#include "PTL/Globals.hh"
#include "PTL/ThreadPool.hh"
#include "PTL/Threading.hh"
#include "PTL/Types.hh"
#include "PTL/VUserTaskQueue.hh"
#include <atomic>
#include <cassert>
#include <deque>
#include <list>
#include <queue>
#include <stack>
namespace PTL
{
class VTask;
//======================================================================================//
class TaskSubQueue
{
public:
template <typename _Tp>
using container = std::deque<_Tp>;
typedef VTask* task_pointer;
typedef container<task_pointer> container_type;
typedef container_type::size_type size_type;
public:
TaskSubQueue(std::atomic_uintmax_t* _ntasks);
TaskSubQueue(const TaskSubQueue&);
~TaskSubQueue();
TaskSubQueue& operator=(const TaskSubQueue&) = delete;
public:
int GetId() const;
bool AcquireClaim();
void ReleaseClaim();
void PushTask(task_pointer);
task_pointer PopTask(bool front = true);
size_type size() const;
bool empty() const;
private:
// used internally to keep number of tasks
std::atomic<size_type> m_ntasks;
// for checking if being modified
std::atomic_bool m_available;
// used my master queue to keep track of number of tasks
std::atomic_uintmax_t* m_all_tasks;
// queue of tasks
container_type m_task_queue;
};
//======================================================================================//
inline TaskSubQueue::TaskSubQueue(std::atomic_uintmax_t* _ntasks)
: m_ntasks(0)
, m_available(true)
, m_all_tasks(_ntasks)
{}
//======================================================================================//
inline TaskSubQueue::TaskSubQueue(const TaskSubQueue& rhs)
: m_ntasks(0)
, m_available(true)
, m_all_tasks(rhs.m_all_tasks)
{}
//======================================================================================//
inline TaskSubQueue::~TaskSubQueue() {}
//======================================================================================//
inline bool
TaskSubQueue::AcquireClaim()
{
bool is_avail = m_available.load(std::memory_order_relaxed);
if(!is_avail)
return false;
return m_available.compare_exchange_strong(is_avail, false,
std::memory_order_relaxed);
}
//======================================================================================//
inline void
TaskSubQueue::ReleaseClaim()
{
// if(m_available.load(std::memory_order_relaxed))
// return;
m_available.store(true, std::memory_order_release);
}
//======================================================================================//
inline TaskSubQueue::size_type
TaskSubQueue::size() const
{
return m_ntasks.load();
}
//======================================================================================//
inline bool
TaskSubQueue::empty() const
{
return (m_ntasks.load() == 0);
}
//======================================================================================//
inline void
TaskSubQueue::PushTask(task_pointer task)
{
// no need to lock these if claim is acquired via atomic
assert(m_available.load(std::memory_order_relaxed) == false);
//++(*m_all_tasks); already incremented
++m_ntasks;
m_task_queue.push_front(task);
}
//======================================================================================//
inline TaskSubQueue::task_pointer
TaskSubQueue::PopTask(bool front)
{
// no need to lock -- claim is acquired via atomic
assert(m_available.load(std::memory_order_relaxed) == false);
if(m_task_queue.empty())
return nullptr;
task_pointer _task;
if(front)
{
_task = std::move(m_task_queue.front());
m_task_queue.pop_front();
}
else
{
_task = std::move(m_task_queue.back());
m_task_queue.pop_back();
}
--m_ntasks;
return _task;
}
//======================================================================================//
} // namespace PTL
+366
View File
@@ -0,0 +1,366 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Global utility functions
//
#pragma once
#include "PTL/Types.hh"
#include <chrono>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <map>
#include <mutex>
#include <set>
#include <sstream>
#include <string>
#include <tuple>
namespace PTL
{
//--------------------------------------------------------------------------------------//
// use this function to get rid of "unused parameter" warnings
//
template <typename... _Args>
void
ConsumeParameters(_Args...)
{}
//--------------------------------------------------------------------------------------//
// a non-string environment option with a string identifier
template <typename _Tp>
using EnvChoice = std::tuple<_Tp, std::string, std::string>;
//--------------------------------------------------------------------------------------//
// list of environment choices with non-string and string identifiers
template <typename _Tp>
using EnvChoiceList = std::set<EnvChoice<_Tp>>;
//--------------------------------------------------------------------------------------//
class EnvSettings
{
public:
typedef std::mutex mutex_t;
typedef std::string string_t;
typedef std::multimap<string_t, string_t> env_map_t;
typedef std::pair<string_t, string_t> env_pair_t;
public:
static EnvSettings* GetInstance()
{
static EnvSettings* _instance = new EnvSettings();
return _instance;
}
public:
template <typename _Tp>
void insert(const std::string& env_id, _Tp val)
{
std::stringstream ss;
ss << std::boolalpha << val;
m_mutex.lock();
if(m_env.find(env_id) != m_env.end())
{
for(const auto& itr : m_env)
if(itr.first == env_id && itr.second == ss.str())
{
m_mutex.unlock();
return;
}
}
m_env.insert(env_pair_t(env_id, ss.str()));
m_mutex.unlock();
}
template <typename _Tp>
void insert(const std::string& env_id, EnvChoice<_Tp> choice)
{
_Tp& val = std::get<0>(choice);
std::string& str_val = std::get<1>(choice);
std::string& descript = std::get<2>(choice);
std::stringstream ss, ss_long;
ss << std::boolalpha << val;
ss_long << std::boolalpha << std::setw(8) << std::left << val << " # (\""
<< str_val << "\") " << descript;
m_mutex.lock();
if(m_env.find(env_id) != m_env.end())
{
for(const auto& itr : m_env)
if(itr.first == env_id && itr.second == ss.str())
{
m_mutex.unlock();
return;
}
}
m_env.insert(env_pair_t(env_id, ss_long.str()));
m_mutex.unlock();
}
const env_map_t& get() const { return m_env; }
mutex_t& mutex() const { return m_mutex; }
friend std::ostream& operator<<(std::ostream& os, const EnvSettings& env)
{
std::stringstream filler;
filler.fill('#');
filler << std::setw(90) << "";
std::stringstream ss;
ss << filler.str() << "\n# Environment settings:\n";
env.mutex().lock();
for(const auto& itr : env.get())
{
ss << "# " << std::setw(35) << std::right << itr.first << "\t = \t"
<< std::left << itr.second << "\n";
}
env.mutex().unlock();
ss << filler.str();
os << ss.str() << std::endl;
return os;
}
private:
env_map_t m_env;
mutable mutex_t m_mutex;
};
//--------------------------------------------------------------------------------------//
// use this function to get an environment variable setting +
// a default if not defined, e.g.
// int num_threads =
// GetEnv<int>("FORCENUMBEROFTHREADS",
// std::thread::hardware_concurrency());
//
template <typename _Tp>
_Tp
GetEnv(const std::string& env_id, _Tp _default = _Tp())
{
char* env_var = std::getenv(env_id.c_str());
if(env_var)
{
std::string str_var = std::string(env_var);
std::istringstream iss(str_var);
_Tp var = _Tp();
iss >> var;
// record value defined by environment
EnvSettings::GetInstance()->insert<_Tp>(env_id, var);
return var;
}
// record default value
EnvSettings::GetInstance()->insert<_Tp>(env_id, _default);
// return default if not specified in environment
return _default;
}
//--------------------------------------------------------------------------------------//
// overload for boolean
//
template <>
inline bool
GetEnv(const std::string& env_id, bool _default)
{
char* env_var = std::getenv(env_id.c_str());
if(env_var)
{
std::string var = std::string(env_var);
bool val = true;
if(var.find_first_not_of("0123456789") == std::string::npos)
val = (bool) atoi(var.c_str());
else
{
for(auto& itr : var)
itr = tolower(itr);
if(var == "off" || var == "false")
val = false;
}
// record value defined by environment
EnvSettings::GetInstance()->insert<bool>(env_id, val);
return val;
}
// record default value
EnvSettings::GetInstance()->insert<bool>(env_id, false);
// return default if not specified in environment
return _default;
}
//--------------------------------------------------------------------------------------//
// overload for GetEnv + message when set
//
template <typename _Tp>
_Tp
GetEnv(const std::string& env_id, _Tp _default, const std::string& msg)
{
char* env_var = std::getenv(env_id.c_str());
if(env_var)
{
std::string str_var = std::string(env_var);
std::istringstream iss(str_var);
_Tp var = _Tp();
iss >> var;
std::cout << "Environment variable \"" << env_id << "\" enabled with "
<< "value == " << var << ". " << msg << std::endl;
// record value defined by environment
EnvSettings::GetInstance()->insert<_Tp>(env_id, var);
return var;
}
// record default value
EnvSettings::GetInstance()->insert<_Tp>(env_id, _default);
// return default if not specified in environment
return _default;
}
//--------------------------------------------------------------------------------------//
// use this function to get an environment variable setting from set of choices
//
// EnvChoiceList<int> choices =
// { EnvChoice<int>(NN, "NN", "nearest neighbor interpolation"),
// EnvChoice<int>(LINEAR, "LINEAR", "bilinear interpolation"),
// EnvChoice<int>(CUBIC, "CUBIC", "bicubic interpolation") };
//
// int eInterp = GetEnv<int>("INTERPOLATION", choices, CUBIC);
//
template <typename _Tp>
_Tp
GetEnv(const std::string& env_id, const EnvChoiceList<_Tp>& _choices, _Tp _default)
{
auto asupper = [](std::string var) {
for(auto& itr : var)
itr = toupper(itr);
return var;
};
char* env_var = std::getenv(env_id.c_str());
if(env_var)
{
std::string str_var = std::string(env_var);
std::string upp_var = asupper(str_var);
_Tp var = _Tp();
// check to see if string matches a choice
for(const auto& itr : _choices)
{
if(asupper(std::get<1>(itr)) == upp_var)
{
// record value defined by environment
EnvSettings::GetInstance()->insert(env_id, itr);
return std::get<0>(itr);
}
}
std::istringstream iss(str_var);
iss >> var;
// check to see if string matches a choice
for(const auto& itr : _choices)
{
if(var == std::get<0>(itr))
{
// record value defined by environment
EnvSettings::GetInstance()->insert(env_id, itr);
return var;
}
}
// the value set in env did not match any choices
std::stringstream ss;
ss << "\n### Environment setting error @ " << __FUNCTION__ << " (line "
<< __LINE__ << ")! Invalid selection for \"" << env_id
<< "\". Valid choices are:\n";
for(const auto& itr : _choices)
ss << "\t\"" << std::get<0>(itr) << "\" or \"" << std::get<1>(itr) << "\" ("
<< std::get<2>(itr) << ")\n";
std::cerr << ss.str() << std::endl;
abort();
}
std::string _name = "???";
std::string _desc = "description not provided";
for(const auto& itr : _choices)
if(std::get<0>(itr) == _default)
{
_name = std::get<1>(itr);
_desc = std::get<2>(itr);
break;
}
// record default value
EnvSettings::GetInstance()->insert(env_id, EnvChoice<_Tp>(_default, _name, _desc));
// return default if not specified in environment
return _default;
}
//--------------------------------------------------------------------------------------//
template <typename _Tp>
_Tp
GetChoice(const EnvChoiceList<_Tp>& _choices, const std::string str_var)
{
auto asupper = [](std::string var) {
for(auto& itr : var)
itr = toupper(itr);
return var;
};
std::string upp_var = asupper(str_var);
_Tp var = _Tp();
// check to see if string matches a choice
for(const auto& itr : _choices)
{
if(asupper(std::get<1>(itr)) == upp_var)
{
// record value defined by environment
return std::get<0>(itr);
}
}
std::istringstream iss(str_var);
iss >> var;
// check to see if string matches a choice
for(const auto& itr : _choices)
{
if(var == std::get<0>(itr))
{
// record value defined by environment
return var;
}
}
// the value set in env did not match any choices
std::stringstream ss;
ss << "\n### Environment setting error @ " << __FUNCTION__ << " (line " << __LINE__
<< ")! Invalid selection \"" << str_var << "\". Valid choices are:\n";
for(const auto& itr : _choices)
ss << "\t\"" << std::get<0>(itr) << "\" or \"" << std::get<1>(itr) << "\" ("
<< std::get<2>(itr) << ")\n";
std::cerr << ss.str() << std::endl;
abort();
}
//--------------------------------------------------------------------------------------//
inline void
PrintEnv(std::ostream& os = std::cout)
{
os << (*EnvSettings::GetInstance());
}
//--------------------------------------------------------------------------------------//
} // namespace PTL
+108
View File
@@ -0,0 +1,108 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Tasking class header file
//
// Class Description:
//
// This file creates an abstract base class for the thread-pool tasking
// system
//
// ---------------------------------------------------------------
// Author: Jonathan Madsen (Feb 13th 2018)
// ---------------------------------------------------------------
#pragma once
#include "PTL/AutoLock.hh"
#include "PTL/TaskAllocator.hh"
#include "PTL/Threading.hh"
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <future>
#include <string>
#include <thread>
#include <tuple>
#include <utility>
namespace PTL
{
class VTaskGroup;
class ThreadPool;
//======================================================================================//
/// \brief VTask is the abstract class stored in thread_pool
class VTask
{
public:
typedef std::thread::id tid_type;
typedef size_t size_type;
typedef VTask this_type;
typedef std::atomic_uintmax_t count_t;
typedef VTask* iterator;
typedef const VTask* const_iterator;
typedef std::function<void()> void_func_t;
public:
VTask();
explicit VTask(VTaskGroup* task_group);
explicit VTask(ThreadPool* pool);
virtual ~VTask();
public:
// execution operator
virtual void operator()() = 0;
public:
// used by thread_pool
void operator--();
virtual bool is_native_task() const;
virtual ThreadPool* pool() const;
VTaskGroup* group() const { return m_group; }
public:
// used by task tree
iterator begin() { return this; }
iterator end() { return this + 1; }
const_iterator begin() const { return this; }
const_iterator end() const { return this + 1; }
const_iterator cbegin() const { return this; }
const_iterator cend() const { return this + 1; }
intmax_t& depth() { return m_depth; }
const intmax_t& depth() const { return m_depth; }
protected:
static tid_type this_tid() { return std::this_thread::get_id(); }
protected:
intmax_t m_depth;
VTaskGroup* m_group;
ThreadPool* m_pool;
void_func_t m_func = []() {};
};
//======================================================================================//
} // namespace PTL
+160
View File
@@ -0,0 +1,160 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class header file
//
// Class Description:
//
// This file creates an abstract base class for the grouping the thread-pool
// tasking system into independently joinable units
//
// ---------------------------------------------------------------
// Author: Jonathan Madsen (Feb 13th 2018)
// ---------------------------------------------------------------
#pragma once
#include "PTL/AutoLock.hh"
#include "PTL/Threading.hh"
#include "PTL/VTask.hh"
#include <atomic>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <deque>
#include <list>
#include <map>
#include <memory>
#include <unordered_map>
#include <vector>
namespace PTL
{
class ThreadPool;
#define _MOVE_MEMBER(_member) _member = std::move(rhs._member)
class VTaskGroup
{
public:
template <typename _Tp>
using container_type = std::vector<_Tp>;
template <typename _Tp>
using list_type = std::vector<_Tp>;
typedef VTaskGroup this_type;
typedef std::thread::id tid_type;
typedef VTask task_type;
typedef uintmax_t size_type;
typedef Mutex lock_t;
typedef std::atomic_intmax_t atomic_int;
typedef std::atomic_uintmax_t atomic_uint;
typedef Condition condition_t;
typedef task_type* task_pointer;
typedef container_type<task_pointer> vtask_list_type;
public:
// Constructor and Destructors
explicit VTaskGroup(ThreadPool* tp = nullptr);
// Virtual destructors are required by abstract classes
// so add it by default, just in case
virtual ~VTaskGroup();
VTaskGroup(const this_type&) = delete;
VTaskGroup(this_type&& rhs) = default;
this_type& operator=(const this_type&) = delete;
this_type& operator=(this_type&& rhs) = default;
public:
//------------------------------------------------------------------------//
// wait to finish
virtual void wait();
//------------------------------------------------------------------------//
// increment (prefix)
intmax_t operator++() { return ++(*m_tot_task_count); }
intmax_t operator++(int) { return (*m_tot_task_count)++; }
//------------------------------------------------------------------------//
// increment (prefix)
intmax_t operator--() { return --(*m_tot_task_count); }
intmax_t operator--(int) { return (*m_tot_task_count)--; }
//------------------------------------------------------------------------//
// size
intmax_t size() const { return m_tot_task_count->load(); }
// get the locks/conditions
std::shared_ptr<condition_t> task_cond() { return m_task_cond; }
// identifier
const uintmax_t& id() const { return m_id; }
// thread pool
void set_pool(ThreadPool* tp) { m_pool = tp; }
ThreadPool*& pool() { return m_pool; }
ThreadPool* pool() const { return m_pool; }
void clear();
virtual bool is_native_task_group() const { return true; }
virtual bool is_master() const { return this_tid() == m_main_tid; }
//------------------------------------------------------------------------//
// check if any tasks are still pending
virtual intmax_t pending() { return m_tot_task_count->load(); }
static void set_verbose(int level) { f_verbose = level; }
protected:
//------------------------------------------------------------------------//
// get the thread id
static tid_type this_tid() { return std::this_thread::get_id(); }
//------------------------------------------------------------------------//
// get the task count
atomic_int& task_count() { return *m_tot_task_count; }
const atomic_int& task_count() const { return *m_tot_task_count; }
protected:
// Private variables
uintmax_t m_id;
ThreadPool* m_pool;
std::shared_ptr<atomic_int> m_tot_task_count = std::make_shared<atomic_int>(0);
std::shared_ptr<condition_t> m_task_cond = std::make_shared<condition_t>();
std::shared_ptr<lock_t> m_task_lock = std::make_shared<lock_t>();
tid_type m_main_tid;
vtask_list_type vtask_list;
static int f_verbose;
};
inline void
VTaskGroup::clear()
{
for(auto& itr : vtask_list)
delete itr;
vtask_list.clear();
}
//--------------------------------------------------------------------------------------//
// don't pollute
#undef _MOVE_MEMBER
} // namespace PTL
+216
View File
@@ -0,0 +1,216 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class header
// Class Description:
// Abstract base class for creating a task queue used by
// ThreadPool
// ---------------------------------------------------------------
// Author: Jonathan Madsen
// ---------------------------------------------------------------
#pragma once
#include "PTL/Globals.hh"
#include "PTL/Threading.hh"
#include "PTL/Types.hh"
#include <cstddef>
#include <set>
#include <tuple>
#include <type_traits>
#include <utility>
namespace PTL
{
class VTask;
class VTaskGroup;
class ThreadPool;
class ThreadData;
class VUserTaskQueue
{
public:
typedef VTask* task_pointer;
typedef std::atomic<intmax_t> AtomicInt;
typedef uintmax_t size_type;
typedef std::function<void()> function_type;
typedef std::set<ThreadId> ThreadIdSet;
public:
// Constructor - accepting the number of workers
explicit VUserTaskQueue(intmax_t nworkers = -1);
// Virtual destructors are required by abstract classes
// so add it by default, just in case
virtual ~VUserTaskQueue();
public:
// Virtual function for getting a task from the queue
// parameters:
// 1. int - get from specific sub-queue
// 2. int - number of iterations
// returns:
// VTask* - a task or nullptr
virtual task_pointer GetTask(intmax_t subq = -1, intmax_t nitr = -1) = 0;
// Virtual function for inserting a task into the queue
// parameters:
// 1. VTask* - task to insert
// 2. int - sub-queue to inserting into
// return:
// int - subqueue inserted into
virtual intmax_t InsertTask(task_pointer, ThreadData* = nullptr,
intmax_t subq = -1) = 0;
// Overload this function to hold threads
virtual void Wait() = 0;
virtual intmax_t GetThreadBin() const = 0;
virtual void resize(intmax_t) = 0;
// these are used for stanard checking
virtual size_type size() const = 0;
virtual bool empty() const = 0;
virtual size_type bin_size(size_type bin) const = 0;
virtual bool bin_empty(size_type bin) const = 0;
// these are for slower checking, default to returning normal size()/empty
virtual size_type true_size() const { return size(); }
virtual bool true_empty() const { return empty(); }
// a method of executing a specific function on all threads
virtual void ExecuteOnAllThreads(ThreadPool* tp, function_type f) = 0;
virtual void ExecuteOnSpecificThreads(ThreadIdSet tid_set, ThreadPool* tp,
function_type f) = 0;
intmax_t workers() const { return m_workers; }
virtual VUserTaskQueue* clone() = 0;
// operator for number of tasks
// prefix versions
// virtual uintmax_t operator++() = 0;
// virtual uintmax_t operator--() = 0;
// postfix versions
// virtual uintmax_t operator++(int) = 0;
// virtual uintmax_t operator--(int) = 0;
public:
template <typename _Container, size_t... Idx>
static auto ContainerToTupleImpl(_Container&& container,
details::index_sequence<Idx...>)
-> decltype(std::make_tuple(std::forward<_Container>(container)[Idx]...))
{
return std::make_tuple(std::forward<_Container>(container)[Idx]...);
}
template <std::size_t _N, typename _Container>
static auto ContainerToTuple(_Container&& container)
-> decltype(ContainerToTupleImpl(std::forward<_Container>(container),
details::make_index_sequence<_N>{}))
{
return ContainerToTupleImpl(std::forward<_Container>(container),
details::make_index_sequence<_N>{});
}
template <std::size_t _N, std::size_t _Nt, typename _Tuple,
enable_if_t<(_N == _Nt), int> = 0>
static void _Executor(_Tuple&& _t)
{
if(std::get<_N>(_t).get())
(*(std::get<_N>(_t)))();
}
template <std::size_t _N, std::size_t _Nt, typename _Tuple,
enable_if_t<(_N < _Nt), int> = 0>
static void _Executor(_Tuple&& _t)
{
if(std::get<_N>(_t).get())
(*(std::get<_N>(_t)))();
_Executor<_N + 1, _Nt, _Tuple>(std::forward<_Tuple>(_t));
}
template <typename _Tuple, std::size_t _N = std::tuple_size<decay_t<_Tuple>>::value>
static void Executor(_Tuple&& __t)
{
_Executor<0, _N - 1, _Tuple>(std::forward<_Tuple>(__t));
}
template <typename Container,
typename std::enable_if<std::is_same<Container, task_pointer>::value,
int>::type = 0>
static void Execute(Container& obj)
{
if(obj.get())
(*obj)();
}
template <typename Container,
typename std::enable_if<!std::is_same<Container, task_pointer>::value,
int>::type = 0>
static void Execute(Container& tasks)
{
/*
for(auto& itr : tasks)
{
if(itr.get())
(*itr)();
}*/
size_type n = tasks.size();
size_type max_n = 4;
while(n > 0)
{
auto compute = (n > max_n) ? max_n : n;
switch(compute)
{
case 4: {
auto t = ContainerToTuple<4>(tasks);
Executor(t);
break;
}
case 3: {
auto t = ContainerToTuple<3>(tasks);
Executor(t);
break;
}
case 2: {
auto t = ContainerToTuple<2>(tasks);
Executor(t);
break;
}
case 1: {
auto t = ContainerToTuple<1>(tasks);
Executor(t);
break;
}
case 0: break;
}
// tasks.erase(tasks.begin(), tasks.begin() + compute);
n -= compute;
}
}
protected:
intmax_t m_workers = 0;
};
} // namespace PTL
+40
View File
@@ -0,0 +1,40 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class implementation
//
//
#include "PTL/TaskAllocator.hh"
#include "PTL/TaskAllocatorList.hh"
using namespace PTL;
//======================================================================================//
TaskAllocatorBase::TaskAllocatorBase()
{
TaskAllocatorList::GetAllocatorList()->Register(this);
}
//======================================================================================//
TaskAllocatorBase::~TaskAllocatorBase() {}
//======================================================================================//
+129
View File
@@ -0,0 +1,129 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class implementation
//
#include <iomanip>
#include "PTL/TaskAllocator.hh"
#include "PTL/TaskAllocatorList.hh"
using namespace PTL;
//======================================================================================//
TaskAllocatorList*&
TaskAllocatorList::fAllocatorList()
{
static thread_local TaskAllocatorList* _instance = nullptr;
return _instance;
}
//======================================================================================//
TaskAllocatorList*
TaskAllocatorList::GetAllocatorList()
{
if(!fAllocatorList())
{
fAllocatorList() = new TaskAllocatorList;
}
return fAllocatorList();
}
//======================================================================================//
TaskAllocatorList*
TaskAllocatorList::GetAllocatorListIfExist()
{
return fAllocatorList();
}
//======================================================================================//
TaskAllocatorList::TaskAllocatorList() {}
//======================================================================================//
TaskAllocatorList::~TaskAllocatorList() { fAllocatorList() = nullptr; }
//======================================================================================//
void
TaskAllocatorList::Register(TaskAllocatorBase* alloc)
{
fList.push_back(alloc);
}
//======================================================================================//
void
TaskAllocatorList::Destroy(int nStat, int verboseLevel)
{
int i = 0, j = 0;
double tmem = 0;
if(verboseLevel > 0)
{
std::cout << "================== Deleting memory pools ==================="
<< std::endl;
}
for(auto& itr : fList)
{
double mem = itr->GetAllocatedSize();
if(i < nStat)
{
i++;
tmem += mem;
itr->ResetStorage();
continue;
}
j++;
tmem += mem;
if(verboseLevel > 1)
{
std::cout << "Pool ID '" << itr->GetPoolType()
<< "', size : " << std::setprecision(3) << mem / 1048576
<< std::setprecision(6) << " MB" << std::endl;
}
itr->ResetStorage();
// delete *itr;
}
if(verboseLevel > 0)
{
std::cout << "Number of memory pools allocated: " << Size()
<< "; of which, static: " << i << std::endl;
std::cout << "Dynamic pools deleted: " << j
<< " / Total memory freed: " << std::setprecision(2) << tmem / 1048576
<< std::setprecision(6) << " MB" << std::endl;
std::cout << "============================================================"
<< std::endl;
}
fList.clear();
}
//======================================================================================//
int
TaskAllocatorList::Size() const
{
return fList.size();
}
//======================================================================================//
+124
View File
@@ -0,0 +1,124 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class implementation
//
// TaskAllocatorPool
//
// Implementation file
//
// Author: G.Cosmo, November 2000
//
#include "PTL/TaskAllocatorPool.hh"
using namespace PTL;
// ************************************************************
// TaskAllocatorPool constructor
// ************************************************************
//
TaskAllocatorPool::TaskAllocatorPool(unsigned int sz)
: esize((sz < sizeof(PoolLink)) ? sizeof(PoolLink) : sz)
, csize((sz < 1024 / 2 - 16) ? (1024 - 16) : (sz * 10 - 16))
, chunks(nullptr)
, head(nullptr)
, nchunks(0)
{}
// ************************************************************
// TaskAllocatorPool copy constructor
// ************************************************************
//
TaskAllocatorPool::TaskAllocatorPool(const TaskAllocatorPool& right)
: esize(right.esize)
, csize(right.csize)
, chunks(right.chunks)
, head(right.head)
, nchunks(right.nchunks)
{}
// ************************************************************
// TaskAllocatorPool operator=
// ************************************************************
//
TaskAllocatorPool&
TaskAllocatorPool::operator=(const TaskAllocatorPool& right)
{
if(&right == this)
return *this;
chunks = right.chunks;
head = right.head;
nchunks = right.nchunks;
return *this;
}
// ************************************************************
// TaskAllocatorPool destructor
// ************************************************************
//
TaskAllocatorPool::~TaskAllocatorPool() { Reset(); }
// ************************************************************
// Reset
// ************************************************************
//
void
TaskAllocatorPool::Reset()
{
// Free all chunks
//
PoolChunk* n = chunks;
PoolChunk* p = nullptr;
while(n)
{
p = n;
n = n->next;
delete p;
}
head = nullptr;
chunks = nullptr;
nchunks = 0;
}
// ************************************************************
// Grow
// ************************************************************
//
void
TaskAllocatorPool::Grow()
{
// Allocate new chunk, organize it as a linked list of
// elements of size 'esize'
//
PoolChunk* n = new PoolChunk(csize);
n->next = chunks;
chunks = n;
nchunks++;
const int nelem = csize / esize;
char* start = n->mem;
char* last = &start[(nelem - 1) * esize];
for(char* p = start; p < last; p += esize)
{
reinterpret_cast<PoolLink*>(p)->next = reinterpret_cast<PoolLink*>(p + esize);
}
reinterpret_cast<PoolLink*>(last)->next = nullptr;
head = reinterpret_cast<PoolLink*>(start);
}
+152
View File
@@ -0,0 +1,152 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class implementation
#include "PTL/TaskRunManager.hh"
#include "PTL/AutoLock.hh"
#include "PTL/Task.hh"
#include "PTL/TaskGroup.hh"
#include "PTL/TaskManager.hh"
#include "PTL/ThreadPool.hh"
#include "PTL/Threading.hh"
#include "PTL/Utility.hh"
#include <cstdlib>
#include <cstring>
#include <iterator>
using namespace PTL;
//======================================================================================//
TaskRunManager::pointer&
TaskRunManager::GetPrivateMasterRunManager(bool init, bool useTBB)
{
static pointer _instance = (init) ? new TaskRunManager(useTBB) : nullptr;
return _instance;
}
//======================================================================================//
TaskRunManager*
TaskRunManager::GetMasterRunManager(bool useTBB)
{
static pointer& _instance = GetPrivateMasterRunManager(true, useTBB);
return _instance;
}
//======================================================================================//
TaskRunManager*
TaskRunManager::GetInstance(bool useTBB)
{
return GetMasterRunManager(useTBB);
}
//======================================================================================//
TaskRunManager::TaskRunManager(bool useTBB)
: m_is_initialized(false)
, m_verbose(0)
, m_workers(std::thread::hardware_concurrency())
, m_task_queue(nullptr)
, m_thread_pool(nullptr)
, m_task_manager(nullptr)
{
if(!GetPrivateMasterRunManager(false))
{
GetPrivateMasterRunManager(false) = this;
}
#ifdef PTL_USE_TBB
auto _useTBB = GetEnv<bool>("FORCE_TBB", useTBB);
if(_useTBB)
useTBB = true;
#endif
// handle TBB
ThreadPool::set_use_tbb(useTBB);
m_workers = GetEnv<uint64_t>("PTL_NUM_THREADS", m_workers);
}
//======================================================================================//
TaskRunManager::~TaskRunManager() {}
//======================================================================================//
void
TaskRunManager::Initialize(uint64_t n)
{
m_workers = n;
// create threadpool if needed + task manager
if(!m_thread_pool)
{
if(m_verbose > 0)
std::cout << "TaskRunManager :: Creating thread pool..." << std::endl;
m_thread_pool = new ThreadPool(m_workers, m_task_queue);
if(m_verbose > 0)
std::cout << "TaskRunManager :: Creating task manager..." << std::endl;
m_task_manager = new TaskManager(m_thread_pool);
}
// or resize
else if(m_workers != m_thread_pool->size())
{
if(m_verbose > 0)
{
std::cout << "TaskRunManager :: Resizing thread pool from "
<< m_thread_pool->size() << " to " << m_workers << " threads ..."
<< std::endl;
}
m_thread_pool->resize(m_workers);
}
// create the joiners
if(ThreadPool::using_tbb())
{
if(m_verbose > 0)
std::cout << "TaskRunManager :: Using TBB..." << std::endl;
}
else
{
if(m_verbose > 0)
std::cout << "TaskRunManager :: Using ThreadPool..." << std::endl;
}
m_is_initialized = true;
if(m_verbose > 0)
std::cout << "TaskRunManager :: initialized..." << std::endl;
}
//======================================================================================//
void
TaskRunManager::Terminate()
{
m_is_initialized = false;
m_thread_pool->destroy_threadpool();
delete m_task_manager;
delete m_thread_pool;
m_task_manager = nullptr;
m_thread_pool = nullptr;
}
//======================================================================================//
+65
View File
@@ -0,0 +1,65 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class implementation
//
#include "PTL/ThreadData.hh"
#include "PTL/ThreadPool.hh"
#include "PTL/Threading.hh"
#include "PTL/VUserTaskQueue.hh"
#include <iostream>
using namespace PTL;
//======================================================================================//
ThreadData*&
ThreadData::GetInstance()
{
static thread_local ThreadData* _instance = nullptr;
return _instance;
}
//======================================================================================//
ThreadData::ThreadData(ThreadPool* tp)
: is_master(tp->is_master())
, within_task(false)
, task_depth(0)
, thread_pool(tp)
, current_queue(tp->get_queue())
, queue_stack({ current_queue })
{}
//======================================================================================//
void
ThreadData::update()
{
current_queue = thread_pool->get_queue();
queue_stack.push_back(current_queue);
}
//======================================================================================//
ThreadData::~ThreadData() {}
//======================================================================================//
+759
View File
@@ -0,0 +1,759 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class implementation
//
// Class Description:
//
// This file creates a class for an efficient thread-pool that
// accepts work in the form of tasks.
//
// ---------------------------------------------------------------
// Author: Jonathan Madsen (Feb 13th 2018)
// ---------------------------------------------------------------
#include "PTL/ThreadPool.hh"
#include "PTL/Globals.hh"
#include "PTL/ThreadData.hh"
#include "PTL/UserTaskQueue.hh"
#include "PTL/VUserTaskQueue.hh"
#include <cstdlib>
using namespace PTL;
//======================================================================================//
inline intmax_t
ncores()
{
return static_cast<intmax_t>(Thread::hardware_concurrency());
}
//======================================================================================//
ThreadPool::thread_id_map_t ThreadPool::f_thread_ids;
//======================================================================================//
namespace
{
ThreadData*&
thread_data()
{
return ThreadData::GetInstance();
}
} // namespace
//======================================================================================//
bool ThreadPool::f_use_tbb = false;
//======================================================================================//
// static member function that calls the member function we want the thread to
// run
void
ThreadPool::start_thread(ThreadPool* tp, intmax_t _idx)
{
{
AutoLock lock(TypeMutex<ThreadPool>(), std::defer_lock);
if(!lock.owns_lock())
lock.lock();
if(_idx < 0)
_idx = f_thread_ids.size();
f_thread_ids[std::this_thread::get_id()] = _idx;
}
static thread_local std::unique_ptr<ThreadData> _unique_data(new ThreadData(tp));
thread_data() = _unique_data.get();
tp->record_entry();
tp->execute_thread(thread_data()->current_queue);
tp->record_exit();
}
//======================================================================================//
// static member function that checks enabling of tbb library
bool
ThreadPool::using_tbb()
{
return f_use_tbb;
}
//======================================================================================//
// static member function that initialized tbb library
void
ThreadPool::set_use_tbb(bool enable)
{
#if defined(PTL_USE_TBB)
f_use_tbb = enable;
#else
ConsumeParameters<bool>(enable);
#endif
}
//======================================================================================//
const ThreadPool::thread_id_map_t&
ThreadPool::GetThreadIDs()
{
return f_thread_ids;
}
//======================================================================================//
uintmax_t
ThreadPool::GetThisThreadID()
{
auto _tid = ThisThread::get_id();
{
AutoLock lock(TypeMutex<ThreadPool>(), std::defer_lock);
if(!lock.owns_lock())
lock.lock();
if(f_thread_ids.find(_tid) == f_thread_ids.end())
{
auto _idx = f_thread_ids.size();
f_thread_ids[_tid] = _idx;
}
}
return f_thread_ids[_tid];
}
//======================================================================================//
ThreadPool::ThreadPool(const size_type& pool_size, VUserTaskQueue* task_queue,
bool _use_affinity, const affinity_func_t& _affinity_func)
: m_use_affinity(_use_affinity)
, m_tbb_tp(false)
, m_verbose(0)
, m_pool_size(0)
, m_master_tid(ThisThread::get_id())
, m_alive_flag(std::make_shared<std::atomic_bool>(false))
, m_pool_state(std::make_shared<std::atomic_short>(thread_pool::state::NONINIT))
, m_thread_awake(std::make_shared<std::atomic_uintmax_t>(0))
, m_task_lock(std::make_shared<Mutex>())
, m_task_cond(std::make_shared<Condition>())
, m_task_queue(task_queue)
, m_tbb_task_group(nullptr)
, m_init_func([]() { return; })
, m_affinity_func(_affinity_func)
{
m_verbose = GetEnv<int>("PTL_VERBOSE", m_verbose);
auto master_id = GetThisThreadID();
if(master_id != 0 && m_verbose > 1)
std::cerr << "ThreadPool created on non-master slave" << std::endl;
thread_data() = new ThreadData(this);
// initialize after GetThisThreadID so master is zero
this->initialize_threadpool(pool_size);
if(!m_task_queue)
m_task_queue = new UserTaskQueue(m_pool_size);
}
//======================================================================================//
ThreadPool::~ThreadPool()
{
//------------------------------------------------------------------------//
// set state to stopped
m_pool_state->store(thread_pool::state::STOPPED);
//------------------------------------------------------------------------//
// notify all threads we are shutting down
m_task_lock->lock();
CONDITIONBROADCAST(m_task_cond.get());
m_task_lock->unlock();
//--------------------------------------------------------------------//
// set to dead
m_alive_flag->store(false);
//--------------------------------------------------------------------//
// delete tbb task scheduler
#ifdef PTL_USE_TBB
if(m_tbb_tp && tbb_global_control())
{
tbb_global_control_t*& _global_control = tbb_global_control();
delete _global_control;
_global_control = nullptr;
m_tbb_tp = false;
std::cout << "ThreadPool [TBB] destroyed" << std::endl;
}
//--------------------------------------------------------------------//
// delete tbb task-group
if(m_tbb_task_group)
{
m_tbb_task_group->wait();
delete m_tbb_task_group;
m_tbb_task_group = nullptr;
}
#endif
auto _tid = std::this_thread::get_id();
if(f_thread_ids.find(_tid) != f_thread_ids.end())
f_thread_ids.erase(f_thread_ids.find(_tid));
for(auto& itr : m_main_threads)
if(f_thread_ids.find(itr) != f_thread_ids.end())
f_thread_ids.erase(f_thread_ids.find(itr));
m_thread_awake.reset();
std::cout << "ThreadPool destroyed" << std::endl;
/*
// Release resources
if(m_pool_state.load() != thread_pool::state::STOPPED)
{
size_type ret = destroy_threadpool();
while(ret > 0)
ret = stop_thread();
}
// wait until thread pool is fully destroyed
while(is_alive())
;
// delete thread-local allocator and erase thread IDS
auto _tid = std::this_thread::get_id();
if(f_thread_ids.find(_tid) != f_thread_ids.end())
f_thread_ids.erase(f_thread_ids.find(_tid));
// deleted by ThreadData
// delete m_task_queue;
*/
}
//======================================================================================//
bool
ThreadPool::is_initialized() const
{
return !(m_pool_state->load() == thread_pool::state::NONINIT);
}
//======================================================================================//
void
ThreadPool::set_affinity(intmax_t i, Thread& _thread)
{
try
{
NativeThread native_thread = _thread.native_handle();
intmax_t _pin = m_affinity_func(i);
if(m_verbose > 0)
{
std::cout << "Setting pin affinity for thread " << _thread.get_id() << " to "
<< _pin << std::endl;
}
Threading::SetPinAffinity(_pin, native_thread);
} catch(std::runtime_error& e)
{
std::cout << "Error setting pin affinity" << std::endl;
std::cerr << e.what() << std::endl; // issue assigning affinity
}
}
//======================================================================================//
ThreadPool::size_type
ThreadPool::initialize_threadpool(size_type proposed_size)
{
//--------------------------------------------------------------------//
// return before initializing
if(proposed_size < 1)
return 0;
//--------------------------------------------------------------------//
// store that has been started
if(!m_alive_flag->load())
m_pool_state->store(thread_pool::state::STARTED);
//--------------------------------------------------------------------//
// handle tbb task scheduler
#ifdef PTL_USE_TBB
if(f_use_tbb)
{
m_tbb_tp = true;
m_pool_size = proposed_size;
tbb_global_control_t*& _global_control = tbb_global_control();
// delete if wrong size
if(m_pool_size != proposed_size)
{
delete _global_control;
_global_control = nullptr;
}
if(!_global_control)
{
_global_control = new tbb_global_control_t(
tbb::global_control::max_allowed_parallelism, proposed_size + 1);
if(m_verbose > 0)
{
std::cout << "ThreadPool [TBB] initialized with " << m_pool_size
<< " threads." << std::endl;
}
}
// create task group (used for async)
if(!m_tbb_task_group)
m_tbb_task_group = new tbb_task_group_t();
return m_pool_size;
}
// NOLINT(readability-else-after-return)
if(f_use_tbb && tbb_global_control())
{
m_tbb_tp = false;
tbb_global_control_t*& _global_control = tbb_global_control();
if(_global_control)
{
delete _global_control;
_global_control = nullptr;
}
// delete task group (used for async)
if(m_tbb_task_group)
{
m_tbb_task_group->wait();
delete m_tbb_task_group;
m_tbb_task_group = nullptr;
}
}
#endif
m_alive_flag->store(true);
//--------------------------------------------------------------------//
// if started, stop some thread if smaller or return if equal
if(m_pool_state->load() == thread_pool::state::STARTED)
{
if(m_pool_size > proposed_size)
{
while(stop_thread() > proposed_size)
;
if(m_verbose > 0)
{
std::cout << "ThreadPool initialized with " << m_pool_size << " threads."
<< std::endl;
}
if(!m_task_queue)
m_task_queue = new UserTaskQueue(m_pool_size);
return m_pool_size;
}
else if(m_pool_size == proposed_size) // NOLINT
{
if(m_verbose > 0)
{
std::cout << "ThreadPool initialized with " << m_pool_size << " threads."
<< std::endl;
}
if(!m_task_queue)
m_task_queue = new UserTaskQueue(m_pool_size);
return m_pool_size;
}
}
//--------------------------------------------------------------------//
// reserve enough space to prevent realloc later
{
AutoLock _task_lock(*m_task_lock);
m_is_joined.reserve(proposed_size);
}
auto this_tid = GetThisThreadID();
for(size_type i = m_pool_size; i < proposed_size; ++i)
{
// add the threads
try
{
Thread tid(ThreadPool::start_thread, this, this_tid + i + 1);
// only reaches here if successful creation of thread
++m_pool_size;
// store thread
m_main_threads.push_back(tid.get_id());
// list of joined thread booleans
m_is_joined.push_back(false);
// set the affinity
if(m_use_affinity)
set_affinity(i, tid);
// detach
tid.detach();
} catch(std::runtime_error& e)
{
std::cerr << e.what() << std::endl; // issue creating thread
continue;
} catch(std::bad_alloc& e)
{
std::cerr << e.what() << std::endl;
continue;
}
}
//------------------------------------------------------------------------//
AutoLock _task_lock(*m_task_lock);
// thread pool size doesn't match with join vector
// this will screw up joining later
if(m_is_joined.size() != m_main_threads.size())
{
std::stringstream ss;
ss << "ThreadPool::initialize_threadpool - boolean is_joined vector "
<< "is a different size than threads vector: " << m_is_joined.size() << " vs. "
<< m_main_threads.size() << " (tid: " << std::this_thread::get_id() << ")";
throw std::runtime_error(ss.str());
}
if(m_verbose > 0)
{
std::cout << "ThreadPool initialized with " << m_pool_size << " threads."
<< std::endl;
}
if(!m_task_queue)
m_task_queue = new UserTaskQueue(m_main_threads.size());
return m_main_threads.size();
}
//======================================================================================//
ThreadPool::size_type
ThreadPool::destroy_threadpool()
{
// Note: this is not for synchronization, its for thread communication!
// destroy_threadpool() will only be called from the main thread, yet
// the modified m_pool_state may not show up to other threads until its
// modified in a lock!
//------------------------------------------------------------------------//
m_pool_state->store(thread_pool::state::STOPPED);
//--------------------------------------------------------------------//
// handle tbb task scheduler
#ifdef PTL_USE_TBB
if(m_tbb_tp && tbb_global_control())
{
tbb_global_control_t*& _global_control = tbb_global_control();
delete _global_control;
_global_control = nullptr;
m_tbb_tp = false;
std::cout << "ThreadPool [TBB] destroyed" << std::endl;
}
if(m_tbb_task_group)
{
m_tbb_task_group->wait();
delete m_tbb_task_group;
m_tbb_task_group = nullptr;
}
#endif
if(!m_alive_flag->load())
return 0;
//------------------------------------------------------------------------//
// notify all threads we are shutting down
m_task_lock->lock();
CONDITIONBROADCAST(m_task_cond.get());
m_task_lock->unlock();
//------------------------------------------------------------------------//
if(m_is_joined.size() != m_main_threads.size())
{
std::stringstream ss;
ss << " ThreadPool::destroy_thread_pool - boolean is_joined vector "
<< "is a different size than threads vector: " << m_is_joined.size() << " vs. "
<< m_main_threads.size() << " (tid: " << std::this_thread::get_id() << ")";
throw std::runtime_error(ss.str());
}
for(size_type i = 0; i < m_is_joined.size(); i++)
{
//--------------------------------------------------------------------//
// if its joined already, nothing else needs to be done
if(m_is_joined.at(i))
continue;
//--------------------------------------------------------------------//
// join
if(std::this_thread::get_id() == m_main_threads[i])
continue;
//--------------------------------------------------------------------//
// thread id and index
auto _tid = m_main_threads[i];
//--------------------------------------------------------------------//
// erase thread from thread ID list
if(f_thread_ids.find(_tid) != f_thread_ids.end())
f_thread_ids.erase(f_thread_ids.find(_tid));
//--------------------------------------------------------------------//
// it's joined
m_is_joined.at(i) = true;
//--------------------------------------------------------------------//
// try waking up a bunch of threads that are still waiting
CONDITIONBROADCAST(m_task_cond.get());
//--------------------------------------------------------------------//
}
m_main_threads.clear();
m_is_joined.clear();
m_alive_flag->store(false);
auto start = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration<double>{};
// wait maximum of 30 seconds for threads to exit
while(m_thread_active->load() > 0 && elapsed.count() < 30)
{
std::this_thread::sleep_for(std::chrono::milliseconds(50));
elapsed = std::chrono::steady_clock::now() - start;
}
auto _active = m_thread_active->load();
if(_active == 0)
std::cout << "ThreadPool destroyed" << std::endl;
else
std::cout << "ThreadPool destroyed but " << _active
<< " threads might still be active (and cause a termination error)"
<< std::endl;
return 0;
}
//======================================================================================//
ThreadPool::size_type
ThreadPool::stop_thread()
{
if(!m_alive_flag->load() || m_pool_size == 0)
return 0;
//------------------------------------------------------------------------//
// notify all threads we are shutting down
m_task_lock->lock();
m_is_stopped.push_back(true);
CONDITIONNOTIFY(m_task_cond.get());
m_task_lock->unlock();
//------------------------------------------------------------------------//
// lock up the task queue
AutoLock _task_lock(*m_task_lock);
while(!m_stop_threads.empty())
{
auto tid = m_stop_threads.front();
// remove from stopped
m_stop_threads.pop_front();
// remove from main
for(auto itr = m_main_threads.begin(); itr != m_main_threads.end(); ++itr)
{
if(*itr == tid)
{
m_main_threads.erase(itr);
break;
}
}
// remove from join list
m_is_joined.pop_back();
}
m_pool_size = m_main_threads.size();
return m_main_threads.size();
}
//======================================================================================//
void
ThreadPool::execute_thread(VUserTaskQueue* _task_queue)
{
// how long the thread waits on condition variable
// static int wait_time = GetEnv<int>("PTL_POOL_WAIT_TIME", 5);
++(*m_thread_awake);
// initialization function
m_init_func();
ThreadId tid = ThisThread::get_id();
ThreadData* data = thread_data();
// auto thread_bin = _task_queue->GetThreadBin();
// auto workers = _task_queue->workers();
auto start = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration<double>{};
// check for updates for 60 seconds max
while(!_task_queue && elapsed.count() < 60)
{
elapsed = std::chrono::steady_clock::now() - start;
data->update();
_task_queue = data->current_queue;
}
if(!_task_queue)
{
--(*m_thread_awake);
throw std::runtime_error("No task queue was found after 60 seconds!");
}
assert(data->current_queue != nullptr);
assert(_task_queue == data->current_queue);
// essentially a dummy run
if(_task_queue)
{
data->within_task = true;
auto _task = _task_queue->GetTask();
if(_task)
{
(*_task)();
if(!_task->group())
delete _task;
}
data->within_task = false;
}
// threads stay in this loop forever until thread-pool destroyed
while(true)
{
static thread_local auto p_task_lock = m_task_lock;
//--------------------------------------------------------------------//
// Try to pick a task
AutoLock _task_lock(*p_task_lock, std::defer_lock);
//--------------------------------------------------------------------//
auto leave_pool = [&]() {
auto _state = [&]() { return static_cast<int>(m_pool_state->load()); };
auto _pool_state = _state();
if(_pool_state > 0)
{
// stop whole pool
if(_pool_state == thread_pool::state::STOPPED)
{
if(_task_lock.owns_lock())
_task_lock.unlock();
return true;
}
// single thread stoppage
else if(_pool_state == thread_pool::state::PARTIAL) // NOLINT
{
if(!_task_lock.owns_lock())
_task_lock.lock();
if(!m_is_stopped.empty() && m_is_stopped.back())
{
m_stop_threads.push_back(tid);
m_is_stopped.pop_back();
if(_task_lock.owns_lock())
_task_lock.unlock();
// exit entire function
return true;
}
if(_task_lock.owns_lock())
_task_lock.unlock();
}
}
return false;
};
// We need to put condition.wait() in a loop for two reasons:
// 1. There can be spurious wake-ups (due to signal/ENITR)
// 2. When mutex is released for waiting, another thread can be woken up
// from a signal/broadcast and that thread can mess up the condition.
// So when the current thread wakes up the condition may no longer be
// actually true!
while(_task_queue->empty())
{
auto _state = [&]() { return static_cast<int>(m_pool_state->load()); };
auto _size = [&]() { return _task_queue->true_size(); };
auto _empty = [&]() { return _task_queue->empty(); };
auto _wake = [&]() { return (!_empty() || _size() > 0 || _state() > 0); };
if(leave_pool())
return;
if(_task_queue->true_size() == 0)
{
if(m_thread_awake && m_thread_awake->load() > 0)
--(*m_thread_awake);
// lock before sleeping on condition
if(!_task_lock.owns_lock())
_task_lock.lock();
// Wait until there is a task in the queue
// Unlocks mutex while waiting, then locks it back when signaled
// use lambda to control waking
m_task_cond->wait(_task_lock, _wake);
if(_state() == thread_pool::state::STOPPED)
return;
// unlock if owned
if(_task_lock.owns_lock())
_task_lock.unlock();
// notify that is awake
if(m_thread_awake && m_thread_awake->load() < m_pool_size)
++(*m_thread_awake);
}
else
break;
}
// release the lock
if(_task_lock.owns_lock())
_task_lock.unlock();
//----------------------------------------------------------------//
// leave pool if conditions dictate it
if(leave_pool())
return;
// activate guard against recursive deadlock
data->within_task = true;
//----------------------------------------------------------------//
// execute the task(s)
while(!_task_queue->empty())
{
auto _task = _task_queue->GetTask();
if(_task)
{
(*_task)();
if(!_task->group())
delete _task;
}
}
//----------------------------------------------------------------//
// disable guard against recursive deadlock
data->within_task = false;
//----------------------------------------------------------------//
}
}
//======================================================================================//
+127
View File
@@ -0,0 +1,127 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class implementation
//
// Threading.cc
//
// ---------------------------------------------------------------
// Author: Andrea Dotti (15 Feb 2013): First Implementation
// ---------------------------------------------------------------
#include "PTL/Threading.hh"
#include "PTL/AutoLock.hh"
#include "PTL/Globals.hh"
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
# include <Windows.h>
#else
# include <sys/syscall.h>
# include <sys/types.h>
# include <unistd.h>
#endif
#include <atomic>
using namespace PTL;
//======================================================================================//
namespace
{
thread_local int ThreadID = Threading::MASTER_ID;
std::atomic_int numActThreads(0);
} // namespace
//======================================================================================//
Pid_t
Threading::GetPidId()
{
// In multithreaded mode return Thread ID
return std::this_thread::get_id();
}
//======================================================================================//
unsigned
Threading::GetNumberOfCores()
{
return std::thread::hardware_concurrency();
}
//======================================================================================//
void
Threading::SetThreadId(int value)
{
ThreadID = value;
}
int
Threading::GetThreadId()
{
return ThreadID;
}
bool
Threading::IsWorkerThread()
{
return (ThreadID >= 0);
}
bool
Threading::IsMasterThread()
{
return (ThreadID == MASTER_ID);
}
//======================================================================================//
bool
Threading::SetPinAffinity(int cpu, NativeThread& aT)
{
#if defined(__linux__) || defined(_AIX)
cpu_set_t* aset = new cpu_set_t;
CPU_ZERO(aset);
CPU_SET(cpu, aset);
pthread_t& _aT = static_cast<pthread_t&>(aT);
return (pthread_setaffinity_np(_aT, sizeof(cpu_set_t), aset) == 0);
#else // Not available for Mac, WIN,...
ConsumeParameters(cpu, aT);
return true;
#endif
}
//======================================================================================//
int
Threading::WorkerThreadLeavesPool()
{
return numActThreads--;
}
int
Threading::WorkerThreadJoinsPool()
{
return numActThreads++;
}
int
Threading::GetNumberOfRunningWorkerThreads()
{
return numActThreads.load();
}
//======================================================================================//
+472
View File
@@ -0,0 +1,472 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class implementation
// Class Description:
// ---------------------------------------------------------------
// Author: Jonathan Madsen
// ---------------------------------------------------------------
#include "PTL/UserTaskQueue.hh"
#include "PTL/Task.hh"
#include "PTL/TaskGroup.hh"
#include "PTL/ThreadPool.hh"
#include <cassert>
using namespace PTL;
//======================================================================================//
UserTaskQueue::UserTaskQueue(intmax_t nworkers, UserTaskQueue* parent)
: VUserTaskQueue(nworkers)
, m_is_clone((parent) ? true : false)
, m_thread_bin((parent) ? (ThreadPool::GetThisThreadID() % (nworkers + 1)) : 0)
, m_insert_bin((parent) ? (ThreadPool::GetThisThreadID() % (nworkers + 1)) : 0)
, m_hold((parent) ? parent->m_hold : new std::atomic_bool(false))
, m_ntasks((parent) ? parent->m_ntasks : new std::atomic_uintmax_t(0))
, m_subqueues((parent) ? parent->m_subqueues : new TaskSubQueueContainer())
{
// create nthreads + 1 subqueues so there is always a subqueue available
if(!parent)
{
for(intmax_t i = 0; i < nworkers + 1; ++i)
m_subqueues->push_back(new TaskSubQueue(m_ntasks));
}
#if defined(DEBUG)
if(GetEnv<int>("PTL_VERBOSE", 0) > 3)
{
RecursiveAutoLock l(TypeRecursiveMutex<decltype(std::cout)>());
std::stringstream ss;
ss << ThreadPool::GetThisThreadID() << "> " << ThisThread::get_id() << " ["
<< __FUNCTION__ << ":" << __LINE__ << "] "
<< "this = " << this << ", "
<< "clone = " << std::boolalpha << m_is_clone << ", "
<< "thread = " << m_thread_bin << ", "
<< "insert = " << m_insert_bin << ", "
<< "hold = " << m_hold->load() << " @ " << m_hold << ", "
<< "tasks = " << m_ntasks->load() << " @ " << m_ntasks << ", "
<< "subqueue = " << m_subqueues << ", "
<< "size = " << true_size() << ", "
<< "empty = " << true_empty();
std::cout << ss.str() << std::endl;
}
#endif
}
//======================================================================================//
UserTaskQueue::~UserTaskQueue()
{
if(!m_is_clone)
{
for(auto& itr : *m_subqueues)
{
assert(itr->size() == 0);
delete itr;
}
m_subqueues->clear();
delete m_hold;
delete m_ntasks;
delete m_subqueues;
}
}
//======================================================================================//
void
UserTaskQueue::resize(intmax_t n)
{
AutoLock l(m_mutex);
if(m_workers < n)
{
while(m_workers < n)
{
m_subqueues->push_back(new TaskSubQueue(m_ntasks));
++m_workers;
}
}
else if(m_workers > n)
{
while(m_workers > n)
{
delete m_subqueues->back();
m_subqueues->pop_back();
--m_workers;
}
}
}
//======================================================================================//
VUserTaskQueue*
UserTaskQueue::clone()
{
return new UserTaskQueue(workers(), this);
}
//======================================================================================//
intmax_t
UserTaskQueue::GetThreadBin() const
{
// get a thread id number
static thread_local intmax_t tl_bin =
(m_thread_bin + ThreadPool::GetThisThreadID()) % (m_workers + 1);
return tl_bin;
}
//======================================================================================//
intmax_t
UserTaskQueue::GetInsertBin() const
{
return (++m_insert_bin % (m_workers + 1));
}
//======================================================================================//
UserTaskQueue::task_pointer
UserTaskQueue::GetThreadBinTask()
{
intmax_t tbin = GetThreadBin();
TaskSubQueue* task_subq = (*m_subqueues)[tbin % (m_workers + 1)];
task_pointer _task = nullptr;
//------------------------------------------------------------------------//
auto get_task = [&]() {
if(task_subq->AcquireClaim())
{
// run task
_task = task_subq->PopTask(true);
// release the claim on the bin
task_subq->ReleaseClaim();
}
if(_task)
--(*m_ntasks);
// return success if valid pointer
return (_task != nullptr);
};
//------------------------------------------------------------------------//
// while not empty
while(!task_subq->empty())
{
if(get_task())
break;
}
return _task;
}
//======================================================================================//
UserTaskQueue::task_pointer
UserTaskQueue::GetTask(intmax_t subq, intmax_t nitr)
{
// exit if empty
if(this->true_empty())
return nullptr;
// ensure the thread has a bin assignment
intmax_t tbin = GetThreadBin();
intmax_t n = (subq < 0) ? tbin : subq;
if(nitr < 1)
nitr = (m_workers + 1); // * m_ntasks->load(std::memory_order_relaxed);
if(m_hold->load(std::memory_order_relaxed))
{
return GetThreadBinTask();
}
task_pointer _task = nullptr;
//------------------------------------------------------------------------//
auto get_task = [&](intmax_t _n) {
TaskSubQueue* task_subq = (*m_subqueues)[_n % (m_workers + 1)];
// try to acquire a claim for the bin
// if acquired, no other threads will access bin until claim is released
if(!task_subq->empty() && task_subq->AcquireClaim())
{
// pop task out of bin
_task = task_subq->PopTask(n == tbin);
// release the claim on the bin
task_subq->ReleaseClaim();
}
if(_task)
--(*m_ntasks);
// return success if valid pointer
return (_task != nullptr);
};
//------------------------------------------------------------------------//
// there are num_workers+1 bins so there is always a bin that is open
// execute num_workers+2 iterations so the thread checks its bin twice
// while(!empty())
{
for(intmax_t i = 0; i < nitr; ++i, ++n)
{
if(get_task(n % (m_workers + 1)))
return _task;
}
}
// only reached if looped over all bins (and looked in own bin twice)
// and found no work so return an empty task and the thread will be put to
// sleep if there is still no work by the time it reaches its
// condition variable
return _task;
}
//======================================================================================//
intmax_t
UserTaskQueue::InsertTask(task_pointer task, ThreadData* data, intmax_t subq)
{
// skip increment here (handled externally)
++(*m_ntasks);
bool spin = m_hold->load(std::memory_order_relaxed);
intmax_t tbin = GetThreadBin();
if(data && data->within_task)
{
subq = tbin;
// spin = true;
}
// subq is -1 unless specified so unless specified
// GetInsertBin() call increments a counter and returns
// counter % (num_workers + 1) so that tasks are distributed evenly
// among the bins
intmax_t n = (subq < 0) ? GetInsertBin() : subq;
//------------------------------------------------------------------------//
auto insert_task = [&](intmax_t _n) {
TaskSubQueue* task_subq = (*m_subqueues)[_n];
// TaskSubQueue* next_subq = (*m_subqueues)[(_n + 1) % (m_workers + 1)];
// if not threads bin and size difference, insert into smaller
// if(n != tbin && next_subq->size() < task_subq->size())
// task_subq = next_subq;
// try to acquire a claim for the bin
// if acquired, no other threads will access bin until claim is released
if(task_subq->AcquireClaim())
{
// push the task into the bin
task_subq->PushTask(task);
// release the claim on the bin
task_subq->ReleaseClaim();
// return success
return true;
}
return false;
};
//------------------------------------------------------------------------//
// if not in "hold/spin mode", where thread only inserts tasks into
// specified bin, then move onto next bin
//
if(spin)
{
n = n % (m_workers + 1);
while(!insert_task(n))
;
return n;
}
// there are num_workers+1 bins so there is always a bin that is open
// execute num_workers+2 iterations so the thread checks its bin twice
while(true)
{
auto _n = (n++) % (m_workers + 1);
if(insert_task(_n))
return _n;
}
return GetThreadBin();
}
//======================================================================================//
void
UserTaskQueue::ExecuteOnAllThreads(ThreadPool* tp, function_type func)
{
typedef TaskGroup<int, int> task_group_type;
typedef std::map<int64_t, bool> thread_execute_map_t;
if(!tp->is_alive())
{
func();
return;
}
auto join_func = [=](int& ref, int i) {
ref += i;
return ref;
};
task_group_type* tg = new task_group_type(join_func, tp);
// wait for all threads to finish any work
// NOTE: will cause deadlock if called from a task
while(tp->get_active_threads_count() > 0)
ThisThread::sleep_for(std::chrono::milliseconds(10));
thread_execute_map_t* thread_execute_map = new thread_execute_map_t();
AcquireHold();
for(int i = 0; i < (m_workers + 1); ++i)
{
if(i == GetThreadBin())
continue;
//--------------------------------------------------------------------//
auto thread_specific_func = [&]() {
static Mutex _mtx;
_mtx.lock();
bool& _executed = (*thread_execute_map)[GetThreadBin()];
_mtx.unlock();
if(!_executed)
{
func();
_executed = true;
return 1;
}
return 0;
};
//--------------------------------------------------------------------//
auto _task = tg->wrap(thread_specific_func);
//++(*m_ntasks);
// TaskSubQueue* task_subq = (*m_subqueues)[i];
// task_subq->PushTask(_task);
InsertTask(_task, ThreadData::GetInstance(), i);
}
tp->notify_all();
int nexecuted = tg->join();
if(nexecuted != m_workers)
{
std::stringstream msg;
msg << "Failure executing routine on all threads! Only " << nexecuted
<< " threads executed function out of " << m_workers;
std::cerr << msg.str() << std::endl;
// Exception("UserTaskQueue::ExecuteOnAllThreads", "TaskQueue0000",
// JustWarning, msg);
}
delete thread_execute_map;
ReleaseHold();
}
//======================================================================================//
void
UserTaskQueue::ExecuteOnSpecificThreads(ThreadIdSet tid_set, ThreadPool* tp,
function_type func)
{
typedef TaskGroup<int, int> task_group_type;
typedef std::map<int64_t, bool> thread_execute_map_t;
auto join_func = [=](int& ref, int i) {
ref += i;
return ref;
};
task_group_type* tg = new task_group_type(join_func, tp);
// wait for all threads to finish any work
// NOTE: will cause deadlock if called from a task
while(tp->get_active_threads_count() > 0)
ThisThread::sleep_for(std::chrono::milliseconds(10));
if(!tp->is_alive())
{
func();
return;
}
thread_execute_map_t* thread_execute_map = new thread_execute_map_t();
//========================================================================//
// wrap the function so that it will only be executed if the thread
// has an ID in the set
auto thread_specific_func = [=]() {
static Mutex _mtx;
_mtx.lock();
bool& _executed = (*thread_execute_map)[GetThreadBin()];
_mtx.unlock();
if(!_executed && tid_set.count(ThisThread::get_id()) > 0)
{
func();
_executed = true;
return 1;
}
return 0;
};
//========================================================================//
if(tid_set.count(ThisThread::get_id()) > 0)
func();
AcquireHold();
for(int i = 0; i < (m_workers + 1); ++i)
{
if(i == GetThreadBin())
continue;
auto _task = tg->wrap(thread_specific_func);
InsertTask(_task, ThreadData::GetInstance(), i);
}
tp->notify_all();
int nexecuted = tg->join();
if(nexecuted != m_workers)
{
std::stringstream msg;
msg << "Failure executing routine on all threads! Only " << nexecuted
<< " threads executed function out of " << tid_set.size();
std::cerr << msg.str() << std::endl;
// Exception("UserTaskQueue::ExecuteOnSpecificThreads", "TaskQueue0001",
// JustWarning, msg);
}
delete thread_execute_map;
ReleaseHold();
}
//======================================================================================//
void
UserTaskQueue::AcquireHold()
{
bool _hold;
while(!(_hold = m_hold->load(std::memory_order_relaxed)))
{
m_hold->compare_exchange_strong(_hold, true, std::memory_order_release,
std::memory_order_relaxed);
}
}
//======================================================================================//
void
UserTaskQueue::ReleaseHold()
{
bool _hold;
while((_hold = m_hold->load(std::memory_order_relaxed)))
{
m_hold->compare_exchange_strong(_hold, false, std::memory_order_release,
std::memory_order_relaxed);
}
}
//======================================================================================//
+108
View File
@@ -0,0 +1,108 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class implementation
//
// Class Description:
//
// This file creates an abstract base class for the thread-pool tasking
// system
//
// ---------------------------------------------------------------
// Author: Jonathan Madsen (Feb 13th 2018)
// ---------------------------------------------------------------
#include "PTL/VTask.hh"
#include "PTL/ThreadData.hh"
#include "PTL/ThreadPool.hh"
#include "PTL/VTaskGroup.hh"
using namespace PTL;
//======================================================================================//
VTask::VTask()
: m_depth(0)
, m_group(nullptr)
, m_pool(nullptr)
{}
//======================================================================================//
VTask::VTask(VTaskGroup* task_group)
: m_depth(0)
, m_group(task_group)
, m_pool((m_group) ? task_group->pool() : nullptr)
{}
//======================================================================================//
VTask::VTask(ThreadPool* tp)
: m_depth(0)
, m_group(nullptr)
, m_pool(tp)
{}
//======================================================================================//
VTask::~VTask() {}
//======================================================================================//
void
VTask::operator--()
{
if(m_group)
{
intmax_t _count = --(*m_group);
if(_count < 2)
{
try
{
m_group->task_cond()->notify_all();
} catch(std::system_error& e)
{
auto tid = ThreadPool::GetThisThreadID();
AutoLock l(TypeMutex<decltype(std::cerr)>(), std::defer_lock);
if(!l.owns_lock())
l.lock();
std::cerr << "[" << tid << "] Caught system error: " << e.what()
<< std::endl;
}
}
}
}
//======================================================================================//
bool
VTask::is_native_task() const
{
return (m_group) ? m_group->is_native_task_group() : false;
}
//======================================================================================//
ThreadPool*
VTask::pool() const
{
return (!m_pool && m_group) ? m_group->pool() : m_pool;
}
//======================================================================================//
+220
View File
@@ -0,0 +1,220 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class implementation
//
// Class Description:
//
// This file creates an abstract base class for the grouping the thread-pool
// tasking system into independently joinable units
//
// ---------------------------------------------------------------
// Author: Jonathan Madsen (Feb 13th 2018)
// ---------------------------------------------------------------
#include "PTL/VTaskGroup.hh"
#include "PTL/Globals.hh"
#include "PTL/Task.hh"
#include "PTL/TaskRunManager.hh"
#include "PTL/ThreadData.hh"
#include "PTL/ThreadPool.hh"
#include "PTL/VTask.hh"
using namespace PTL;
//======================================================================================//
std::atomic_uintmax_t&
vtask_group_counter()
{
static std::atomic_uintmax_t _instance(0);
return _instance;
}
//======================================================================================//
int VTaskGroup::f_verbose = GetEnv<int>("PTL_VERBOSE", 0);
//======================================================================================//
VTaskGroup::VTaskGroup(ThreadPool* tp)
: m_id(vtask_group_counter()++)
, m_pool(tp)
, m_tot_task_count(std::make_shared<atomic_int>(0))
, m_task_cond(std::make_shared<condition_t>())
, m_task_lock(std::make_shared<lock_t>())
, m_main_tid(std::this_thread::get_id())
{
if(!m_pool && TaskRunManager::GetMasterRunManager())
m_pool = TaskRunManager::GetMasterRunManager()->GetThreadPool();
if(!m_pool)
{
std::cerr << __FUNCTION__ << "@" << __LINE__ << " :: Warning! "
<< "nullptr to thread pool!" << std::endl;
}
}
//======================================================================================//
VTaskGroup::~VTaskGroup() {}
//======================================================================================//
void
VTaskGroup::wait()
{
// if no pool was initially present at creation
if(!m_pool)
{
// check for master MT run-manager
if(TaskRunManager::GetMasterRunManager())
m_pool = TaskRunManager::GetMasterRunManager()->GetThreadPool();
// if MTRunManager does not exist or no thread pool created
if(!m_pool)
{
if(f_verbose > 0)
{
fprintf(stderr, "%s @ %i :: Warning! nullptr to thread-pool (%p)\n",
__FUNCTION__, __LINE__, static_cast<void*>(m_pool));
std::cerr << __FUNCTION__ << "@" << __LINE__ << " :: Warning! "
<< "nullptr to thread pool!" << std::endl;
}
return;
}
}
ThreadData* data = ThreadData::GetInstance();
if(!data)
return;
ThreadPool* tpool = (m_pool) ? m_pool : data->thread_pool;
VUserTaskQueue* taskq = (tpool) ? tpool->get_queue() : data->current_queue;
bool _is_master = data->is_master;
bool _within_task = data->within_task;
auto is_active_state = [&]() {
return (tpool->state()->load(std::memory_order_relaxed) !=
thread_pool::state::STOPPED);
};
auto execute_this_threads_tasks = [&]() {
if(!taskq)
return;
// only want to process if within a task
if((!_is_master || tpool->size() < 2) && _within_task)
{
int bin = static_cast<int>(taskq->GetThreadBin());
// const auto nitr = (tpool) ? tpool->size() : Thread::hardware_concurrency();
while(this->pending() > 0)
{
task_pointer _task = taskq->GetTask(bin);
if(_task)
(*_task)();
}
}
};
// checks for validity
if(!is_native_task_group())
{
// for external threads
if(!_is_master || tpool->size() < 2)
return;
}
else if(f_verbose > 0)
{
if(!tpool || !taskq)
{
// something is wrong, didn't create thread-pool?
fprintf(
stderr,
"%s @ %i :: Warning! nullptr to thread data (%p) or task-queue (%p)\n",
__FUNCTION__, __LINE__, static_cast<void*>(tpool),
static_cast<void*>(taskq));
}
// return if thread pool isn't built
else if(is_native_task_group() && !tpool->is_alive())
{
fprintf(stderr, "%s @ %i :: Warning! thread-pool is not alive!\n",
__FUNCTION__, __LINE__);
}
else if(!is_active_state())
{
fprintf(stderr, "%s @ %i :: Warning! thread-pool is not active!\n",
__FUNCTION__, __LINE__);
}
}
intmax_t wake_size = 2;
AutoLock _lock(*m_task_lock, std::defer_lock);
while(is_active_state())
{
execute_this_threads_tasks();
// while loop protects against spurious wake-ups
while(_is_master && pending() > 0 && is_active_state())
{
// auto _wake = [&]() { return (wake_size > pending() || !is_active_state());
// };
// lock before sleeping on condition
if(!_lock.owns_lock())
_lock.lock();
// Wait until signaled that a task has been competed
// Unlock mutex while wait, then lock it back when signaled
// when true, this wakes the thread
if(pending() >= wake_size)
{
m_task_cond->wait(_lock);
}
else
{
m_task_cond->wait_for(_lock, std::chrono::microseconds(100));
}
// unlock
if(_lock.owns_lock())
_lock.unlock();
}
// if pending is not greater than zero, we are joined
if(pending() <= 0)
break;
}
if(_lock.owns_lock())
_lock.unlock();
intmax_t ntask = this->task_count().load();
if(ntask > 0)
{
std::stringstream ss;
ss << "\nWarning! Join operation issue! " << ntask << " tasks still "
<< "are running!" << std::endl;
std::cerr << ss.str();
this->wait();
}
}
//======================================================================================//
+60
View File
@@ -0,0 +1,60 @@
//
// MIT License
// Copyright (c) 2020 Jonathan R. Madsen
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// ---------------------------------------------------------------
// Tasking class implementation
// Class Description:
// Abstract base class for creating a task queue used by
// ThreadPool
// ---------------------------------------------------------------
// Author: Jonathan Madsen
// ---------------------------------------------------------------
#include "PTL/VUserTaskQueue.hh"
#include "PTL/TaskRunManager.hh"
using namespace PTL;
//======================================================================================//
VUserTaskQueue::VUserTaskQueue(intmax_t nworkers)
: m_workers(nworkers)
{
if(m_workers < 0)
{
TaskRunManager* rm = TaskRunManager::GetMasterRunManager();
m_workers = (rm) ? rm->GetNumberOfThreads() + 1 // number of threads + 1
: (2 * std::thread::hardware_concurrency()) + 1;
// hyperthreads + 1
}
}
//======================================================================================//
VUserTaskQueue::~VUserTaskQueue() {}
//======================================================================================//
/*
intmax_t& VUserTaskQueue::ThisThreadNumber() const
{
// get a thread id number
static thread_local intmax_t _tid;
return _tid;
}
*/
//======================================================================================//
+21 -17
View File
@@ -19,21 +19,25 @@ committal in the CVS repository !
History file for G4 zlib package
--------------------------------
28 May 2020 Ben Morgan (zlib-V10-06-00)
- sources.cmake: Add needed include directories to G4zlib
module when using new cmake system.
21 June 2018 Gunter Folger (zlib-V10-04-03)
- gzwrite.c: one more cast to int,
- gzwrite.c: one more cast to int,
20 June 2018 Gunter Folger (zlib-V10-04-02)
- fixes in gzread.c/gzwrite.c for Windows 64 bit builds.
- fixes in gzread.c/gzwrite.c for Windows 64 bit builds.
Correctioins taken from https://github.com/madler/zlib/pull/279
14 June 2018 Gunter Folger (zlib-V10-04-01)
- move all includes in src/ to include/; This helps keep
geant4_validate_sources.cmake simpler
- move all includes in src/ to include/; This helps keep
geant4_validate_sources.cmake simpler
12 June 2018 Gunter Folger (zlib-V10-04-00)
- addressing compilation warnings from gcc8:
switch back to original source from zlib 1.2.11, and use C compiler.
18 May 2017 Gunter Folger (zlib-V10-03-01)
- disable warning on fall-through on many case statements issued by gcc7
add directive -Wno-implicit-fallthrough in CMakeLists.txt, and fix copy-paste typo.
@@ -47,28 +51,28 @@ History file for G4 zlib package
19 April 2015 Gunter Folger (zlib-V10-02-01)
- fix in gzguts.h for Windows; as of Studio 2015 in 64 bit compilation,
_snprintf is no longer provided, but snprintf finally is available.
_snprintf is no longer provided, but snprintf finally is available.
15 January 2015Gunter Folger (zlib-V10-02-00)
- fix clang compiler warning in inflate.cc: return explicit value
- fix clang compiler warning in inflate.cc: return explicit value
of -1L << 16 (0xffffFFFFffff000L)
10 July 2015 Gunter Folger (zlib-V10-01-01)
- Add type casts to fix windows compilations errors.
8 July 2015 Gunter Folger (zlib-V10-01-00)
- Update to zlib-1.2.8, keeping out structure.
- Update to zlib-1.2.8, keeping out structure.
Compare files to new zlib, and transfer changes, keeping local
modifications, like function declarationss, and removed register keyword.
Note that test06 now contains the test code.
10 April 2014 Gabriele Cosmo (zlib-V10-00-01)
- Get rid of deprecated 'register' storage class specifier in source files
crc32.cc, deflate.cc and trees.cc.
18 March 2014 Gabriele Cosmo (zlib-V10-00-00)
- Fixed compilation warnings from clang-3.4 for unused variables in
deflate.cc and inftrees.cc
deflate.cc and inftrees.cc
7 May 2013 Gunter Folger (zlib-V09-06-03)
- Fixes for Windows:
@@ -80,15 +84,15 @@ History file for G4 zlib package
6 May 2013 Gunter Folger (zlib-V09-06-02)
- Adapt full zlib to G4 cmake:
- copied portions of zlib CMakeLists.tzt to sources.cmake,
using standard G4 CMakeLists.txt
- Private header are in src, include only has zlib.h. zconf.h is created
in build tree from src/zconf.h.cmakein; zlib.h and zconf.h are installed.
- copied portions of zlib CMakeLists.tzt to sources.cmake,
using standard G4 CMakeLists.txt
- Private header are in src, include only has zlib.h. zconf.h is created
in build tree from src/zconf.h.cmakein; zlib.h and zconf.h are installed.
27 March 2013 Gunter Folger (zlib-V09-06-01)
- Update to zlib 1.2.7
- include complete zlib, not only writer.
20 March 2013 Gabriele Cosmo (zlib-V09-06-00)
- Moved from visualization/externals module
+6 -1
View File
@@ -137,4 +137,9 @@ GEANT4_DEFINE_MODULE(NAME G4zlib
LINK_LIBRARIES
)
# List any source specific properties here
# List any source specific properties here
if(GEANT4_USE_NEW_CMAKE)
geant4_module_include_directories(G4zlib
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src;${CMAKE_CURRENT_BINARY_DIR}>
)
endif()