Committing TBB 2019 Update 5 source code
authortbbdev <inteltbbdevelopers@intel.com>
Thu, 21 Mar 2019 11:27:03 +0000 (14:27 +0300)
committertbbdev <inteltbbdevelopers@intel.com>
Thu, 21 Mar 2019 15:28:21 +0000 (18:28 +0300)
49 files changed:
CHANGES
README.md
cmake/README.rst
cmake/TBBInstallConfig.cmake [new file with mode: 0644]
cmake/TBBMakeConfig.cmake
cmake/tbb_config_installer.cmake [new file with mode: 0644]
cmake/templates/TBBConfig.cmake.in
cmake/templates/TBBConfigInternal.cmake.in [moved from cmake/templates/TBBConfigForSource.cmake.in with 88% similarity]
include/tbb/concurrent_hash_map.h
include/tbb/concurrent_priority_queue.h
include/tbb/concurrent_unordered_map.h
include/tbb/concurrent_unordered_set.h
include/tbb/global_control.h
include/tbb/internal/_allocator_traits.h
include/tbb/internal/_concurrent_unordered_impl.h
include/tbb/internal/_x86_rtm_rw_mutex_impl.h
include/tbb/iterators.h
include/tbb/partitioner.h
include/tbb/spin_rw_mutex.h
include/tbb/task_scheduler_observer.h
include/tbb/tbb_config.h
include/tbb/tbb_stddef.h
src/old/test_task_scheduler_observer_v3.cpp
src/rml/server/thread_monitor.h
src/tbb/market.cpp
src/tbb/observer_proxy.cpp
src/tbb/observer_proxy.h
src/tbb/pipeline.cpp
src/tbb/queuing_rw_mutex.cpp
src/tbb/tbb_main.cpp
src/tbb/tbb_misc.h
src/tbbmalloc/frontend.cpp
src/tbbmalloc/proxy.cpp
src/tbbmalloc/tbb_function_replacement.cpp
src/test/harness_defs.h
src/test/test_concurrent_hash_map.cpp
src/test/test_concurrent_priority_queue.cpp
src/test/test_concurrent_unordered_common.h
src/test/test_concurrent_unordered_map.cpp
src/test/test_concurrent_unordered_set.cpp
src/test/test_global_control.cpp
src/test/test_iterators.cpp
src/test/test_malloc_pools.cpp
src/test/test_malloc_whitebox.cpp
src/test/test_mutex.cpp
src/test/test_pipeline_with_tbf.cpp
src/test/test_task_arena.cpp
src/test/test_task_scheduler_observer.cpp
src/test/test_tbb_version.cpp

diff --git a/CHANGES b/CHANGES
index 7e791f2..afcf1b1 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,42 @@
 The list of most significant changes made over time in
 Intel(R) Threading Building Blocks (Intel(R) TBB).
 
+Intel TBB 2019 Update 5
+TBB_INTERFACE_VERSION == 11005
+
+Changes (w.r.t. Intel TBB 2019 Update 4):
+
+- Associating a task_scheduler_observer with an implicit or explicit
+    task arena is now a fully supported feature.
+- Added a CMake module TBBInstallConfig that allows to generate and
+    install CMake configuration files for TBB packages.
+    Inspired by Hans Johnson (https://github.com/01org/tbb/pull/119).
+- Added node handles, methods merge() and unsafe_extract() to concurrent
+    unordered containers.
+- Added constructors with Compare argument to concurrent_priority_queue
+    (https://github.com/01org/tbb/issues/109).
+- Controlling the stack size of worker threads is now supported for
+    Universal Windows Platform.
+- Improved tbb::zip_iterator to work with algorithms that swap values
+    via iterators.
+- Improved support for user-specified allocators in concurrent_hash_map,
+    including construction of allocator-aware data types.
+- For ReaderWriterMutex types, upgrades and downgrades now succeed if
+    the mutex is already in the requested state.
+    Inspired by Niadb (https://github.com/01org/tbb/pull/122).
+
+Preview Features:
+
+- The task_scheduler_observer::may_sleep() method has been removed.
+
+Bugs fixed:
+
+- Fixed the issue with a pipeline parallel filter executing serially if
+    it follows a thread-bound filter.
+- Fixed a performance regression observed when multiple parallel
+    algorithms start simultaneously.
+    
+------------------------------------------------------------------------
 Intel TBB 2019 Update 4
 TBB_INTERFACE_VERSION == 11004
 
index ea92310..c4a1005 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-# Threading Building Blocks 2019 Update 4
-[![Stable release](https://img.shields.io/badge/version-2019_U4-green.svg)](https://github.com/01org/tbb/releases/tag/2019_U4)
+# Threading Building Blocks 2019 Update 5
+[![Stable release](https://img.shields.io/badge/version-2019_U5-green.svg)](https://github.com/01org/tbb/releases/tag/2019_U5)
 [![Apache License Version 2.0](https://img.shields.io/badge/license-Apache_2.0-green.svg)](LICENSE)
 
 Threading Building Blocks (TBB) lets you easily write parallel C++ programs that take
index 17480d5..68c3081 100644 (file)
@@ -2,26 +2,26 @@
 
 Introduction
 ------------
-Many developers use CMake to manage their development projects, so the Intel(R) Threading Building Blocks (Intel(R) TBB)
-team created the set of CMake modules to simplify integration of the Intel TBB library into a CMake project.
-The modules are available starting from Intel TBB 2017 U7 in `<tbb_root>/cmake <https://github.com/01org/tbb/tree/tbb_2017/cmake>`_.
+Many developers use CMake to manage their development projects, so the Threading Building Blocks (TBB)
+team created the set of CMake modules to simplify integration of the TBB library into a CMake project.
+The modules are available starting from TBB 2017 U7 in `<tbb_root>/cmake <https://github.com/01org/tbb/tree/tbb_2017/cmake>`_.
 
-About Intel TBB
+About TBB
 ^^^^^^^^^^^^^^^
-Intel TBB is a library that supports scalable parallel programming using standard ISO C++ code. It does not require special languages or compilers. It is designed to promote scalable data parallel programming. Additionally, it fully supports nested parallelism, so you can build larger parallel components from smaller parallel components. To use the library, you specify tasks, not threads, and let the library map tasks onto threads in an efficient manner.
+TBB is a library that supports scalable parallel programming using standard ISO C++ code. It does not require special languages or compilers. It is designed to promote scalable data parallel programming. Additionally, it fully supports nested parallelism, so you can build larger parallel components from smaller parallel components. To use the library, you specify tasks, not threads, and let the library map tasks onto threads in an efficient manner.
 
-Many of the library interfaces employ generic programming, in which interfaces are defined by requirements on types and not specific types. The C++ Standard Template Library (STL) is an example of generic programming. Generic programming enables Intel TBB to be flexible yet efficient. The generic interfaces enable you to customize components to your specific needs.
+Many of the library interfaces employ generic programming, in which interfaces are defined by requirements on types and not specific types. The C++ Standard Template Library (STL) is an example of generic programming. Generic programming enables TBB to be flexible yet efficient. The generic interfaces enable you to customize components to your specific needs.
 
-The net result is that Intel TBB enables you to specify parallelism far more conveniently than using raw threads, and at the same time can improve performance.
+The net result is that TBB enables you to specify parallelism far more conveniently than using raw threads, and at the same time can improve performance.
 
 References
 ^^^^^^^^^^
-* `Official Intel TBB open source site <https://www.threadingbuildingblocks.org/>`_
+* `Official TBB open source site <https://www.threadingbuildingblocks.org/>`_
 * `Official GitHub repository <https://github.com/01org/tbb>`_
 
 Engineering team contacts
 ^^^^^^^^^^^^^^^^^^^^^^^^^
-The Intel TBB team is very interested in convenient integration of the Intel TBB library into customer projects. These CMake modules were created to provide such a possibility for CMake projects using a simple but powerful interface. We hope you will try these modules and we are looking forward to receiving your feedback!
+The TBB team is very interested in convenient integration of the TBB library into customer projects. These CMake modules were created to provide such a possibility for CMake projects using a simple but powerful interface. We hope you will try these modules and we are looking forward to receiving your feedback!
 
 E-mail us: `inteltbbdevelopers@intel.com <mailto:inteltbbdevelopers@intel.com>`_.
 
@@ -30,36 +30,35 @@ Visit our `forum <https://software.intel.com/en-us/forums/intel-threading-buildi
 Release Notes
 -------------
 * Minimum supported CMake version: ``3.0.0``.
-* Intel TBB versioning via `find_package <https://cmake.org/cmake/help/latest/command/find_package.html>`_ has the following format: ``find_package(TBB <major>.<minor>.<interface> ...)``. Intel TBB interface version can also be obtained in the customer project via the ``TBB_INTERFACE_VERSION`` variable.
+* TBB versioning via `find_package <https://cmake.org/cmake/help/latest/command/find_package.html>`_ has the following format: ``find_package(TBB <major>.<minor>.<interface> ...)``. TBB interface version can also be obtained in the customer project via the ``TBB_INTERFACE_VERSION`` variable.
 
-Use cases of Intel TBB integration into CMake-aware projects
+Use cases of TBB integration into CMake-aware projects
 ------------------------------------------------------------
-There are two types of Intel TBB packages:
- * Binary packages with pre-built binaries for Windows* OS, Linux* OS and macOS*. They are available on the releases page of the Github repository: https://github.com/01org/tbb/releases. The main purpose of the binary package integration is the ability to build Intel TBB header files and binaries into your CMake-aware project.
- * A source package is also available to download from the release page via the "Source code" link. In addition, it can be cloned from the repository by ``git clone https://github.com/01org/tbb.git``. The main purpose of the source package integration is to allow you to do a custom build of the Intel TBB library from the source files and then build that into your CMake-aware project.
+There are two types of TBB packages:
+ * Binary packages with pre-built binaries for Windows* OS, Linux* OS and macOS*. They are available on the releases page of the Github repository: https://github.com/01org/tbb/releases. The main purpose of the binary package integration is the ability to build TBB header files and binaries into your CMake-aware project.
+ * A source package is also available to download from the release page via the "Source code" link. In addition, it can be cloned from the repository by ``git clone https://github.com/01org/tbb.git``. The main purpose of the source package integration is to allow you to do a custom build of the TBB library from the source files and then build that into your CMake-aware project.
 
-There are four types of CMake modules that can be used to integrate Intel TBB: `TBBConfig`, `TBBGet`, `TBBMakeConfig` and `TBBBuild`. See `Technical documentation for CMake modules`_ section for additional details.
+There are four types of CMake modules that can be used to integrate TBB: `TBBConfig`, `TBBGet`, `TBBMakeConfig` and `TBBBuild`. See `Technical documentation for CMake modules`_ section for additional details.
 
 Binary package integration
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-The following use case is valid for packages starting from Intel TBB 2017 U7:
+The following use case is valid for packages starting from TBB 2017 U7:
 
 * Download package manually and make integration.
 
- Pre-condition: Location of TBBConfig.cmake is available via ``TBB_DIR`` or ``CMAKE_PREFIX_PATH`` contains path to Intel TBB root.
+ Pre-condition: Location of TBBConfig.cmake is available via ``TBB_DIR`` or ``CMAKE_PREFIX_PATH`` contains path to TBB root.
 
  CMake code for integration:
-
   .. code:: cmake
 
    find_package(TBB <options>)
 
-The following use case is valid for all Intel TBB 2017 packages.
+The following use case is valid for all TBB 2017 packages.
 
 * Download package using TBBGet_ and make integration.
 
- Pre-condition: Intel TBB CMake modules are available via <path-to-tbb-cmake-modules>.
+ Pre-condition: TBB CMake modules are available via <path-to-tbb-cmake-modules>.
 
  CMake code for integration:
   .. code:: cmake
@@ -70,9 +69,9 @@ The following use case is valid for all Intel TBB 2017 packages.
 
 Source package integration
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
-* Build Intel TBB from existing source files using TBBBuild_ and make integration.
+* Build TBB from existing source files using TBBBuild_ and make integration.
 
- Pre-condition: Intel TBB source code is available via <tbb_root> and Intel TBB CMake modules are available via <path-to-tbb-cmake-modules>.
+ Pre-condition: TBB source code is available via <tbb_root> and TBB CMake modules are available via <path-to-tbb-cmake-modules>.
 
  CMake code for integration:
   .. code:: cmake
@@ -81,9 +80,9 @@ Source package integration
    tbb_build(TBB_ROOT <tbb_root> CONFIG_DIR TBB_DIR)
    find_package(TBB <options>)
 
-* Download Intel TBB source files using TBBGet_, build it using TBBBuild_ and make integration.
+* Download TBB source files using TBBGet_, build it using TBBBuild_ and make integration.
 
- Pre-condition: Intel TBB CMake modules are available via <path-to-tbb-cmake-modules>.
+ Pre-condition: TBB CMake modules are available via <path-to-tbb-cmake-modules>.
 
  CMake code for integration:
   .. code:: cmake
@@ -94,14 +93,14 @@ Source package integration
    tbb_build(TBB_ROOT ${tbb_root} CONFIG_DIR TBB_DIR)
    find_package(TBB <options>)
 
-Tutorials: Intel TBB integration using CMake
+Tutorials: TBB integration using CMake
 --------------------------------------------
-Binary Intel TBB integration to the sub_string_finder sample (Windows* OS)
+Binary TBB integration to the sub_string_finder sample (Windows* OS)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-In this example, we will integrate binary Intel TBB package into the sub_string_finder sample on Windows* OS (Microsoft* Visual Studio).
+In this example, we will integrate binary TBB package into the sub_string_finder sample on Windows* OS (Microsoft* Visual Studio).
 This example is also applicable for other platforms with slight changes.
-Place holders <version> and <date> should be replaced with the actual values for the Intel TBB package being used. The example is written for `CMake 3.7.1`.
+Place holders <version> and <date> should be replaced with the actual values for the TBB package being used. The example is written for `CMake 3.7.1`.
 
 Precondition:
   * `Microsoft* Visual Studio 11` or higher.
@@ -119,7 +118,7 @@ Precondition:
         # find_package will search for available TBBConfig using variables CMAKE_PREFIX_PATH and TBB_DIR.
         find_package(TBB REQUIRED tbb)
 
-        # Link Intel TBB imported targets to the executable;
+        # Link TBB imported targets to the executable;
         # "TBB::tbb" can be used instead of "${TBB_IMPORTED_TARGETS}".
         target_link_libraries(sub_string_finder ${TBB_IMPORTED_TARGETS})
 #. Run CMake GUI and:
@@ -137,17 +136,17 @@ Precondition:
     * Push the button ``Generate`` and choose a proper generator for your Microsoft* Visual Studio version.
 #. Now you can open the generated solution ``C:/demo_tbb_cmake/tbb<version>_<date>oss/examples/GettingStarted/sub_string_finder/build/sub_string_finder.sln`` in your Microsoft* Visual Studio and build it.
 
-Source code integration of Intel TBB to the sub_string_finder sample (Linux* OS)
+Source code integration of TBB to the sub_string_finder sample (Linux* OS)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-In this example, we will build Intel TBB from source code with enabled Community Preview Features and link the sub_string_finder sample with the built library.
+In this example, we will build TBB from source code with enabled Community Preview Features and link the sub_string_finder sample with the built library.
 This example is also applicable for other platforms with slight changes.
 
 Precondition:
   * `CMake 3.0.0` or higher.
-  * `Git` (to clone the Intel TBB repository from GitHub)
+  * `Git` (to clone the TBB repository from GitHub)
 
-#. Create the directory ``~/demo_tbb_cmake``, go to the created directory and clone the Intel TBB repository there:
+#. Create the directory ``~/demo_tbb_cmake``, go to the created directory and clone the TBB repository there:
     ``mkdir ~/demo_tbb_cmake ; cd ~/demo_tbb_cmake ; git clone https://github.com/01org/tbb.git``
 #. In the directory ``~/demo_tbb_cmake/tbb/examples/GettingStarted/sub_string_finder`` create ``CMakeLists.txt`` file with following content:
     .. code:: cmake
@@ -159,17 +158,17 @@ Precondition:
 
      include(${TBB_ROOT}/cmake/TBBBuild.cmake)
 
-     # Build Intel TBB with enabled Community Preview Features (CPF).
+     # Build TBB with enabled Community Preview Features (CPF).
      tbb_build(TBB_ROOT ${TBB_ROOT} CONFIG_DIR TBB_DIR MAKE_ARGS tbb_cpf=1)
 
      find_package(TBB REQUIRED tbb_preview)
 
-     # Link Intel TBB imported targets to the executable;
+     # Link TBB imported targets to the executable;
      # "TBB::tbb_preview" can be used instead of "${TBB_IMPORTED_TARGETS}".
      target_link_libraries(sub_string_finder ${TBB_IMPORTED_TARGETS})
 #. Create a build directory for the sub_string_finder sample to perform build out of source, go to the created directory
     ``mkdir ~/demo_tbb_cmake/tbb/examples/GettingStarted/sub_string_finder/build ; cd ~/demo_tbb_cmake/tbb/examples/GettingStarted/sub_string_finder/build``
-#. Run CMake to prepare Makefile for the sub_string_finder sample and provide Intel TBB location (root) where to perform build:
+#. Run CMake to prepare Makefile for the sub_string_finder sample and provide TBB location (root) where to perform build:
     ``cmake -DTBB_ROOT=${HOME}/demo_tbb_cmake/tbb ..``
 #. Make an executable and run it:
     ``make ; ./sub_string_finder``
@@ -179,15 +178,15 @@ Technical documentation for CMake modules
 TBBConfig
 ^^^^^^^^^
 
-Configuration module for ``Intel(R) Threading Building Blocks (Intel(R) TBB)`` library.
+Configuration module for TBB library.
 
 How to use this module in your CMake project:
- #. Add location of Intel TBB (root) to `CMAKE_PREFIX_PATH <https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html>`_
+ #. Add location of TBB (root) to `CMAKE_PREFIX_PATH <https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html>`_
     or specify location of TBBConfig.cmake in ``TBB_DIR``.
- #. Use `find_package <https://cmake.org/cmake/help/latest/command/find_package.html>`_ to configure Intel TBB.
- #. Use provided variables and/or imported targets (described below) to work with Intel TBB.
+ #. Use `find_package <https://cmake.org/cmake/help/latest/command/find_package.html>`_ to configure TBB.
+ #. Use provided variables and/or imported targets (described below) to work with TBB.
 
-Intel TBB components can be passed to `find_package <https://cmake.org/cmake/help/latest/command/find_package.html>`_
+TBB components can be passed to `find_package <https://cmake.org/cmake/help/latest/command/find_package.html>`_
 after keyword ``COMPONENTS`` or ``REQUIRED``.
 Use basic names of components (``tbb``, ``tbbmalloc``, ``tbb_preview``, etc.).
 
@@ -198,72 +197,146 @@ If ``tbbmalloc_proxy`` is requested, ``tbbmalloc`` component will also be added
 TBBConfig creates `imported targets <https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#imported-targets>`_ as
 shared libraries using the following format: ``TBB::<component>`` (for example, ``TBB::tbb``, ``TBB::tbbmalloc``).
 
-Variables set during Intel TBB configuration:
+Variables set during TBB configuration:
 
 =========================  ================================================
          Variable                            Description
 =========================  ================================================
-``TBB_FOUND``              Intel TBB library is found
-``TBB_<component>_FOUND``  specific Intel TBB component is found
-``TBB_IMPORTED_TARGETS``   all created Intel TBB imported targets
-``TBB_VERSION``            Intel TBB version (format: ``<major>.<minor>``)
-``TBB_INTERFACE_VERSION``  Intel TBB interface version
+``TBB_FOUND``              TBB library is found
+``TBB_<component>_FOUND``  specific TBB component is found
+``TBB_IMPORTED_TARGETS``   all created TBB imported targets
+``TBB_VERSION``            TBB version (format: ``<major>.<minor>``)
+``TBB_INTERFACE_VERSION``  TBB interface version
 =========================  ================================================
 
+TBBInstallConfig
+^^^^^^^^^^^^^^^^
+
+Module for generation and installation of TBB CMake configuration files (TBBConfig.cmake and TBBConfigVersion.cmake files) on Linux and macOS.
+
+Provides the following functions:
+
+ .. code:: cmake
+
+  tbb_install_config(INSTALL_DIR <install_dir> SYSTEM_NAME Linux|Darwin
+                     [TBB_VERSION <major>.<minor>.<interface>|TBB_VERSION_FILE <version_file>]
+                     [LIB_REL_PATH <lib_rel_path> INC_REL_PATH <inc_rel_path>]
+                     [LIB_PATH <lib_path> INC_PATH <inc_path>])``
+
+**Note: the module overwrites existing TBBConfig.cmake and TBBConfigVersion.cmake files in <install_dir>.**
+
+``tbb_config_installer.cmake`` allows to run ``TBBInstallConfig.cmake`` from command line.
+It accepts the same parameters as ``tbb_install_config`` function, run ``cmake -P tbb_config_installer.cmake`` to get help.
+
+Use cases
+"""""""""
+**Prepare TBB CMake configuration files for custom TBB package.**
+
+The use case is applicable for package maintainers who create own TBB packages and want to create TBBConfig.cmake and TBBConfigVersion.cmake for these packages.
+
+===========================================  ===========================================================
+              Parameter                                      Description
+===========================================  ===========================================================
+``INSTALL_DIR <directory>``                  Directory to install CMake configuration files
+``SYSTEM_NAME Linux|Darwin``                 OS name to generate config files for
+``TBB_VERSION_FILE <version_file>``          Path to ``tbb_stddef.h`` to parse version from and
+                                             write it to TBBConfigVersion.cmake
+``TBB_VERSION <major>.<minor>.<interface>``  Directly specified TBB version;
+                                             alternative to ``TBB_VERSION_FILE`` parameter
+``LIB_REL_PATH <lib_rel_path>``              Relative path to TBB binaries, default: ``../..``
+``INC_REL_PATH <inc_rel_path>``              Relative path to TBB headers, default: ``../../../include``
+===========================================  ===========================================================
+
+*Example*
+
+ Assume your package is installed to the following structure:
+
+ * Binaries go to ``<prefix>/lib``
+ * Headers go to ``<prefix>/include``
+ * CMake configuration files go to ``<prefix>/lib/cmake/<package>``
+
+ The package is packed from ``/my/package/content`` directory.
+
+ ``cmake -DINSTALL_DIR=/my/package/content/lib/cmake/TBB -DSYSTEM_NAME=Linux -DTBB_VERSION_FILE=/my/package/content/include/tbb/tbb_stddef.h -P tbb_config_installer.cmake`` (default relative paths will be used)
+
+**Install TBB CMake configuration files for installed TBB.**
+
+The use case is applicable for users who have installed TBB, but do not have (or have incorrect) CMake configuration files for this TBB.
+
+============================  ==============================================
+      Parameter                            Description
+============================  ==============================================
+``INSTALL_DIR <directory>``   Directory to install CMake configuration files
+``SYSTEM_NAME Linux|Darwin``  OS name to generate config files for
+``LIB_PATH <lib_path>``       Path to installed TBB binaries
+``INC_PATH <inc_path>``       Path to installed TBB headers
+============================  ==============================================
+
+``LIB_PATH`` and ``INC_PATH`` will be converted to relative paths based on ``INSTALL_DIR``.
+By default TBB version will be parsed from ``<inc_path>/tbb/tbb_stddef.h``,
+but it can be overridden by optional parameters ``TBB_VERSION_FILE`` or ``TBB_VERSION``.
+
+*Example*
+
+ TBB is installed to ``/usr`` directory.
+ In order to create TBBConfig.cmake and TBBConfigVersion.cmake in ``/usr/lib/cmake/TBB`` run
+
+ ``cmake -DINSTALL_DIR=/usr/lib/cmake/TBB -DSYSTEM_NAME=Linux -DLIB_PATH=/usr/lib -DINC_PATH=/usr/include -P tbb_config_installer.cmake``.
+
 TBBGet
 ^^^^^^
 
-Module for getting ``Intel(R) Threading Building Blocks (Intel(R) TBB)`` library from `GitHub <https://github.com/01org/tbb>`_.
+Module for getting TBB library from `GitHub <https://github.com/01org/tbb>`_.
 
 Provides the following functions:
  ``tbb_get(TBB_ROOT <variable> [RELEASE_TAG <release_tag>|LATEST] [SAVE_TO <path>] [SYSTEM_NAME Linux|Windows|Darwin] [CONFIG_DIR <variable> | SOURCE_CODE])``
-  downloads Intel TBB from GitHub and creates TBBConfig for the downloaded binary package if there is no TBBConfig.
+  downloads TBB from GitHub and creates TBBConfig for the downloaded binary package if there is no TBBConfig.
 
   ====================================  ====================================
                      Parameter                       Description
   ====================================  ====================================
-  ``TBB_ROOT <variable>``               a variable to save Intel TBB root in, ``<variable>-NOTFOUND`` will be provided in case ``tbb_get`` is unsuccessful
-  ``RELEASE_TAG <release_tag>|LATEST``  Intel TBB release tag to be downloaded (for example, ``2017_U6``), ``LATEST`` is used by default
-  ``SAVE_TO <path>``                    path to location at which to unpack downloaded Intel TBB, ``${CMAKE_CURRENT_BINARY_DIR}/tbb_downloaded`` is used by default
+  ``TBB_ROOT <variable>``               a variable to save TBB root in, ``<variable>-NOTFOUND`` will be provided in case ``tbb_get`` is unsuccessful
+  ``RELEASE_TAG <release_tag>|LATEST``  TBB release tag to be downloaded (for example, ``2017_U6``), ``LATEST`` is used by default
+  ``SAVE_TO <path>``                    path to location at which to unpack downloaded TBB, ``${CMAKE_CURRENT_BINARY_DIR}/tbb_downloaded`` is used by default
   ``SYSTEM_NAME Linux|Windows|Darwin``  operating system name to download a binary package for,
                                         value of `CMAKE_SYSTEM_NAME <https://cmake.org/cmake/help/latest/variable/CMAKE_SYSTEM_NAME.html>`_ is used by default
   ``CONFIG_DIR <variable>``             a variable to save location of TBBConfig.cmake and TBBConfigVersion.cmake. Ignored if ``SOURCE_CODE`` specified
-  ``SOURCE_CODE``                       flag to get Intel TBB source code (instead of binary package)
+  ``SOURCE_CODE``                       flag to get TBB source code (instead of binary package)
   ====================================  ====================================
 
 TBBMakeConfig
 ^^^^^^^^^^^^^
 
-Module for making TBBConfig in ``Intel(R) Threading Building Blocks (Intel(R) TBB)`` binary package.
+Module for making TBBConfig in `official TBB binary packages published on GitHub <https://github.com/01org/tbb/releases>`_.
 
 This module is to be used for packages that do not have TBBConfig.
 
 Provides the following functions:
  ``tbb_make_config(TBB_ROOT <path> CONFIG_DIR <variable> [SYSTEM_NAME Linux|Windows|Darwin])``
-  creates CMake configuration files (TBBConfig.cmake and TBBConfigVersion.cmake) for Intel TBB binary package.
+  creates CMake configuration files (TBBConfig.cmake and TBBConfigVersion.cmake) for TBB binary package.
 
   ====================================  ====================================
                      Parameter                       Description
   ====================================  ====================================
-  ``TBB_ROOT <variable>``               path to Intel TBB root
+  ``TBB_ROOT <variable>``               path to TBB root
   ``CONFIG_DIR <variable>``             a variable to store location of the created configuration files
-  ``SYSTEM_NAME Linux|Windows|Darwin``  operating system name of the binary Intel TBB package,
+  ``SYSTEM_NAME Linux|Windows|Darwin``  operating system name of the binary TBB package,
                                         value of `CMAKE_SYSTEM_NAME <https://cmake.org/cmake/help/latest/variable/CMAKE_SYSTEM_NAME.html>`_ is used by default
   ====================================  ====================================
 
 TBBBuild
 ^^^^^^^^
 
-Module for building ``Intel(R) Threading Building Blocks (Intel(R) TBB)`` library from the source code.
+Module for building TBB library from the source code.
 
 Provides the following functions:
  ``tbb_build(TBB_ROOT <tbb_root> CONFIG_DIR <variable> [MAKE_ARGS <custom_make_arguments>])``
-  builds Intel TBB from source code using the ``Makefile``, creates and provides the location of the CMake configuration files (TBBConfig.cmake and TBBConfigVersion.cmake) .
+  builds TBB from source code using the ``Makefile``, creates and provides the location of the CMake configuration files (TBBConfig.cmake and TBBConfigVersion.cmake) .
 
   =====================================  ====================================
                 Parameter                             Description
   =====================================  ====================================
-  ``TBB_ROOT <variable>``                path to Intel TBB root
+  ``TBB_ROOT <variable>``                path to TBB root
   ``CONFIG_DIR <variable>``              a variable to store location of the created configuration files,
                                          ``<variable>-NOTFOUND`` will be provided in case ``tbb_build`` is unsuccessful
   ``MAKE_ARGS <custom_make_arguments>``  custom arguments to be passed to ``make`` tool.
diff --git a/cmake/TBBInstallConfig.cmake b/cmake/TBBInstallConfig.cmake
new file mode 100644 (file)
index 0000000..c1ee526
--- /dev/null
@@ -0,0 +1,98 @@
+# Copyright (c) 2017-2019 Intel Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+#
+#
+
+include(CMakeParseArguments)
+
+# Save the location of Intel TBB CMake modules here, as it will not be possible to do inside functions,
+# see for details: https://cmake.org/cmake/help/latest/variable/CMAKE_CURRENT_LIST_DIR.html
+set(_tbb_cmake_module_path ${CMAKE_CURRENT_LIST_DIR})
+
+function(tbb_install_config)
+    set(oneValueArgs INSTALL_DIR
+                     SYSTEM_NAME
+                     LIB_REL_PATH INC_REL_PATH TBB_VERSION TBB_VERSION_FILE
+                     LIB_PATH INC_PATH)                                      # If TBB is installed on the system
+
+    cmake_parse_arguments(tbb_IC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+    get_filename_component(config_install_dir ${tbb_IC_INSTALL_DIR} ABSOLUTE)
+    file(MAKE_DIRECTORY ${config_install_dir})
+
+    # --- TBB_LIB_REL_PATH handling ---
+    set(TBB_LIB_REL_PATH "../..")
+
+    if (tbb_IC_LIB_REL_PATH)
+        set(TBB_LIB_REL_PATH ${tbb_IC_LIB_REL_PATH})
+    endif()
+
+    if (tbb_IC_LIB_PATH)
+        get_filename_component(lib_abs_path ${tbb_IC_LIB_PATH} ABSOLUTE)
+        file(RELATIVE_PATH TBB_LIB_REL_PATH ${config_install_dir} ${lib_abs_path})
+        unset(lib_abs_path)
+    endif()
+    # ------
+
+    # --- TBB_INC_REL_PATH handling ---
+    set(TBB_INC_REL_PATH "../../../include")
+
+    if (tbb_IC_INC_REL_PATH)
+        set(TBB_INC_REL_PATH ${tbb_IC_INC_REL_PATH})
+    endif()
+
+    if (tbb_IC_INC_PATH)
+        get_filename_component(inc_abs_path ${tbb_IC_INC_PATH} ABSOLUTE)
+        file(RELATIVE_PATH TBB_INC_REL_PATH ${config_install_dir} ${inc_abs_path})
+        unset(inc_abs_path)
+    endif()
+    # ------
+
+    # --- TBB_VERSION handling ---
+    if (tbb_IC_TBB_VERSION)
+        set(TBB_VERSION ${tbb_IC_TBB_VERSION})
+    else()
+        set(tbb_version_file "${config_install_dir}/${TBB_INC_REL_PATH}/tbb/tbb_stddef.h")
+        if (tbb_IC_TBB_VERSION_FILE)
+            set(tbb_version_file ${tbb_IC_TBB_VERSION_FILE})
+        endif()
+
+        file(READ ${tbb_version_file} _tbb_stddef)
+        string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1" _tbb_ver_major "${_tbb_stddef}")
+        string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1" _tbb_ver_minor "${_tbb_stddef}")
+        string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" _tbb_ver_interface "${_tbb_stddef}")
+        set(TBB_VERSION "${_tbb_ver_major}.${_tbb_ver_minor}.${_tbb_ver_interface}")
+    endif()
+    # ------
+
+    set(tbb_system_name ${CMAKE_SYSTEM_NAME})
+    if (tbb_IC_SYSTEM_NAME)
+        set(tbb_system_name ${tbb_IC_SYSTEM_NAME})
+    endif()
+
+    if (tbb_system_name STREQUAL "Linux")
+        set(TBB_LIB_PREFIX "lib")
+        set(TBB_LIB_EXT "so.2")
+    elseif (tbb_system_name STREQUAL "Darwin")
+        set(TBB_LIB_PREFIX "lib")
+        set(TBB_LIB_EXT "dylib")
+    else()
+        message(FATAL_ERROR "Unsupported OS name: ${tbb_system_name}")
+    endif()
+
+    configure_file(${_tbb_cmake_module_path}/templates/TBBConfig.cmake.in ${config_install_dir}/TBBConfig.cmake @ONLY)
+    configure_file(${_tbb_cmake_module_path}/templates/TBBConfigVersion.cmake.in ${config_install_dir}/TBBConfigVersion.cmake @ONLY)
+endfunction()
index d4dd515..8b17ccf 100644 (file)
@@ -120,11 +120,15 @@ if (WINDOWS_STORE)
 endif()")
 
         if (tbb_MK_CONFIG_FOR_SOURCE)
-            set(TBB_IMPLIB_RELEASE "\nIMPORTED_IMPLIB_RELEASE \"${tbb_MK_TBB_RELEASE_DIR}/\${_tbb_component}.lib\"")
-            set(TBB_IMPLIB_DEBUG "\nIMPORTED_IMPLIB_DEBUG \"${tbb_MK_TBB_DEBUG_DIR}/\${_tbb_component}_debug.lib\"")
+            set(TBB_IMPLIB_RELEASE "
+                                  IMPORTED_IMPLIB_RELEASE \"${tbb_MK_TBB_RELEASE_DIR}/\${_tbb_component}.lib\"")
+            set(TBB_IMPLIB_DEBUG "
+                                  IMPORTED_IMPLIB_DEBUG \"${tbb_MK_TBB_DEBUG_DIR}/\${_tbb_component}_debug.lib\"")
         else()
-            set(TBB_IMPLIB_RELEASE "\nIMPORTED_IMPLIB_RELEASE \"\${_tbb_root}/lib/\${_tbb_arch_subdir}/\${_tbb_compiler_subdir}/\${_tbb_component}.lib\"")
-            set(TBB_IMPLIB_DEBUG "\nIMPORTED_IMPLIB_DEBUG \"\${_tbb_root}/lib/\${_tbb_arch_subdir}/\${_tbb_compiler_subdir}/\${_tbb_component}_debug.lib\"")
+            set(TBB_IMPLIB_RELEASE "
+                                  IMPORTED_IMPLIB_RELEASE \"\${_tbb_root}/lib/\${_tbb_arch_subdir}/\${_tbb_compiler_subdir}/\${_tbb_component}.lib\"")
+            set(TBB_IMPLIB_DEBUG "
+                                  IMPORTED_IMPLIB_DEBUG \"\${_tbb_root}/lib/\${_tbb_arch_subdir}/\${_tbb_compiler_subdir}/\${_tbb_component}_debug.lib\"")
         endif()
 
         # Note: multiline variable
@@ -156,11 +160,34 @@ endif()")
     set(TBB_VERSION "${_tbb_ver_major}.${_tbb_ver_minor}.${TBB_INTERFACE_VERSION}")
 
     if (tbb_MK_CONFIG_FOR_SOURCE)
-        set(_tbb_config_template TBBConfigForSource.cmake.in)
+        set(TBB_CHOOSE_ARCH_AND_COMPILER "")
+        set(TBB_RELEASE_LIB_PATH "${TBB_RELEASE_DIR}")
+        set(TBB_DEBUG_LIB_PATH "${TBB_DEBUG_DIR}")
+        set(TBB_UNSET_ADDITIONAL_VARIABLES "")
     else()
-        set(_tbb_config_template TBBConfig.cmake.in)
+        # Note: multiline variable
+        set(TBB_CHOOSE_ARCH_AND_COMPILER "
+if (CMAKE_SIZEOF_VOID_P EQUAL 8)
+    set(_tbb_arch_subdir ${TBB_X64_SUBDIR})
+else()
+    set(_tbb_arch_subdir ${TBB_X32_SUBDIR})
+endif()
+
+${TBB_CHOOSE_COMPILER_SUBDIR}
+
+get_filename_component(_tbb_lib_path \"\${_tbb_root}/${TBB_SHARED_LIB_DIR}/\${_tbb_arch_subdir}/\${_tbb_compiler_subdir}\" ABSOLUTE)
+")
+
+    set(TBB_RELEASE_LIB_PATH "\${_tbb_lib_path}")
+    set(TBB_DEBUG_LIB_PATH "\${_tbb_lib_path}")
+
+    # Note: multiline variable
+    set(TBB_UNSET_ADDITIONAL_VARIABLES "
+unset(_tbb_arch_subdir)
+unset(_tbb_compiler_subdir)")
     endif()
-    configure_file(${_tbb_cmake_module_path}/templates/${_tbb_config_template}   ${tbb_config_dir}/TBBConfig.cmake @ONLY)
+
+    configure_file(${_tbb_cmake_module_path}/templates/TBBConfigInternal.cmake.in ${tbb_config_dir}/TBBConfig.cmake @ONLY)
     configure_file(${_tbb_cmake_module_path}/templates/TBBConfigVersion.cmake.in ${tbb_config_dir}/TBBConfigVersion.cmake @ONLY)
 
     set(${tbb_MK_CONFIG_DIR} ${tbb_config_dir} PARENT_SCOPE)
diff --git a/cmake/tbb_config_installer.cmake b/cmake/tbb_config_installer.cmake
new file mode 100644 (file)
index 0000000..3da396a
--- /dev/null
@@ -0,0 +1,50 @@
+# Copyright (c) 2017-2019 Intel Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+#
+#
+
+function(tbb_conf_gen_print_help)
+    message("Usage: cmake -DINSTALL_DIR=<config_install_dir> -DSYSTEM_NAME=Linux|Darwin <parameters> -P tbb_config_generator.cmake
+
+Parameters:
+  For custom TBB package:
+    -DTBB_VERSION_FILE=<tbb_version_file>
+    -DTBB_VERSION=<major>.<minor>.<interface> (alternative to TBB_VERSION_FILE)
+    -DLIB_REL_PATH=<relative_path_to_tbb_binaries>
+    -DINC_REL_PATH=<relative_path_to_tbb_headers>
+  For installed TBB:
+    -DLIB_PATH=<path_to_installed_tbb_binaries>
+    -DINC_PATH=<path_to_installed_tbb_headers>
+")
+endfunction()
+
+if (NOT DEFINED INSTALL_DIR)
+    tbb_conf_gen_print_help()
+    message(FATAL_ERROR "Required parameter INSTALL_DIR is not defined")
+endif()
+
+if (NOT DEFINED SYSTEM_NAME)
+    tbb_conf_gen_print_help()
+    message(FATAL_ERROR "Required parameter SYSTEM_NAME is not defined")
+endif()
+
+foreach (arg TBB_VERSION LIB_REL_PATH INC_REL_PATH TBB_VERSION_FILE LIB_PATH INC_PATH)
+    set(optional_args ${optional_args} ${arg} ${${arg}})
+endforeach()
+
+include(${CMAKE_CURRENT_LIST_DIR}/TBBInstallConfig.cmake)
+tbb_install_config(INSTALL_DIR ${INSTALL_DIR} SYSTEM_NAME ${SYSTEM_NAME} ${optional_args})
+message(STATUS "TBBConfig files were created in ${INSTALL_DIR}")
index d12cff6..4bc8af3 100644 (file)
 #
 #
 
-# TBB_FOUND should not be set explicitly. It is defined automatically by CMake.
-# Handling of TBB_VERSION is in TBBConfigVersion.cmake.
+# It defines the following variables:
+#     TBB_tbb_FOUND
+#     TBB_tbbmalloc_FOUND
+#     TBB_tbbmalloc_proxy_FOUND
+#     TBB_IMPORTED_TARGETS
+#
+# TBBConfigVersion.cmake defines TBB_VERSION
+#
+# Initialize to default values
+if (NOT TBB_tbb_FOUND)
+    set(TBB_tbb_FOUND 0)
+endif()
+if (NOT TBB_tbbmalloc_FOUND)
+    set(TBB_tbbmalloc_FOUND 0)
+endif()
+if (NOT TBB_tbbmalloc_proxy_FOUND)
+    set(TBB_tbbmalloc_proxy_FOUND 0)
+endif()
+if (NOT TBB_IMPORTED_TARGETS)
+    set(TBB_IMPORTED_TARGETS "")
+endif()
 
 if (NOT TBB_FIND_COMPONENTS)
-    set(TBB_FIND_COMPONENTS "@TBB_DEFAULT_COMPONENTS@")
+    set(TBB_FIND_COMPONENTS "tbb;tbbmalloc;tbbmalloc_proxy")
     foreach (_tbb_component ${TBB_FIND_COMPONENTS})
         set(TBB_FIND_REQUIRED_${_tbb_component} 1)
     endforeach()
@@ -34,66 +53,47 @@ if (NOT _tbbmalloc_proxy_ix EQUAL -1)
         list(APPEND TBB_FIND_COMPONENTS tbbmalloc)
         set(TBB_FIND_REQUIRED_tbbmalloc ${TBB_FIND_REQUIRED_tbbmalloc_proxy})
     endif()
+    unset(_tbbmalloc_ix)
 endif()
-
-set(TBB_INTERFACE_VERSION @TBB_INTERFACE_VERSION@)
-
-get_filename_component(_tbb_root "${CMAKE_CURRENT_LIST_FILE}" PATH)
-get_filename_component(_tbb_root "${_tbb_root}" PATH)
-
-set(_tbb_x32_subdir @TBB_X32_SUBDIR@)
-set(_tbb_x64_subdir @TBB_X64_SUBDIR@)
-
-if (CMAKE_SIZEOF_VOID_P EQUAL 8)
-    set(_tbb_arch_subdir ${_tbb_x64_subdir})
-else()
-    set(_tbb_arch_subdir ${_tbb_x32_subdir})
-endif()
-
-@TBB_CHOOSE_COMPILER_SUBDIR@
-
-get_filename_component(_tbb_lib_path "${_tbb_root}/@TBB_SHARED_LIB_DIR@/${_tbb_arch_subdir}/${_tbb_compiler_subdir}" ABSOLUTE)
+unset(_tbbmalloc_proxy_ix)
 
 foreach (_tbb_component ${TBB_FIND_COMPONENTS})
-    set(_tbb_release_lib "${_tbb_lib_path}/@TBB_LIB_PREFIX@${_tbb_component}.@TBB_LIB_EXT@")
-    set(_tbb_debug_lib "${_tbb_lib_path}/@TBB_LIB_PREFIX@${_tbb_component}_debug.@TBB_LIB_EXT@")
+    set(_tbb_release_lib "${CMAKE_CURRENT_LIST_DIR}/@TBB_LIB_REL_PATH@/@TBB_LIB_PREFIX@${_tbb_component}.@TBB_LIB_EXT@")
+    set(_tbb_debug_lib "${CMAKE_CURRENT_LIST_DIR}/@TBB_LIB_REL_PATH@/@TBB_LIB_PREFIX@${_tbb_component}_debug.@TBB_LIB_EXT@")
 
     if (EXISTS "${_tbb_release_lib}" OR EXISTS "${_tbb_debug_lib}")
-        add_library(TBB::${_tbb_component} SHARED IMPORTED)
-        set_target_properties(TBB::${_tbb_component} PROPERTIES
-                              INTERFACE_INCLUDE_DIRECTORIES "${_tbb_root}/include"@TBB_COMPILE_DEFINITIONS@)
-
-
-        if (EXISTS "${_tbb_release_lib}")
-            set_target_properties(TBB::${_tbb_component} PROPERTIES
-                                  IMPORTED_LOCATION_RELEASE "${_tbb_release_lib}"@TBB_IMPLIB_RELEASE@)
-            set_property(TARGET TBB::${_tbb_component} APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
-        endif()
-
-        if (EXISTS "${_tbb_debug_lib}")
+        if (NOT TARGET TBB::${_tbb_component})
+            add_library(TBB::${_tbb_component} SHARED IMPORTED)
             set_target_properties(TBB::${_tbb_component} PROPERTIES
-                                  IMPORTED_LOCATION_DEBUG "${_tbb_debug_lib}"@TBB_IMPLIB_DEBUG@)
-            set_property(TARGET TBB::${_tbb_component} APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
-        endif()
-
-        # Add internal dependencies for imported targets: TBB::tbbmalloc_proxy -> TBB::tbbmalloc
-        if (_tbb_component STREQUAL tbbmalloc_proxy)
-            set_target_properties(TBB::tbbmalloc_proxy PROPERTIES INTERFACE_LINK_LIBRARIES TBB::tbbmalloc)
+                                  INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}/@TBB_INC_REL_PATH@")
+
+            if (EXISTS "${_tbb_release_lib}")
+                set_target_properties(TBB::${_tbb_component} PROPERTIES
+                                      IMPORTED_LOCATION_RELEASE "${_tbb_release_lib}")
+                set_property(TARGET TBB::${_tbb_component} APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+            endif()
+
+            if (EXISTS "${_tbb_debug_lib}")
+                set_target_properties(TBB::${_tbb_component} PROPERTIES
+                                      IMPORTED_LOCATION_DEBUG "${_tbb_debug_lib}")
+                set_property(TARGET TBB::${_tbb_component} APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
+            endif()
+
+            # Add internal dependencies for imported targets: TBB::tbbmalloc_proxy -> TBB::tbbmalloc
+            if (_tbb_component STREQUAL tbbmalloc_proxy)
+                set_target_properties(TBB::tbbmalloc_proxy PROPERTIES INTERFACE_LINK_LIBRARIES TBB::tbbmalloc)
+            endif()
+            list(APPEND TBB_IMPORTED_TARGETS TBB::${_tbb_component})
+        else()
+            message(STATUS "Using previously found TBB::${_tbb_component}")
         endif()
-
-        list(APPEND TBB_IMPORTED_TARGETS TBB::${_tbb_component})
         set(TBB_${_tbb_component}_FOUND 1)
     elseif (TBB_FIND_REQUIRED AND TBB_FIND_REQUIRED_${_tbb_component})
-        message(FATAL_ERROR "Missed required Intel TBB component: ${_tbb_component}")
+        message(STATUS "Missed required Intel TBB component: ${_tbb_component}")
+        message(STATUS "  one or both of:\n   ${_tbb_release_lib}\n    ${_tbb_debug_lib}\n   files must exist.")
+        set(TBB_FOUND FALSE)
+        set(TBB_${_tbb_component}_FOUND 0)
     endif()
 endforeach()
-
-unset(_tbb_x32_subdir)
-unset(_tbb_x64_subdir)
-unset(_tbb_arch_subdir)
-unset(_tbb_compiler_subdir)
-unset(_tbbmalloc_proxy_ix)
-unset(_tbbmalloc_ix)
-unset(_tbb_lib_path)
 unset(_tbb_release_lib)
 unset(_tbb_debug_lib)
similarity index 88%
rename from cmake/templates/TBBConfigForSource.cmake.in
rename to cmake/templates/TBBConfigInternal.cmake.in
index bb1b308..5bceaa5 100644 (file)
@@ -40,17 +40,16 @@ set(TBB_INTERFACE_VERSION @TBB_INTERFACE_VERSION@)
 
 get_filename_component(_tbb_root "${CMAKE_CURRENT_LIST_FILE}" PATH)
 get_filename_component(_tbb_root "${_tbb_root}" PATH)
-
+@TBB_CHOOSE_ARCH_AND_COMPILER@
 foreach (_tbb_component ${TBB_FIND_COMPONENTS})
-    set(_tbb_release_lib "@TBB_RELEASE_DIR@/@TBB_LIB_PREFIX@${_tbb_component}.@TBB_LIB_EXT@")
-    set(_tbb_debug_lib "@TBB_DEBUG_DIR@/@TBB_LIB_PREFIX@${_tbb_component}_debug.@TBB_LIB_EXT@")
+    set(_tbb_release_lib "@TBB_RELEASE_LIB_PATH@/@TBB_LIB_PREFIX@${_tbb_component}.@TBB_LIB_EXT@")
+    set(_tbb_debug_lib "@TBB_DEBUG_LIB_PATH@/@TBB_LIB_PREFIX@${_tbb_component}_debug.@TBB_LIB_EXT@")
 
     if (EXISTS "${_tbb_release_lib}" OR EXISTS "${_tbb_debug_lib}")
         add_library(TBB::${_tbb_component} SHARED IMPORTED)
         set_target_properties(TBB::${_tbb_component} PROPERTIES
                               INTERFACE_INCLUDE_DIRECTORIES "${_tbb_root}/include"@TBB_COMPILE_DEFINITIONS@)
 
-
         if (EXISTS "${_tbb_release_lib}")
             set_target_properties(TBB::${_tbb_component} PROPERTIES
                                   IMPORTED_LOCATION_RELEASE "${_tbb_release_lib}"@TBB_IMPLIB_RELEASE@)
@@ -71,10 +70,12 @@ foreach (_tbb_component ${TBB_FIND_COMPONENTS})
         list(APPEND TBB_IMPORTED_TARGETS TBB::${_tbb_component})
         set(TBB_${_tbb_component}_FOUND 1)
     elseif (TBB_FIND_REQUIRED AND TBB_FIND_REQUIRED_${_tbb_component})
-        message(FATAL_ERROR "Missed required Intel TBB component: ${_tbb_component}")
+        message(STATUS "Missed required Intel TBB component: ${_tbb_component}")
+        set(TBB_FOUND FALSE)
+        set(TBB_${_tbb_component}_FOUND 0)
     endif()
 endforeach()
-
+@TBB_UNSET_ADDITIONAL_VARIABLES@
 unset(_tbbmalloc_proxy_ix)
 unset(_tbbmalloc_ix)
 unset(_tbb_lib_path)
index 7371fab..e7b1067 100644 (file)
 #include <cstring>      // Need std::memset
 #include __TBB_STD_SWAP_HEADER
 
-#include "cache_aligned_allocator.h"
 #include "tbb_allocator.h"
 #include "spin_rw_mutex.h"
 #include "atomic.h"
 #include "tbb_exception.h"
 #include "tbb_profiling.h"
+#include "aligned_space.h"
 #include "internal/_tbb_hash_compare_impl.h"
 #include "internal/_template_helpers.h"
 #include "internal/_allocator_traits.h"
 #if __TBB_STATISTICS
 #include <stdio.h>
 #endif
+#if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && __TBB_CPP11_TUPLE_PRESENT
+// Definition of __TBB_CPP11_RVALUE_REF_PRESENT includes __TBB_CPP11_TUPLE_PRESENT
+// for most of platforms, tuple present macro was added for logical correctness
+#include <tuple>
+#endif
 
 namespace tbb {
 
@@ -181,22 +186,25 @@ namespace interface5 {
         };
 
         //! Enable segment
-        void enable_segment( segment_index_t k, bool is_initial = false ) {
+        template<typename Allocator>
+        void enable_segment( segment_index_t k, const Allocator& allocator, bool is_initial = false ) {
+            typedef typename tbb::internal::allocator_rebind<Allocator, bucket>::type bucket_allocator_type;
+            typedef tbb::internal::allocator_traits<bucket_allocator_type> bucket_allocator_traits;
+            bucket_allocator_type bucket_allocator(allocator);
             __TBB_ASSERT( k, "Zero segment must be embedded" );
             enable_segment_failsafe watchdog( my_table, k );
-            cache_aligned_allocator<bucket> alloc;
             size_type sz;
             __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
             if( k >= first_block ) {
                 sz = segment_size( k );
-                segment_ptr_t ptr = alloc.allocate( sz );
+                segment_ptr_t ptr = bucket_allocator_traits::allocate(bucket_allocator, sz);
                 init_buckets( ptr, sz, is_initial );
                 itt_hide_store_word( my_table[k], ptr );
                 sz <<= 1;// double it to get entire capacity of the container
             } else { // the first block
                 __TBB_ASSERT( k == embedded_block, "Wrong segment index" );
                 sz = segment_size( first_block );
-                segment_ptr_t ptr = alloc.allocate( sz - embedded_buckets );
+                segment_ptr_t ptr = bucket_allocator_traits::allocate(bucket_allocator, sz - embedded_buckets);
                 init_buckets( ptr, sz - embedded_buckets, is_initial );
                 ptr -= segment_base(embedded_block);
                 for(segment_index_t i = embedded_block; i < first_block; i++) // calc the offsets
@@ -206,6 +214,22 @@ namespace interface5 {
             watchdog.my_segment_ptr = 0;
         }
 
+        template<typename Allocator>
+        void delete_segment(segment_index_t s, const Allocator& allocator) {
+            typedef typename tbb::internal::allocator_rebind<Allocator, bucket>::type bucket_allocator_type;
+            typedef tbb::internal::allocator_traits<bucket_allocator_type> bucket_allocator_traits;
+            bucket_allocator_type bucket_allocator(allocator);
+            segment_ptr_t buckets_ptr = my_table[s];
+            size_type sz = segment_size( s ? s : 1 );
+
+            if( s >= first_block) // the first segment or the next
+                bucket_allocator_traits::deallocate(bucket_allocator, buckets_ptr, sz);
+            else if( s == embedded_block && embedded_block != first_block )
+                bucket_allocator_traits::deallocate(bucket_allocator, buckets_ptr,
+                                                    segment_size(first_block) - embedded_buckets);
+            if( s >= embedded_block ) my_table[s] = 0;
+        }
+
         //! Get bucket by (masked) hashcode
         bucket *get_bucket( hashcode_t h ) const throw() { // TODO: add throw() everywhere?
             segment_index_t s = segment_index_of( h );
@@ -274,11 +298,12 @@ namespace interface5 {
         }
 
         //! Prepare enough segments for number of buckets
-        void reserve(size_type buckets) {
+        template<typename Allocator>
+        void reserve(size_type buckets, const Allocator& allocator) {
             if( !buckets-- ) return;
             bool is_initial = !my_size;
             for( size_type m = my_mask; buckets > m; m = my_mask )
-                enable_segment( segment_index_of( m+1 ), is_initial );
+                enable_segment( segment_index_of( m+1 ), allocator, is_initial );
         }
         //! Swap hash_map_bases
         void internal_swap(hash_map_base &table) {
@@ -369,7 +394,7 @@ namespace interface5 {
         {}
         Value& operator*() const {
             __TBB_ASSERT( hash_map_base::is_valid(my_node), "iterator uninitialized or at end of container?" );
-            return my_node->item;
+            return my_node->value();
         }
         Value* operator->() const {return &operator*();}
         hash_map_iterator& operator++();
@@ -555,62 +580,78 @@ public:
 
 protected:
     friend class const_accessor;
-    struct node;
+    class node;
     typedef typename tbb::internal::allocator_rebind<Allocator, node>::type node_allocator_type;
+    typedef tbb::internal::allocator_traits<node_allocator_type> node_allocator_traits;
     node_allocator_type my_allocator;
     HashCompare my_hash_compare;
 
-    struct node : public node_base {
-        value_type item;
-        node( const Key &key ) : item(key, T()) {}
-        node( const Key &key, const T &t ) : item(key, t) {}
-#if __TBB_CPP11_RVALUE_REF_PRESENT
-        node( const Key &key, T &&t ) : item(key, std::move(t)) {}
-        node( value_type&& i ) : item(std::move(i)){}
-#if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
-        template<typename... Args>
-        node( Args&&... args ) : item(std::forward<Args>(args)...) {}
-#if __TBB_COPY_FROM_NON_CONST_REF_BROKEN
-        node( value_type& i ) : item(const_cast<const value_type&>(i)) {}
-#endif //__TBB_COPY_FROM_NON_CONST_REF_BROKEN
-#endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
-#endif //__TBB_CPP11_RVALUE_REF_PRESENT
-        node( const value_type& i ) : item(i) {}
-
-        // exception-safe allocation, see C++ Standard 2003, clause 5.3.4p17
-        void *operator new( size_t /*size*/, node_allocator_type &a ) {
-            void *ptr = a.allocate(1);
-            if(!ptr)
-                tbb::internal::throw_exception(tbb::internal::eid_bad_alloc);
-            return ptr;
-        }
-        // match placement-new form above to be called if exception thrown in constructor
-        void operator delete( void *ptr, node_allocator_type &a ) { a.deallocate(static_cast<node*>(ptr),1); }
+    class node : public node_base {
+        tbb::aligned_space<value_type> my_value;
+    public:
+        value_type* storage() { return my_value.begin(); }
+        value_type& value() { return *storage(); }
     };
 
     void delete_node( node_base *n ) {
-        my_allocator.destroy( static_cast<node*>(n) );
-        my_allocator.deallocate( static_cast<node*>(n), 1);
+        node_allocator_traits::destroy(my_allocator, static_cast<node*>(n)->storage());
+        node_allocator_traits::destroy(my_allocator, static_cast<node*>(n));
+        node_allocator_traits::deallocate(my_allocator, static_cast<node*>(n), 1);
+    }
+
+    struct node_scoped_guard : tbb::internal::no_copy {
+        node* my_node;
+        node_allocator_type& my_alloc;
+
+        node_scoped_guard(node* n, node_allocator_type& alloc) : my_node(n), my_alloc(alloc) {}
+        ~node_scoped_guard() {
+            if(my_node) {
+                node_allocator_traits::destroy(my_alloc, my_node);
+                node_allocator_traits::deallocate(my_alloc, my_node, 1);
+            }
+        }
+        void dismiss() { my_node = NULL; }
+    };
+
+#if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
+    template<typename... Args>
+    static node* create_node(node_allocator_type& allocator, Args&&... args)
+#else
+    template<typename Arg1, typename Arg2>
+    static node* create_node(node_allocator_type& allocator, __TBB_FORWARDING_REF(Arg1) arg1, __TBB_FORWARDING_REF(Arg2) arg2)
+#endif
+    {
+        node* node_ptr = node_allocator_traits::allocate(allocator, 1);
+        node_scoped_guard guard(node_ptr, allocator);
+        node_allocator_traits::construct(allocator, node_ptr);
+#if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
+        node_allocator_traits::construct(allocator, node_ptr->storage(), std::forward<Args>(args)...);
+#else
+        node_allocator_traits::construct(allocator, node_ptr->storage(), tbb::internal::forward<Arg1>(arg1), tbb::internal::forward<Arg2>(arg2));
+#endif
+        guard.dismiss();
+        return node_ptr;
     }
 
     static node* allocate_node_copy_construct(node_allocator_type& allocator, const Key &key, const T * t){
-        return  new( allocator ) node(key, *t);
+        return create_node(allocator, key, *t);
     }
 
 #if __TBB_CPP11_RVALUE_REF_PRESENT
     static node* allocate_node_move_construct(node_allocator_type& allocator, const Key &key, const T * t){
-        return  new( allocator ) node(key, std::move(*const_cast<T*>(t)));
+        return create_node(allocator, key, std::move(*const_cast<T*>(t)));
     }
-#if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
-    template<typename... Args>
-    static node* allocate_node_emplace_construct(node_allocator_type& allocator, Args&&... args){
-        return  new( allocator ) node(std::forward<Args>(args)...);
-    }
-#endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
-#endif //__TBB_CPP11_RVALUE_REF_PRESENT
+#endif
 
     static node* allocate_node_default_construct(node_allocator_type& allocator, const Key &key, const T * ){
-        return  new( allocator ) node(key);
+#if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && __TBB_CPP11_TUPLE_PRESENT
+        // Emplace construct an empty T object inside the pair
+        return create_node(allocator, std::piecewise_construct,
+                           std::forward_as_tuple(key), std::forward_as_tuple());
+#else
+        T obj; // Use of temporary object in impossible, because create_node takes non-const reference
+        return create_node(allocator, key, tbb::internal::move(obj));
+#endif
     }
 
     static node* do_not_allocate_node(node_allocator_type& , const Key &, const T * ){
@@ -620,7 +661,7 @@ protected:
 
     node *search_bucket( const key_type &key, bucket *b ) const {
         node *n = static_cast<node*>( b->node_list );
-        while( is_valid(n) && !my_hash_compare.equal(key, n->item.first) )
+        while( is_valid(n) && !my_hash_compare.equal(key, n->value().first) )
             n = static_cast<node*>( n->next );
         __TBB_ASSERT(n != internal::rehash_req, "Search can be executed only for rehashed bucket");
         return n;
@@ -665,7 +706,7 @@ protected:
         __TBB_ASSERT( (mask&(mask+1))==0 && (h & mask) == h, NULL );
     restart:
         for( node_base **p = &b_old()->node_list, *n = __TBB_load_with_acquire(*p); is_valid(n); n = *p ) {
-            hashcode_t c = my_hash_compare.hash( static_cast<node*>(n)->item.first );
+            hashcode_t c = my_hash_compare.hash( static_cast<node*>(n)->value().first );
 #if TBB_USE_ASSERT
             hashcode_t bmask = h & (mask>>1);
             bmask = bmask==0? 1 : ( 1u<<(__TBB_Log2( bmask )+1 ) ) - 1; // minimal mask of parent bucket
@@ -717,7 +758,7 @@ public:
         //! Return reference to associated value in hash table.
         const_reference operator*() const {
             __TBB_ASSERT( my_node, "attempt to dereference empty accessor" );
-            return my_node->item;
+            return my_node->value();
         }
 
         //! Return pointer to associated value in hash table.
@@ -747,7 +788,7 @@ public:
         //! Return reference to associated value in hash table.
         reference operator*() const {
             __TBB_ASSERT( this->my_node, "attempt to dereference empty accessor" );
-            return this->my_node->item;
+            return this->my_node->value();
         }
 
         //! Return pointer to associated value in hash table.
@@ -769,13 +810,13 @@ public:
     concurrent_hash_map( size_type n, const allocator_type &a = allocator_type() )
         : internal::hash_map_base(), my_allocator(a)
     {
-        reserve( n );
+        reserve( n, my_allocator );
     }
 
     concurrent_hash_map( size_type n, const HashCompare& compare, const allocator_type& a = allocator_type() )
         : internal::hash_map_base(), my_allocator(a), my_hash_compare(compare)
     {
-        reserve( n );
+        reserve( n, my_allocator );
     }
 
     //! Copy constructor
@@ -1088,8 +1129,8 @@ protected:
     template<typename Accessor, typename... Args>
     bool generic_emplace( Accessor && result, Args &&... args ) {
         result.release();
-        node * node_ptr = allocate_node_emplace_construct(my_allocator, std::forward<Args>(args)...);
-        return lookup(/*insert*/true, node_ptr->item.first, NULL, accessor_location(result), is_write_access_needed(result), &do_not_allocate_node, node_ptr );
+        node * node_ptr = create_node(my_allocator, std::forward<Args>(args)...);
+        return lookup(/*insert*/true, node_ptr->value().first, NULL, accessor_location(result), is_write_access_needed(result), &do_not_allocate_node, node_ptr );
     }
 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
 #endif //__TBB_CPP11_RVALUE_REF_PRESENT
@@ -1236,7 +1277,7 @@ check_growth:
 #if __TBB_STATISTICS
         my_info_resizes++; // concurrent ones
 #endif
-        enable_segment( grow_segment );
+        enable_segment( grow_segment, my_allocator );
     }
     if( tmp_n ) // if op_insert only
         delete_node( tmp_n );
@@ -1304,7 +1345,7 @@ restart:
     search:
         node_base **p = &b()->node_list;
         n = *p;
-        while( is_valid(n) && !my_hash_compare.equal(key, static_cast<node*>(n)->item.first ) ) {
+        while( is_valid(n) && !my_hash_compare.equal(key, static_cast<node*>(n)->value().first ) ) {
             p = &n->next;
             n = *p;
         }
@@ -1340,7 +1381,7 @@ void concurrent_hash_map<Key,T,HashCompare,A>::swap(concurrent_hash_map<Key,T,Ha
 
 template<typename Key, typename T, typename HashCompare, typename A>
 void concurrent_hash_map<Key,T,HashCompare,A>::rehash(size_type sz) {
-    reserve( sz ); // TODO: add reduction of number of buckets as well
+    reserve( sz, my_allocator ); // TODO: add reduction of number of buckets as well
     hashcode_t mask = my_mask;
     hashcode_t b = (mask+1)>>1; // size or first index of the last segment
     __TBB_ASSERT((b&(b-1))==0, NULL); // zero or power of 2
@@ -1359,7 +1400,7 @@ void concurrent_hash_map<Key,T,HashCompare,A>::rehash(size_type sz) {
             // now h - is index of the root rehashed bucket b_old
             mark_rehashed_levels( h ); // mark all non-rehashed children recursively across all segments
             for( node_base **p = &b_old->node_list, *q = *p; is_valid(q); q = *p ) {
-                hashcode_t c = my_hash_compare.hash( static_cast<node*>(q)->item.first );
+                hashcode_t c = my_hash_compare.hash( static_cast<node*>(q)->value().first );
                 if( (c & mask) != h ) { // should be rehashed
                     *p = q->next; // exclude from b_old
                     bucket *b_new = get_bucket( c & mask );
@@ -1386,7 +1427,7 @@ void concurrent_hash_map<Key,T,HashCompare,A>::rehash(size_type sz) {
 #endif
 #if TBB_USE_ASSERT
         for( ; is_valid(n); n = n->next ) {
-            hashcode_t h = my_hash_compare.hash( static_cast<node*>(n)->item.first ) & mask;
+            hashcode_t h = my_hash_compare.hash( static_cast<node*>(n)->value().first ) & mask;
             __TBB_ASSERT( h == b, "hash() function changed for key in table or internal error" );
         }
 #endif
@@ -1433,7 +1474,7 @@ void concurrent_hash_map<Key,T,HashCompare,A>::clear() {
 #endif
 #if __TBB_EXTRA_DEBUG
         for(; is_valid(n); n = n->next ) {
-            hashcode_t h = my_hash_compare.hash( static_cast<node*>(n)->item.first );
+            hashcode_t h = my_hash_compare.hash( static_cast<node*>(n)->value().first );
             h &= m;
             __TBB_ASSERT( h == b || get_bucket(h)->node_list == internal::rehash_req, "hash() function changed for key in table or internal error" );
         }
@@ -1467,7 +1508,6 @@ void concurrent_hash_map<Key,T,HashCompare,A>::clear() {
     my_size = 0;
     segment_index_t s = segment_index_of( m );
     __TBB_ASSERT( s+1 == pointers_per_table || !my_table[s+1], "wrong mask or concurrent grow" );
-    cache_aligned_allocator<bucket> alloc;
     do {
         __TBB_ASSERT( is_valid( my_table[s] ), "wrong mask or concurrent grow" );
         segment_ptr_t buckets_ptr = my_table[s];
@@ -1477,11 +1517,7 @@ void concurrent_hash_map<Key,T,HashCompare,A>::clear() {
                 buckets_ptr[i].node_list = n->next;
                 delete_node( n );
             }
-        if( s >= first_block) // the first segment or the next
-            alloc.deallocate( buckets_ptr, sz );
-        else if( s == embedded_block && embedded_block != first_block )
-            alloc.deallocate( buckets_ptr, segment_size(first_block)-embedded_buckets );
-        if( s >= embedded_block ) my_table[s] = 0;
+        delete_segment(s, my_allocator);
     } while(s-- > 0);
     my_mask = embedded_buckets - 1;
 }
@@ -1490,7 +1526,7 @@ template<typename Key, typename T, typename HashCompare, typename A>
 void concurrent_hash_map<Key,T,HashCompare,A>::internal_copy( const concurrent_hash_map& source ) {
     hashcode_t mask = source.my_mask;
     if( my_mask == mask ) { // optimized version
-        reserve( source.my_size ); // TODO: load_factor?
+        reserve( source.my_size, my_allocator ); // TODO: load_factor?
         bucket *dst = 0, *src = 0;
         bool rehash_required = false;
         for( hashcode_t k = 0; k <= mask; k++ ) {
@@ -1502,7 +1538,8 @@ void concurrent_hash_map<Key,T,HashCompare,A>::internal_copy( const concurrent_h
                 rehash_required = true;
                 dst->node_list = internal::rehash_req;
             } else for(; n; n = static_cast<node*>( n->next ) ) {
-                add_to_bucket( dst, new( my_allocator ) node(n->item.first, n->item.second) );
+                node* node_ptr = create_node(my_allocator, n->value().first, n->value().second);
+                add_to_bucket( dst, node_ptr);
                 ++my_size; // TODO: replace by non-atomic op
             }
         }
@@ -1513,14 +1550,14 @@ void concurrent_hash_map<Key,T,HashCompare,A>::internal_copy( const concurrent_h
 template<typename Key, typename T, typename HashCompare, typename A>
 template<typename I>
 void concurrent_hash_map<Key,T,HashCompare,A>::internal_copy(I first, I last, size_type reserve_size) {
-    reserve( reserve_size ); // TODO: load_factor?
+    reserve( reserve_size, my_allocator ); // TODO: load_factor?
     hashcode_t m = my_mask;
     for(; first != last; ++first) {
         hashcode_t h = my_hash_compare.hash( (*first).first );
         bucket *b = get_bucket( h & m );
         __TBB_ASSERT( b->node_list != internal::rehash_req, "Invalid bucket in destination table");
-        node *n = new( my_allocator ) node(*first);
-        add_to_bucket( b, n );
+        node* node_ptr = create_node(my_allocator, (*first).first, (*first).second);
+        add_to_bucket( b, node_ptr );
         ++my_size; // TODO: replace by non-atomic op
     }
 }
index ea58ef3..75b29fc 100644 (file)
@@ -27,6 +27,7 @@
 #include "tbb_stddef.h"
 #include "tbb_profiling.h"
 #include "internal/_aggregator_impl.h"
+#include "internal/_template_helpers.h"
 #include <vector>
 #include <iterator>
 #include <functional>
@@ -85,14 +86,28 @@ class concurrent_priority_queue {
     typedef A allocator_type;
 
     //! Constructs a new concurrent_priority_queue with default capacity
-    explicit concurrent_priority_queue(const allocator_type& a = allocator_type()) : mark(0), my_size(0), data(a)
+    explicit concurrent_priority_queue(const allocator_type& a = allocator_type()) : mark(0), my_size(0), compare(), data(a)
+    {
+        my_aggregator.initialize_handler(my_functor_t(this));
+    }
+
+    //! Constructs a new concurrent_priority_queue with default capacity
+    explicit concurrent_priority_queue(const Compare& c, const allocator_type& a = allocator_type()) : mark(0), my_size(0), compare(c), data(a)
     {
         my_aggregator.initialize_handler(my_functor_t(this));
     }
 
     //! Constructs a new concurrent_priority_queue with init_sz capacity
     explicit concurrent_priority_queue(size_type init_capacity, const allocator_type& a = allocator_type()) :
-        mark(0), my_size(0), data(a)
+        mark(0), my_size(0), compare(), data(a)
+    {
+        data.reserve(init_capacity);
+        my_aggregator.initialize_handler(my_functor_t(this));
+    }
+
+    //! Constructs a new concurrent_priority_queue with init_sz capacity
+    explicit concurrent_priority_queue(size_type init_capacity, const Compare& c, const allocator_type& a = allocator_type()) :
+        mark(0), my_size(0), compare(c), data(a)
     {
         data.reserve(init_capacity);
         my_aggregator.initialize_handler(my_functor_t(this));
@@ -101,7 +116,17 @@ class concurrent_priority_queue {
     //! [begin,end) constructor
     template<typename InputIterator>
     concurrent_priority_queue(InputIterator begin, InputIterator end, const allocator_type& a = allocator_type()) :
-        mark(0), data(begin, end, a)
+        mark(0), compare(), data(begin, end, a)
+    {
+        my_aggregator.initialize_handler(my_functor_t(this));
+        heapify();
+        my_size = data.size();
+    }
+
+    //! [begin,end) constructor
+    template<typename InputIterator>
+    concurrent_priority_queue(InputIterator begin, InputIterator end, const Compare& c, const allocator_type& a = allocator_type()) :
+        mark(0), compare(c), data(begin, end, a)
     {
         my_aggregator.initialize_handler(my_functor_t(this));
         heapify();
@@ -111,7 +136,16 @@ class concurrent_priority_queue {
 #if __TBB_INITIALIZER_LISTS_PRESENT
     //! Constructor from std::initializer_list
     concurrent_priority_queue(std::initializer_list<T> init_list, const allocator_type &a = allocator_type()) :
-        mark(0),data(init_list.begin(), init_list.end(), a)
+        mark(0), compare(), data(init_list.begin(), init_list.end(), a)
+    {
+        my_aggregator.initialize_handler(my_functor_t(this));
+        heapify();
+        my_size = data.size();
+    }
+
+    //! Constructor from std::initializer_list
+    concurrent_priority_queue(std::initializer_list<T> init_list, const Compare& c, const allocator_type &a = allocator_type()) :
+        mark(0), compare(c), data(init_list.begin(), init_list.end(), a)
     {
         my_aggregator.initialize_handler(my_functor_t(this));
         heapify();
@@ -482,12 +516,29 @@ class concurrent_priority_queue {
 };
 
 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
+namespace internal {
+
+template<typename T, typename... Args>
+using priority_queue_t = concurrent_priority_queue<
+    T,
+    std::conditional_t< (sizeof...(Args)>0) && !is_allocator_v< pack_element_t<0, Args...> >,
+                        pack_element_t<0, Args...>, std::less<T> >,
+    std::conditional_t< (sizeof...(Args)>0) && is_allocator_v< pack_element_t<sizeof...(Args)-1, Args...> >,
+                         pack_element_t<sizeof...(Args)-1, Args...>, cache_aligned_allocator<T> >
+>;
+}
+
 // Deduction guide for the constructor from two iterators
 template<typename InputIterator,
          typename T = typename std::iterator_traits<InputIterator>::value_type,
-         typename A = cache_aligned_allocator<T>
-> concurrent_priority_queue(InputIterator, InputIterator, const A& = A())
--> concurrent_priority_queue<T, std::less<T>, A>;
+         typename... Args
+> concurrent_priority_queue(InputIterator, InputIterator, Args...)
+-> internal::priority_queue_t<T, Args...>;
+
+template<typename T, typename CompareOrAllocalor>
+concurrent_priority_queue(std::initializer_list<T> init_list, CompareOrAllocalor)
+-> internal::priority_queue_t<T, CompareOrAllocalor>;
+
 #endif /* __TBB_CPP17_DEDUCTION_GUIDES_PRESENT */
 } // namespace interface5
 
index 45bb24e..5c6dd29 100644 (file)
@@ -24,7 +24,6 @@
 #ifndef __TBB_concurrent_unordered_map_H
 #define __TBB_concurrent_unordered_map_H
 
-#include "internal/_template_helpers.h"
 #include "internal/_concurrent_unordered_impl.h"
 
 namespace tbb
@@ -41,6 +40,9 @@ protected:
     typedef Key key_type;
     typedef Hash_compare hash_compare;
     typedef typename tbb::internal::allocator_rebind<Allocator, value_type>::type allocator_type;
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    typedef internal::node_handle<Key, value_type, Allocator> node_type;
+#endif // __TBB_UNORDERED_NODE_HANDLE_PRESENT
 
     enum { allow_multimapping = Allow_multimapping };
 
@@ -55,6 +57,9 @@ protected:
     hash_compare my_hash_compare; // the comparator predicate for keys
 };
 
+template<typename Key, typename T, typename Hasher, typename Key_equality, typename Allocator>
+class concurrent_unordered_multimap;
+
 template <typename Key, typename T, typename Hasher = tbb::tbb_hash<Key>, typename Key_equality = std::equal_to<Key>,
          typename Allocator = tbb::tbb_allocator<std::pair<const Key, T> > >
 class concurrent_unordered_map :
@@ -95,6 +100,9 @@ public:
     typedef typename base_type::const_iterator const_iterator;
     typedef typename base_type::iterator local_iterator;
     typedef typename base_type::const_iterator const_local_iterator;
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    typedef typename base_type::node_type node_type;
+#endif // __TBB_UNORDERED_NODE_HANDLE_PRESENT
 
     // Construction/destruction/copying
     explicit concurrent_unordered_map(size_type n_of_buckets = base_type::initial_bucket_number,
@@ -163,8 +171,8 @@ public:
 
 #endif //# __TBB_INITIALIZER_LISTS_PRESENT
 
-#if __TBB_CPP11_RVALUE_REF_PRESENT
-#if !__TBB_IMPLICIT_MOVE_PRESENT
+
+#if __TBB_CPP11_RVALUE_REF_PRESENT && !__TBB_IMPLICIT_MOVE_PRESENT
     concurrent_unordered_map(const concurrent_unordered_map& table)
         : base_type(table)
     {}
@@ -182,11 +190,31 @@ public:
     {
         return static_cast<concurrent_unordered_map&>(base_type::operator=(std::move(table)));
     }
-#endif //!__TBB_IMPLICIT_MOVE_PRESENT
+#endif //__TBB_CPP11_RVALUE_REF_PRESENT && !__TBB_IMPLICIT_MOVE_PRESENT
 
+#if __TBB_CPP11_RVALUE_REF_PRESENT
     concurrent_unordered_map(concurrent_unordered_map&& table, const Allocator& a) : base_type(std::move(table), a)
     {}
-#endif //__TBB_CPP11_RVALUE_REF_PRESENT
+#endif /*__TBB_CPP11_RVALUE_REF_PRESENT*/
+
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_map<Key, T, Hash, Equality, Allocator>& source)
+              { this->internal_merge(source); }
+
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_map<Key, T, Hash, Equality, Allocator>&& source)
+              { this->internal_merge(source); }
+
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_multimap<Key, T, Hash, Equality, Allocator>& source)
+              { this->internal_merge(source); }
+
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_multimap<Key, T, Hash, Equality, Allocator>&& source)
+              { this->internal_merge(source); }
+
+#endif //__TBB_UNORDERED_NODE_HANDLE_PRESENT
 
     concurrent_unordered_map(const concurrent_unordered_map& table, const Allocator& a)
         : base_type(table, a)
@@ -307,6 +335,9 @@ public:
     typedef typename base_type::const_iterator const_iterator;
     typedef typename base_type::iterator local_iterator;
     typedef typename base_type::const_iterator const_local_iterator;
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    typedef typename base_type::node_type node_type;
+#endif //__TBB_UNORDERED_NODE_HANDLE_PRESENT
 
     // Construction/destruction/copying
     explicit concurrent_unordered_multimap(size_type n_of_buckets = base_type::initial_bucket_number,
@@ -375,8 +406,7 @@ public:
 
 #endif //# __TBB_INITIALIZER_LISTS_PRESENT
 
-#if __TBB_CPP11_RVALUE_REF_PRESENT
-#if !__TBB_IMPLICIT_MOVE_PRESENT
+#if __TBB_CPP11_RVALUE_REF_PRESENT && !__TBB_IMPLICIT_MOVE_PRESENT
     concurrent_unordered_multimap(const concurrent_unordered_multimap& table)
         : base_type(table)
     {}
@@ -394,11 +424,31 @@ public:
     {
         return static_cast<concurrent_unordered_multimap&>(base_type::operator=(std::move(table)));
     }
-#endif //!__TBB_IMPLICIT_MOVE_PRESENT
+#endif //__TBB_CPP11_RVALUE_REF_PRESENT && !__TBB_IMPLICIT_MOVE_PRESENT
 
+#if __TBB_CPP11_RVALUE_REF_PRESENT
     concurrent_unordered_multimap(concurrent_unordered_multimap&& table, const Allocator& a) : base_type(std::move(table), a)
     {}
-#endif //__TBB_CPP11_RVALUE_REF_PRESENT
+#endif /*__TBB_CPP11_RVALUE_REF_PRESENT*/
+
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_map<Key, T, Hash, Equality, Allocator>& source)
+              { this->internal_merge(source); }
+
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_map<Key, T, Hash, Equality, Allocator>&& source)
+              { this->internal_merge(source); }
+
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_multimap<Key, T, Hash, Equality, Allocator>& source)
+              { this->internal_merge(source); }
+
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_multimap<Key, T, Hash, Equality, Allocator>&& source)
+              { this->internal_merge(source); }
+
+#endif //__TBB_UNORDERED_NODE_HANDLE_PRESENT
 
     concurrent_unordered_multimap(const concurrent_unordered_multimap& table, const Allocator& a)
         : base_type(table, a)
index cdcecda..e4c8ef4 100644 (file)
@@ -24,7 +24,6 @@
 #ifndef __TBB_concurrent_unordered_set_H
 #define __TBB_concurrent_unordered_set_H
 
-#include "internal/_template_helpers.h"
 #include "internal/_concurrent_unordered_impl.h"
 
 namespace tbb
@@ -41,6 +40,9 @@ protected:
     typedef Key key_type;
     typedef Hash_compare hash_compare;
     typedef typename tbb::internal::allocator_rebind<Allocator, value_type>::type allocator_type;
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    typedef internal::node_handle<Key, Key, allocator_type> node_type;
+#endif // __TBB_UNORDERED_NODE_HANDLE_PRESENT
 
     enum { allow_multimapping = Allow_multimapping };
 
@@ -54,6 +56,9 @@ protected:
     hash_compare my_hash_compare; // the comparator predicate for keys
 };
 
+template<typename Key, typename Hasher, typename Key_equality, typename Allocator>
+class concurrent_unordered_multiset;
+
 template <typename Key, typename Hasher = tbb::tbb_hash<Key>, typename Key_equality = std::equal_to<Key>, typename Allocator = tbb::tbb_allocator<Key> >
 class concurrent_unordered_set : public internal::concurrent_unordered_base< concurrent_unordered_set_traits<Key, internal::hash_compare<Key, Hasher, Key_equality>, Allocator, false> >
 {
@@ -89,6 +94,9 @@ public:
     typedef typename base_type::const_iterator const_iterator;
     typedef typename base_type::iterator local_iterator;
     typedef typename base_type::const_iterator const_local_iterator;
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    typedef typename base_type::node_type node_type;
+#endif /*__TBB_UNORDERED_NODE_HANDLE_PRESENT*/
 
     // Construction/destruction/copying
     explicit concurrent_unordered_set(size_type n_of_buckets = base_type::initial_bucket_number, const hasher& a_hasher = hasher(),
@@ -152,8 +160,7 @@ public:
 
 #endif //# __TBB_INITIALIZER_LISTS_PRESENT
 
-#if __TBB_CPP11_RVALUE_REF_PRESENT
-#if !__TBB_IMPLICIT_MOVE_PRESENT
+#if __TBB_CPP11_RVALUE_REF_PRESENT && !__TBB_IMPLICIT_MOVE_PRESENT
     concurrent_unordered_set(const concurrent_unordered_set& table)
         : base_type(table)
     {}
@@ -171,12 +178,32 @@ public:
     {
         return static_cast<concurrent_unordered_set&>(base_type::operator=(std::move(table)));
     }
-#endif //!__TBB_IMPLICIT_MOVE_PRESENT
+#endif //__TBB_CPP11_RVALUE_REF_PRESENT && !__TBB_IMPLICIT_MOVE_PRESENT
 
+#if __TBB_CPP11_RVALUE_REF_PRESENT
     concurrent_unordered_set(concurrent_unordered_set&& table, const Allocator& a)
         : base_type(std::move(table), a)
     {}
-#endif //__TBB_CPP11_RVALUE_REF_PRESENT
+#endif /*__TBB_CPP11_RVALUE_REF_PRESENT*/
+
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_set<Key, Hash, Equality, Allocator>& source)
+              { this->internal_merge(source); }
+
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_set<Key, Hash, Equality, Allocator>&& source)
+              { this->internal_merge(source); }
+
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_multiset<Key, Hash, Equality, Allocator>& source)
+              { this->internal_merge(source); }
+
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_multiset<Key, Hash, Equality, Allocator>&& source)
+              { this->internal_merge(source); }
+
+#endif //__TBB_UNORDERED_NODE_HANDLE_PRESENT
 
     concurrent_unordered_set(const concurrent_unordered_set& table, const Allocator& a)
         : base_type(table, a)
@@ -261,6 +288,9 @@ public:
     typedef typename base_type::const_iterator const_iterator;
     typedef typename base_type::iterator local_iterator;
     typedef typename base_type::const_iterator const_local_iterator;
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    typedef typename base_type::node_type node_type;
+#endif // __TBB_UNORDERED_NODE_HANDLE_PRESENT
 
     // Construction/destruction/copying
     explicit concurrent_unordered_multiset(size_type n_of_buckets = base_type::initial_bucket_number,
@@ -329,8 +359,8 @@ public:
 
 #endif //# __TBB_INITIALIZER_LISTS_PRESENT
 
-#if __TBB_CPP11_RVALUE_REF_PRESENT
-#if !__TBB_IMPLICIT_MOVE_PRESENT
+
+#if __TBB_CPP11_RVALUE_REF_PRESENT && !__TBB_IMPLICIT_MOVE_PRESENT
     concurrent_unordered_multiset(const concurrent_unordered_multiset& table)
         : base_type(table)
     {}
@@ -348,13 +378,33 @@ public:
     {
         return static_cast<concurrent_unordered_multiset&>(base_type::operator=(std::move(table)));
     }
-#endif //!__TBB_IMPLICIT_MOVE_PRESENT
+#endif //__TBB_CPP11_RVALUE_REF_PRESENT && !__TBB_IMPLICIT_MOVE_PRESENT
 
+#if __TBB_CPP11_RVALUE_REF_PRESENT
     concurrent_unordered_multiset(concurrent_unordered_multiset&& table, const Allocator& a)
         : base_type(std::move(table), a)
     {
     }
-#endif //__TBB_CPP11_RVALUE_REF_PRESENT
+#endif /*__TBB_CPP11_RVALUE_REF_PRESENT*/
+
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_set<Key, Hash, Equality, Allocator>& source)
+              { this->internal_merge(source); }
+
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_set<Key, Hash, Equality, Allocator>&& source)
+              { this->internal_merge(source); }
+
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_multiset<Key, Hash, Equality, Allocator>& source)
+              { this->internal_merge(source); }
+
+    template<typename Hash, typename Equality>
+    void merge(concurrent_unordered_multiset<Key, Hash, Equality, Allocator>&& source)
+              { this->internal_merge(source); }
+
+#endif //__TBB_UNORDERED_NODE_HANDLE_PRESENT
 
     concurrent_unordered_multiset(const concurrent_unordered_multiset& table, const Allocator& a)
         : base_type(table, a)
index 0c9a23c..0d474c1 100644 (file)
@@ -37,8 +37,8 @@ public:
     global_control(parameter p, size_t value) :
         my_value(value), my_next(NULL), my_param(p) {
         __TBB_ASSERT(my_param < parameter_max, "Invalid parameter");
-#if __TBB_WIN8UI_SUPPORT
-        // For Windows Store* apps it's impossible to set stack size
+#if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)
+        // For Windows Store* apps it's impossible to set stack size
         if (p==thread_stack_size)
             return;
 #elif __TBB_x86_64 && (_WIN32 || _WIN64)
@@ -52,8 +52,8 @@ public:
 
     ~global_control() {
         __TBB_ASSERT(my_param < parameter_max, "Invalid parameter. Probably the object was corrupted.");
-#if __TBB_WIN8UI_SUPPORT
-        // For Windows Store* apps it's impossible to set stack size
+#if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)
+        // For Windows Store* apps it's impossible to set stack size
         if (my_param==thread_stack_size)
             return;
 #endif
index ee11beb..e0403b4 100644 (file)
@@ -93,6 +93,42 @@ struct allocator_traits {
     template <typename U> struct rebind_alloc {
         typedef typename Alloc::template rebind<U>::other other;
     };
+
+    static pointer allocate(Alloc& a, size_type n) {
+        return a.allocate(n);
+    }
+
+    static void deallocate(Alloc& a, pointer p, size_type n) {
+        a.deallocate(p, n);
+    }
+
+    template<typename PT>
+    static void construct(Alloc&, PT* p) {
+        ::new (static_cast<void*>(p)) PT();
+    }
+
+    template<typename PT, typename T1>
+    static void construct(Alloc&, PT* p, __TBB_FORWARDING_REF(T1) t1) {
+        ::new (static_cast<void*>(p)) PT(tbb::internal::forward<T1>(t1));
+    }
+
+    template<typename PT, typename T1, typename T2>
+    static void construct(Alloc&, PT* p, __TBB_FORWARDING_REF(T1) t1, __TBB_FORWARDING_REF(T2) t2) {
+        ::new (static_cast<void*>(p)) PT(tbb::internal::forward<T1>(t1), tbb::internal::forward<T2>(t2));
+    }
+
+    template<typename PT, typename T1, typename T2, typename T3>
+    static void construct(Alloc&, PT* p, __TBB_FORWARDING_REF(T1) t1,
+                          __TBB_FORWARDING_REF(T2) t2, __TBB_FORWARDING_REF(T3) t3) {
+        ::new (static_cast<void*>(p)) PT(tbb::internal::forward<T1>(t1), tbb::internal::forward<T2>(t2),
+                                         tbb::internal::forward<T3>(t3));
+    }
+
+    template<typename T>
+    static void destroy(Alloc&, T* p) {
+        p->~T();
+        tbb::internal::suppress_unused_warning(p);
+    }
 };
 #endif // __TBB_ALLOCATOR_TRAITS_PRESENT
 
index 2a0fbfb..fbeb7d4 100644 (file)
     #include <initializer_list>
 #endif
 
+#if __TBB_CPP11_RVALUE_REF_PRESENT && !__TBB_IMPLICIT_COPY_DELETION_BROKEN
+    #define __TBB_UNORDERED_NODE_HANDLE_PRESENT 1
+#endif
+
 #include "_allocator_traits.h"
 #include "_tbb_hash_compare_impl.h"
+#include "_template_helpers.h"
 
 namespace tbb {
 namespace interface5 {
@@ -125,6 +130,8 @@ class solist_iterator : public flist_iterator<Solist, Value>
     friend class split_ordered_list;
     template<class M, typename V>
     friend class solist_iterator;
+    template <typename Traits>
+    friend class concurrent_unordered_base;
     template<typename M, typename T, typename U>
     friend bool operator==( const solist_iterator<M,T> &i, const solist_iterator<M,U> &j );
     template<typename M, typename T, typename U>
@@ -568,27 +575,49 @@ public:
 
     }
 
-    // This erase function can handle both real and dummy nodes
-    void erase_node(raw_iterator previous, raw_const_iterator& where)
-    {
+    nodeptr_t  erase_node_impl(raw_iterator previous, raw_const_iterator& where) {
         nodeptr_t pnode = (where++).get_node_ptr();
         nodeptr_t prevnode = previous.get_node_ptr();
         __TBB_ASSERT(prevnode->my_next == pnode, "Erase must take consecutive iterators");
         prevnode->my_next = pnode->my_next;
+        return pnode;
+    }
 
+    // This erase function can handle both real and dummy nodes
+    void erase_node(raw_iterator previous, raw_const_iterator& where,
+                    /*allow_destroy*/tbb::internal::true_type)
+    {
+        nodeptr_t pnode = erase_node_impl(previous, where);
         destroy_node(pnode);
     }
 
+    void erase_node(raw_iterator previous, raw_const_iterator& where,
+                    /*allow_destroy*/tbb::internal::false_type)
+    {
+        erase_node_impl(previous, where);
+    }
+
+    void erase_node(raw_iterator previous, raw_const_iterator& where) {
+        erase_node(previous, where, /*allow_destroy*/tbb::internal::true_type());
+    }
+
     // Erase the element (previous node needs to be passed because this is a forward only list)
-    iterator erase_node(raw_iterator previous, const_iterator where)
+    template<typename AllowDestroy>
+    iterator erase_node(raw_iterator previous, const_iterator where, AllowDestroy)
     {
         raw_const_iterator it = where;
-        erase_node(previous, it);
+        erase_node(previous, it, AllowDestroy());
         my_element_count--;
 
         return get_iterator(first_real_iterator(it));
     }
 
+    iterator erase_node(raw_iterator previous, const_iterator& where) {
+        return erase_node(previous, where, /*allow_destroy*/tbb::internal::true_type());
+    }
+
+
+
     // Move all elements from the passed in split-ordered list to this one
     void move_all(self_type& source)
     {
@@ -683,12 +712,19 @@ protected:
     typedef typename solist_t::const_iterator const_iterator;
     typedef iterator local_iterator;
     typedef const_iterator const_local_iterator;
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    typedef typename Traits::node_type node_type;
+#endif // __TBB_UNORDERED_NODE_HANDLE_PRESENT
     using Traits::my_hash_compare;
     using Traits::get_key;
     using Traits::allow_multimapping;
 
     static const size_type initial_bucket_number = 8;                               // Initial number of buckets
+
 private:
+    template<typename OtherTraits>
+    friend class concurrent_unordered_base;
+
     typedef std::pair<iterator, iterator> pairii_t;
     typedef std::pair<const_iterator, const_iterator> paircc_t;
 
@@ -831,6 +867,43 @@ protected:
         internal_clear();
     }
 
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    template<typename SourceType>
+    void internal_merge(SourceType& source) {
+        typedef typename SourceType::iterator source_iterator;
+        __TBB_STATIC_ASSERT((tbb::internal::is_same_type<node_type,
+                            typename SourceType::node_type>::value),
+                            "Incompatible containers cannot be merged");
+
+        for(source_iterator it = source.begin(); it != source.end();) {
+            source_iterator where = it++;
+            if (allow_multimapping || find(get_key(*where)) == end()) {
+                std::pair<node_type, raw_iterator> extract_result = source.internal_extract(where);
+                
+                // If the insertion fails, it returns ownership of the node to extract_result.first
+                // extract_result.first remains valid node handle
+                if (!insert(std::move(extract_result.first)).second) {
+                    raw_iterator next = extract_result.second;
+                    raw_iterator current = next++;
+
+                    __TBB_ASSERT(extract_result.first.my_node->get_order_key() >= current.get_node_ptr()->get_order_key(),
+                                "Wrong nodes order in source container");
+                    __TBB_ASSERT(next==source.my_solist.raw_end() ||
+                                 extract_result.first.my_node->get_order_key() <= next.get_node_ptr()->get_order_key(),
+                                 "Wrong nodes order in source container");
+
+                    size_t new_count = 0;// To use try_insert()
+                    bool insert_result =
+                        source.my_solist.try_insert(current, next, extract_result.first.my_node, &new_count).second;
+                    __TBB_ASSERT_EX(insert_result, "Return to source must be successful. "
+                                                   "Changing source container while merging is unsafe.");
+                }
+                extract_result.first.deactivate();
+            }
+        }
+    }
+#endif // __TBB_UNORDERED_NODE_HANDLE_PRESENT
+
 public:
     allocator_type get_allocator() const {
         return my_solist.get_allocator();
@@ -967,7 +1040,8 @@ public:
 
     // Modifiers
     std::pair<iterator, bool> insert(const value_type& value) {
-        return internal_insert</*AllowCreate=*/tbb::internal::true_type>(value);
+        return internal_insert</*AllowCreate=*/tbb::internal::true_type,
+                               /*AllowDestroy=*/tbb::internal::true_type>(value);
     }
 
     iterator insert(const_iterator, const value_type& value) {
@@ -977,23 +1051,43 @@ public:
 
 #if __TBB_CPP11_RVALUE_REF_PRESENT
     std::pair<iterator, bool> insert(value_type&& value) {
-        return internal_insert</*AllowCreate=*/tbb::internal::true_type>(std::move(value));
+        return internal_insert</*AllowCreate=*/tbb::internal::true_type,
+                               /*AllowDestroy=*/tbb::internal::true_type>(std::move(value));
     }
 
     iterator insert(const_iterator, value_type&& value) {
         // Ignore hint
         return insert(std::move(value)).first;
     }
+#endif /*__TBB_CPP11_RVALUE_REF_PRESENT*/
+
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    std::pair<iterator, bool> insert(node_type&& nh) {
+        if (!nh.empty()) {
+            nodeptr_t handled_node = nh.my_node;
+            std::pair<iterator, bool> insert_result =
+                                      internal_insert</*AllowCreate=*/tbb::internal::false_type,
+                                                      /*AllowDestroy=*/tbb::internal::false_type>
+                                                      (handled_node->my_element, handled_node);
+            if (insert_result.second)
+                nh.deactivate();
+            return insert_result;
+        }
+        return std::pair<iterator, bool>(end(), false);
+    }
+
+    iterator insert(const_iterator, node_type&& nh) {
+        return insert(std::move(nh)).first;
+    }
+#endif // __TBB_UNORDERED_NODE_HANDLE_PRESENT
 
-#if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
+#if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && __TBB_CPP11_RVALUE_REF_PRESENT
     template<typename... Args>
     std::pair<iterator, bool> emplace(Args&&... args) {
         nodeptr_t pnode = my_solist.create_node_v(tbb::internal::forward<Args>(args)...);
-        const sokey_t hashed_element_key = (sokey_t) my_hash_compare(get_key(pnode->my_element));
-        const sokey_t order_key = split_order_key_regular(hashed_element_key);
-        pnode->init(order_key);
 
-        return internal_insert</*AllowCreate=*/tbb::internal::false_type>(pnode->my_element, pnode);
+        return internal_insert</*AllowCreate=*/tbb::internal::false_type,
+                               /*AllowDestroy=*/tbb::internal::true_type>(pnode->my_element, pnode);
     }
 
     template<typename... Args>
@@ -1001,9 +1095,8 @@ public:
         // Ignore hint
         return emplace(tbb::internal::forward<Args>(args)...).first;
     }
+#endif // __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && __TBB_CPP11_RVALUE_REF_PRESENT
 
-#endif // __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
-#endif // __TBB_CPP11_RVALUE_REF_PRESENT
 
     template<class Iterator>
     void insert(Iterator first, Iterator last) {
@@ -1035,6 +1128,18 @@ public:
         return item_count;
     }
 
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    node_type unsafe_extract(const_iterator where) {
+        return internal_extract(where).first;
+    }
+
+    node_type unsafe_extract(const key_type& key) {
+        pairii_t where = equal_range(key);
+        if (where.first == end()) return node_type(); // element was not found
+        return internal_extract(where.first).first;
+    }
+#endif // __TBB_UNORDERED_NODE_HANDLE_PRESENT
+
     void swap(concurrent_unordered_base& right) {
         if (this != &right) {
             std::swap(my_hash_compare, right.my_hash_compare); // TODO: check what ADL meant here
@@ -1269,7 +1374,7 @@ private:
     }
 
     // Insert an element in the hash given its value
-    template<typename AllowCreate, typename ValueType>
+    template<typename AllowCreate, typename AllowDestroy, typename ValueType>
     std::pair<iterator, bool> internal_insert(__TBB_FORWARDING_REF(ValueType) value, nodeptr_t pnode = NULL)
     {
         const key_type *pkey = &get_key(value);
@@ -1294,6 +1399,11 @@ private:
                     // If the value was moved, the known reference to key might be invalid
                     pkey = &get_key(pnode->my_element);
                 }
+                else
+                {
+                    // Set new order_key to node
+                    pnode->init(order_key);
+                }
 
                 // Try to insert 'pnode' between 'previous' and 'where'
                 std::pair<iterator, bool> result = my_solist.try_insert(previous, where, pnode, &new_count);
@@ -1318,7 +1428,7 @@ private:
             else if (!allow_multimapping && solist_t::get_order_key(where) == order_key &&
                      !my_hash_compare(get_key(*where), *pkey)) // TODO: fix negation
             { // Element already in the list, return it
-                 if (pnode)
+                 if (pnode && AllowDestroy::value)
                      my_solist.destroy_node(pnode);
                 return std::pair<iterator, bool>(my_solist.get_iterator(where), false);
             }
@@ -1364,14 +1474,33 @@ private:
         __TBB_ASSERT(previous != last, "Invalid head node");
 
         // First node is a dummy node
-        for (raw_iterator where = previous; ; previous = where) {
+        for (raw_iterator where = previous; where != last; previous = where) {
             ++where;
-            if (where == last)
-                return end();
-            else if (my_solist.get_iterator(where) == it)
+            if (my_solist.get_iterator(where) == it)
                 return my_solist.erase_node(previous, it);
         }
+        return end();
+    }
+
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    std::pair<node_type, raw_iterator> internal_extract(const_iterator it) {
+        sokey_t hash_key = sokey_t(my_hash_compare(get_key(*it)));
+        raw_iterator previous = prepare_bucket(hash_key);
+        raw_iterator last = my_solist.raw_end();
+        __TBB_ASSERT(previous != last, "Invalid head node");
+
+        for(raw_iterator where = previous; where != last; previous = where) {
+            ++where;
+            if (my_solist.get_iterator(where) == it) {
+                const_iterator result = it;
+                my_solist.erase_node(previous, it, /*allow_destroy*/tbb::internal::false_type());
+                return std::pair<node_type, raw_iterator>( node_type(result.get_node_ptr()),
+                                                           previous);
+            }
+        }
+        return std::pair<node_type, iterator>(node_type(), end());
     }
+#endif // __TBB_UNORDERED_NODE_HANDLE_PRESENT
 
     // Return the [begin, end) pair of iterators with the same key values.
     // This operation makes sense only if mapping is many-to-one.
@@ -1522,6 +1651,124 @@ private:
 #pragma warning(pop) // warning 4127 is back
 #endif
 
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+
+template<typename Value, typename Allocator>
+class node_handle_base {
+public:
+    typedef Allocator allocator_type;
+protected:
+    typedef typename split_ordered_list<Value, allocator_type>::node node;
+public:
+
+    node_handle_base() : my_node(NULL), my_allocator() {}
+    node_handle_base(node_handle_base&& nh) : my_node(nh.my_node),
+                                              my_allocator(std::move(nh.my_allocator)) {
+        nh.my_node = NULL;
+    }
+
+    bool empty() const { return my_node == NULL; }
+    explicit operator bool() const { return my_node != NULL; }
+
+    ~node_handle_base() { internal_destroy(); }
+
+    node_handle_base& operator=(node_handle_base&& nh) {
+        internal_destroy();
+        my_node = nh.my_node;
+        typedef typename tbb::internal::allocator_traits<allocator_type>::
+                         propagate_on_container_move_assignment pocma_type;
+        tbb::internal::allocator_move_assignment(my_allocator, nh.my_allocator, pocma_type());
+        nh.deactivate();
+        return *this;
+    }
+
+    void swap(node_handle_base& nh) {
+        std::swap(my_node, nh.my_node);
+        typedef typename tbb::internal::allocator_traits<allocator_type>::
+                         propagate_on_container_swap pocs_type;
+        tbb::internal::allocator_swap(my_allocator, nh.my_allocator, pocs_type());
+    }
+
+    allocator_type get_allocator() const {
+        return my_allocator;
+    }
+
+protected:
+    node_handle_base(node* n) : my_node(n) {}
+
+    void internal_destroy() {
+        if(my_node) {
+            my_allocator.destroy(&(my_node->my_element));
+            // TODO: Consider using node_allocator from the container
+            typename tbb::internal::allocator_rebind<allocator_type, node>::type node_allocator;
+            node_allocator.deallocate(my_node, 1);
+        }
+    }
+
+    void deactivate() { my_node = NULL; }
+
+    node* my_node;
+    allocator_type my_allocator;
+};
+
+// node handle for concurrent_unordered maps
+template<typename Key, typename Value, typename Allocator>
+class node_handle : public node_handle_base<Value, Allocator> {
+    typedef node_handle_base<Value, Allocator> base_type;
+public:
+    typedef Key key_type;
+    typedef typename Value::second_type mapped_type;
+    typedef typename base_type::allocator_type allocator_type;
+
+    node_handle() : base_type() {}
+
+    key_type& key() const {
+        __TBB_ASSERT(!this->empty(), "Cannot get key from the empty node_type object");
+        return *const_cast<key_type*>(&(this->my_node->my_element.first));
+    }
+
+    mapped_type& mapped() const {
+        __TBB_ASSERT(!this->empty(), "Cannot get mapped value from the empty node_type object");
+        return this->my_node->my_element.second;
+    }
+
+private:
+    template<typename T, typename A>
+    friend class split_ordered_list;
+
+    template<typename Traits>
+    friend class concurrent_unordered_base;
+
+    node_handle(typename base_type::node* n) : base_type(n) {}
+};
+
+// node handle for concurrent_unordered sets
+template<typename Key, typename Allocator>
+class node_handle<Key, Key, Allocator> : public node_handle_base<Key, Allocator> {
+    typedef node_handle_base<Key, Allocator> base_type;
+public:
+    typedef Key value_type;
+    typedef typename base_type::allocator_type allocator_type;
+
+    node_handle() : base_type() {}
+
+    value_type& value() const {
+        __TBB_ASSERT(!this->empty(), "Cannot get value from the empty node_type object");
+        return *const_cast<value_type*>(&(this->my_node->my_element));
+    }
+
+private:
+    template<typename T, typename A>
+    friend class split_ordered_list;
+
+    template<typename Traits>
+    friend class concurrent_unordered_base;
+
+    node_handle(typename base_type::node* n) : base_type(n) {}
+};
+
+#endif // __TBB_UNORDERED_NODE_HANDLE_PRESENT
+
 } // namespace internal
 //! @endcond
 } // namespace interface5
index b7ff582..066704d 100644 (file)
@@ -171,7 +171,8 @@ private:
         bool upgrade_to_writer() {
             x86_rtm_rw_mutex* mutex = x86_rtm_rw_mutex::internal_get_mutex(my_scoped_lock);
             __TBB_ASSERT( mutex, "lock is not acquired" );
-            __TBB_ASSERT( transaction_state==RTM_transacting_reader || transaction_state==RTM_real_reader, "Invalid state for upgrade" );
+            if (transaction_state == RTM_transacting_writer || transaction_state == RTM_real_writer)
+                return true; // Already a writer
             return mutex->internal_upgrade(*this);
         }
 
@@ -180,7 +181,8 @@ private:
         bool downgrade_to_reader() {
             x86_rtm_rw_mutex* mutex = x86_rtm_rw_mutex::internal_get_mutex(my_scoped_lock);
             __TBB_ASSERT( mutex, "lock is not acquired" );
-            __TBB_ASSERT( transaction_state==RTM_transacting_writer || transaction_state==RTM_real_writer, "Invalid state for downgrade" );
+            if (transaction_state == RTM_transacting_reader || transaction_state == RTM_real_reader)
+                return true; // Already a reader
             return mutex->internal_downgrade(*this);
         }
 
index 5967db5..56107b0 100644 (file)
@@ -43,8 +43,8 @@ public:
     typedef const IntType& reference;
     typedef std::random_access_iterator_tag iterator_category;
 
-    counting_iterator(): my_counter() {}
-    explicit counting_iterator(IntType init): my_counter(init) {}
+    counting_iterator() : my_counter() {}
+    explicit counting_iterator(IntType init) : my_counter(init) {}
 
     reference operator*() const { return my_counter; }
     value_type operator[](difference_type i) const { return *(*this + i); }
@@ -118,7 +118,41 @@ template <typename TupleReturnType>
 struct make_references {
     template <typename TupleType, std::size_t... Is>
     TupleReturnType operator()(const TupleType& t, tbb::internal::index_sequence<Is...>) {
-        return std::tie((*std::get<Is>(t))...);
+        return std::tie( *std::get<Is>(t)... );
+    }
+};
+
+// A simple wrapper over a tuple of references.
+// The class is designed to hold a temporary tuple of reference
+// after dereferencing a zip_iterator; in particular, it is needed
+// to swap these rvalue tuples. Any other usage is not supported.
+template<typename... T>
+struct tuplewrapper : public std::tuple<typename std::enable_if<std::is_reference<T>::value, T&&>::type...> {
+    // In the context of this class, T is a reference, so T&& is a "forwarding reference"
+    typedef std::tuple<T&&...> base_type;
+    // Construct from the result of std::tie
+    tuplewrapper(const base_type& in) : base_type(in) {}
+#if __INTEL_COMPILER
+    // ICC cannot generate copy ctor & assignment
+    tuplewrapper(const tuplewrapper& rhs) : base_type(rhs) {}
+    tuplewrapper& operator=(const tuplewrapper& rhs) {
+        *this = base_type(rhs);
+        return *this;
+    }
+#endif
+    // Assign any tuple convertible to std::tuple<T&&...>: *it = a_tuple;
+    template<typename... U>
+    tuplewrapper& operator=(const std::tuple<U...>& other) {
+        base_type::operator=(other);
+        return *this;
+    }
+#if _LIBCPP_VERSION
+    // (Necessary for libc++ tuples) Convert to a tuple of values: v = *it;
+    operator std::tuple<typename std::remove_reference<T>::type...>() { return base_type(*this); }
+#endif
+    // Swap rvalue tuples: swap(*it1,*it2);
+    friend void swap(tuplewrapper&& a, tuplewrapper&& b) {
+        std::swap<T&&...>(a,b);
     }
 };
 
@@ -128,16 +162,20 @@ template <typename... Types>
 class zip_iterator {
     __TBB_STATIC_ASSERT(sizeof...(Types), "Cannot instantiate zip_iterator with empty template parameter pack");
     static const std::size_t num_types = sizeof...(Types);
-    typedef typename std::tuple<Types...> it_types;
+    typedef std::tuple<Types...> it_types;
 public:
     typedef typename std::make_signed<std::size_t>::type difference_type;
     typedef std::tuple<typename std::iterator_traits<Types>::value_type...> value_type;
+#if __INTEL_COMPILER && __INTEL_COMPILER < 1800 && _MSC_VER
     typedef std::tuple<typename std::iterator_traits<Types>::reference...> reference;
+#else
+    typedef tbb::internal::tuplewrapper<typename std::iterator_traits<Types>::reference...> reference;
+#endif
     typedef std::tuple<typename std::iterator_traits<Types>::pointer...> pointer;
     typedef std::random_access_iterator_tag iterator_category;
 
-    zip_iterator(): my_it() {}
-    explicit zip_iterator(Types... args): my_it(std::make_tuple(args...)) {}
+    zip_iterator() : my_it() {}
+    explicit zip_iterator(Types... args) : my_it(std::make_tuple(args...)) {}
     zip_iterator(const zip_iterator& input) : my_it(input.my_it) {}
     zip_iterator& operator=(const zip_iterator& input) {
         my_it = input.my_it;
@@ -192,7 +230,6 @@ public:
     bool operator>(const zip_iterator& it) const { return it < *this; }
     bool operator<=(const zip_iterator& it) const { return !(*this > it); }
     bool operator>=(const zip_iterator& it) const { return !(*this < it); }
-
 private:
     it_types my_it;
 };
@@ -213,7 +250,7 @@ public:
     typedef typename std::iterator_traits<Iter>::pointer pointer;
     typedef typename std::random_access_iterator_tag iterator_category;
 
-    transform_iterator(Iter it, UnaryFunc unary_func): my_it(it), my_unary_func(unary_func) {
+    transform_iterator(Iter it, UnaryFunc unary_func) : my_it(it), my_unary_func(unary_func) {
         __TBB_STATIC_ASSERT((std::is_same<typename std::iterator_traits<Iter>::iterator_category,
                              std::random_access_iterator_tag>::value), "Random access iterator required.");
     }
index cb4469e..a11a800 100644 (file)
@@ -258,25 +258,6 @@ struct partition_type_base {
     }
 };
 
-//! Class determines whether template parameter has static boolean constant
-//! 'is_splittable_in_proportion' initialized with value of 'true' or not.
-/** If template parameter has such field that has been initialized with non-zero
-*  value then class field will be set to 'true', otherwise - 'false'
-*/
-template <typename Range>
-class is_splittable_in_proportion {
-private:
-    typedef char yes[1];
-    typedef char no[2];
-
-    template <typename range_type> static yes& decide(typename enable_if<range_type::is_splittable_in_proportion>::type *);
-    template <typename range_type> static no& decide(...);
-public:
-    // equals to 'true' if and only if static const variable 'is_splittable_in_proportion' of template parameter
-    // initialized with the value of 'true'
-    static const bool value = (sizeof(decide<Range>(0)) == sizeof(yes));
-};
-
 //! Provides default splitting strategy for partition objects.
 template <typename Partition>
 struct adaptive_mode : partition_type_base<Partition> {
@@ -295,6 +276,28 @@ struct adaptive_mode : partition_type_base<Partition> {
     }
 };
 
+//! A helper class to create a proportional_split object for a given type of Range.
+/** If the Range has static boolean constant 'is_splittable_in_proportion' set to 'true',
+    the created object splits a provided value in an implemenation-defined proportion;
+    otherwise it represents equal-size split. */
+// TODO: check if this helper can be a nested class of proportional_mode.
+template <typename Range, typename = void>
+struct proportion_helper {
+    static proportional_split get_split(size_t) { return proportional_split(1,1); }
+};
+template <typename Range>
+struct proportion_helper<Range, typename enable_if<Range::is_splittable_in_proportion, void>::type> {
+    static proportional_split get_split(size_t n) {
+#if __TBB_NONUNIFORM_TASK_CREATION
+        size_t right = (n + 2) / 3;
+#else
+        size_t right = n / 2;
+#endif
+        size_t left = n - right;
+        return proportional_split(left, right);
+    }
+};
+
 //! Provides proportional splitting strategy for partition objects
 template <typename Partition>
 struct proportional_mode : adaptive_mode<Partition> {
@@ -325,29 +328,11 @@ struct proportional_mode : adaptive_mode<Partition> {
     bool is_divisible() { // part of old should_execute_range()
         return self().my_divisor > my_partition::factor;
     }
-#if _MSC_VER && !defined(__INTEL_COMPILER)
-    // Suppress "conditional expression is constant" warning.
-    #pragma warning( push )
-    #pragma warning( disable: 4127 )
-#endif
     template <typename Range>
     proportional_split get_split() {
-        if (is_splittable_in_proportion<Range>::value) {
-            size_t size = self().my_divisor / my_partition::factor;
-#if __TBB_NONUNIFORM_TASK_CREATION
-            size_t right = (size + 2) / 3;
-#else
-            size_t right = size / 2;
-#endif
-            size_t left = size - right;
-            return proportional_split(left, right);
-        } else {
-            return proportional_split(1, 1);
-        }
+        // Create a proportion for the number of threads expected to handle "this" subrange
+        return proportion_helper<Range>::get_split( self().my_divisor / my_partition::factor );
     }
-#if _MSC_VER && !defined(__INTEL_COMPILER)
-    #pragma warning( pop )
-#endif // warning 4127 is back
 };
 
 static size_t get_initial_partition_head() {
@@ -409,7 +394,7 @@ struct dynamic_grainsize_mode : Mode {
 #endif
         , my_delay(begin)
         , my_max_depth(p.my_max_depth) {}
-    bool check_being_stolen( task &t) { // part of old should_execute_range()
+    bool check_being_stolen(task &t) { // part of old should_execute_range()
         if( !(self().my_divisor / Mode::my_partition::factor) ) { // if not from the top P tasks of binary tree
             self().my_divisor = 1; // TODO: replace by on-stack flag (partition_state's member)?
             if( t.is_stolen_task() && t.parent()->ref_count() >= 2 ) { // runs concurrently with the left task
index a81e6a5..7b2b437 100644 (file)
@@ -119,7 +119,7 @@ public:
         /** Returns whether the upgrade happened without releasing and re-acquiring the lock */
         bool upgrade_to_writer() {
             __TBB_ASSERT( mutex, "mutex is not acquired" );
-            __TBB_ASSERT( !is_writer, "not a reader" );
+            if (is_writer) return true; // Already a writer
             is_writer = true;
             return mutex->internal_upgrade();
         }
@@ -141,7 +141,7 @@ public:
         //! Downgrade writer to become a reader.
         bool downgrade_to_reader() {
             __TBB_ASSERT( mutex, "mutex is not acquired" );
-            __TBB_ASSERT( is_writer, "not a writer" );
+            if (!is_writer) return true; // Already a reader
 #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT
             mutex->internal_downgrade();
 #else
index c2a4476..5844a68 100644 (file)
@@ -22,7 +22,7 @@
 #define __TBB_task_scheduler_observer_H
 
 #include "atomic.h"
-#if __TBB_ARENA_OBSERVER || __TBB_SLEEP_PERMISSION
+#if __TBB_ARENA_OBSERVER
 #include "task_arena.h"
 #endif
 
@@ -88,7 +88,7 @@ public:
 
 } // namespace internal
 
-#if __TBB_ARENA_OBSERVER || __TBB_SLEEP_PERMISSION
+#if __TBB_ARENA_OBSERVER
 namespace interface6 {
 class task_scheduler_observer : public internal::task_scheduler_observer_v3 {
     friend class internal::task_scheduler_observer_v3;
@@ -149,23 +149,13 @@ public:
         }
         internal::task_scheduler_observer_v3::observe(state);
     }
-
-#if  __TBB_SLEEP_PERMISSION
-    //! Return commands for may_sleep()
-    enum { keep_awake = false, allow_sleep = true };
-
-    //! The callback can be invoked by a worker thread before it goes to sleep.
-    /** If it returns false ('keep_awake'), the thread will keep spinning and looking for work.
-        It will not be called for master threads. **/
-    virtual bool may_sleep() { return allow_sleep; }
-#endif /*__TBB_SLEEP_PERMISSION*/
 };
 
 } //namespace interface6
 using interface6::task_scheduler_observer;
-#else /*__TBB_ARENA_OBSERVER || __TBB_SLEEP_PERMISSION*/
+#else /*__TBB_ARENA_OBSERVER*/
 typedef tbb::internal::task_scheduler_observer_v3 task_scheduler_observer;
-#endif /*__TBB_ARENA_OBSERVER || __TBB_SLEEP_PERMISSION*/
+#endif /*__TBB_ARENA_OBSERVER*/
 
 } // namespace tbb
 
index bfb8938..b3114bd 100644 (file)
     #define __TBB_CPP11_TEMPLATE_ALIASES_PRESENT            (__INTEL_CXX11_MODE__ && __INTEL_COMPILER >= 1210)
     #define __TBB_CPP14_INTEGER_SEQUENCE_PRESENT            (__cplusplus >= 201402L)
     #define __TBB_CPP14_VARIABLE_TEMPLATES_PRESENT          (__cplusplus >= 201402L)
-    #define __TBB_CPP17_DEDUCTION_GUIDES_PRESENT            (__INTEL_COMPILER > 1900)
+    #define __TBB_CPP17_DEDUCTION_GUIDES_PRESENT            (__INTEL_COMPILER > 1901) // a future version
     #define __TBB_CPP17_INVOKE_RESULT_PRESENT               (__cplusplus >= 201703L)
 #elif __clang__
 /** TODO: these options need to be rechecked **/
@@ -561,13 +561,9 @@ There are four cases that are supported:
 #define __TBB_RECYCLE_TO_ENQUEUE __TBB_BUILD // keep non-official
 
 #ifndef __TBB_ARENA_OBSERVER
-    #define __TBB_ARENA_OBSERVER ((__TBB_BUILD||TBB_PREVIEW_LOCAL_OBSERVER)&& __TBB_SCHEDULER_OBSERVER)
+    #define __TBB_ARENA_OBSERVER __TBB_SCHEDULER_OBSERVER
 #endif /* __TBB_ARENA_OBSERVER */
 
-#ifndef __TBB_SLEEP_PERMISSION
-    #define __TBB_SLEEP_PERMISSION ((__TBB_CPF_BUILD||TBB_PREVIEW_LOCAL_OBSERVER)&& __TBB_SCHEDULER_OBSERVER)
-#endif /* __TBB_SLEEP_PERMISSION */
-
 #ifndef __TBB_TASK_ISOLATION
     #define __TBB_TASK_ISOLATION 1
 #endif /* __TBB_TASK_ISOLATION */
@@ -623,6 +619,7 @@ There are four cases that are supported:
 
 /** __TBB_WIN8UI_SUPPORT enables support of Windows* Store Apps and limit a possibility to load
     shared libraries at run time only from application container **/
+// TODO: Separate this single macro into two for Windows 8 Store* (win8ui mode) and UWP/UWD modes.
 #if defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_APP
     #define __TBB_WIN8UI_SUPPORT 1
 #else
@@ -786,6 +783,11 @@ There are four cases that are supported:
 // In some cases decltype of a function adds a reference to a return type.
 #define __TBB_CPP11_DECLTYPE_OF_FUNCTION_RETURN_TYPE_BROKEN (_MSC_VER == 1600 && !__INTEL_COMPILER)
 
+// Visual Studio 2013 does not delete the copy constructor when a user-defined move constructor is provided
+#if _MSC_VER && _MSC_VER <= 1800
+    #define __TBB_IMPLICIT_COPY_DELETION_BROKEN 1
+#endif
+
 /** End of __TBB_XXX_BROKEN macro section **/
 
 #if defined(_MSC_VER) && _MSC_VER>=1500 && !defined(__INTEL_COMPILER)
index 07534ba..81f514a 100644 (file)
@@ -26,7 +26,7 @@
 #define TBB_VERSION_MINOR 0
 
 // Engineering-focused interface version
-#define TBB_INTERFACE_VERSION 11004
+#define TBB_INTERFACE_VERSION 11005
 #define TBB_INTERFACE_VERSION_MAJOR TBB_INTERFACE_VERSION/1000
 
 // The oldest major interface version still supported
index a23f17f..440861d 100644 (file)
@@ -18,7 +18,7 @@
 
 */
 
-//TODO: when removing TBB_PREVIEW_LOCAL_OBSERVER, change the header or defines here
+#define __TBB_ARENA_OBSERVER 0
 #include "tbb/task_scheduler_observer.h"
 
 typedef uintptr_t FlagType;
@@ -95,7 +95,7 @@ public:
     DoTest( int n ) : nthread(n) {}
     void operator()( int i ) const {
         LocalState->IsMaster = true;
-        if( i==0 ) {   
+        if( i==0 ) {
             tbb::task_scheduler_init init(nthread);
             DoFib(0);
         } else {
@@ -112,8 +112,8 @@ void TestObserver( int p, int q ) {
 }
 
 int TestMain () {
-    for( int p=MinThread; p<=MaxThread; ++p ) 
-        for( int q=MinThread; q<=MaxThread; ++q ) 
+    for( int p=MinThread; p<=MaxThread; ++p )
+        for( int q=MinThread; q<=MaxThread; ++q )
             TestObserver(p,q);
     ASSERT( EntryCount>0, "on_scheduler_entry not exercised" );
     ASSERT( ExitCount>0, "on_scheduler_exit not exercised" );
index 25e77a1..242393d 100644 (file)
@@ -28,7 +28,7 @@
 #include <process.h>
 #include <malloc.h> //_alloca
 #include "tbb/tbb_misc.h" // support for processor groups
-#if __TBB_WIN8UI_SUPPORT
+#if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)
 #include <thread>
 #endif
 #elif USE_PTHREAD
@@ -144,13 +144,14 @@ private:
 #define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000
 #endif
 
-#if __TBB_WIN8UI_SUPPORT
+// _beginthreadex API is not available in Windows 8 Store* applications, so use std::thread instead
+#if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)
 inline thread_monitor::handle_type thread_monitor::launch( thread_routine_type thread_function, void* arg, size_t, const size_t*) {
 //TODO: check that exception thrown from std::thread is not swallowed silently
     std::thread* thread_tmp=new std::thread(thread_function, arg);
     return thread_tmp->native_handle();
 }
-#else //__TBB_WIN8UI_SUPPORT
+#else
 inline thread_monitor::handle_type thread_monitor::launch( thread_routine_type thread_routine, void* arg, size_t stack_size, const size_t* worker_index ) {
     unsigned thread_id;
     int number_of_processor_groups = ( worker_index ) ? tbb::internal::NumberOfProcessorGroups() : 0;
@@ -167,7 +168,7 @@ inline thread_monitor::handle_type thread_monitor::launch( thread_routine_type t
     }
     return h;
 }
-#endif //__TBB_WIN8UI_SUPPORT
+#endif //__TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)
 
 void thread_monitor::join(handle_type handle) {
 #if TBB_USE_ASSERT
@@ -192,10 +193,10 @@ void thread_monitor::detach_thread(handle_type handle) {
 
 inline void thread_monitor::yield() {
 // TODO: consider unification via __TBB_Yield or tbb::this_tbb_thread::yield
-#if !__TBB_WIN8UI_SUPPORT
-    SwitchToThread();
-#else
+#if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)
     std::this_thread::yield();
+#else
+    SwitchToThread();
 #endif
 }
 #endif /* USE_WINTHREAD */
index ec1cff9..1b6cafe 100644 (file)
@@ -700,42 +700,20 @@ void market::process( job& j ) {
     // s.my_arena can be dead. Don't access it until arena_in_need is called
     arena *a = s.my_arena;
     __TBB_ASSERT( governor::is_set(&s), NULL );
-    enum {
-        query_interval = 1000,
-        first_interval = 1
-    };
-    for(int i = first_interval; ; i--) {
-        while ( (a = arena_in_need(a)) )
-        {
+
+    for (int i = 0; i < 2; ++i) {
+        while ( (a = arena_in_need(a)) ) {
             a->process(s);
-            a = NULL; // To avoid double checks in arena_in_need
-            i = first_interval;
+            a = NULL; // to avoid double checks in arena_in_need(arena*) for the same priority level
         }
         // Workers leave market because there is no arena in need. It can happen earlier than
         // adjust_job_count_estimate() decreases my_slack and RML can put this thread to sleep.
         // It might result in a busy-loop checking for my_slack<0 and calling this method instantly.
-        // first_interval>0 and the yield refines this spinning.
-        if( i > 0 )
-            __TBB_Yield();
-        else
-#if !__TBB_SLEEP_PERMISSION
-            break;
-#else
-        { // i == 0
-#if __TBB_TASK_PRIORITY
-            arena_list_type &al = my_priority_levels[my_global_top_priority].arenas;
-#else /* __TBB_TASK_PRIORITY */
-            arena_list_type &al = my_arenas;
-#endif /* __TBB_TASK_PRIORITY */
-            if( al.empty() ) // races if any are innocent TODO: replace by an RML query interface
-                break; // no arenas left, perhaps going to shut down
-            if( the_global_observer_list.ask_permission_to_leave() )
-                break; // go sleep
+        // the yield refines this spinning.
+        if ( !i )
             __TBB_Yield();
-            i = query_interval;
-        }
-#endif// !__TBB_SLEEP_PERMISSION
     }
+    
     GATHER_STATISTIC( ++s.my_counters.market_roundtrips );
 }
 
index f0c442d..1b4eb28 100644 (file)
@@ -47,7 +47,7 @@ struct check_observer_proxy_count {
 static check_observer_proxy_count the_check_observer_proxy_count;
 #endif /* TBB_USE_ASSERT */
 
-#if __TBB_ARENA_OBSERVER || __TBB_SLEEP_PERMISSION
+#if __TBB_ARENA_OBSERVER
 interface6::task_scheduler_observer* observer_proxy::get_v6_observer() {
     if(my_version != 6) return NULL;
     return static_cast<interface6::task_scheduler_observer*>(my_observer);
@@ -295,64 +295,6 @@ void observer_list::do_notify_exit_observers( observer_proxy* last, bool worker
     }
 }
 
-#if __TBB_SLEEP_PERMISSION
-bool observer_list::ask_permission_to_leave() {
-    __TBB_ASSERT( this == &the_global_observer_list, "This method cannot be used on lists of arena observers" );
-    if( !my_head ) return true;
-    // Pointer p marches though the list
-    observer_proxy *p = NULL, *prev = NULL;
-    bool result = true;
-    while( result ) {
-        task_scheduler_observer* tso = NULL;
-        // Hold lock on list only long enough to advance to the next proxy in the list.
-        {
-            scoped_lock lock(mutex(), /*is_writer=*/false);
-            do {
-                if( p ) {
-                    // We were already processing the list.
-                    observer_proxy* q = p->my_next;
-                    // read next, remove the previous reference
-                    if( p == prev )
-                        remove_ref_fast(prev); // sets prev to NULL if successful
-                    if( q ) p = q;
-                    else {
-                        // Reached the end of the list.
-                        if( prev ) {
-                            lock.release();
-                            remove_ref(prev);
-                        }
-                        return result;
-                    }
-                } else {
-                    // Starting pass through the list
-                    p = my_head;
-                    if( !p )
-                        return result;
-                }
-                tso = p->get_v6_observer();
-            } while( !tso );
-            ++p->my_ref_count;
-            ++tso->my_busy_count;
-        }
-        __TBB_ASSERT( !prev || p!=prev, NULL );
-        // Release the proxy pinned before p
-        if( prev )
-            remove_ref(prev);
-        // Do not hold any locks on the list while calling user's code.
-        // Do not intercept any exceptions that may escape the callback so that
-        // they are either handled by the TBB scheduler or passed to the debugger.
-        result = tso->may_sleep();
-        __TBB_ASSERT(p->my_ref_count, NULL);
-        intptr_t bc = --tso->my_busy_count;
-        __TBB_ASSERT_EX( bc>=0, "my_busy_count underflowed" );
-        prev = p;
-    }
-    if( prev )
-        remove_ref(prev);
-    return result;
-}
-#endif//__TBB_SLEEP_PERMISSION
-
 void task_scheduler_observer_v3::observe( bool enable ) {
     if( enable ) {
         if( !my_proxy ) {
index 5f853ea..5f7822e 100644 (file)
@@ -92,9 +92,6 @@ public:
 
     //! Call exit notifications on last and observers added before it.
     inline void notify_exit_observers( observer_proxy*& last, bool worker );
-
-    //! Call may_sleep callbacks to ask for permission for a worker thread to leave market
-    bool ask_permission_to_leave();
 }; // class observer_list
 
 //! Wrapper for an observer object
@@ -122,7 +119,7 @@ class observer_proxy {
     //! Version
     char my_version;
 
-#if __TBB_ARENA_OBSERVER || __TBB_SLEEP_PERMISSION
+#if __TBB_ARENA_OBSERVER
     interface6::task_scheduler_observer* get_v6_observer();
 #endif
 #if __TBB_ARENA_OBSERVER
index 71a1677..c41c2ae 100644 (file)
@@ -169,8 +169,8 @@ public:
 
     //! Note that processing of a token is finished.
     /** Fires up processing of the next token, if processing was deferred. */
-    // Using template to avoid explicit dependency on stage_task
-    // this is only called for serial filters, and is the reason for the
+    // Uses template to avoid explicit dependency on stage_task.
+    // This is only called for serial filters, and is the reason for the
     // advance parameter in return_item (we're incrementing low_token here.)
     // Non-TBF serial stages don't advance the token at the start because the presence
     // of the current token in the buffer keeps another stage from being spawned.
@@ -206,8 +206,8 @@ public:
     }
 #endif
 
-    //! return an item, invalidate the queued item, but only advance if advance
-    //  advance == true for parallel filters.  If the filter is serial, leave the
+    //! return an item, invalidate the queued item, but only advance if the filter
+    // is parallel (as indicated by advance == true). If the filter is serial, leave the
     // item in the buffer to keep another stage from being spawned.
     bool return_item(task_info& info, bool advance) {
         spin_mutex::scoped_lock lock( array_mutex );
@@ -425,10 +425,27 @@ class pipeline_root_task: public task {
                 {
                     task_info info;
                     info.reset();
-                    if( current_filter->my_input_buffer->return_item(info, !current_filter->is_serial()) ) {
-                        set_ref_count(1);
+                    task* bypass = NULL;
+                    int refcnt = 0;
+                    task_list list;
+                    // No new tokens are created; it's OK to process all waiting tokens.
+                    // If the filter is serial, the second call to return_item will return false.
+                    while( current_filter->my_input_buffer->return_item(info, !current_filter->is_serial()) ) {
+                        task* t = new( allocate_child() ) stage_task( my_pipeline, current_filter, info );
+                        if( ++refcnt == 1 )
+                            bypass = t;
+                        else // there's more than one task
+                            list.push_back(*t);
+                        // TODO: limit the list size (to arena size?) to spawn tasks sooner
+                        __TBB_ASSERT( refcnt <= int(my_pipeline.token_counter), "token counting error" );
+                        info.reset();
+                    }
+                    if( refcnt ) {
+                        set_ref_count( refcnt );
+                        if( refcnt > 1 )
+                            spawn(list);
                         recycle_as_continuation();
-                        return new( allocate_child() ) stage_task( my_pipeline, current_filter, info);
+                        return bypass;
                     }
                     current_filter = current_filter->next_segment;
                     if( !current_filter ) {
@@ -565,9 +582,7 @@ void pipeline::add_filter( filter& filter_ ) {
             filter_end->next_filter_in_pipeline = &filter_;
         filter_.next_filter_in_pipeline = NULL;
         filter_end = &filter_;
-    }
-    else
-    {
+    } else {
         if( !filter_end )
             filter_end = reinterpret_cast<filter*>(&filter_list);
 
@@ -580,15 +595,13 @@ void pipeline::add_filter( filter& filter_ ) {
             if( filter_.is_bound() )
                 has_thread_bound_filters = true;
             filter_.my_input_buffer = new internal::input_buffer( filter_.is_ordered(), filter_.is_bound() );
-        }
-        else {
+        } else {
             if(filter_.prev_filter_in_pipeline) {
                 if(filter_.prev_filter_in_pipeline->is_bound()) {
                     // successors to bound filters must have an input_buffer
                     filter_.my_input_buffer = new internal::input_buffer( /*is_ordered*/false, false );
                 }
-            }
-            else {  // input filter
+            } else {  // input filter
                 if(filter_.object_may_be_null() ) {
                     //TODO: buffer only needed to hold TLS; could improve
                     filter_.my_input_buffer = new internal::input_buffer( /*is_ordered*/false, false );
@@ -696,14 +709,12 @@ filter::~filter() {
     }
 }
 
-void
-filter::set_end_of_input() {
+void filter::set_end_of_input() {
     __TBB_ASSERT(my_input_buffer, NULL);
     __TBB_ASSERT(object_may_be_null(), NULL);
     if(is_serial()) {
         my_pipeline->end_of_input = true;
-    }
-    else {
+    } else {
         __TBB_ASSERT(my_input_buffer->end_of_input_tls_allocated, NULL);
         my_input_buffer->set_my_tls_end_of_input();
     }
index 790003e..b4db227 100644 (file)
@@ -363,7 +363,7 @@ done:
 
 bool queuing_rw_mutex::scoped_lock::downgrade_to_reader()
 {
-    __TBB_ASSERT( my_state==STATE_WRITER, "no sense to downgrade a reader" );
+    if ( my_state == STATE_ACTIVEREADER ) return true; // Already a reader
 
     ITT_NOTIFY(sync_releasing, my_mutex);
     my_state = STATE_READER;
@@ -390,7 +390,7 @@ bool queuing_rw_mutex::scoped_lock::downgrade_to_reader()
 
 bool queuing_rw_mutex::scoped_lock::upgrade_to_writer()
 {
-    __TBB_ASSERT( my_state==STATE_ACTIVEREADER, "only active reader can be upgraded" );
+    if ( my_state == STATE_WRITER ) return true; // Already a writer
 
     queuing_rw_mutex::scoped_lock * tmp;
     queuing_rw_mutex::scoped_lock * me = this;
index 032a918..706c219 100644 (file)
@@ -64,7 +64,7 @@ bool __TBB_InitOnce::InitializationDone;
 
 #if DO_ITT_NOTIFY
     static bool ITT_Present;
-    static bool ITT_InitializationDone;
+    static atomic<bool> ITT_InitializationDone;
 #endif
 
 #if !(_WIN32||_WIN64) || __TBB_SOURCE_DIRECTLY_INCLUDED
@@ -192,6 +192,8 @@ static void ITT_init() {
 /** Thread-unsafe lazy one-time initialization of tools interop.
     Used by both dummy handlers and general TBB one-time initialization routine. **/
 void ITT_DoUnsafeOneTimeInitialization () {
+    // Double check ITT_InitializationDone is necessary because the first check 
+    // in ITT_DoOneTimeInitialization is not guarded with the __TBB_InitOnce lock.
     if ( !ITT_InitializationDone ) {
         ITT_Present = (__TBB_load_ittnotify()!=0);
         if (ITT_Present) ITT_init();
@@ -204,9 +206,11 @@ void ITT_DoUnsafeOneTimeInitialization () {
     Used by dummy handlers only. **/
 extern "C"
 void ITT_DoOneTimeInitialization() {
-    __TBB_InitOnce::lock();
-    ITT_DoUnsafeOneTimeInitialization();
-    __TBB_InitOnce::unlock();
+    if ( !ITT_InitializationDone ) {
+        __TBB_InitOnce::lock();
+        ITT_DoUnsafeOneTimeInitialization();
+        __TBB_InitOnce::unlock();
+    }
 }
 #endif /* DO_ITT_NOTIFY */
 
@@ -478,8 +482,8 @@ class stack_size_control : public padded<control_storage> {
         return tbb::internal::ThreadStackSize;
     }
     virtual void apply_active() const __TBB_override {
-#if __TBB_WIN8UI_SUPPORT
-        __TBB_ASSERT( false, "For Windows Store* apps we must not set stack size" );
+#if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)
+        __TBB_ASSERT( false, "For Windows Store* apps we must not set stack size" );
 #endif
     }
 };
index 2db3164..c9e0474 100644 (file)
@@ -43,8 +43,9 @@ namespace internal {
 
 const size_t MByte = 1024*1024;
 
-#if __TBB_WIN8UI_SUPPORT
-// In Win8UI mode, TBB uses a thread creation API that does not allow to specify the stack size.
+#if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)
+// In Win8UI mode (Windows 8 Store* applications), TBB uses a thread creation API
+// that does not allow to specify the stack size.
 // Still, the thread stack size value, either explicit or default, is used by the scheduler.
 // So here we set the default value to match the platform's default of 1MB.
 const size_t ThreadStackSize = 1*MByte;
index 70fd80b..6f63a18 100644 (file)
@@ -1980,6 +1980,11 @@ static bool initMemoryManager()
     // POSIX.1-2001-compliant way to get page size
     const size_t granularity = sysconf(_SC_PAGESIZE);
 #endif
+    if (!defaultMemPool) {
+        // Do not rely on static constructors and do the assignment in case
+        // of library static section not initialized at this call yet.
+        defaultMemPool = (MemoryPool*)defaultMemPool_space;
+    }
     bool initOk = defaultMemPool->
         extMemPool.init(0, NULL, NULL, granularity,
                         /*keepAllMemory=*/false, /*fixedPool=*/false);
index 6915525..5d713fe 100644 (file)
@@ -740,6 +740,8 @@ void doMallocReplacement()
         }
         if ( strcmp(modules_to_replace[j].name, "ucrtbase.dll") == 0 ) {
             HMODULE ucrtbase_handle = GetModuleHandle("ucrtbase.dll");
+            if (!ucrtbase_handle)
+                continue;
             // If _o_free function is present and patchable, redirect it to tbbmalloc as well
             // This prevents issues with other _o_* functions which might allocate memory with malloc
             if ( IsPrologueKnown("ucrtbase.dll", "_o_free", known_bytecodes, ucrtbase_handle)) {
index f2ebb38..cc56837 100644 (file)
@@ -563,10 +563,13 @@ FRR_TYPE ReplaceFunctionW(const wchar_t *dllName, const char *funcName, FUNCPTR
 bool IsPrologueKnown(const char* dllName, const char *funcName, const char **opcodes, HMODULE module)
 {
     FARPROC inpFunc = GetProcAddress(module, funcName);
-    if (!inpFunc)
+    FunctionInfo functionInfo = { funcName, dllName };
+
+    if (!inpFunc) {
+        Log::record(functionInfo, "unknown", /*status*/ false);
         return false;
+    }
 
-    FunctionInfo functionInfo = { funcName, dllName };
     return CheckOpcodes( opcodes, (void*)inpFunc, /*abortOnError=*/false, &functionInfo) != 0;
 }
 
index a9a6422..1a89c96 100644 (file)
     (__TBB_GCC_VERSION>=40700 && __TBB_GCC_VERSION<40704 || __TBB_GCC_VERSION>=40800 && __TBB_GCC_VERSION<40803 )
 #endif
 
+// Swapping of scoped_allocator_adaptors is broken on GCC 4.9 and lower and on Android for Windows
+// Allocator propagation into std::pair is broken for Apple clang, lower then 9.0
+// Compilation of <scoped_allocator> header is broken for Visual Studio 2017 with ICC 17.8
+#define __TBB_SCOPED_ALLOCATOR_BROKEN (__TBB_GCC_VERSION <= 50100 || (__APPLE__ && __TBB_CLANG_VERSION < 90000) || \
+                                      (__FreeBSD__ && __TBB_CLANG_VERSION <= 60000) ||  \
+                                      (__ANDROID__ && (_WIN32 || _WIN64)) || \
+                                      (_MSC_VER && _MSC_VER == 1912 && __INTEL_COMPILER == 1700))
+
+
+
 // The tuple-based tests with more inputs take a long time to compile.  If changes
 // are made to the tuple implementation or any switch that controls it, or if testing
 // with a new platform implementation of std::tuple, the test should be compiled with
index 8196cc6..5796303 100644 (file)
@@ -883,9 +883,33 @@ void TestRehash() {
     }
 }
 
+template<typename base_alloc_t, typename count_t = tbb::atomic<size_t> >
+class only_node_counting_allocator : public local_counting_allocator<base_alloc_t, count_t> {
+    typedef local_counting_allocator<base_alloc_t, count_t> base_type;
+public:
+    template<typename U>
+    struct rebind {
+        typedef only_node_counting_allocator<typename base_alloc_t::template rebind<U>::other,count_t> other;
+    };
+
+    only_node_counting_allocator() : base_type() {}
+    only_node_counting_allocator(const only_node_counting_allocator& a) : base_type(a) {}
+
+    template<typename U>
+    only_node_counting_allocator(const only_node_counting_allocator<U>& a) : base_type(a) {}
+
+    typename base_type::pointer allocate(const typename base_type::size_type n) {
+        if ( n > 1) {
+            return base_alloc_t::allocate(n);
+        } else {
+            return base_type::allocate(n);
+        }
+    }
+};
+
 #if TBB_USE_EXCEPTIONS
 void TestExceptions() {
-    typedef local_counting_allocator<tbb::tbb_allocator<MyData2> > allocator_t;
+    typedef only_node_counting_allocator<tbb::tbb_allocator<MyData2> > allocator_t;
     typedef tbb::concurrent_hash_map<MyKey,MyData2,MyHashCompare,allocator_t> ThrowingTable;
     enum methods {
         zero_method = 0,
@@ -916,6 +940,14 @@ void TestExceptions() {
                         victim = src;
                     } break;
                 case op_insert: {
+#if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && __TBB_CPP11_TUPLE_PRESENT
+                        // Insertion in cpp11 don't make copy constructions
+                        // during the insertion, so we need to decrement limit
+                        // to throw an exception in the right place and to prevent
+                        // successful insertion of one unexpected item
+                        if (MyDataCountLimit)
+                            --MyDataCountLimit;
+#endif
                         FillTable( victim, 1000 );
                     } break;
                 default:;
@@ -1488,6 +1520,97 @@ void TestHashCompareConstructors() {
 #endif
 }
 
+#if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && !__TBB_SCOPED_ALLOCATOR_BROKEN
+#include <scoped_allocator>
+
+template<typename Allocator>
+class allocator_aware_data {
+public:
+    static bool assert_on_constructions;
+    typedef Allocator allocator_type;
+
+    allocator_aware_data(const allocator_type& allocator = allocator_type())
+        : my_allocator(allocator), my_value(0) {}
+    allocator_aware_data(int v, const allocator_type& allocator = allocator_type())
+        : my_allocator(allocator), my_value(v) {}
+    allocator_aware_data(const allocator_aware_data&) {
+        ASSERT(!assert_on_constructions, "Allocator should propogate to the data during copy construction");
+    }
+    allocator_aware_data(allocator_aware_data&&) {
+        ASSERT(!assert_on_constructions, "Allocator should propogate to the data during move construction");
+    }
+    allocator_aware_data(const allocator_aware_data& rhs, const allocator_type& allocator)
+        : my_allocator(allocator), my_value(rhs.my_value) {}
+    allocator_aware_data(allocator_aware_data&& rhs, const allocator_type& allocator)
+        : my_allocator(allocator), my_value(rhs.my_value) {}
+
+    int value() const { return my_value; }
+private:
+    allocator_type my_allocator;
+    int my_value;
+};
+
+struct custom_hash_compare {
+    template<typename Allocator>
+    static size_t hash(const allocator_aware_data<Allocator>& key) {
+        return tbb::tbb_hash_compare<int>::hash(key.value());
+    }
+
+    template<typename Allocator>
+    static bool equal(const allocator_aware_data<Allocator>& key1, const allocator_aware_data<Allocator>& key2) {
+        return tbb::tbb_hash_compare<int>::equal(key1.value(), key2.value());
+    }
+};
+
+template<typename Allocator>
+bool allocator_aware_data<Allocator>::assert_on_constructions = false;
+
+void TestScopedAllocator() {
+    typedef allocator_aware_data<std::scoped_allocator_adaptor<tbb::tbb_allocator<int>>> allocator_data_type;
+    typedef std::scoped_allocator_adaptor<tbb::tbb_allocator<allocator_data_type>> allocator_type;
+    typedef tbb::concurrent_hash_map<allocator_data_type, allocator_data_type,
+                                     custom_hash_compare, allocator_type> hash_map_type;
+
+    allocator_type allocator;
+    allocator_data_type key1(1, allocator), key2(2, allocator);
+    allocator_data_type data1(1, allocator), data2(data1, allocator);
+    hash_map_type map1(allocator), map2(allocator);
+
+    hash_map_type::value_type v1(key1, data1), v2(key2, data2);
+
+    auto init_list = { v1, v2 };
+
+    allocator_data_type::assert_on_constructions = true;
+    map1.emplace(key1, data1);
+    map2.emplace(key2, std::move(data2));
+
+    map1.clear();
+    map2.clear();
+
+    map1.insert(v1);
+    map2.insert(std::move(v2));
+
+    map1.clear();
+    map2.clear();
+
+    map1.insert(init_list);
+
+    map1.clear();
+    map2.clear();
+
+    hash_map_type::accessor a;
+    map2.insert(a, allocator_data_type(3));
+    a.release();
+
+    map1 = map2;
+    map2 = std::move(map1);
+
+    hash_map_type map3(allocator);
+    map3.rehash(1000);
+    map3 = map2;
+}
+#endif
+
 //------------------------------------------------------------------------
 // Test driver
 //------------------------------------------------------------------------
@@ -1551,6 +1674,9 @@ int TestMain () {
 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
     TestDeductionGuides<tbb::concurrent_hash_map>();
 #endif
+#if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && !__TBB_SCOPED_ALLOCATOR_BROKEN
+    TestScopedAllocator();
+#endif
 
     return Harness::Done;
 }
index af271cb..88c501a 100644 (file)
@@ -213,12 +213,26 @@ void TestHelpers(){
     TestToVector();
 }
 
+//Comparator with assert in default consructor
+template<typename T>
+class less_a : public std::less<T>
+{
+public:
+    explicit less_a(bool no_assert = false) {
+        ASSERT(no_assert,"empty consructor should not be called");
+    };
+};
+
 void TestConstructorsDestructorsAccessors() {
     std::vector<int> v;
     std::allocator<int> a;
     concurrent_priority_queue<int, std::less<int> > *q, *qo;
     concurrent_priority_queue<int, std::less<int>, std::allocator<int>  > *qi;
 
+    less_a<int> l(true);
+    concurrent_priority_queue<int, less_a<int> > *ql;
+    concurrent_priority_queue<int, less_a<int>, std::allocator<int>  > *qla;
+
     // Test constructors/destructors
     REMARK("Testing default constructor.\n");
     q = new concurrent_priority_queue<int, std::less<int> >();
@@ -228,7 +242,6 @@ void TestConstructorsDestructorsAccessors() {
     REMARK("Testing destructor.\n");
     delete q;
     REMARK("Destruction complete.\n");
-
     REMARK("Testing capacity constructor.\n");
     q = new concurrent_priority_queue<int, std::less<int> >(42);
     REMARK("Capacity constructor complete.\n");
@@ -247,6 +260,24 @@ void TestConstructorsDestructorsAccessors() {
     delete qi;
     REMARK("Destruction complete.\n");
 
+    REMARK("Testing compare constructor.\n");
+    ql = new concurrent_priority_queue<int, less_a<int> >(l);
+    REMARK("Compare constructor complete.\n");
+    ASSERT(ql->size()==0, "FAILED size test.");
+    ASSERT(ql->empty(), "FAILED empty test.");
+    REMARK("Testing destructor.\n");
+    delete ql;
+    REMARK("Destruction complete.\n");
+
+    REMARK("Testing compare+allocator constructor.\n");
+    qla = new concurrent_priority_queue<int, less_a<int>, std::allocator<int> >(l, a);
+    REMARK("Compare+allocator constructor complete.\n");
+    ASSERT(qla->size()==0, "FAILED size test.");
+    ASSERT(qla->empty(), "FAILED empty test.");
+    REMARK("Testing destructor.\n");
+    delete qla;
+    REMARK("Destruction complete.\n");
+
     REMARK("Testing capacity+allocator constructor.\n");
     qi = new concurrent_priority_queue<int, std::less<int>, std::allocator<int> >(42, a);
     REMARK("Capacity+allocator constructor complete.\n");
@@ -256,6 +287,25 @@ void TestConstructorsDestructorsAccessors() {
     delete qi;
     REMARK("Destruction complete.\n");
 
+    REMARK("Testing capacity+compare constructor.\n");
+    ql = new concurrent_priority_queue<int, less_a<int> >(42, l);
+    REMARK("Capacity+compare constructor complete.\n");
+    ASSERT(ql->size()==0, "FAILED size test.");
+    ASSERT(ql->empty(), "FAILED empty test.");
+    REMARK("Testing destructor.\n");
+    delete ql;
+    REMARK("Destruction complete.\n");
+
+    REMARK("Testing capacity+compare+allocator constructor.\n");
+    qla = new concurrent_priority_queue<int, less_a<int>, std::allocator<int> >(42, l, a);
+    REMARK("Capacity+compare+allocator constructor complete.\n");
+    ASSERT(qla->size()==0, "FAILED size test.");
+    ASSERT(qla->empty(), "FAILED empty test.");
+    REMARK("Testing destructor.\n");
+    delete qla;
+    REMARK("Destruction complete.\n");
+
+    REMARK("Destruction complete.\n");
     REMARK("Testing iterator filler constructor.\n");
     for (int i=0; i<42; ++i)
         v.push_back(i);
@@ -265,6 +315,16 @@ void TestConstructorsDestructorsAccessors() {
     ASSERT(!q->empty(), "FAILED vector/empty test.");
     ASSERT(*q == v, "FAILED vector/equality test.");
 
+    REMARK("Destruction complete.\n");
+    REMARK("Testing iterator filler +compare constructor.\n");
+    ql = new concurrent_priority_queue<int, less_a<int> >(v.begin(), v.end(), l);
+    REMARK("Iterator filler +compare constructor complete.\n");
+    ASSERT(ql->size()==42, "FAILED vector/size test.");
+    ASSERT(!ql->empty(), "FAILED vector/empty test.");
+    REMARK("Testing destructor.\n");
+    delete ql;
+    REMARK("Destruction complete.\n");
+
     REMARK("Testing copy constructor.\n");
     qo = new concurrent_priority_queue<int, std::less<int> >(*q);
     REMARK("Copy constructor complete.\n");
@@ -1072,38 +1132,56 @@ void TestDeductionGuides() {
     auto l = {ComplexType(&s), ComplexType(&s) };
 
     // check TQueue(InputIterator, InputIterator)
-    TQueue q1(v.begin(), v.end());
-    static_assert(std::is_same<decltype(q1), TQueue<ComplexType>>::value);
+    TQueue qv(v.begin(), v.end());
+    static_assert(std::is_same<decltype(qv), TQueue<ComplexType> >::value);
 
     // check TQueue(InputIterator, InputIterator, Allocator)
-    TQueue q2(v.begin(), v.end(), std::allocator<ComplexType>());
-    static_assert(std::is_same<decltype(q2), TQueue<ComplexType, std::less<ComplexType>,
+    TQueue qva(v.begin(), v.end(), std::allocator<ComplexType>());
+    static_assert(std::is_same<decltype(qva), TQueue<ComplexType, std::less<ComplexType>,
+        std::allocator<ComplexType>>>::value);
+
+    // check TQueue(InputIterator, InputIterator, Compare)
+    TQueue qvc(v.begin(), v.end(), less_a<ComplexType>(true));
+    static_assert(std::is_same<decltype(qvc), TQueue<ComplexType, less_a<ComplexType>>>::value);
+
+    // check TQueue(InputIterator, InputIterator, Compare, Allocator)
+    TQueue qvca(v.begin(), v.end(), less_a<ComplexType>(true), std::allocator<ComplexType>());
+    static_assert(std::is_same<decltype(qvca), TQueue<ComplexType, less_a<ComplexType>,
         std::allocator<ComplexType>>>::value);
 
     // check TQueue(std::initializer_list)
-    TQueue q3(l);
-    static_assert(std::is_same<decltype(q3), TQueue<ComplexType>>::value);
+    TQueue ql(l);
+    static_assert(std::is_same<decltype(ql), TQueue<ComplexType>>::value);
 
     // check TQueue(std::initializer_list, Allocator)
-    TQueue q4(l, std::allocator<ComplexType>());
-    static_assert(std::is_same<decltype(q4), TQueue<ComplexType, std::less<ComplexType>,
+    TQueue qla(l, std::allocator<ComplexType>());
+    static_assert(std::is_same<decltype(qla), TQueue<ComplexType, std::less<ComplexType>,
+        std::allocator<ComplexType>>>::value);
+
+    // check TQueue(std::initializer_list, Compare)
+    TQueue qlc(l, less_a<ComplexType>(true));
+    static_assert(std::is_same<decltype(qlc), TQueue<ComplexType, less_a<ComplexType>>>::value);
+
+    // check TQueue(std::initializer_list, Compare, Allocator)
+    TQueue qlca(l, less_a<ComplexType>(true), std::allocator<ComplexType>());
+    static_assert(std::is_same<decltype(qlca), TQueue<ComplexType, less_a<ComplexType>,
         std::allocator<ComplexType>>>::value);
 
     // check TQueue(TQueue &)
-    TQueue q5(q1);
-    static_assert(std::is_same<decltype(q5), decltype(q5)>::value);
+    TQueue qc(qv);
+    static_assert(std::is_same<decltype(qv), decltype(qv)>::value);
 
     // check TQueue(TQueue &, Allocator)
-    TQueue q6(q4, std::allocator<ComplexType>());
-    static_assert(std::is_same<decltype(q6), decltype(q4)>::value);
+    TQueue qca(qva, std::allocator<ComplexType>());
+    static_assert(std::is_same<decltype(qca), decltype(qva)>::value);
 
     // check TQueue(TQueue &&)
-    TQueue q7(std::move(q1));
-    static_assert(std::is_same<decltype(q7), decltype(q1)>::value);
+    TQueue qm(std::move(qv));
+    static_assert(std::is_same<decltype(qm), decltype(qv)>::value);
 
     // check TQueue(TQueue &&, Allocator)
-    TQueue q8(std::move(q4), std::allocator<ComplexType>());
-    static_assert(std::is_same<decltype(q8), decltype(q4)>::value);
+    TQueue qma(std::move(qva), std::allocator<ComplexType>());
+    static_assert(std::is_same<decltype(qma), decltype(qva)>::value);
 }
 #endif
 
index a14a04c..e34938a 100644 (file)
@@ -988,3 +988,405 @@ namespace test_select_size_t_constant{
     __TBB_STATIC_ASSERT((tbb::internal::select_size_t_constant<0x12345678U,0x091A2B3C091A2B3CULL>::value % ~0U == 0x12345678U),
             "select_size_t_constant have chosen the wrong constant");
 }
+
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+namespace node_handling{
+    template<typename Handle>
+    bool compare_handle_getters(
+        const Handle& node, const std::pair<typename Handle::key_type, typename Handle::mapped_type>& expected
+    ) {
+        return node.key() == expected.first && node.mapped() == expected.second;
+    }
+
+    template<typename Handle>
+    bool compare_handle_getters( const Handle& node, const typename Handle::value_type& value) {
+        return node.value() == value;
+    }
+
+    template<typename Handle>
+    void set_node_handle_value(
+        Handle& node, const std::pair<typename Handle::key_type, typename Handle::mapped_type>& value
+    ) {
+        node.key() = value.first;
+        node.mapped() = value.second;
+    }
+
+    template<typename Handle>
+    void set_node_handle_value( Handle& node, const typename Handle::value_type& value) {
+        node.value() = value;
+    }
+
+    template <typename node_type>
+    void TestTraits() {
+        ASSERT( !std::is_copy_constructible<node_type>::value,
+                "Node handle: Handle is copy constructable" );
+        ASSERT( !std::is_copy_assignable<node_type>::value,
+                "Node handle: Handle is copy assignable" );
+        ASSERT( std::is_move_constructible<node_type>::value,
+                "Node handle: Handle is not move constructable" );
+        ASSERT( std::is_move_assignable<node_type>::value,
+                "Node handle: Handle is not move constructable" );
+        ASSERT( std::is_default_constructible<node_type>::value,
+                "Node handle:  Handle is not default constructable" );
+        ASSERT( std::is_destructible<node_type>::value,
+                "Node handle: Handle is not destructible" );
+    }
+
+    template <typename Table>
+    void TestHandle( Table test_table ) {
+        ASSERT( test_table.size()>1, "Node handle: Container must contains 2 or more elements" );
+        // Initialization
+        using node_type = typename Table::node_type;
+
+        TestTraits<node_type>();
+
+        // Default Ctor and empty function
+        node_type nh;
+        ASSERT( nh.empty(), "Node handle: Node is not empty after initialization" );
+
+        // Move Assign
+        // key/mapped/value function
+        auto expected_value = *test_table.begin();
+
+        nh = test_table.unsafe_extract(test_table.begin());
+        ASSERT( !nh.empty(), "Node handle: Node handle is empty after valid move assigning" );
+        ASSERT( compare_handle_getters(nh,expected_value),
+                "Node handle: After valid move assigning "
+                "node handle does not contains expected value");
+
+        // Move Ctor
+        // key/mapped/value function
+        node_type nh2(std::move(nh));
+        ASSERT( nh.empty(), "Node handle: After valid move construction node handle is empty" );
+        ASSERT( !nh2.empty(), "Node handle: After valid move construction "
+                              "argument hode handle was not moved" );
+        ASSERT( compare_handle_getters(nh2,expected_value),
+                "Node handle: After valid move construction "
+                "node handle does not contains expected value" );
+
+        // Bool conversion
+        ASSERT( nh2, "Node hanlde: Wrong not handle bool conversion" );
+
+        // Change key/mapped/value of node handle
+        auto expected_value2 = *test_table.begin();
+        set_node_handle_value(nh2, expected_value2);
+        ASSERT( compare_handle_getters(nh2, expected_value2),
+                "Node handle: Wrong node handle key/mapped/value changing behavior" );
+
+        // Member/non member swap check
+        node_type empty_node;
+        // We extract this element for nh2 and nh3 difference
+        test_table.unsafe_extract(test_table.begin());
+        auto expected_value3 =  *test_table.begin();
+        node_type nh3(test_table.unsafe_extract(test_table.begin()));
+
+        // Both of node handles are not empty
+        nh3.swap(nh2);
+        ASSERT( compare_handle_getters(nh3, expected_value2),
+                "Node handle: Wrong node handle swap behavior" );
+        ASSERT( compare_handle_getters(nh2, expected_value3),
+                "Node handle: Wrong node handle swap behavior" );
+
+        std::swap(nh2,nh3);
+        ASSERT( compare_handle_getters(nh3, expected_value3),
+                "Node handle: Wrong node handle swap behavior" );
+        ASSERT( compare_handle_getters(nh2, expected_value2),
+                "Node handle: Wrong node handle swap behavior" );
+        ASSERT( !nh2.empty(), "Node handle: Wrong node handle swap behavior" );
+        ASSERT( !nh3.empty(), "Node handle: Wrong node handle swap behavior" );
+
+        // One of nodes is empty
+        nh3.swap(empty_node);
+        ASSERT( compare_handle_getters(std::move(empty_node), expected_value3),
+                "Node handle: Wrong node handle swap behavior" );
+        ASSERT( nh3.empty(), "Node handle: Wrong node handle swap behavior" );
+
+        std::swap(empty_node, nh3);
+        ASSERT( compare_handle_getters(std::move(nh3), expected_value3),
+                "Node handle: Wrong node handle swap behavior" );
+        ASSERT( empty_node.empty(), "Node handle: Wrong node handle swap behavior" );
+
+        empty_node.swap(nh3);
+        ASSERT( compare_handle_getters(std::move(empty_node), expected_value3),
+                "Node handle: Wrong node handle swap behavior" );
+        ASSERT( nh3.empty(), "Node handle: Wrong node handle swap behavior" );
+    }
+
+    template <typename Table>
+    typename Table::node_type GenerateNodeHandle(const typename Table::value_type& value) {
+        Table temp_table;
+        temp_table.insert(value);
+        return temp_table.unsafe_extract(temp_table.cbegin());
+    }
+
+    // overload for multitable or insertion with hint iterator
+    template <typename Table>
+    void InsertAssertion( const Table& table,
+                          const typename Table::iterator& result,
+                          bool,
+                          const typename Table::value_type* node_value = nullptr ) {
+        if (node_value==nullptr) {
+            ASSERT( result==table.end(), "Insert: Result iterator does not "
+                                         "contains end pointer after empty node insertion" );
+        } else {
+            if (!Table::allow_multimapping) {
+                ASSERT( result==table.find(Value<Table>::key( *node_value )) &&
+                        result != table.end(),
+                        "Insert: After node insertion result iterator"
+                        " doesn't contains address to equal element in table" );
+            } else {
+                ASSERT( *result==*node_value, "Insert: Result iterator contains"
+                                              "wrong content after successful insertion" );
+
+                for (auto it = table.begin(); it != table.end(); ++it) {
+                    if (it == result) return;
+                }
+                ASSERT( false, "Insert: After successful insertion result "
+                               "iterator contains address that is not in the table" );
+            }
+        }
+    }
+
+    // Not multitable overload
+    template <typename Table>
+    void InsertAssertion( const Table& table,
+                          const std::pair<typename Table::iterator, bool>& result,
+                          bool is_existing_key,
+                          const typename Table::value_type* node_value = nullptr ) {
+        // Empty node insertion
+        if (node_value == nullptr) {
+            ASSERT( result.first == table.end(),
+                    "Insert: Returned iterator does not contains "
+                    "pointer end after empty node insertion" );
+            ASSERT( !result.second,
+                    "Insert: Returned bool return true after empty node insertion" );
+        } else {
+            ASSERT( result.first == table.find(Value<Table>::key( *node_value )),
+                    "Insert: Returned iterator not contains iterator "
+                    "to equal node in table after node insertion" );
+            ASSERT( result.second == (!is_existing_key || Table::allow_multimapping),
+                    "Insert: Returned bool wrong value after node insertion" );
+        }
+    }
+
+#if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
+    // Internal func for testing
+    // Can't delete ref from "Table" argument because hint must point to element of table
+    namespace {
+        template <typename Table, typename... Hint>
+        void TestInsertOverloads( Table& table_to_insert,
+                                  const typename Table::value_type &value, const Hint&... hint ) {
+            // Insert empty element
+            typename Table::node_type nh;
+
+            auto table_size = table_to_insert.size();
+            auto result = table_to_insert.insert(hint..., std::move(nh));
+            InsertAssertion(table_to_insert, result, /*is_existing_key*/ false);
+            ASSERT( table_to_insert.size() == table_size,
+                    "Insert: After empty node insertion table size changed" );
+
+            // Standart insertion
+            nh = GenerateNodeHandle<Table>(value);
+
+            result = table_to_insert.insert(hint..., std::move(nh));
+            ASSERT( nh.empty(), "Insert: Not empty handle after successful insertion" );
+            InsertAssertion(table_to_insert, result, /*is_existing_key*/ false, &value);
+
+            // Insert existing node
+            nh = GenerateNodeHandle<Table>(value);
+
+            result = table_to_insert.insert(hint..., std::move(nh));
+
+            InsertAssertion(table_to_insert, result, /*is_existing_key*/ true, &value);
+
+            if (Table::allow_multimapping){
+                ASSERT( nh.empty(), "Insert: Failed insertion to multitable" );
+            } else {
+                ASSERT( !nh.empty() , "Insert: Empty handle after failed insertion" );
+                ASSERT( compare_handle_getters( std::move(nh), value ),
+                        "Insert: Existing data does not equal to the one being inserted" );
+            }
+        }
+    }
+
+    template <typename Table>
+    void TestInsert( Table table, const typename Table::value_type &value) {
+        ASSERT( !table.empty(), "Insert: Map should contains 1 or more elements" );
+        Table table_backup(table);
+
+        TestInsertOverloads(table, value);
+        TestInsertOverloads(table_backup, value, table.begin());
+    }
+#endif /*__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT*/
+
+    template <typename Table>
+    void TestExtract( Table table_for_extract, typename Table::key_type new_key ) {
+        ASSERT( table_for_extract.size()>1, "Extract: Container must contains 2 or more element" );
+        ASSERT( table_for_extract.find(new_key)==table_for_extract.end(),
+                "Extract: Table must not contains new element!");
+
+        // Extract new element
+        auto nh = table_for_extract.unsafe_extract(new_key);
+        ASSERT( nh.empty(), "Extract: Node handle is not empty after wrong key extraction" );
+
+        // Valid key extraction
+        auto expected_value = *table_for_extract.cbegin();
+        auto key = Value<Table>::key( expected_value );
+        auto count = table_for_extract.count(key);
+
+        nh = table_for_extract.unsafe_extract(key);
+        ASSERT( !nh.empty(),
+                "Extract: After successful extraction by key node handle is empty" );
+        ASSERT( compare_handle_getters(std::move(nh), expected_value),
+                "Extract: After successful extraction by key node handle contains wrong value" );
+        ASSERT( table_for_extract.count(key) == count - 1,
+                "Extract: After successful node extraction by key, table still contains this key" );
+
+        // Valid iterator overload
+        auto expected_value2 = *table_for_extract.cbegin();
+        auto key2 = Value<Table>::key( expected_value2 );
+        auto count2 = table_for_extract.count(key2);
+
+        nh = table_for_extract.unsafe_extract(table_for_extract.cbegin());
+        ASSERT( !nh.empty(),
+                "Extract: After successful extraction by iterator node handle is empty" );
+        ASSERT( compare_handle_getters(std::move(nh), expected_value2),
+                "Extract: After successful extraction by iterator node handle contains wrong value" );
+        ASSERT( table_for_extract.count(key2) == count2 - 1,
+                "Extract: After successful extraction table also contains this element" );
+    }
+
+    // All test exclude merge
+    template <typename Table>
+    void NodeHandlingTests ( const Table& table,
+                             const typename Table::value_type& new_value) {
+        TestHandle(table);
+#if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
+        TestInsert(table, new_value);
+#endif /*__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT*/
+        TestExtract(table,  Value<Table>::key( new_value ));
+    }
+
+    template <typename TableType1, typename TableType2>
+    void TestMerge( TableType1 table1, TableType2&& table2 ) {
+        using Table2PureType = typename std::decay<TableType2>::type;
+        // Initialization
+        TableType1 table1_backup = table1;
+        // For copying lvalue
+        Table2PureType table2_backup = table2;
+
+        table1.merge(std::forward<TableType2>(table2));
+        for (auto it: table2) {
+            ASSERT( table1.find( Value<Table2PureType>::key( it ) ) != table1.end(),
+                    "Merge: Some key(s) was not merged" );
+        }
+
+        // After the following step table1 will contains only merged elements from table2
+        for (auto it: table1_backup) {
+            table1.unsafe_extract(Value<TableType1>::key( it ));
+        }
+        // After the following step table2_backup will contains only merged elements from table2
+         for (auto it: table2) {
+            table2_backup.unsafe_extract(Value<Table2PureType>::key( it ));
+        }
+
+        ASSERT ( table1.size() == table2_backup.size(), "Merge: Size of tables is not equal" );
+        for (auto it: table2_backup) {
+            ASSERT( table1.find( Value<Table2PureType>::key( it ) ) != table1.end(),
+                    "Merge: Wrong merge behavior" );
+        }
+    }
+
+    // Testing of rvalue and lvalue overloads
+    template <typename TableType1, typename TableType2>
+    void TestMergeOverloads( const TableType1& table1, TableType2 table2 ) {
+        TableType2 table_backup(table2);
+        TestMerge(table1, table2);
+        TestMerge(table1, std::move(table_backup));
+    }
+
+    template <typename Table, typename MultiTable>
+    void TestMergeTransposition( Table table1, Table table2,
+                                 MultiTable multitable1, MultiTable multitable2 ) {
+        Table empty_map;
+        MultiTable empty_multimap;
+
+        // Map transpositions
+        node_handling::TestMergeOverloads(table1, table2);
+        node_handling::TestMergeOverloads(table1, empty_map);
+        node_handling::TestMergeOverloads(empty_map, table2);
+
+        // Multimap transpositions
+        node_handling::TestMergeOverloads(multitable1, multitable2);
+        node_handling::TestMergeOverloads(multitable1, empty_multimap);
+        node_handling::TestMergeOverloads(empty_multimap, multitable2);
+
+        // Map/Multimap transposition
+        node_handling::TestMergeOverloads(table1, multitable1);
+        node_handling::TestMergeOverloads(multitable2, table2);
+    }
+
+    template <typename Table>
+    void AssertionConcurrentMerge ( Table start_data, Table src_table, std::vector<Table> tables,
+                                    std::true_type) {
+        ASSERT( src_table.size() == start_data.size()*tables.size(),
+                "Merge: Incorrect merge for some elements" );
+
+        for(auto it: start_data) {
+            ASSERT( src_table.count( Value<Table>::key( it ) ) ==
+                    start_data.count( Value<Table>::key( it ) )*tables.size(),
+                                      "Merge: Incorrect merge for some element" );
+        }
+
+        for (size_t i = 0; i < tables.size(); i++) {
+            ASSERT( tables[i].empty(), "Merge: Some elements was not merged" );
+        }
+    }
+
+    template <typename Table>
+    void AssertionConcurrentMerge ( Table start_data, Table src_table, std::vector<Table> tables,
+                                    std::false_type) {
+        Table expected_result;
+        for (auto table: tables)
+            for (auto it: start_data) {
+                // If we cannot find some element in some table, then it has been moved
+                if (table.find( Value<Table>::key( it ) ) == table.end()){
+                    bool result = expected_result.insert( it ).second;
+                    ASSERT( result, "Merge: Some element was merged twice or was not "
+                                    "returned to his owner after unsuccessful merge");
+                }
+            }
+
+        ASSERT( expected_result.size() == src_table.size() && start_data.size() == src_table.size(),
+                "Merge: wrong size of result table");
+        for (auto it: expected_result) {
+            if ( src_table.find( Value<Table>::key( it ) ) != src_table.end() &&
+                 start_data.find( Value<Table>::key( it ) ) != start_data.end() ){
+                src_table.unsafe_extract(Value<Table>::key( it ));
+                start_data.unsafe_extract(Value<Table>::key( it ));
+            } else {
+                ASSERT( false, "Merge: Incorrect merge for some element" );
+            }
+        }
+
+        ASSERT( src_table.empty()&&start_data.empty(), "Merge: Some elements were not merged" );
+    }
+
+    template <typename Table>
+    void TestConcurrentMerge (const Table& table_data) {
+        for (auto num_threads = MinThread + 1; num_threads <= MaxThread; num_threads++){
+            std::vector<Table> tables;
+            Table src_table;
+
+            for (auto j = 0; j < num_threads; j++){
+                tables.push_back(table_data);
+            }
+
+            NativeParallelFor( num_threads, [&](size_t index){ src_table.merge(tables[index]); } );
+
+            AssertionConcurrentMerge( table_data, src_table, tables,
+                                      std::integral_constant<bool,Table::allow_multimapping>{});
+        }
+    }
+}
+#endif /*__TBB_UNORDERED_NODE_HANDLE_PRESENT*/
index df2d9c5..8864df6 100644 (file)
@@ -385,6 +385,83 @@ void TestDeductionGuides() {
 }
 #endif
 
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+
+void TestMerge(){
+    using Map = tbb::concurrent_unordered_map<int, int>;
+    using MultiMap = tbb::concurrent_unordered_multimap<int, int>;
+
+    Map map_for_merge1;
+    map_for_merge1.insert({1, 10});
+    map_for_merge1.insert({2, 20});
+    map_for_merge1.insert({3, 30});
+    Map map_for_merge2;
+    map_for_merge2.insert({1, 40});
+    map_for_merge2.insert({2, 20});
+    map_for_merge2.insert({9, 90});
+
+    MultiMap multimap_for_merge1;
+    multimap_for_merge1.insert({1, 10});
+    multimap_for_merge1.insert({1, 10});
+    multimap_for_merge1.insert({2, 20});
+    multimap_for_merge1.insert({3, 30});
+    multimap_for_merge1.insert({4, 40});
+    MultiMap multimap_for_merge2;
+    multimap_for_merge2.insert({1, 10});
+    multimap_for_merge2.insert({2, 50});
+    multimap_for_merge2.insert({5, 60});
+    multimap_for_merge2.insert({5, 70});
+
+    node_handling::TestMergeTransposition(map_for_merge1, map_for_merge2,
+                                          multimap_for_merge1, multimap_for_merge2);
+
+    // Test merge with different hashers
+    tbb::concurrent_unordered_map<int, int, degenerate_hash<int>> degenerate_hash_map;
+    degenerate_hash_map.insert({1, 10});
+    degenerate_hash_map.insert({2, 20});
+    degenerate_hash_map.insert({9, 90});
+
+    tbb::concurrent_unordered_multimap<int, int, degenerate_hash<int>> degenerate_hash_multimap;
+    degenerate_hash_multimap.insert({1, 10});
+    degenerate_hash_multimap.insert({2, 20});
+    degenerate_hash_multimap.insert({5, 50});
+    degenerate_hash_multimap.insert({5, 60});
+    degenerate_hash_multimap.insert({6, 70});
+
+    node_handling::TestMergeOverloads(map_for_merge1, degenerate_hash_map);
+    node_handling::TestMergeOverloads(multimap_for_merge1, degenerate_hash_multimap);
+
+    int size = 100000;
+
+    Map map_for_merge3(size);
+    for (int i = 0; i<size; i++){
+        map_for_merge3.insert({i,i});
+    }
+    node_handling::TestConcurrentMerge(map_for_merge3);
+
+    MultiMap multimap_for_merge3(size/2);
+    for (int i = 0; i<size/2; i++){
+        multimap_for_merge3.insert({i,i});
+        multimap_for_merge3.insert({i,i});
+    }
+    node_handling::TestConcurrentMerge(multimap_for_merge3);
+}
+
+void TestNodeHandling() {
+    tbb::concurrent_unordered_map<int, int> unordered_map;
+    for (int i = 1; i<5; i++)
+        unordered_map.insert({i,i*10});
+    node_handling::NodeHandlingTests(unordered_map, /*new key for test_data*/{5,90});
+
+    tbb::concurrent_unordered_multimap<int, int> unordered_multimap;
+    for (int i = 1; i<5; i++)
+        unordered_multimap.insert({i,i*10});
+    unordered_multimap.insert({2, 30});
+    node_handling::NodeHandlingTests(unordered_multimap, /*new key for test_data*/{5,90});
+}
+
+#endif /*__TBB_UNORDERED_NODE_HANDLE_PRESENT*/
+
 int TestMain() {
     test_machine();
 
@@ -427,5 +504,10 @@ int TestMain() {
 
     TestTypes();
 
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    TestNodeHandling ();
+    TestMerge();
+#endif /*__TBB_UNORDERED_NODE_HANDLE_PRESENT*/
+
     return Harness::Done;
 }
index 480bbbb..3bc277e 100644 (file)
@@ -128,6 +128,86 @@ void TestTypes( ) {
     REPORT( "Known issue: C++11 smart pointer tests are skipped.\n" );
 #endif /* __TBB_CPP11_SMART_POINTERS_PRESENT */
 }
+
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+
+void TestNodeHandling() {
+    tbb::concurrent_unordered_set<int> unordered_set;
+    for (int i =1; i< 5; i++)
+        unordered_set.insert(i);
+    node_handling::NodeHandlingTests(unordered_set, /*new key for test_data*/5);
+
+    tbb::concurrent_unordered_multiset<int> unordered_multiset;
+    for (int i =1; i< 5; i++)
+        unordered_multiset.insert(i);
+    unordered_multiset.insert(1);
+    unordered_multiset.insert(2);
+    node_handling::NodeHandlingTests(unordered_multiset, /*new key for test_data*/5);
+}
+
+void TestMerge(){
+    using Set = tbb::concurrent_unordered_set<int>;
+    using MultiSet = tbb::concurrent_unordered_multiset<int>;
+
+    Set set_for_merge1;
+    set_for_merge1.insert(1);
+    set_for_merge1.insert(2);
+    set_for_merge1.insert(3);
+    Set set_for_merge2;
+    set_for_merge2.insert(1);
+    set_for_merge2.insert(2);
+    set_for_merge2.insert(9);
+
+    MultiSet multiset_for_merge1;
+    multiset_for_merge1.insert(1);
+    multiset_for_merge1.insert(1);
+    multiset_for_merge1.insert(2);
+    multiset_for_merge1.insert(3);
+    multiset_for_merge1.insert(4);
+    MultiSet multiset_for_merge2;
+    multiset_for_merge2.insert(1);
+    multiset_for_merge2.insert(2);
+    multiset_for_merge2.insert(5);
+    multiset_for_merge2.insert(5);
+    multiset_for_merge2.insert(6);
+
+    node_handling::TestMergeTransposition(set_for_merge1, set_for_merge2,
+                                          multiset_for_merge1, multiset_for_merge2);
+
+    // Test merge with different hashers
+    tbb::concurrent_unordered_set<int, degenerate_hash<int>> degenerate_hash_set;
+    degenerate_hash_set.insert(1);
+    degenerate_hash_set.insert(2);
+    degenerate_hash_set.insert(9);
+
+    tbb::concurrent_unordered_multiset<int, degenerate_hash<int>> degenerate_hash_multiset;
+    degenerate_hash_multiset.insert(1);
+    degenerate_hash_multiset.insert(2);
+    degenerate_hash_multiset.insert(5);
+    degenerate_hash_multiset.insert(5);
+    degenerate_hash_multiset.insert(6);
+
+    node_handling::TestMergeOverloads(set_for_merge1, degenerate_hash_set);
+    node_handling::TestMergeOverloads(multiset_for_merge1, degenerate_hash_multiset);
+
+    int size = 100000;
+
+    Set set_for_merge3(size);
+    for (int i = 0; i<size; i++){
+        set_for_merge3.insert(i);
+    }
+    node_handling::TestConcurrentMerge(set_for_merge3);
+
+    MultiSet multiset_for_merge3(size/2);
+    for (int i = 0; i<size/2; i++){
+        multiset_for_merge3.insert(i);
+        multiset_for_merge3.insert(i);
+    }
+    node_handling::TestConcurrentMerge(multiset_for_merge3);
+}
+
+#endif/*__TBB_UNORDERED_NODE_HANDLE_PRESENT*/
+
 #endif // __TBB_TEST_SECONDARY
 
 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
@@ -285,6 +365,11 @@ int TestMain() {
     TestDeductionGuides<tbb::concurrent_unordered_multiset>();
 #endif
 
+#if __TBB_UNORDERED_NODE_HANDLE_PRESENT
+    TestNodeHandling();
+    TestMerge();
+#endif /*__TBB_UNORDERED_NODE_HANDLE_PRESENT*/
+
     return Harness::Done;
 }
 #endif //#if !__TBB_TEST_SECONDARY
index d8ebd06..08f849b 100644 (file)
@@ -21,7 +21,6 @@
 #define TBB_PREVIEW_WAITING_FOR_WORKERS 1
 #include "tbb/global_control.h"
 #include "harness.h"
-#define TBB_PREVIEW_LOCAL_OBSERVER 1
 #include "tbb/task_scheduler_observer.h"
 
 const size_t MB = 1024*1024;
@@ -253,12 +252,12 @@ void TestTooBigStack()
     const size_t stack_sizes[] = {512*MB, 2*1024*MB, UINT_MAX, 10LU*1024*MB};
 #endif
 
-#if __TBB_WIN8UI_SUPPORT
+#if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)
     size_t default_ss = tbb::global_control::active_value(tbb::global_control::thread_stack_size);
 #endif
     for (unsigned i = 0; i<Harness::array_length(stack_sizes); i++) {
-        // as no stack size setting for Windows Store* apps, skip it
-#if TRY_BAD_EXPR_ENABLED && __TBB_x86_64 && (_WIN32 || _WIN64) && !__TBB_WIN8UI_SUPPORT
+        // No stack size setting for Windows 8 Store* apps, skip it
+#if TRY_BAD_EXPR_ENABLED && __TBB_x86_64 && (_WIN32 || _WIN64) && !(__TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00))
         if (stack_sizes[i] != (unsigned)stack_sizes[i]) {
             size_t curr_ss = tbb::global_control::active_value(tbb::global_control::thread_stack_size);
             tbb::set_assertion_handler( AssertionFailureHandler );
@@ -270,8 +269,8 @@ void TestTooBigStack()
 #endif
         tbb::global_control s1(tbb::global_control::thread_stack_size, stack_sizes[i]);
         size_t actual_stack_sz = tbb::global_control::active_value(tbb::global_control::thread_stack_size);
-#if __TBB_WIN8UI_SUPPORT
-        ASSERT(actual_stack_sz == default_ss, "It's ignored for Windows Store* apps");
+#if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)
+        ASSERT(actual_stack_sz == default_ss, "It's ignored for Windows 8.x Store* apps");
 #else
         ASSERT(actual_stack_sz==stack_sizes[i], NULL);
 #endif
@@ -780,8 +779,9 @@ int TestMain()
 
     size_t default_ss = tbb::global_control::active_value(tbb::global_control::thread_stack_size);
     ASSERT(default_ss, NULL);
-#if !__TBB_WIN8UI_SUPPORT
-    // it's impossible to change stack size for Windows Store* apps, so skip the tests
+
+// it's impossible to change stack size for Windows 8 Store* apps, so skip the tests
+#if !(__TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00))
     TestStackSizeSimpleControl();
     TestStackSizeThreadsControl();
 #endif
index 6d5cb5b..34e35ed 100644 (file)
@@ -37,11 +37,11 @@ template <typename RandomIt>
 void test_random_iterator(const RandomIt& it) {
     // check that RandomIt has all necessary publicly accessible member types
     {
-        typename RandomIt::difference_type{};
-        typename RandomIt::value_type{};
+        auto t1 = typename RandomIt::difference_type{};
+        auto t2 = typename RandomIt::value_type{};
         typename RandomIt::reference ref = *it;
         tbb::internal::suppress_unused_warning(ref);
-        typename RandomIt::pointer{};
+        auto t3 = typename RandomIt::pointer{};
         typename RandomIt::iterator_category{};
     }
 
@@ -140,6 +140,21 @@ struct test_counting_iterator {
     }
 };
 
+struct sort_fun{
+    template<typename T1, typename T2>
+    bool operator()(T1 a1, T2 a2) const {
+        return std::get<0>(a1) < std::get<0>(a2);
+    }
+};
+
+template <typename InputIterator>
+void test_explicit_move(InputIterator i, InputIterator j) {
+    using value_type = typename std::iterator_traits<InputIterator>::value_type;
+    value_type t(std::move(*i));
+    *i = std::move(*j);
+    *j = std::move(t);
+}
+
 struct test_zip_iterator {
     template <typename T1, typename T2>
     void operator()(std::vector<T1>& in1, std::vector<T2>& in2) {
@@ -149,11 +164,41 @@ struct test_zip_iterator {
         b = tbb::make_zip_iterator(in1.begin(), in2.begin());
         auto e = tbb::make_zip_iterator(in1.end(), in2.end());
 
-        //checks in using
+        ASSERT( (b+1) != e, "size of input sequence insufficient for test" );
+
+        //simple check for-loop.
+        {
         std::for_each(b, e, [](const std::tuple<T1&, T2&>& a) { std::get<0>(a) = 1, std::get<1>(a) = 1;});
         auto res = std::all_of(b, e, [](const std::tuple<T1&, T2&>& a) {return std::get<0>(a) == 1 && std::get<1>(a) == 1;});
-        ASSERT(res, "wrong result with zip_iterator iterator");
+        ASSERT(res, "wrong result sequence assignment to (1,1) with zip_iterator iterator");
+        }
 
+        //check swapping de-referenced iterators (required by sort algorithm)
+        {
+        using std::swap;
+        auto t = std::make_tuple(T1(3), T2(2));
+        *b = t;
+        t = *(b+1);
+        ASSERT( std::get<0>(t) == 1 && std::get<1>(t) == 1, "wrong result of assignment from zip_iterator");
+        swap(*b, *(b+1));
+        ASSERT( std::get<0>(*b) == 1 && std::get<1>(*b) == 1, "wrong result swapping zip-iterator");
+        ASSERT( std::get<0>(*(b+1)) == 3 && std::get<1>(*(b+1)) == 2, "wrong result swapping zip-iterator");
+        // Test leaves sequence un-sorted.
+        }
+
+        //sort sequences by first stream.
+        {
+        // sanity check if sequence is un-sorted.
+        auto res = std::is_sorted(b, e, sort_fun());
+        ASSERT(!res, "input sequence to be sorted is already sorted! Test might lead to false positives.");
+        std::sort(tbb::make_zip_iterator(in1.begin(), in2.begin()),
+                  tbb::make_zip_iterator(in1.end(), in2.end()),
+                  sort_fun());
+        res = std::is_sorted(b, e, sort_fun());
+        ASSERT(res, "wrong result sorting sequence using zip-iterator");
+            // TODO: Add simple check: comparison with sort_fun().
+        }
+        test_explicit_move(b, b+1);
         test_random_iterator(b);
     }
 };
index 1237cda..8142d26 100644 (file)
@@ -850,9 +850,15 @@ void TestPoolMSize() {
     const int SZ = 10;
     // Original allocation requests, random numbers from small to large
     size_t requestedSz[SZ] = {8, 16, 500, 1000, 2000, 4000, 8000, 1024*1024, 4242+4242, 8484+8484};
-    // Unlike large objects, small objects do not store its original size along with the object itself
-    size_t allocatedSz[SZ] = {8, 16, 512, 1024, 2688, 4032, 8128, 1024*1024, 4242+4242, 8484+8484};
 
+    // Unlike large objects, small objects do not store its original size along with the object itself
+    // On Power architecture TLS bins are divided differently.
+    size_t allocatedSz[SZ] =
+#if __powerpc64__ || __ppc64__ || __bgp__
+        {8, 16, 512, 1024, 2688, 5376, 8064, 1024*1024, 4242+4242, 8484+8484};
+#else
+        {8, 16, 512, 1024, 2688, 4032, 8128, 1024*1024, 4242+4242, 8484+8484};
+#endif
     for (int i = 0; i < SZ; i++) {
         void* obj = pool_malloc(pool, requestedSz[i]);
         size_t objSize = pool_msize(pool, obj);
index eee1c2f..153f353 100644 (file)
@@ -1286,17 +1286,29 @@ void TestReallocDecreasing() {
 #if !__TBB_WIN8UI_SUPPORT && defined(_WIN32)
 
 #include "../src/tbbmalloc/tbb_function_replacement.cpp"
-
+#include <string>
 namespace FunctionReplacement {
     FunctionInfo funcInfo = { "funcname","dllname" };
     char **func_replacement_log;
     int status;
 
+    void LogCleanup() {
+        // Free all allocated memory
+        for (unsigned i = 0; i < Log::record_number; i++){
+            HeapFree(GetProcessHeap(), 0, Log::records[i]);
+        }
+        for (unsigned i = 0; i < Log::RECORDS_COUNT + 1; i++){
+            Log::records[i] = NULL;
+        }
+        Log::replacement_status = true;
+        Log::record_number = 0;
+    }
+
     void TestEmptyLog() {
         status = TBB_malloc_replacement_log(&func_replacement_log);
 
         ASSERT(status == -1, "Status is true, but log is empty");
-        ASSERT(*func_replacement_log == NULL, "There was no replacement, but log is not empty ");
+        ASSERT(*func_replacement_log == NULL, "Log must be empty");
     }
 
     void TestLogOverload() {
@@ -1308,17 +1320,43 @@ namespace FunctionReplacement {
         for (; *(func_replacement_log + 1) != 0; func_replacement_log++) {}
 
         std::string last_line(*func_replacement_log);
-        ASSERT(status == 0, "False status, but all replacements is correct");
+        ASSERT(status == 0, "False status, but all functions found");
         ASSERT(last_line.compare("Log was truncated.") == 0, "Log overflow was not handled");
+
+        // Change status
+        Log::record(funcInfo, "opcode string", false);
+        status = TBB_malloc_replacement_log(NULL);
+        ASSERT(status == -1, "Status is true, but we have false search case");
+
+        LogCleanup();
     }
 
-    void TestFalseStatusAfterLogOverload() {
-        // Insert false status after log overload
+    void TestFalseSearchCase() {
         Log::record(funcInfo, "opcode string", false);
+        std::string expected_line = "Fail: "+ std::string(funcInfo.funcName) + " (" +
+                         std::string(funcInfo.dllName) + "), byte pattern: <opcode string>";
 
-        status = TBB_malloc_replacement_log(NULL);
+        status = TBB_malloc_replacement_log(&func_replacement_log);
+
+        ASSERT(expected_line.compare(*func_replacement_log) == 0, "Wrong last string contnent");
+        ASSERT(status == -1, "Status is true, but we have false search case");
+        LogCleanup();
+    }
 
-        ASSERT(status == -1, "Status is true, but we have false replacement case");
+    void TestWrongFunctionInDll(){
+        HMODULE ucrtbase_handle = GetModuleHandle("ucrtbase.dll");
+        if (ucrtbase_handle) {
+            IsPrologueKnown("ucrtbase.dll", "fake_function", NULL, ucrtbase_handle);
+            std::string expected_line = "Fail: fake_function (ucrtbase.dll), byte pattern: <unknown>";
+
+            status = TBB_malloc_replacement_log(&func_replacement_log);
+
+            ASSERT(expected_line.compare(*func_replacement_log) == 0, "Wrong last string contnent");
+            ASSERT(status == -1, "Status is true, but we have false search case");
+            LogCleanup();
+        } else {
+            REMARK("Cannot found ucrtbase.dll on system, test skipped!\n");
+        }
     }
 }
 
@@ -1327,7 +1365,8 @@ void TesFunctionReplacementLog() {
     // Do not reorder the test cases
     TestEmptyLog();
     TestLogOverload();
-    TestFalseStatusAfterLogOverload();
+    TestFalseSearchCase();
+    TestWrongFunctionInDll();
 }
 
 #endif /*!__TBB_WIN8UI_SUPPORT && defined(_WIN32)*/
index 396e8f2..2bbe7c4 100644 (file)
@@ -600,6 +600,33 @@ void TestTransaction( const char * name )
 }
 #endif  /* __TBB_TSX_TESTING_ENABLED_FOR_THIS_COMPILER */
 
+template<typename M>
+class RWStateMultipleChangeBody {
+    M& my_mutex;
+public:
+    RWStateMultipleChangeBody(M& m) : my_mutex(m) {}
+
+    void operator()(const tbb::blocked_range<size_t>& r) const {
+        typename M::scoped_lock l(my_mutex, /*write=*/false);
+        for(size_t i = r.begin(); i != r.end(); ++i) {
+            ASSERT(l.downgrade_to_reader(), "Downgrade must succeed for read lock");
+        }
+        l.upgrade_to_writer();
+        for(size_t i = r.begin(); i != r.end(); ++i) {
+            ASSERT(l.upgrade_to_writer(), "Upgrade must succeed for write lock");
+        }
+    }
+};
+
+template<typename M>
+void TestRWStateMultipleChange() {
+    ASSERT(M::is_rw_mutex, "Incorrect mutex type");
+    size_t n = 10000;
+    M mutex;
+    RWStateMultipleChangeBody<M> body(mutex);
+    tbb::parallel_for(tbb::blocked_range<size_t>(0, n, n/10), body);
+}
+
 int TestMain () {
     for( int p=MinThread; p<=MaxThread; ++p ) {
         tbb::task_scheduler_init init( p );
@@ -666,6 +693,10 @@ int TestMain () {
             TestTryAcquire_OneThreadISO<tbb::critical_section>( "ISO Critical Section" );
             TestReaderWriterLockISO<tbb::spin_rw_mutex>( "ISO Spin RW Mutex" );
             TestRecursiveMutexISO<tbb::recursive_mutex>( "ISO Recursive Mutex" );
+
+            TestRWStateMultipleChange<tbb::spin_rw_mutex>();
+            TestRWStateMultipleChange<tbb::speculative_spin_rw_mutex>();
+            TestRWStateMultipleChange<tbb::queuing_rw_mutex>();
         }
     }
 
index d7697c5..84ac946 100644 (file)
@@ -58,12 +58,11 @@ public:
     void probe( ); // defined below
 };
 
-static const unsigned MaxStreamSize = 8000;
+static const unsigned StreamSize = 10;
 //! Maximum number of filters allowed
 static const unsigned MaxFilters = 4;
-static unsigned StreamSize;
 static const unsigned MaxBuffer = 8;
-static bool Done[MaxFilters][MaxStreamSize];
+static bool Done[MaxFilters][StreamSize];
 static waiting_probe WaitTest;
 static unsigned out_of_order_count;
 
@@ -73,16 +72,25 @@ template<typename T>
 class BaseFilter: public T {
     bool* const my_done;
     const bool my_is_last;
-    bool my_is_running;
+    bool concurrency_observed;
+    tbb::atomic<int> running_count;
 public:
     tbb::atomic<tbb::internal::Token> current_token;
     BaseFilter( tbb::filter::mode type, bool done[], bool is_last ) :
         T(type),
         my_done(done),
         my_is_last(is_last),
-        my_is_running(false),
+        concurrency_observed(false),
         current_token()
-    {}
+    {
+        running_count = 0;
+    }
+    ~BaseFilter() {
+        if( this->is_serial() || is_serial_execution )
+            ASSERT( !concurrency_observed, "Unexpected concurrency in a [serial] filter" );
+        else if( sleeptime > 0 )
+            ASSERT( concurrency_observed, "No concurrency in a parallel filter" );
+    }
     virtual Buffer* get_buffer( void* item ) {
         current_token++;
         return static_cast<Buffer*>(item);
@@ -101,18 +109,18 @@ public:
             ASSERT( thread_id == id, "non-thread-bound stages executed on different threads when must be executed on a single one");
         }
         Harness::ConcurrencyTracker ct;
+        concurrency_observed = concurrency_observed || (running_count++ > 0);
         if( this->is_serial() )
-            ASSERT( !my_is_running, "premature entry to serial stage" );
-        my_is_running = true;
+            ASSERT( !concurrency_observed, "premature entry to serial stage" );
         Buffer* b = get_buffer(item);
         if( b ) {
             if(!this->is_bound() && sleeptime > 0) {
                 if(this->is_serial()) {
                     Harness::Sleep((int)sleeptime);
-                }
-                else {
-                    // early parallel tokens sleep longer...
-                    int i = (int)((5 - b->sequence_number) * sleeptime);
+                } else {
+                    // early parallel tokens sleep longer
+                    int i = (int)((5 - (int)b->sequence_number) * sleeptime);
                     if(i < (int)sleeptime) i = (int)sleeptime;
                     Harness::Sleep(i);
                 }
@@ -136,7 +144,7 @@ public:
                 __TBB_store_with_release(b->is_busy, false);
             }
         }
-        my_is_running = false;
+        concurrency_observed = concurrency_observed || (--running_count > 0);
         return b;
     }
 };
@@ -338,7 +346,6 @@ double PipelineTest::TestOneConfiguration(unsigned numeral, unsigned nthread, un
         parallelism_limit = nthread;
     if( parallelism_limit>ntokens )
         parallelism_limit = (unsigned)ntokens;
-    StreamSize = nthread; // min( MaxStreamSize, nthread * MaxStreamItemsPerThread );
 
     for( unsigned i=0; i<number_of_filters; ++i ) {
         static_cast<BaseFilter<tbb::filter>*>(filter[i])->current_token=0;
@@ -462,15 +469,13 @@ void PipelineTest::TestIdleSpinning( unsigned nthread)  {
             if(s0a > 0.0) {
                 ++v0cnt;
                 s0 = (s0a < s0) ? s0a : s0;
-            }
-            else {
+            } else {
                 ++zero_count;
             }
             if(s1a > 0.0) {
                 ++v1cnt;
                 s1 = (s1a < s1) ? s1a : s1;
-            }
-            else {
+            } else {
                 ++zero_count;
             }
         }
index 39afa90..2cc4db1 100644 (file)
@@ -18,7 +18,6 @@
 
 */
 
-#define TBB_PREVIEW_LOCAL_OBSERVER 1
 #define __TBB_EXTRA_DEBUG 1
 
 #include <stdexcept>
@@ -1613,4 +1612,3 @@ int TestMain() {
     TestArenaWorkersMigration();
     return Harness::Done;
 }
-
index 6a11084..5b23870 100644 (file)
 
 */
 
-#if __TBB_CPF_BUILD
-#define TEST_SLEEP_PERMISSION 1
-#define TBB_USE_PREVIEW_BINARY 1
-#endif
 // undefine __TBB_CPF_BUILD to simulate user's setup
 #undef __TBB_CPF_BUILD
 
-#define TBB_PREVIEW_LOCAL_OBSERVER 1
-
 #include "tbb/tbb_config.h"
 #include "harness.h"
 
@@ -51,18 +45,16 @@ struct ObserverStats {
     tbb::atomic<int> m_entries;
     tbb::atomic<int> m_exits;
     tbb::atomic<int> m_workerEntries;
-    tbb::atomic<int> m_workerSleeps;
     tbb::atomic<int> m_workerExits;
 
     void Reset () {
-        m_entries = m_exits = m_workerEntries = m_workerSleeps = m_workerExits = 0;
+        m_entries = m_exits = m_workerEntries = m_workerExits = 0;
     }
 
     void operator += ( const ObserverStats& s ) {
         m_entries += s.m_entries;
         m_exits += s.m_exits;
         m_workerEntries += s.m_workerEntries;
-        m_workerSleeps += s.m_workerSleeps;
         m_workerExits += s.m_workerExits;
     }
 };
@@ -70,14 +62,12 @@ struct ObserverStats {
 struct ThreadState {
     uintptr_t m_flags;
     tbb::task_scheduler_observer *m_dyingObserver;
-    uintptr_t m_maySleepCalls;
-    bool m_canSleep;
     bool m_isMaster;
     ThreadState() { reset(); }
     void reset() {
-        m_maySleepCalls = m_flags = 0;
+        m_flags = 0;
         m_dyingObserver = NULL;
-        m_canSleep = m_isMaster = false;
+        m_isMaster = false;
     }
     static ThreadState &get();
 };
@@ -105,9 +95,7 @@ enum TestMode {
     //! Use local observer.
     tmLocalObservation = 2,
     //! Observer causes autoinitialization of the scheduler
-    tmAutoinitialization = 4,
-    //! test may_sleep
-    tmLeavingControl = 8
+    tmAutoinitialization = 4
 };
 
 uintptr_t theTestMode,
@@ -115,14 +103,11 @@ uintptr_t theTestMode,
 
 class MyObserver : public tbb::task_scheduler_observer, public ObserverStats {
     uintptr_t m_flag;
-    tbb::atomic<int> m_leave_ticket;
     tbb::atomic<bool> m_dying;
 
     void on_scheduler_entry( bool is_worker ) __TBB_override {
         ThreadState& state = ThreadState::get();
         ASSERT( is_worker==!state.m_isMaster, NULL );
-        if ( theTestMode & tmLeavingControl )
-            ASSERT( m_leave_ticket, NULL );
         if ( thePrevMode & tmSynchronized ) {
             ASSERT( !(state.m_flags & m_flag), "Observer repeatedly invoked for the same thread" );
             if ( theTestMode & tmLocalObservation )
@@ -163,72 +148,17 @@ class MyObserver : public tbb::task_scheduler_observer, public ObserverStats {
         if ( is_worker )
             ++m_workerExits;
     }
-    bool may_sleep() __TBB_override {
-        ThreadState& state = ThreadState::get();
-        ++state.m_maySleepCalls;
-        Harness::Sleep(10);     // helps to reproduce the issues
-        ASSERT( !state.m_isMaster, NULL );
-        if( m_dying ) {         // check the anti-starvation logic
-            return keep_awake;  // thread should exit despite the return value
-        }
-        if( state.m_canSleep ) {// the permission for sleep was previously received
-            // though, it is an important check for the test, we still do not guarantee this condition
-            ASSERT_WARNING( !(theTestMode & tmLeavingControl), "may_sleep() called again after leaving permission was granted once, check if repeated");
-            return allow_sleep;
-        }
-        // note, may_sleep can be called before on_entry()
-        if( !(theTestMode & tmLeavingControl) || m_leave_ticket.fetch_and_store(-1) > 0 ) {
-            state.m_canSleep = true;
-            ++m_workerSleeps;
-            return allow_sleep;
-        }
-        return keep_awake;
-    }
 public:
-    // the method is called before the work in new arena starts enabling the leaving test mode
-    // in this mode may_sleep() does not allow a thread to fall asleep unless permitted below
-    void enable_leaving_test() {
-        ASSERT(theTestMode & tmLeavingControl, NULL);
-        m_leave_ticket.store<tbb::relaxed>(-1);
-        ASSERT(!is_observing(), NULL);
-        observe(true);
-    }
-
-    // the work is just done in the only arena, assume workers start entering may_sleep
-    void test_leaving() {
-#if TEST_SLEEP_PERMISSION
-        if( !(theTestMode & tmLeavingControl) )
-            return; // second call to the test TODO: extend the test for the second round as well
-        REMARK( "Testing may_sleep()\n");
-        ASSERT( !m_workerSleeps, "permission for sleep was given before the test starts?");
-        ASSERT( (theTestMode & tmSynchronized) && m_workerEntries >= P-1, "test_leaving assumes full subscription of the only arena");
-        for ( int j = 0; j < m_workerEntries; j++ ) {
-            REMARK( "Round %d: entries %d, sleeps %d\n", j, (int)m_workerEntries, (int)m_workerSleeps );
-            ASSERT( m_leave_ticket == -1, "unexpected mode, signal was not consumed by a worker?" );
-            m_leave_ticket = 1; // dismiss one
-            double n_seconds = 10;
-            (Harness::TimedWaitWhileEq(n_seconds))(m_workerSleeps, j);
-            ASSERT( n_seconds >= 0, "Time out while waiting for a worker to call may_sleep for the first time");
-            __TBB_Yield();
-        }
-        // the first time this method is called the work will be executed again,
-        // the next time time, the scheduler will start shutting down
-        theTestMode &= ~tmLeavingControl;
-        m_leave_ticket = m_workerSleeps = 0; // reset for the next round
-#endif
-    }
-
     MyObserver( uintptr_t flag )
         : tbb::task_scheduler_observer(theTestMode & tmLocalObservation ? true : false)
         , m_flag(flag)
     {
-        m_leave_ticket.store<tbb::relaxed>(0);
         ++theNumObservers;
         Reset();
         m_dying = false;
         // Local observer causes automatic scheduler initialization
         // in the current thread, so here, we must postpone the activation.
-        if ( !(theTestMode & tmLocalObservation) && !(theTestMode & tmLeavingControl) )
+        if ( !(theTestMode & tmLocalObservation))
             observe(true);
     }
 
@@ -328,9 +258,6 @@ public:
         // when mode is local observation but not synchronized and when num threads == default
         if ( theTestMode & tmAutoinitialization )
             o.observe(true); // test autoinitialization can be done by observer
-        // when mode is synchronized observation and when num threads == default
-        if ( theTestMode & tmLeavingControl )
-            o.enable_leaving_test();
         // Observer in enabled state must outlive the scheduler to ensure that
         // all exit notifications are called.
         tbb::task_scheduler_init init(m_numThreads);
@@ -340,8 +267,6 @@ public:
         for ( int j = 0; j < 2; ++j ) {
             tbb::task &t = *new( tbb::task::allocate_root() ) FibTask(m_numThreads, f, o);
             tbb::task::spawn_root_and_wait(t);
-            if ( theTestMode & tmLeavingControl )
-                o.test_leaving();
             thePrevMode = theTestMode;
         }
     }
@@ -402,7 +327,6 @@ int TestMain () {
         theGlobalBarrier.initialize(M * T);
         TestObserver(M, T, 0);
         TestObserver(M, T, tmSynchronized | tmLocalObservation );
-        TestObserver(M, T, tmSynchronized | ( T==P? tmLeavingControl : 0));
         // keep tmAutoInitialization the last, as it does not release worker threads
         TestObserver(M, T, tmLocalObservation | ( T==P? tmAutoinitialization : 0) );
     }
index 5368e53..803812e 100644 (file)
@@ -229,7 +229,7 @@ int main(int argc, char *argv[] ) {
 void initialize_strings_vector(std::vector <string_pair>* vector)
 {
     vector->push_back(string_pair("TBB: VERSION\t\t2019.0", required));       // check TBB_VERSION
-    vector->push_back(string_pair("TBB: INTERFACE VERSION\t11004", required)); // check TBB_INTERFACE_VERSION
+    vector->push_back(string_pair("TBB: INTERFACE VERSION\t11005", required)); // check TBB_INTERFACE_VERSION
     vector->push_back(string_pair("TBB: BUILD_DATE", required));
     vector->push_back(string_pair("TBB: BUILD_HOST", required));
     vector->push_back(string_pair("TBB: BUILD_OS", required));