xref: /oneTBB/src/tbb/global_control.cpp (revision 7eb68ae9)
151c0b2f7Stbbdev /*
2c4568449SPavel Kumbrasev     Copyright (c) 2005-2023 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1749e08aacStbbdev #include "oneapi/tbb/detail/_config.h"
1849e08aacStbbdev #include "oneapi/tbb/detail/_template_helpers.h"
1951c0b2f7Stbbdev 
2049e08aacStbbdev #include "oneapi/tbb/global_control.h"
2149e08aacStbbdev #include "oneapi/tbb/tbb_allocator.h"
2249e08aacStbbdev #include "oneapi/tbb/spin_mutex.h"
2351c0b2f7Stbbdev 
2451c0b2f7Stbbdev #include "governor.h"
25c4568449SPavel Kumbrasev #include "threading_control.h"
2651c0b2f7Stbbdev #include "market.h"
2751c0b2f7Stbbdev #include "misc.h"
2851c0b2f7Stbbdev 
2951c0b2f7Stbbdev #include <atomic>
3051c0b2f7Stbbdev #include <set>
3151c0b2f7Stbbdev 
3251c0b2f7Stbbdev namespace tbb {
3351c0b2f7Stbbdev namespace detail {
3451c0b2f7Stbbdev namespace r1 {
3551c0b2f7Stbbdev 
3651c0b2f7Stbbdev //! Comparator for a set of global_control objects
3751c0b2f7Stbbdev struct control_storage_comparator {
38c4568449SPavel Kumbrasev     bool operator()(const d1::global_control* lhs, const d1::global_control* rhs) const;
3951c0b2f7Stbbdev };
4051c0b2f7Stbbdev 
4151c0b2f7Stbbdev class control_storage {
4251c0b2f7Stbbdev     friend struct global_control_impl;
4351c0b2f7Stbbdev     friend std::size_t global_control_active_value(int);
44c4568449SPavel Kumbrasev     friend void global_control_lock();
45c4568449SPavel Kumbrasev     friend void global_control_unlock();
46c4568449SPavel Kumbrasev     friend std::size_t global_control_active_value_unsafe(d1::global_control::parameter);
4751c0b2f7Stbbdev protected:
4851c0b2f7Stbbdev     std::size_t my_active_value{0};
49c4568449SPavel Kumbrasev     std::set<d1::global_control*, control_storage_comparator, tbb_allocator<d1::global_control*>> my_list{};
5051c0b2f7Stbbdev     spin_mutex my_list_mutex{};
5151c0b2f7Stbbdev public:
52*7eb68ae9SElias Engelbert Plank     virtual ~control_storage() = default;
5351c0b2f7Stbbdev     virtual std::size_t default_value() const = 0;
apply_active(std::size_t new_active)5451c0b2f7Stbbdev     virtual void apply_active(std::size_t new_active) {
5551c0b2f7Stbbdev         my_active_value = new_active;
5651c0b2f7Stbbdev     }
is_first_arg_preferred(std::size_t a,std::size_t b) const5751c0b2f7Stbbdev     virtual bool is_first_arg_preferred(std::size_t a, std::size_t b) const {
5851c0b2f7Stbbdev         return a>b; // prefer max by default
5951c0b2f7Stbbdev     }
active_value()6051c0b2f7Stbbdev     virtual std::size_t active_value() {
6151c0b2f7Stbbdev         spin_mutex::scoped_lock lock(my_list_mutex); // protect my_list.empty() call
6251c0b2f7Stbbdev         return !my_list.empty() ? my_active_value : default_value();
6351c0b2f7Stbbdev     }
64c4568449SPavel Kumbrasev 
active_value_unsafe()65c4568449SPavel Kumbrasev     std::size_t active_value_unsafe() {
66c4568449SPavel Kumbrasev         return !my_list.empty() ? my_active_value : default_value();
67c4568449SPavel Kumbrasev     }
6851c0b2f7Stbbdev };
6951c0b2f7Stbbdev 
7051c0b2f7Stbbdev class alignas(max_nfs_size) allowed_parallelism_control : public control_storage {
default_value() const71ba947f18SIlya Isaev     std::size_t default_value() const override {
7251c0b2f7Stbbdev         return max(1U, governor::default_num_threads());
7351c0b2f7Stbbdev     }
is_first_arg_preferred(std::size_t a,std::size_t b) const74ba947f18SIlya Isaev     bool is_first_arg_preferred(std::size_t a, std::size_t b) const override {
7551c0b2f7Stbbdev         return a<b; // prefer min allowed parallelism
7651c0b2f7Stbbdev     }
apply_active(std::size_t new_active)77ba947f18SIlya Isaev     void apply_active(std::size_t new_active) override {
7851c0b2f7Stbbdev         control_storage::apply_active(new_active);
7957f524caSIlya Isaev         __TBB_ASSERT(my_active_value >= 1, nullptr);
80b15aabb3Stbbdev         // -1 to take external thread into account
81c4568449SPavel Kumbrasev         threading_control::set_active_num_workers(my_active_value - 1);
8251c0b2f7Stbbdev     }
active_value()83ba947f18SIlya Isaev     std::size_t active_value() override {
8451c0b2f7Stbbdev         spin_mutex::scoped_lock lock(my_list_mutex); // protect my_list.empty() call
85c4568449SPavel Kumbrasev         if (my_list.empty()) {
8651c0b2f7Stbbdev             return default_value();
87c4568449SPavel Kumbrasev         }
88c4568449SPavel Kumbrasev 
8951c0b2f7Stbbdev         // non-zero, if market is active
90c4568449SPavel Kumbrasev         const std::size_t workers = threading_control::max_num_workers();
9151c0b2f7Stbbdev         // We can't exceed market's maximal number of workers.
92b15aabb3Stbbdev         // +1 to take external thread into account
9351c0b2f7Stbbdev         return workers ? min(workers + 1, my_active_value) : my_active_value;
9451c0b2f7Stbbdev     }
9551c0b2f7Stbbdev };
9651c0b2f7Stbbdev 
9751c0b2f7Stbbdev class alignas(max_nfs_size) stack_size_control : public control_storage {
default_value() const98ba947f18SIlya Isaev     std::size_t default_value() const override {
997631793aSAlex #if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
1007631793aSAlex         static auto ThreadStackSizeDefault = [] {
1017631793aSAlex             ULONG_PTR hi, lo;
1027631793aSAlex             GetCurrentThreadStackLimits(&lo, &hi);
1037631793aSAlex             return hi - lo;
1047631793aSAlex         }();
1057631793aSAlex         return ThreadStackSizeDefault;
1067631793aSAlex #else
10751c0b2f7Stbbdev         return ThreadStackSize;
1087631793aSAlex #endif
10951c0b2f7Stbbdev     }
apply_active(std::size_t new_active)110ba947f18SIlya Isaev     void apply_active(std::size_t new_active) override {
11151c0b2f7Stbbdev         control_storage::apply_active(new_active);
11251c0b2f7Stbbdev #if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)
11351c0b2f7Stbbdev         __TBB_ASSERT( false, "For Windows 8 Store* apps we must not set stack size" );
11451c0b2f7Stbbdev #endif
11551c0b2f7Stbbdev     }
11651c0b2f7Stbbdev };
11751c0b2f7Stbbdev 
11851c0b2f7Stbbdev class alignas(max_nfs_size) terminate_on_exception_control : public control_storage {
default_value() const119ba947f18SIlya Isaev     std::size_t default_value() const override {
12051c0b2f7Stbbdev         return 0;
12151c0b2f7Stbbdev     }
12251c0b2f7Stbbdev };
12351c0b2f7Stbbdev 
12451c0b2f7Stbbdev class alignas(max_nfs_size) lifetime_control : public control_storage {
is_first_arg_preferred(std::size_t,std::size_t) const125ba947f18SIlya Isaev     bool is_first_arg_preferred(std::size_t, std::size_t) const override {
12651c0b2f7Stbbdev         return false; // not interested
12751c0b2f7Stbbdev     }
default_value() const128ba947f18SIlya Isaev     std::size_t default_value() const override {
12951c0b2f7Stbbdev         return 0;
13051c0b2f7Stbbdev     }
apply_active(std::size_t new_active)131ba947f18SIlya Isaev     void apply_active(std::size_t new_active) override {
13251c0b2f7Stbbdev         if (new_active == 1) {
13351c0b2f7Stbbdev             // reserve the market reference
134c4568449SPavel Kumbrasev             threading_control::register_lifetime_control();
13551c0b2f7Stbbdev         } else if (new_active == 0) { // new_active == 0
136c4568449SPavel Kumbrasev             threading_control::unregister_lifetime_control(/*blocking_terminate*/ false);
13751c0b2f7Stbbdev         }
13851c0b2f7Stbbdev         control_storage::apply_active(new_active);
13951c0b2f7Stbbdev     }
14051c0b2f7Stbbdev };
14151c0b2f7Stbbdev 
14251c0b2f7Stbbdev static allowed_parallelism_control allowed_parallelism_ctl;
14351c0b2f7Stbbdev static stack_size_control stack_size_ctl;
14451c0b2f7Stbbdev static terminate_on_exception_control terminate_on_exception_ctl;
14551c0b2f7Stbbdev static lifetime_control lifetime_ctl;
14651c0b2f7Stbbdev static control_storage *controls[] = {&allowed_parallelism_ctl, &stack_size_ctl, &terminate_on_exception_ctl, &lifetime_ctl};
14751c0b2f7Stbbdev 
global_control_lock()148c4568449SPavel Kumbrasev void global_control_lock() {
149c4568449SPavel Kumbrasev     for (auto& ctl : controls) {
150c4568449SPavel Kumbrasev         ctl->my_list_mutex.lock();
151c4568449SPavel Kumbrasev     }
152c4568449SPavel Kumbrasev }
153c4568449SPavel Kumbrasev 
global_control_unlock()154c4568449SPavel Kumbrasev void global_control_unlock() {
155c4568449SPavel Kumbrasev     int N = std::distance(std::begin(controls), std::end(controls));
156c4568449SPavel Kumbrasev     for (int i = N - 1; i >= 0; --i) {
157c4568449SPavel Kumbrasev         controls[i]->my_list_mutex.unlock();
158c4568449SPavel Kumbrasev     }
159c4568449SPavel Kumbrasev }
160c4568449SPavel Kumbrasev 
global_control_active_value_unsafe(d1::global_control::parameter param)161c4568449SPavel Kumbrasev std::size_t global_control_active_value_unsafe(d1::global_control::parameter param) {
162c4568449SPavel Kumbrasev     __TBB_ASSERT_RELEASE(param < d1::global_control::parameter_max, nullptr);
163c4568449SPavel Kumbrasev     return controls[param]->active_value_unsafe();
164c4568449SPavel Kumbrasev }
165c4568449SPavel Kumbrasev 
16651c0b2f7Stbbdev //! Comparator for a set of global_control objects
operator ()(const d1::global_control * lhs,const d1::global_control * rhs) const167c4568449SPavel Kumbrasev inline bool control_storage_comparator::operator()(const d1::global_control* lhs, const d1::global_control* rhs) const {
168c4568449SPavel Kumbrasev     __TBB_ASSERT_RELEASE(lhs->my_param < d1::global_control::parameter_max , nullptr);
16951c0b2f7Stbbdev     return lhs->my_value < rhs->my_value || (lhs->my_value == rhs->my_value && lhs < rhs);
17051c0b2f7Stbbdev }
17151c0b2f7Stbbdev 
terminate_on_exception()17251c0b2f7Stbbdev bool terminate_on_exception() {
173c4568449SPavel Kumbrasev     return d1::global_control::active_value(d1::global_control::terminate_on_exception) == 1;
17451c0b2f7Stbbdev }
17551c0b2f7Stbbdev 
17651c0b2f7Stbbdev struct global_control_impl {
17751c0b2f7Stbbdev private:
erase_if_presenttbb::detail::r1::global_control_impl17851c0b2f7Stbbdev     static bool erase_if_present(control_storage* const c, d1::global_control& gc) {
17951c0b2f7Stbbdev         auto it = c->my_list.find(&gc);
18051c0b2f7Stbbdev         if (it != c->my_list.end()) {
18151c0b2f7Stbbdev             c->my_list.erase(it);
18251c0b2f7Stbbdev             return true;
18351c0b2f7Stbbdev         }
18451c0b2f7Stbbdev         return false;
18551c0b2f7Stbbdev     }
18651c0b2f7Stbbdev 
18751c0b2f7Stbbdev public:
18851c0b2f7Stbbdev 
createtbb::detail::r1::global_control_impl18951c0b2f7Stbbdev     static void create(d1::global_control& gc) {
190c4568449SPavel Kumbrasev         __TBB_ASSERT_RELEASE(gc.my_param < d1::global_control::parameter_max, nullptr);
19151c0b2f7Stbbdev         control_storage* const c = controls[gc.my_param];
19251c0b2f7Stbbdev 
19351c0b2f7Stbbdev         spin_mutex::scoped_lock lock(c->my_list_mutex);
19451c0b2f7Stbbdev         if (c->my_list.empty() || c->is_first_arg_preferred(gc.my_value, c->my_active_value)) {
19551c0b2f7Stbbdev             // to guarantee that apply_active() is called with current active value,
19651c0b2f7Stbbdev             // calls it here and in internal_destroy() under my_list_mutex
19751c0b2f7Stbbdev             c->apply_active(gc.my_value);
19851c0b2f7Stbbdev         }
19951c0b2f7Stbbdev         c->my_list.insert(&gc);
20051c0b2f7Stbbdev     }
20151c0b2f7Stbbdev 
destroytbb::detail::r1::global_control_impl20251c0b2f7Stbbdev     static void destroy(d1::global_control& gc) {
203c4568449SPavel Kumbrasev         __TBB_ASSERT_RELEASE(gc.my_param < d1::global_control::parameter_max, nullptr);
20451c0b2f7Stbbdev         control_storage* const c = controls[gc.my_param];
205a080baf9SAlex         // Concurrent reading and changing global parameter is possible.
206a080baf9SAlex         spin_mutex::scoped_lock lock(c->my_list_mutex);
207c4568449SPavel Kumbrasev         __TBB_ASSERT(gc.my_param == d1::global_control::scheduler_handle || !c->my_list.empty(), nullptr);
20851c0b2f7Stbbdev         std::size_t new_active = (std::size_t)(-1), old_active = c->my_active_value;
20951c0b2f7Stbbdev 
21051c0b2f7Stbbdev         if (!erase_if_present(c, gc)) {
211c4568449SPavel Kumbrasev             __TBB_ASSERT(gc.my_param == d1::global_control::scheduler_handle , nullptr);
21251c0b2f7Stbbdev             return;
21351c0b2f7Stbbdev         }
21451c0b2f7Stbbdev         if (c->my_list.empty()) {
21557f524caSIlya Isaev             __TBB_ASSERT(new_active == (std::size_t) - 1, nullptr);
21651c0b2f7Stbbdev             new_active = c->default_value();
21751c0b2f7Stbbdev         } else {
21851c0b2f7Stbbdev             new_active = (*c->my_list.begin())->my_value;
21951c0b2f7Stbbdev         }
22051c0b2f7Stbbdev         if (new_active != old_active) {
22151c0b2f7Stbbdev             c->apply_active(new_active);
22251c0b2f7Stbbdev         }
22351c0b2f7Stbbdev     }
22451c0b2f7Stbbdev 
remove_and_check_if_emptytbb::detail::r1::global_control_impl22551c0b2f7Stbbdev     static bool remove_and_check_if_empty(d1::global_control& gc) {
226c4568449SPavel Kumbrasev         __TBB_ASSERT_RELEASE(gc.my_param < d1::global_control::parameter_max, nullptr);
22751c0b2f7Stbbdev         control_storage* const c = controls[gc.my_param];
22851c0b2f7Stbbdev 
22951c0b2f7Stbbdev         spin_mutex::scoped_lock lock(c->my_list_mutex);
23057f524caSIlya Isaev         __TBB_ASSERT(!c->my_list.empty(), nullptr);
23151c0b2f7Stbbdev         erase_if_present(c, gc);
23251c0b2f7Stbbdev         return c->my_list.empty();
23351c0b2f7Stbbdev     }
23451c0b2f7Stbbdev #if TBB_USE_ASSERT
is_presenttbb::detail::r1::global_control_impl23551c0b2f7Stbbdev     static bool is_present(d1::global_control& gc) {
236c4568449SPavel Kumbrasev         __TBB_ASSERT_RELEASE(gc.my_param < d1::global_control::parameter_max, nullptr);
23751c0b2f7Stbbdev         control_storage* const c = controls[gc.my_param];
23851c0b2f7Stbbdev 
23951c0b2f7Stbbdev         spin_mutex::scoped_lock lock(c->my_list_mutex);
24051c0b2f7Stbbdev         auto it = c->my_list.find(&gc);
24151c0b2f7Stbbdev         if (it != c->my_list.end()) {
24251c0b2f7Stbbdev             return true;
24351c0b2f7Stbbdev         }
24451c0b2f7Stbbdev         return false;
24551c0b2f7Stbbdev     }
24651c0b2f7Stbbdev #endif // TBB_USE_ASSERT
24751c0b2f7Stbbdev };
24851c0b2f7Stbbdev 
create(d1::global_control & gc)24951c0b2f7Stbbdev void __TBB_EXPORTED_FUNC create(d1::global_control& gc) {
25051c0b2f7Stbbdev     global_control_impl::create(gc);
25151c0b2f7Stbbdev }
destroy(d1::global_control & gc)25251c0b2f7Stbbdev void __TBB_EXPORTED_FUNC destroy(d1::global_control& gc) {
25351c0b2f7Stbbdev     global_control_impl::destroy(gc);
25451c0b2f7Stbbdev }
25551c0b2f7Stbbdev 
remove_and_check_if_empty(d1::global_control & gc)25651c0b2f7Stbbdev bool remove_and_check_if_empty(d1::global_control& gc) {
25751c0b2f7Stbbdev     return global_control_impl::remove_and_check_if_empty(gc);
25851c0b2f7Stbbdev }
25951c0b2f7Stbbdev #if TBB_USE_ASSERT
is_present(d1::global_control & gc)26051c0b2f7Stbbdev bool is_present(d1::global_control& gc) {
26151c0b2f7Stbbdev     return global_control_impl::is_present(gc);
26251c0b2f7Stbbdev }
26351c0b2f7Stbbdev #endif // TBB_USE_ASSERT
global_control_active_value(int param)26451c0b2f7Stbbdev std::size_t __TBB_EXPORTED_FUNC global_control_active_value(int param) {
265c4568449SPavel Kumbrasev     __TBB_ASSERT_RELEASE(param < d1::global_control::parameter_max, nullptr);
26651c0b2f7Stbbdev     return controls[param]->active_value();
26751c0b2f7Stbbdev }
26851c0b2f7Stbbdev 
26951c0b2f7Stbbdev } // namespace r1
27051c0b2f7Stbbdev } // namespace detail
27151c0b2f7Stbbdev } // namespace tbb
272