diff --git a/doc/main/reference/blocking_terminate.rst b/doc/main/reference/blocking_terminate.rst deleted file mode 100644 index 9b838c12e9..0000000000 --- a/doc/main/reference/blocking_terminate.rst +++ /dev/null @@ -1,165 +0,0 @@ -.. _task_scheduler_handle_reference: - -task_scheduler_handle Class -=========================== - -.. note:: - To enable this feature, set the ``TBB_PREVIEW_WAITING_FOR_WORKERS`` macro to 1. - -.. contents:: - :local: - :depth: 1 - -Description -*********** - -The ``oneapi::tbb::task_scheduler_handle`` class and the ``oneapi::tbb::finalize`` function allow to wait for completion of worker threads. - -When the ``oneapi::tbb::finalize`` function is called with an ``oneapi::tbb::task_scheduler_handle`` instance, it blocks the calling -thread until the completion of all worker threads that were implicitly created by the library. - -API -*** - -Header ------- - -.. code:: cpp - - #include - -Synopsis --------- - -.. code:: cpp - - namespace oneapi { - namespace tbb { - - class task_scheduler_handle { - public: - task_scheduler_handle() = default; - ~task_scheduler_handle(); - - task_scheduler_handle(const task_scheduler_handle& other) = delete; - task_scheduler_handle(task_scheduler_handle&& other) noexcept; - task_scheduler_handle& operator=(const task_scheduler_handle& other) = delete; - task_scheduler_handle& operator=(task_scheduler_handle&& other) noexcept; - - explicit operator bool() const noexcept; - - static task_scheduler_handle get(); - - static void release(task_scheduler_handle& handle); - }; - - void finalize(task_scheduler_handle& handle); - bool finalize(task_scheduler_handle& handle, const std::nothrow_t&) noexcept; - - } // namespace tbb - } // namespace oneapi - -Member Functions ----------------- - -.. cpp:function:: task_scheduler_handle() - - **Effects**: Creates an instance of the ``task_scheduler_handle`` class that does not contain any reference to the task scheduler. - -------------------------------------------------------- - -.. cpp:function:: ~task_scheduler_handle() - - **Effects**: Destroys an instance of the ``task_scheduler_handle`` class. - Releases a reference to the task scheduler and deactivates an instance of the ``task_scheduler_handle`` class. - -------------------------------------------------------- - -.. cpp:function:: task_scheduler_handle(task_scheduler_handle&& other) noexcept - - **Effects**: Creates an instance of the ``task_scheduler_handle`` class that references the task scheduler referenced by ``other``. In turn, ``other`` releases its reference to the task scheduler. - -------------------------------------------------------- - -.. cpp:function:: task_scheduler_handle& operator=(task_scheduler_handle&& other) noexcept - - **Effects**: Releases a reference to the task scheduler referenced by ``this``. Adds a reference to the task scheduler referenced by ``other``. -In turn, ``other`` releases its reference to the task scheduler. - -------------------------------------------------------- - -.. cpp:function:: explicit operator bool() const noexcept - - **Returns**: ``true`` if ``this`` references any task scheduler; ``false`` otherwise. - -------------------------------------------------------- - -.. cpp:function:: task_scheduler_handle get() - - **Returns**: An instance of the ``task_scheduler_handle`` class that holds a reference to the task scheduler preventing - its premature destruction. - -------------------------------------------------------- - -.. cpp:function:: void release(task_scheduler_handle& handle) - - **Effects**: Releases a reference to the task scheduler and deactivates an instance of the ``task_scheduler_handle`` - class. Non-blocking method. - -Non-member Functions --------------------- - -.. cpp:function:: void finalize(task_scheduler_handle& handle) - - **Effects**: Blocks the program execution until all worker threads have been completed. Throws the ``oneapi::tbb::unsafe_wait`` - exception if it is not safe to wait for the completion of the worker threads. - -The following conditions should be met for finalization to succeed: - -- No active (not yet terminated) instances of class ``task_arena`` exist in the whole program; -- ``task_scheduler_handle::release`` is called for each other active instance of class ``task_scheduler_handle``, possibly by different application threads. - -Under these conditions, it is guaranteed that at least one ``finalize`` call succeeds, -at which point all worker threads have been completed. -If calls are performed simultaneously, more than one call might succeed. - -.. note:: - - If you know how many active ``task_scheduler_handle`` instances exist in the program, - it is necessary to ``release`` all but the last one, then call ``finalize`` for - the last instance. - -.. caution:: - - The method always fails if called within a task, a parallel algorithm, or a flow graph node. - -------------------------------------------------------- - -.. cpp:function:: bool finalize(task_scheduler_handle& handle, const std::nothrow_t&) noexcept - - **Effects**: Blocks the program execution until all worker threads have been completed. Same as above, but returns ``true`` if all worker - threads have been completed successfully, or ``false`` if it is not safe to wait for the completion of the worker threads. - -Examples -******** - -.. code:: cpp - - #define TBB_PREVIEW_WAITING_FOR_WORKERS 1 - #include - #include - - #include - - int main() { - oneapi::tbb::task_scheduler_handle handle = oneapi::tbb::task_scheduler_handle::get(); - // Do some parallel work here, e.g. - oneapi::tbb::parallel_for(0, 10000, [](int){}); - try { - oneapi::tbb::finalize(handle); - // oneTBB worker threads are terminated at this point. - } catch (const oneapi::tbb::unsafe_wait&) { - std::cerr << "Failed to terminate the worker threads." << std::endl; - } - return 0; - } diff --git a/doc/main/reference/reference.rst b/doc/main/reference/reference.rst index 95a003a192..855c3a869a 100644 --- a/doc/main/reference/reference.rst +++ b/doc/main/reference/reference.rst @@ -44,7 +44,6 @@ The key properties of a preview feature are: :titlesonly: type_specified_message_keys - blocking_terminate scalable_memory_pools helpers_for_expressing_graphs concurrent_lru_cache_cls diff --git a/doc/main/tbb_userguide/Migration_Guide/Task_Scheduler_Init.rst b/doc/main/tbb_userguide/Migration_Guide/Task_Scheduler_Init.rst index bdac766577..f1fafaeafe 100644 --- a/doc/main/tbb_userguide/Migration_Guide/Task_Scheduler_Init.rst +++ b/doc/main/tbb_userguide/Migration_Guide/Task_Scheduler_Init.rst @@ -145,12 +145,11 @@ allows waiting for oneTBB worker threads completion: .. code:: cpp - #define TBB_PREVIEW_WAITING_FOR_WORKERS 1 #include #include int main() { - oneapi::tbb::task_scheduler_handle handle = oneapi::tbb::task_scheduler_handle::get(); + oneapi::tbb::task_scheduler_handle handle{tbb::attach{}}; // Do some parallel work here oneapi::tbb::parallel_for(/* ... */); oneapi::tbb::finalize(handle); diff --git a/include/oneapi/tbb/detail/_attach.h b/include/oneapi/tbb/detail/_attach.h new file mode 100644 index 0000000000..45f29727a0 --- /dev/null +++ b/include/oneapi/tbb/detail/_attach.h @@ -0,0 +1,32 @@ +/* + Copyright (c) 2021 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. +*/ + +#ifndef __TBB_detail__attach_H +#define __TBB_detail__attach_H + +#include "_config.h" + +namespace tbb { +namespace detail { +namespace d1 { + + struct attach {}; + +} // namespace d1 +} // namespace detail +} // namespace tbb + +#endif // __TBB_detail__attach_H diff --git a/include/oneapi/tbb/detail/_config.h b/include/oneapi/tbb/detail/_config.h index 75cc3ec494..9444b27845 100644 --- a/include/oneapi/tbb/detail/_config.h +++ b/include/oneapi/tbb/detail/_config.h @@ -374,10 +374,6 @@ #define __TBB_ARENA_BINDING 1 #endif -#if TBB_PREVIEW_WAITING_FOR_WORKERS || __TBB_BUILD - #define __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE 1 -#endif - #if (TBB_PREVIEW_TASK_ARENA_CONSTRAINTS_EXTENSION || __TBB_BUILD) && __TBB_ARENA_BINDING #define __TBB_PREVIEW_TASK_ARENA_CONSTRAINTS_EXTENSION_PRESENT 1 #endif diff --git a/include/oneapi/tbb/detail/_exception.h b/include/oneapi/tbb/detail/_exception.h index 7fec3b4344..21c61188d0 100644 --- a/include/oneapi/tbb/detail/_exception.h +++ b/include/oneapi/tbb/detail/_exception.h @@ -21,9 +21,7 @@ #include // std::bad_alloc #include // std::exception -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE #include // std::runtime_error -#endif namespace tbb { namespace detail { @@ -67,13 +65,11 @@ class TBB_EXPORT missing_wait : public std::exception { const char* __TBB_EXPORTED_METHOD what() const noexcept(true) override; }; -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE //! Exception for impossible finalization of task_sheduler_handle -class unsafe_wait : public std::runtime_error { +class TBB_EXPORT unsafe_wait : public std::runtime_error { public: unsafe_wait(const char* msg) : std::runtime_error(msg) {} }; -#endif // __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE //! Gathers all throw operators in one place. /** Its purpose is to minimize code bloat that can be caused by throw operators diff --git a/include/oneapi/tbb/global_control.h b/include/oneapi/tbb/global_control.h index 2bf1924d36..57f3b9dbcd 100644 --- a/include/oneapi/tbb/global_control.h +++ b/include/oneapi/tbb/global_control.h @@ -18,24 +18,22 @@ #define __TBB_global_control_H #include "detail/_config.h" -#include "detail/_namespace_injection.h" + #include "detail/_assert.h" -#include "detail/_template_helpers.h" +#include "detail/_attach.h" #include "detail/_exception.h" +#include "detail/_namespace_injection.h" +#include "detail/_template_helpers.h" -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE -#include // std::nothrow_t -#endif #include +#include // std::nothrow_t namespace tbb { namespace detail { namespace d1 { class global_control; -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE class task_scheduler_handle; -#endif } namespace r1 { @@ -44,12 +42,10 @@ TBB_EXPORT void __TBB_EXPORTED_FUNC destroy(d1::global_control&); TBB_EXPORT std::size_t __TBB_EXPORTED_FUNC global_control_active_value(int); struct global_control_impl; struct control_storage_comparator; -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE void release_impl(d1::task_scheduler_handle& handle); bool finalize_impl(d1::task_scheduler_handle& handle); TBB_EXPORT void __TBB_EXPORTED_FUNC get(d1::task_scheduler_handle&); TBB_EXPORT bool __TBB_EXPORTED_FUNC finalize(d1::task_scheduler_handle&, std::intptr_t mode); -#endif } namespace d1 { @@ -60,11 +56,7 @@ class global_control { max_allowed_parallelism, thread_stack_size, terminate_on_exception, -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE scheduler_handle, // not a public parameter -#else - reserved1, // not a public parameter -#endif parameter_max // insert new parameters above this point }; @@ -109,7 +101,6 @@ class global_control { friend struct r1::control_storage_comparator; }; -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE //! Finalization options. //! Outside of the class to avoid extensive friendship. static constexpr std::intptr_t release_nothrowing = 0; @@ -119,9 +110,17 @@ static constexpr std::intptr_t finalize_throwing = 2; //! User side wrapper for a task scheduler lifetime control object class task_scheduler_handle { public: + //! Creates an empty task_scheduler_handle task_scheduler_handle() = default; + + //! Creates an attached instance of task_scheduler_handle + task_scheduler_handle(attach) { + r1::get(*this); + } + + //! Release a reference if any ~task_scheduler_handle() { - release(*this); + release(); } //! No copy @@ -137,21 +136,16 @@ class task_scheduler_handle { return *this; }; + //! Checks if the task_scheduler_handle is empty explicit operator bool() const noexcept { return m_ctl != nullptr; } - //! Get and active instance of task_scheduler_handle - static task_scheduler_handle get() { - task_scheduler_handle handle; - r1::get(handle); - return handle; - } - //! Release the reference and deactivate handle - static void release(task_scheduler_handle& handle) { - if (handle.m_ctl != nullptr) { - r1::finalize(handle, release_nothrowing); + void release() { + if (m_ctl != nullptr) { + r1::finalize(*this, release_nothrowing); + m_ctl = nullptr; } } @@ -160,31 +154,45 @@ class task_scheduler_handle { friend bool r1::finalize_impl(task_scheduler_handle& handle); friend void __TBB_EXPORTED_FUNC r1::get(task_scheduler_handle&); + friend void finalize(task_scheduler_handle&); + friend bool finalize(task_scheduler_handle&, const std::nothrow_t&) noexcept; + global_control* m_ctl{nullptr}; }; #if TBB_USE_EXCEPTIONS //! Waits for worker threads termination. Throws exception on error. inline void finalize(task_scheduler_handle& handle) { - r1::finalize(handle, finalize_throwing); + try_call([&] { + if (handle.m_ctl != nullptr) { + bool finalized = r1::finalize(handle, finalize_throwing); + __TBB_ASSERT_EX(finalized, "r1::finalize did not respect finalize_throwing ?"); + + } + }).on_completion([&] { + __TBB_ASSERT(!handle, "The handle should be empty after finalize"); + }); } #endif //! Waits for worker threads termination. Returns false on error. inline bool finalize(task_scheduler_handle& handle, const std::nothrow_t&) noexcept { - return r1::finalize(handle, finalize_nothrowing); + bool finalized = true; + if (handle.m_ctl != nullptr) { + finalized = r1::finalize(handle, finalize_nothrowing); + } + __TBB_ASSERT(!handle, "The handle should be empty after finalize"); + return finalized; } -#endif // __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE } // namespace d1 } // namespace detail inline namespace v1 { using detail::d1::global_control; -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE +using detail::d1::attach; using detail::d1::finalize; using detail::d1::task_scheduler_handle; using detail::r1::unsafe_wait; -#endif } // namespace v1 } // namespace tbb diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h index 297234efdc..aa6f56c0e7 100644 --- a/include/oneapi/tbb/task_arena.h +++ b/include/oneapi/tbb/task_arena.h @@ -17,11 +17,14 @@ #ifndef __TBB_task_arena_H #define __TBB_task_arena_H -#include "detail/_namespace_injection.h" -#include "detail/_task.h" -#include "detail/_exception.h" +#include "detail/_config.h" + #include "detail/_aligned_space.h" +#include "detail/_attach.h" +#include "detail/_exception.h" +#include "detail/_namespace_injection.h" #include "detail/_small_object_pool.h" +#include "detail/_task.h" #include "detail/_task_handle.h" @@ -302,6 +305,11 @@ class task_arena : public task_arena_base { } } + //! Creates an instance of task_arena attached to the current arena of the thread + explicit task_arena(d1::attach) + : task_arena(attach{}) + {} + //! Forces allocation of the resources for the task_arena as specified in constructor arguments void initialize() { atomic_do_once([this]{ r1::initialize(*this); }, my_initialization_state); @@ -353,6 +361,11 @@ class task_arena : public task_arena_base { } } + //! Attaches this instance to the current arena of the thread + void initialize(d1::attach) { + initialize(attach{}); + } + //! Removes the reference to the internal arena representation. //! Not thread safe wrt concurrent invocations of other methods. void terminate() { @@ -476,6 +489,7 @@ using r1::submit; inline namespace v1 { using detail::d1::task_arena; +using detail::d1::attach; #if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS using detail::d1::is_inside_task; diff --git a/src/tbb/exception.cpp b/src/tbb/exception.cpp index 92b6d4c7bb..49b4ebd76e 100644 --- a/src/tbb/exception.cpp +++ b/src/tbb/exception.cpp @@ -88,9 +88,7 @@ void throw_exception ( exception_id eid ) { case exception_id::invalid_load_factor: DO_THROW(std::out_of_range, ("Invalid hash load factor")); break; case exception_id::invalid_key: DO_THROW(std::out_of_range, ("invalid key")); break; case exception_id::bad_tagged_msg_cast: DO_THROW(std::runtime_error, ("Illegal tagged_msg cast")); break; -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE case exception_id::unsafe_wait: DO_THROW(unsafe_wait, ("Unsafe to wait further")); break; -#endif default: __TBB_ASSERT ( false, "Unknown exception ID" ); } __TBB_ASSERT(false, "Unreachable code"); diff --git a/src/tbb/global_control.cpp b/src/tbb/global_control.cpp index 29ef9ae043..4c8ea27bb5 100644 --- a/src/tbb/global_control.cpp +++ b/src/tbb/global_control.cpp @@ -114,7 +114,6 @@ class alignas(max_nfs_size) terminate_on_exception_control : public control_stor } }; -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE class alignas(max_nfs_size) lifetime_control : public control_storage { bool is_first_arg_preferred(std::size_t, std::size_t) const override { return false; // not interested @@ -146,17 +145,12 @@ class alignas(max_nfs_size) lifetime_control : public control_storage { return my_list.empty(); } }; -#endif // __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE static allowed_parallelism_control allowed_parallelism_ctl; static stack_size_control stack_size_ctl; static terminate_on_exception_control terminate_on_exception_ctl; -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE static lifetime_control lifetime_ctl; static control_storage *controls[] = {&allowed_parallelism_ctl, &stack_size_ctl, &terminate_on_exception_ctl, &lifetime_ctl}; -#else -static control_storage *controls[] = {&allowed_parallelism_ctl, &stack_size_ctl, &terminate_on_exception_ctl}; -#endif // __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE //! Comparator for a set of global_control objects inline bool control_storage_comparator::operator()(const global_control* lhs, const global_control* rhs) const { @@ -172,11 +166,9 @@ bool terminate_on_exception() { return global_control::active_value(global_control::terminate_on_exception) == 1; } -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE unsigned market::is_lifetime_control_present() { return !lifetime_ctl.is_empty(); } -#endif // __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE struct global_control_impl { private: @@ -209,20 +201,12 @@ struct global_control_impl { control_storage* const c = controls[gc.my_param]; // Concurrent reading and changing global parameter is possible. spin_mutex::scoped_lock lock(c->my_list_mutex); -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE __TBB_ASSERT(gc.my_param == global_control::scheduler_handle || !c->my_list.empty(), NULL); -#else - __TBB_ASSERT(!c->my_list.empty(), NULL); -#endif // __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE std::size_t new_active = (std::size_t)(-1), old_active = c->my_active_value; if (!erase_if_present(c, gc)) { -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE __TBB_ASSERT(gc.my_param == global_control::scheduler_handle , NULL); return; -#else - __TBB_ASSERT(false, "Unreachable code"); -#endif // __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE } if (c->my_list.empty()) { __TBB_ASSERT(new_active == (std::size_t) - 1, NULL); diff --git a/src/tbb/governor.cpp b/src/tbb/governor.cpp index ddea7a43d5..b268440474 100644 --- a/src/tbb/governor.cpp +++ b/src/tbb/governor.cpp @@ -41,11 +41,9 @@ namespace r1 { void clear_address_waiter_table(); -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE //! global_control.cpp contains definition bool remove_and_check_if_empty(d1::global_control& gc); bool is_present(d1::global_control& gc); -#endif // __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE namespace rml { tbb_server* make_private_server( tbb_client& client ); @@ -248,7 +246,6 @@ void governor::initialize_rml_factory () { UsePrivateRML = res != ::rml::factory::st_success; } -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE void __TBB_EXPORTED_FUNC get(d1::task_scheduler_handle& handle) { handle.m_ctl = new(allocate_memory(sizeof(global_control))) global_control(global_control::scheduler_handle, 1); } @@ -300,7 +297,6 @@ bool __TBB_EXPORTED_FUNC finalize(d1::task_scheduler_handle& handle, std::intptr return ok; } } -#endif // __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE #if __TBB_ARENA_BINDING diff --git a/src/tbb/market.cpp b/src/tbb/market.cpp index 267c90abfa..8d05b96bc0 100644 --- a/src/tbb/market.cpp +++ b/src/tbb/market.cpp @@ -147,12 +147,10 @@ market& market::global_market(bool is_public, unsigned workers_requested, std::s market* m = new (storage) market( workers_soft_limit, workers_hard_limit, stack_size ); if( is_public ) m->my_public_ref_count.store(1, std::memory_order_relaxed); -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE if (market::is_lifetime_control_present()) { ++m->my_public_ref_count; ++m->my_ref_count; } -#endif // __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE theMarket = m; // This check relies on the fact that for shared RML default_concurrency==max_concurrency if ( !governor::UsePrivateRML && m->my_server->default_concurrency() < workers_soft_limit ) diff --git a/src/tbb/market.h b/src/tbb/market.h index 08326bb8ba..aa510d0012 100644 --- a/src/tbb/market.h +++ b/src/tbb/market.h @@ -37,11 +37,9 @@ namespace tbb { namespace detail { -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE namespace d1 { class task_scheduler_handle; } -#endif namespace r1 { @@ -58,9 +56,7 @@ class market : no_copy, rml::tbb_client { template friend class custom_scheduler; friend class task_group_context; friend class governor; -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE friend class lifetime_control; -#endif public: //! Keys for the arena map array. The lower the value the higher priority of the arena list. @@ -68,9 +64,7 @@ class market : no_copy, rml::tbb_client { private: friend void ITT_DoUnsafeOneTimeInitialization (); -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE friend bool finalize_impl(d1::task_scheduler_handle& handle); -#endif typedef intrusive_list arena_list_type; typedef intrusive_list thread_data_list_type; @@ -269,10 +263,8 @@ class market : no_copy, rml::tbb_client { //! Reports active parallelism level according to user's settings static unsigned app_parallelism_limit(); -#if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE //! Reports if any active global lifetime references are present static unsigned is_lifetime_control_present(); -#endif //! Finds all contexts affected by the state change and propagates the new state to them. /** The propagation is relayed to the market because tasks created by one diff --git a/test/common/dummy_body.h b/test/common/dummy_body.h index 9c80476a3e..2a26f20321 100644 --- a/test/common/dummy_body.h +++ b/test/common/dummy_body.h @@ -29,7 +29,7 @@ static void doDummyWork(std::size_t N) { class DummyBody { int m_numIters; public: - explicit DummyBody( int iters ) : m_numIters( iters ) {} + explicit DummyBody( int iters = 0 ) : m_numIters( iters ) {} void operator()( int ) const { doDummyWork(m_numIters); } diff --git a/test/conformance/conformance_global_control.cpp b/test/conformance/conformance_global_control.cpp index 3e0552e096..d4ea190831 100644 --- a/test/conformance/conformance_global_control.cpp +++ b/test/conformance/conformance_global_control.cpp @@ -230,6 +230,110 @@ TEST_CASE("terminate_on_exception: nested") { delete c0; } +//! Testing setting the same value but different objects +//! \brief \ref interface \ref error_guessing +TEST_CASE("setting same value") { + const std::size_t value = 2; + + oneapi::tbb::global_control* ctl1 = new oneapi::tbb::global_control(oneapi::tbb::global_control::max_allowed_parallelism, value); + oneapi::tbb::global_control* ctl2 = new oneapi::tbb::global_control(oneapi::tbb::global_control::max_allowed_parallelism, value); + + std::size_t active = oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism); + REQUIRE(active == value); + delete ctl2; + + active = oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism); + REQUIRE_MESSAGE(active == value, "Active value should not change, because of value duplication"); + delete ctl1; +} + +//! Testing lifetime control conformance +//! \brief \ref interface \ref requirement +TEST_CASE("prolong lifetime simple") { + tbb::task_scheduler_handle hdl1{ tbb::attach{} }; + { + tbb::parallel_for(0, 10, utils::DummyBody()); + + tbb::task_scheduler_handle hdl2; + hdl2 = tbb::task_scheduler_handle{ tbb::attach{} }; + hdl2.release(); + } + bool ok = tbb::finalize(hdl1, std::nothrow); + REQUIRE(ok); +} + +//! Testing handle check for emptiness +//! \brief \ref interface \ref requirement +TEST_CASE("null handle check") { + tbb::task_scheduler_handle hndl; + REQUIRE_FALSE(hndl); +} + +//! Testing handle check for emptiness +//! \brief \ref interface \ref requirement +TEST_CASE("null handle check 2") { + tbb::task_scheduler_handle hndl{ tbb::attach{} }; + bool not_empty = (bool)hndl; + + tbb::finalize(hndl, std::nothrow); + + REQUIRE(not_empty); + REQUIRE_FALSE(hndl); +} + +//! Testing handle check for emptiness +//! \brief \ref interface \ref requirement +TEST_CASE("null handle check 3") { + tbb::task_scheduler_handle handle1{ tbb::attach{} }; + tbb::task_scheduler_handle handle2(std::move(handle1)); + + bool handle1_empty = !handle1; + bool handle2_not_empty = (bool)handle2; + + tbb::finalize(handle2, std::nothrow); + + REQUIRE(handle1_empty); + REQUIRE(handle2_not_empty); +} + +//! Testing task_scheduler_handle is created on one thread and destroyed on another. +//! \brief \ref interface \ref requirement +TEST_CASE("cross thread 1") { + // created task_scheduler_handle, parallel_for on another thread - finalize + tbb::task_scheduler_handle handle{ tbb::attach{} }; + utils::NativeParallelFor(1, [&](int) { + tbb::parallel_for(0, 10, utils::DummyBody()); + bool res = tbb::finalize(handle, std::nothrow); + REQUIRE(res); + }); +} + +//! Testing task_scheduler_handle is created on one thread and destroyed on another. +//! \brief \ref interface \ref requirement +TEST_CASE("cross thread 2") { + // created task_scheduler_handle, called parallel_for on this thread, killed the thread - and finalize on another thread + tbb::task_scheduler_handle handle; + utils::NativeParallelFor(1, [&](int) { + handle = tbb::task_scheduler_handle{ tbb::attach{} }; + tbb::parallel_for(0, 10, utils::DummyBody()); + }); + bool res = tbb::finalize(handle, std::nothrow); + REQUIRE(res); +} + +//! Testing multiple wait +//! \brief \ref interface \ref requirement +TEST_CASE("simple prolong lifetime 3") { + // Parallel region + tbb::parallel_for(0, 10, utils::DummyBody()); + // Termination + tbb::task_scheduler_handle handle = tbb::task_scheduler_handle{ tbb::attach{} }; + bool res = tbb::finalize(handle, std::nothrow); + REQUIRE(res); + // New parallel region + tbb::parallel_for(0, 10, utils::DummyBody()); +} + // The test cannot work correctly with statically linked runtime. // TODO: investigate a failure in debug with MSVC #if !_MSC_VER || (defined(_DLL) && !defined(_DEBUG)) @@ -289,20 +393,3 @@ TEST_CASE("terminate_on_exception: enabled") { CHECK(terminate_handler_called); } #endif - -//! Testing setting the same value but different objects -//! \brief \ref interface \ref error_guessing -TEST_CASE("setting same value") { - const std::size_t value = 2; - - oneapi::tbb::global_control* ctl1 = new oneapi::tbb::global_control(oneapi::tbb::global_control::max_allowed_parallelism, value); - oneapi::tbb::global_control* ctl2 = new oneapi::tbb::global_control(oneapi::tbb::global_control::max_allowed_parallelism, value); - - std::size_t active = oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism); - REQUIRE(active == value); - delete ctl2; - - active = oneapi::tbb::global_control::active_value(oneapi::tbb::global_control::max_allowed_parallelism); - REQUIRE_MESSAGE(active == value, "Active value should not change, because of value duplication"); - delete ctl1; -} diff --git a/test/tbb/test_concurrent_monitor.cpp b/test/tbb/test_concurrent_monitor.cpp index d0b2a541f0..a9233f8cce 100644 --- a/test/tbb/test_concurrent_monitor.cpp +++ b/test/tbb/test_concurrent_monitor.cpp @@ -23,8 +23,6 @@ #pragma warning( disable: 4324 ) #endif -#define TBB_PREVIEW_WAITING_FOR_WORKERS 1 - #include "common/test.h" #include "common/utils.h" #include "common/utils_concurrency_limit.h" @@ -51,7 +49,7 @@ TEST_CASE("Stress test") { std::size_t threads_number = utils::get_platform_max_threads(); // Need to prolong lifetime of the exposed concurrent_monitor - tbb::task_scheduler_handle handler = tbb::task_scheduler_handle::get(); + tbb::task_scheduler_handle handler{tbb::attach{}}; utils::SpinBarrier barrier(threads_number); diff --git a/test/tbb/test_global_control.cpp b/test/tbb/test_global_control.cpp index 386ff337a7..23bdc243cc 100644 --- a/test/tbb/test_global_control.cpp +++ b/test/tbb/test_global_control.cpp @@ -17,8 +17,6 @@ //! \file test_global_control.cpp //! \brief Test for [sched.global_control] specification -#define TBB_PREVIEW_WAITING_FOR_WORKERS 1 - #include "common/test.h" #include "common/utils.h" @@ -36,11 +34,11 @@ struct task_scheduler_handle_guard { tbb::task_scheduler_handle m_handle{}; task_scheduler_handle_guard() { - m_handle = tbb::task_scheduler_handle::get(); + m_handle = tbb::task_scheduler_handle{tbb::attach{}}; } ~task_scheduler_handle_guard() { - tbb::task_scheduler_handle::release(m_handle); + m_handle.release(); } tbb::task_scheduler_handle& get() { @@ -50,14 +48,9 @@ struct task_scheduler_handle_guard { namespace TestBlockingTerminateNS { - struct EmptyBody { - void operator()() const {} - void operator()( int ) const {} - }; - struct TestAutoInitBody { void operator()( int ) const { - tbb::parallel_for( 0, 100, EmptyBody() ); + tbb::parallel_for( 0, 100, utils::DummyBody() ); } }; @@ -71,7 +64,7 @@ namespace TestBlockingTerminateNS { void operator()( int ) const { task_scheduler_handle_guard init; if ( !myAutoInit ) { - tbb::parallel_for(0, 10, EmptyBody()); + tbb::parallel_for(0, 10, utils::DummyBody()); } utils::FastRandom<> rnd( ++gSeed ); // In case of auto init sub-tests we skip @@ -81,18 +74,18 @@ namespace TestBlockingTerminateNS { switch ( rnd.get() % numCases ) { case 0: { tbb::task_arena a; - a.enqueue( EmptyBody() ); + a.enqueue(utils::DummyBody() ); break; } case 1: { tbb::task_group tg; - EmptyBody eb; + utils::DummyBody eb; tg.run( eb ); tg.wait(); break; } case 2: - tbb::parallel_for( 0, 100, EmptyBody() ); + tbb::parallel_for( 0, 100, utils::DummyBody() ); break; case 3: /* do nothing */ @@ -158,7 +151,7 @@ namespace TestBlockingTerminateNS { void operator()() { task_scheduler_handle_guard tsi2; - tbb::parallel_for(0, 2, EmptyBody()); // auto-init + tbb::parallel_for(0, 2, utils::DummyBody()); // auto-init tbb::finalize((myIndex == 0 ? tsi1.get() : tsi2.get())); REQUIRE_MESSAGE( false, "Blocking terminate did not throw the exception" ); } @@ -210,7 +203,7 @@ void TestTerminationAndAutoinit(bool autoinit) { task_scheduler_handle_guard ctl2; if (autoinit) { - tbb::parallel_for(0, 10, TestBlockingTerminateNS::EmptyBody()); + tbb::parallel_for(0, 10, utils::DummyBody()); } bool res1 = tbb::finalize(ctl1.get(), std::nothrow); if (autoinit) { @@ -225,7 +218,7 @@ void TestTerminationAndAutoinit(bool autoinit) { //! Check no reference leak for an external thread //! \brief \ref regression \ref error_guessing TEST_CASE("test decrease reference") { - tbb::task_scheduler_handle handle = tbb::task_scheduler_handle::get(); + tbb::task_scheduler_handle handle = tbb::task_scheduler_handle{tbb::attach{}}; std::thread thr([] { tbb::parallel_for(0, 1, [](int) {}); } ); thr.join(); @@ -233,64 +226,16 @@ TEST_CASE("test decrease reference") { REQUIRE(tbb::finalize(handle, std::nothrow)); } -//! Testing lifetime control conformance -//! \brief \ref interface \ref requirement -TEST_CASE("prolong lifetime simple") { - tbb::task_scheduler_handle hdl1 = tbb::task_scheduler_handle::get(); - { - tbb::parallel_for(0, 10, TestBlockingTerminateNS::EmptyBody()); - - tbb::task_scheduler_handle hdl2 = tbb::task_scheduler_handle::get(); - tbb::task_scheduler_handle::release(hdl2); - } - bool ok = tbb::finalize(hdl1, std::nothrow); - REQUIRE(ok); -} - -//! Testing lifetime control conformance -//! \brief \ref interface \ref requirement -TEST_CASE("prolong lifetime simple 2") { +//! Testing lifetime control +//! \brief \ref error_guessing +TEST_CASE("prolong lifetime auto init") { TestTerminationAndAutoinit(false); TestTerminationAndAutoinit(true); } -//! Testing handle check for emptiness -//! \brief \ref interface \ref requirement -TEST_CASE("null handle check") { - tbb::task_scheduler_handle hndl; - REQUIRE_FALSE(hndl); -} - -//! Testing handle check for emptiness -//! \brief \ref interface \ref requirement -TEST_CASE("null handle check 2") { - tbb::task_scheduler_handle hndl = tbb::task_scheduler_handle::get(); - bool not_empty = (bool)hndl; - - tbb::finalize(hndl, std::nothrow); - - REQUIRE(not_empty); - REQUIRE_FALSE(hndl); -} - -//! Testing handle check for emptiness -//! \brief \ref interface \ref requirement -TEST_CASE("null handle check 3") { - tbb::task_scheduler_handle handle1 = tbb::task_scheduler_handle::get(); - tbb::task_scheduler_handle handle2(std::move(handle1)); - - bool handle1_empty = !handle1; - bool handle2_not_empty = (bool)handle2; - - tbb::finalize(handle2, std::nothrow); - - REQUIRE(handle1_empty); - REQUIRE(handle2_not_empty); -} - #if TBB_USE_EXCEPTIONS //! Testing lifetime control advanced -//! \brief \ref interface \ref requirement +//! \brief \ref error_guessing TEST_CASE("prolong lifetime advanced") { // Exceptions test leaves auto-initialized sheduler after, // because all blocking terminate calls are inside the parallel region, @@ -301,46 +246,8 @@ TEST_CASE("prolong lifetime advanced") { #endif //! Testing multiple wait -//! \brief \ref interface \ref requirement +//! \brief \ref error_guessing TEST_CASE("prolong lifetime multiple wait") { TestBlockingTerminateNS::TestMultpleWait(); } -//! Testing global_control is created on one thread and destroyed on another. -//! \brief \ref interface \ref requirement -TEST_CASE("cross thread 1") { - // created GC, parallel_for on another thread - finalize - tbb::task_scheduler_handle ctl = tbb::task_scheduler_handle::get(); - utils::NativeParallelFor(1, [&](int) { - tbb::parallel_for(0, 10, TestBlockingTerminateNS::EmptyBody()); - bool res = tbb::finalize(ctl, std::nothrow); - REQUIRE(res); - }); -} - -//! Testing global_control is created on one thread and destroyed on another. -//! \brief \ref interface \ref requirement -TEST_CASE("cross thread 2") { - // created GC, called parallel_for on this thread, killed the thread - and finalize on another thread - tbb::task_scheduler_handle ctl; - utils::NativeParallelFor(1, [&](int) { - ctl = tbb::task_scheduler_handle::get(); - tbb::parallel_for(0, 10, TestBlockingTerminateNS::EmptyBody()); - }); - bool res = tbb::finalize(ctl, std::nothrow); - REQUIRE(res); -} - -//! Testing multiple wait -//! \brief \ref interface \ref requirement -TEST_CASE("simple prolong lifetime 3") { - // Parallel region - tbb::parallel_for(0, 10, TestBlockingTerminateNS::EmptyBody()); - // Termination - tbb::task_scheduler_handle ctl = tbb::task_scheduler_handle::get(); - bool res = tbb::finalize(ctl, std::nothrow); - REQUIRE(res); - // New parallel region - tbb::parallel_for(0, 10, TestBlockingTerminateNS::EmptyBody()); -} - diff --git a/test/tbb/test_limiter_node.cpp b/test/tbb/test_limiter_node.cpp index 222c68fcbc..bb0ed893ef 100644 --- a/test/tbb/test_limiter_node.cpp +++ b/test/tbb/test_limiter_node.cpp @@ -18,8 +18,6 @@ #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated #endif -#define TBB_PREVIEW_WAITING_FOR_WORKERS 1 - #include "common/config.h" #include "tbb/flow_graph.h" @@ -622,6 +620,6 @@ TEST_CASE("Test correct node deallocation while using small_object_pool") { CHECK( input_node.try_put( TestLargeStruct{} ) ); graph.wait_for_all(); - tbb::task_scheduler_handle handle = tbb::task_scheduler_handle::get(); + tbb::task_scheduler_handle handle{ tbb::attach{} }; REQUIRE_NOTHROW( tbb::finalize( handle, std::nothrow ) ); } diff --git a/test/tbb/test_scheduler_mix.cpp b/test/tbb/test_scheduler_mix.cpp index 1131fbc225..30e9850cbd 100644 --- a/test/tbb/test_scheduler_mix.cpp +++ b/test/tbb/test_scheduler_mix.cpp @@ -14,7 +14,6 @@ limitations under the License. */ -#define TBB_PREVIEW_WAITING_FOR_WORKERS 1 #define TBB_PREVIEW_TASK_GROUP_EXTENSIONS 1 #include "common/config.h" diff --git a/test/tbb/test_tbb_fork.cpp b/test/tbb/test_tbb_fork.cpp index 14df84ee4f..b0f09098a3 100644 --- a/test/tbb/test_tbb_fork.cpp +++ b/test/tbb/test_tbb_fork.cpp @@ -17,8 +17,6 @@ //! \file test_tbb_fork.cpp //! \brief Test for [sched.global_control] specification -#define TBB_PREVIEW_WAITING_FOR_WORKERS 1 - #include "tbb/global_control.h" #include "tbb/blocked_range.h" #include "tbb/cache_aligned_allocator.h" @@ -113,13 +111,13 @@ class RunWorkersBody : utils::NoAssign { public: RunWorkersBody(bool waitWorkers) : wait_workers(waitWorkers) {} void operator()(const int /*threadID*/) const { - tbb::task_scheduler_handle tsi = tbb::task_scheduler_handle::get(); + tbb::task_scheduler_handle tsi{tbb::attach{}}; CallParallelFor(); if (wait_workers) { bool ok = tbb::finalize(tsi, std::nothrow); ASSERT(ok, NULL); } else { - tbb::task_scheduler_handle::release(tsi); + tsi.release(); } } }; @@ -138,20 +136,20 @@ class RunInNativeThread : utils::NoAssign { public: RunInNativeThread(bool blocking_) : blocking(blocking_) {} void operator()(const int /*threadID*/) const { - tbb::task_scheduler_handle tsi = tbb::task_scheduler_handle::get(); + tbb::task_scheduler_handle tsi = tbb::task_scheduler_handle{tbb::attach{}}; CallParallelFor(); if (blocking) { bool ok = tbb::finalize(tsi, std::nothrow); ASSERT(!ok, "Nested blocking terminate must fail."); } else { - tbb::task_scheduler_handle::release(tsi); + tsi.release(); } } }; void TestTasksInThread() { - tbb::task_scheduler_handle sch = tbb::task_scheduler_handle::get(); + tbb::task_scheduler_handle sch{tbb::attach{}}; CallParallelFor(); utils::NativeParallelFor(2, RunInNativeThread(/*blocking=*/false)); bool ok = tbb::finalize(sch, std::nothrow); @@ -205,12 +203,13 @@ void TestNestingTSI() { // nesting with and without blocking is possible for (int i=0; i<2; i++) { - tbb::task_scheduler_handle schBlock = tbb::task_scheduler_handle::get(); + tbb::task_scheduler_handle schBlock = tbb::task_scheduler_handle{tbb::attach{}}; CallParallelFor(); - tbb::task_scheduler_handle schBlock1 = tbb::task_scheduler_handle::get(); + tbb::task_scheduler_handle schBlock1; + schBlock1 = tbb::task_scheduler_handle{tbb::attach{}}; CallParallelFor(); if (i) { - tbb::task_scheduler_handle::release(schBlock1); + schBlock1.release(); } else { bool ok = tbb::finalize(schBlock1, std::nothrow); ASSERT(!ok, "Nested blocking terminate must fail."); @@ -219,7 +218,7 @@ void TestNestingTSI() ASSERT(ok, NULL); } { - tbb::task_scheduler_handle schBlock = tbb::task_scheduler_handle::get(); + tbb::task_scheduler_handle schBlock{tbb::attach{}}; utils::NativeParallelFor(1, RunInNativeThread(/*blocking=*/true)); bool ok = tbb::finalize(schBlock, std::nothrow); ASSERT(ok, NULL); @@ -269,11 +268,11 @@ int main() for (int i=0; i<20; i++) { tbb::global_control ctl(tbb::global_control::max_allowed_parallelism, threads); { - tbb::task_scheduler_handle sch = tbb::task_scheduler_handle::get(); + tbb::task_scheduler_handle sch{tbb::attach{}}; bool ok = tbb::finalize( sch, std::nothrow ); ASSERT(ok, NULL); } - tbb::task_scheduler_handle sch = tbb::task_scheduler_handle::get(); + tbb::task_scheduler_handle sch{tbb::attach{}}; CallParallelFor(); bool ok = tbb::finalize( sch, std::nothrow ); ASSERT(ok, NULL);