Import Geant4 10.7.0.beta source tree
This commit is contained in:
Vendored
+40
@@ -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()
|
||||
|
||||
Vendored
+4
@@ -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
|
||||
|
||||
Vendored
+12
-1
@@ -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.
|
||||
|
||||
|
||||
Vendored
+5
-1
@@ -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()
|
||||
|
||||
Vendored
+65
@@ -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
|
||||
...
|
||||
Vendored
+40
@@ -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'
|
||||
...
|
||||
Vendored
+170
@@ -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()
|
||||
|
||||
Vendored
+22
@@ -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
|
||||
|
||||
Vendored
+36
@@ -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.
|
||||
Vendored
+21
@@ -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.
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
# Parallel Tasking Library (PTL)
|
||||
Lightweight C++11 multithreading tasking system featuring thread-pool, task-groups, and lock-free task queue
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
0.0.2
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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
@@ -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})
|
||||
@@ -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
@@ -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()
|
||||
@@ -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)
|
||||
+2819
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
Vendored
+108
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
*/
|
||||
//======================================================================================//
|
||||
Vendored
+21
-17
@@ -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
|
||||
|
||||
|
||||
Vendored
+6
-1
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user