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