xref: /oneTBB/src/tbb/threading_control.cpp (revision 71e1bb8e)
1c4568449SPavel Kumbrasev /*
2c4568449SPavel Kumbrasev     Copyright (c) 2022-2023 Intel Corporation
3c4568449SPavel Kumbrasev 
4c4568449SPavel Kumbrasev     Licensed under the Apache License, Version 2.0 (the "License");
5c4568449SPavel Kumbrasev     you may not use this file except in compliance with the License.
6c4568449SPavel Kumbrasev     You may obtain a copy of the License at
7c4568449SPavel Kumbrasev 
8c4568449SPavel Kumbrasev         http://www.apache.org/licenses/LICENSE-2.0
9c4568449SPavel Kumbrasev 
10c4568449SPavel Kumbrasev     Unless required by applicable law or agreed to in writing, software
11c4568449SPavel Kumbrasev     distributed under the License is distributed on an "AS IS" BASIS,
12c4568449SPavel Kumbrasev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c4568449SPavel Kumbrasev     See the License for the specific language governing permissions and
14c4568449SPavel Kumbrasev     limitations under the License.
15c4568449SPavel Kumbrasev */
16c4568449SPavel Kumbrasev 
17c4568449SPavel Kumbrasev #include "threading_control.h"
18c4568449SPavel Kumbrasev #include "permit_manager.h"
19c4568449SPavel Kumbrasev #include "market.h"
20*71e1bb8eSPavel Kumbrasev #include "tcm_adaptor.h"
21c4568449SPavel Kumbrasev #include "thread_dispatcher.h"
22c4568449SPavel Kumbrasev #include "governor.h"
23c4568449SPavel Kumbrasev #include "thread_dispatcher_client.h"
24c4568449SPavel Kumbrasev 
25c4568449SPavel Kumbrasev namespace tbb {
26c4568449SPavel Kumbrasev namespace detail {
27c4568449SPavel Kumbrasev namespace r1 {
28c4568449SPavel Kumbrasev 
29c4568449SPavel Kumbrasev // ---------------------------------------- threading_control_impl --------------------------------------------------------------
30c4568449SPavel Kumbrasev 
31c4568449SPavel Kumbrasev std::size_t global_control_active_value_unsafe(d1::global_control::parameter);
32c4568449SPavel Kumbrasev 
calculate_workers_limits()33c4568449SPavel Kumbrasev std::pair<unsigned, unsigned> threading_control_impl::calculate_workers_limits() {
34c4568449SPavel Kumbrasev     // Expecting that 4P is suitable for most applications.
35c4568449SPavel Kumbrasev     // Limit to 2P for large thread number.
36c4568449SPavel Kumbrasev     // TODO: ask RML for max concurrency and possibly correct hard_limit
37c4568449SPavel Kumbrasev     unsigned factor = governor::default_num_threads() <= 128 ? 4 : 2;
38c4568449SPavel Kumbrasev 
39c4568449SPavel Kumbrasev     // The requested number of threads is intentionally not considered in
40c4568449SPavel Kumbrasev     // computation of the hard limit, in order to separate responsibilities
41c4568449SPavel Kumbrasev     // and avoid complicated interactions between global_control and task_scheduler_init.
42c4568449SPavel Kumbrasev     // The threading control guarantees that at least 256 threads might be created.
43c4568449SPavel Kumbrasev     unsigned workers_app_limit = global_control_active_value_unsafe(global_control::max_allowed_parallelism);
44c4568449SPavel Kumbrasev     unsigned workers_hard_limit = max(max(factor * governor::default_num_threads(), 256u), workers_app_limit);
45c4568449SPavel Kumbrasev     unsigned workers_soft_limit = calc_workers_soft_limit(workers_hard_limit);
46c4568449SPavel Kumbrasev 
47c4568449SPavel Kumbrasev     return std::make_pair(workers_soft_limit, workers_hard_limit);
48c4568449SPavel Kumbrasev }
49c4568449SPavel Kumbrasev 
calc_workers_soft_limit(unsigned workers_hard_limit)50c4568449SPavel Kumbrasev unsigned threading_control_impl::calc_workers_soft_limit(unsigned workers_hard_limit) {
51c4568449SPavel Kumbrasev     unsigned workers_soft_limit{};
52c4568449SPavel Kumbrasev     unsigned soft_limit = global_control_active_value_unsafe(global_control::max_allowed_parallelism);
53c4568449SPavel Kumbrasev 
54c4568449SPavel Kumbrasev     // if user set no limits (yet), use default value
55c4568449SPavel Kumbrasev     workers_soft_limit = soft_limit != 0 ? soft_limit - 1 : governor::default_num_threads() - 1;
56c4568449SPavel Kumbrasev 
57c4568449SPavel Kumbrasev     if (workers_soft_limit >= workers_hard_limit) {
58c4568449SPavel Kumbrasev         workers_soft_limit = workers_hard_limit - 1;
59c4568449SPavel Kumbrasev     }
60c4568449SPavel Kumbrasev 
61c4568449SPavel Kumbrasev     return workers_soft_limit;
62c4568449SPavel Kumbrasev }
63c4568449SPavel Kumbrasev 
make_permit_manager(unsigned workers_soft_limit)64c4568449SPavel Kumbrasev cache_aligned_unique_ptr<permit_manager> threading_control_impl::make_permit_manager(unsigned workers_soft_limit) {
65*71e1bb8eSPavel Kumbrasev      if (tcm_adaptor::is_initialized()) {
66*71e1bb8eSPavel Kumbrasev         auto tcm = make_cache_aligned_unique<tcm_adaptor>();
67*71e1bb8eSPavel Kumbrasev         if (tcm->is_connected()) {
68*71e1bb8eSPavel Kumbrasev             return tcm;
69*71e1bb8eSPavel Kumbrasev         }
70*71e1bb8eSPavel Kumbrasev     }
71c4568449SPavel Kumbrasev     return make_cache_aligned_unique<market>(workers_soft_limit);
72c4568449SPavel Kumbrasev }
73c4568449SPavel Kumbrasev 
make_thread_dispatcher(threading_control & tc,unsigned workers_soft_limit,unsigned workers_hard_limit)74c4568449SPavel Kumbrasev cache_aligned_unique_ptr<thread_dispatcher> threading_control_impl::make_thread_dispatcher(threading_control& tc,
75c4568449SPavel Kumbrasev                                                                                            unsigned workers_soft_limit,
76c4568449SPavel Kumbrasev                                                                                            unsigned workers_hard_limit)
77c4568449SPavel Kumbrasev {
78c4568449SPavel Kumbrasev     stack_size_type stack_size = global_control_active_value_unsafe(global_control::thread_stack_size);
79c4568449SPavel Kumbrasev 
80c4568449SPavel Kumbrasev     cache_aligned_unique_ptr<thread_dispatcher> td =
81c4568449SPavel Kumbrasev         make_cache_aligned_unique<thread_dispatcher>(tc, workers_hard_limit, stack_size);
82c4568449SPavel Kumbrasev     // This check relies on the fact that for shared RML default_concurrency == max_concurrency
83c4568449SPavel Kumbrasev     if (!governor::UsePrivateRML && td->my_server->default_concurrency() < workers_soft_limit) {
84c4568449SPavel Kumbrasev         runtime_warning("RML might limit the number of workers to %u while %u is requested.\n",
85c4568449SPavel Kumbrasev             td->my_server->default_concurrency(), workers_soft_limit);
86c4568449SPavel Kumbrasev     }
87c4568449SPavel Kumbrasev 
88c4568449SPavel Kumbrasev     return td;
89c4568449SPavel Kumbrasev }
90c4568449SPavel Kumbrasev 
threading_control_impl(threading_control * tc)91c4568449SPavel Kumbrasev threading_control_impl::threading_control_impl(threading_control* tc) {
92c4568449SPavel Kumbrasev     unsigned workers_soft_limit{}, workers_hard_limit{};
93c4568449SPavel Kumbrasev     std::tie(workers_soft_limit, workers_hard_limit) = calculate_workers_limits();
94c4568449SPavel Kumbrasev 
95c4568449SPavel Kumbrasev     my_permit_manager = make_permit_manager(workers_soft_limit);
96c4568449SPavel Kumbrasev     my_thread_dispatcher = make_thread_dispatcher(*tc, workers_soft_limit, workers_hard_limit);
97c4568449SPavel Kumbrasev     my_thread_request_serializer =
98c4568449SPavel Kumbrasev         make_cache_aligned_unique<thread_request_serializer_proxy>(*my_thread_dispatcher, workers_soft_limit);
99c4568449SPavel Kumbrasev     my_permit_manager->set_thread_request_observer(*my_thread_request_serializer);
100c4568449SPavel Kumbrasev 
101c4568449SPavel Kumbrasev     my_cancellation_disseminator = make_cache_aligned_unique<cancellation_disseminator>();
102c4568449SPavel Kumbrasev     my_waiting_threads_monitor = make_cache_aligned_unique<thread_control_monitor>();
103c4568449SPavel Kumbrasev }
104c4568449SPavel Kumbrasev 
release(bool blocking_terminate)105c4568449SPavel Kumbrasev void threading_control_impl::release(bool blocking_terminate) {
106c4568449SPavel Kumbrasev     my_thread_dispatcher->release(blocking_terminate);
107c4568449SPavel Kumbrasev }
108c4568449SPavel Kumbrasev 
set_active_num_workers(unsigned soft_limit)109c4568449SPavel Kumbrasev void threading_control_impl::set_active_num_workers(unsigned soft_limit) {
110c4568449SPavel Kumbrasev     __TBB_ASSERT(soft_limit <= my_thread_dispatcher->my_num_workers_hard_limit, nullptr);
111c4568449SPavel Kumbrasev     my_thread_request_serializer->set_active_num_workers(soft_limit);
112c4568449SPavel Kumbrasev     my_permit_manager->set_active_num_workers(soft_limit);
113c4568449SPavel Kumbrasev }
114c4568449SPavel Kumbrasev 
create_client(arena & a)115c4568449SPavel Kumbrasev threading_control_client threading_control_impl::create_client(arena& a) {
116c4568449SPavel Kumbrasev     pm_client* pm_client = my_permit_manager->create_client(a);
117c4568449SPavel Kumbrasev     thread_dispatcher_client* td_client = my_thread_dispatcher->create_client(a);
118c4568449SPavel Kumbrasev 
119c4568449SPavel Kumbrasev     return threading_control_client{pm_client, td_client};
120c4568449SPavel Kumbrasev }
121c4568449SPavel Kumbrasev 
prepare_client_destruction(threading_control_client client)122c4568449SPavel Kumbrasev threading_control_impl::client_snapshot threading_control_impl::prepare_client_destruction(threading_control_client client) {
123c4568449SPavel Kumbrasev     auto td_client = client.get_thread_dispatcher_client();
124c4568449SPavel Kumbrasev     return {td_client->get_aba_epoch(), td_client->priority_level(), td_client, client.get_pm_client()};
125c4568449SPavel Kumbrasev }
126c4568449SPavel Kumbrasev 
try_destroy_client(threading_control_impl::client_snapshot snapshot)127c4568449SPavel Kumbrasev bool threading_control_impl::try_destroy_client(threading_control_impl::client_snapshot snapshot) {
128c4568449SPavel Kumbrasev     if (my_thread_dispatcher->try_unregister_client(snapshot.my_td_client, snapshot.aba_epoch, snapshot.priority_level)) {
129c4568449SPavel Kumbrasev         my_permit_manager->unregister_and_destroy_client(*snapshot.my_pm_client);
130c4568449SPavel Kumbrasev         return true;
131c4568449SPavel Kumbrasev     }
132c4568449SPavel Kumbrasev     return false;
133c4568449SPavel Kumbrasev }
134c4568449SPavel Kumbrasev 
publish_client(threading_control_client tc_client,d1::constraints & constraints)135*71e1bb8eSPavel Kumbrasev void threading_control_impl::publish_client(threading_control_client tc_client, d1::constraints& constraints) {
136*71e1bb8eSPavel Kumbrasev     my_permit_manager->register_client(tc_client.get_pm_client(), constraints);
137c4568449SPavel Kumbrasev     my_thread_dispatcher->register_client(tc_client.get_thread_dispatcher_client());
138c4568449SPavel Kumbrasev }
139c4568449SPavel Kumbrasev 
register_thread(thread_data & td)140c4568449SPavel Kumbrasev void threading_control_impl::register_thread(thread_data& td) {
141c4568449SPavel Kumbrasev     my_cancellation_disseminator->register_thread(td);
142c4568449SPavel Kumbrasev }
unregister_thread(thread_data & td)143c4568449SPavel Kumbrasev void threading_control_impl::unregister_thread(thread_data& td) {
144c4568449SPavel Kumbrasev     my_cancellation_disseminator->unregister_thread(td);
145c4568449SPavel Kumbrasev }
146c4568449SPavel Kumbrasev 
propagate_task_group_state(std::atomic<uint32_t> d1::task_group_context::* mptr_state,d1::task_group_context & src,uint32_t new_state)147c4568449SPavel Kumbrasev void threading_control_impl::propagate_task_group_state(std::atomic<uint32_t> d1::task_group_context::*mptr_state,
148c4568449SPavel Kumbrasev                                                         d1::task_group_context& src, uint32_t new_state)
149c4568449SPavel Kumbrasev {
150c4568449SPavel Kumbrasev     my_cancellation_disseminator->propagate_task_group_state(mptr_state, src, new_state);
151c4568449SPavel Kumbrasev }
152c4568449SPavel Kumbrasev 
worker_stack_size()153c4568449SPavel Kumbrasev std::size_t threading_control_impl::worker_stack_size() {
154c4568449SPavel Kumbrasev     return my_thread_dispatcher->worker_stack_size();
155c4568449SPavel Kumbrasev }
156c4568449SPavel Kumbrasev 
max_num_workers()157c4568449SPavel Kumbrasev unsigned threading_control_impl::max_num_workers() {
158c4568449SPavel Kumbrasev     return my_thread_dispatcher->my_num_workers_hard_limit;
159c4568449SPavel Kumbrasev }
160c4568449SPavel Kumbrasev 
adjust_demand(threading_control_client tc_client,int mandatory_delta,int workers_delta)161c4568449SPavel Kumbrasev void threading_control_impl::adjust_demand(threading_control_client tc_client, int mandatory_delta, int workers_delta) {
162c4568449SPavel Kumbrasev     auto& c = *tc_client.get_pm_client();
163c4568449SPavel Kumbrasev     my_thread_request_serializer->register_mandatory_request(mandatory_delta);
164c4568449SPavel Kumbrasev     my_permit_manager->adjust_demand(c, mandatory_delta, workers_delta);
165c4568449SPavel Kumbrasev }
166c4568449SPavel Kumbrasev 
get_waiting_threads_monitor()167c4568449SPavel Kumbrasev thread_control_monitor& threading_control_impl::get_waiting_threads_monitor() {
168c4568449SPavel Kumbrasev     return *my_waiting_threads_monitor;
169c4568449SPavel Kumbrasev }
170c4568449SPavel Kumbrasev 
171c4568449SPavel Kumbrasev // ---------------------------------------- threading_control -------------------------------------------------------------------
172c4568449SPavel Kumbrasev 
173c4568449SPavel Kumbrasev // Defined in global_control.cpp
174c4568449SPavel Kumbrasev void global_control_lock();
175c4568449SPavel Kumbrasev void global_control_unlock();
176c4568449SPavel Kumbrasev 
add_ref(bool is_public)177c4568449SPavel Kumbrasev void threading_control::add_ref(bool is_public) {
178c4568449SPavel Kumbrasev     ++my_ref_count;
179c4568449SPavel Kumbrasev     if (is_public) {
180c4568449SPavel Kumbrasev         my_public_ref_count++;
181c4568449SPavel Kumbrasev     }
182c4568449SPavel Kumbrasev }
183c4568449SPavel Kumbrasev 
remove_ref(bool is_public)184c4568449SPavel Kumbrasev bool threading_control::remove_ref(bool is_public) {
185c4568449SPavel Kumbrasev     if (is_public) {
186c4568449SPavel Kumbrasev         __TBB_ASSERT(g_threading_control == this, "Global threading control instance was destroyed prematurely?");
187c4568449SPavel Kumbrasev         __TBB_ASSERT(my_public_ref_count.load(std::memory_order_relaxed), nullptr);
188c4568449SPavel Kumbrasev         --my_public_ref_count;
189c4568449SPavel Kumbrasev     }
190c4568449SPavel Kumbrasev 
191c4568449SPavel Kumbrasev     bool is_last_ref = --my_ref_count == 0;
192c4568449SPavel Kumbrasev     if (is_last_ref) {
193c4568449SPavel Kumbrasev         __TBB_ASSERT(!my_public_ref_count.load(std::memory_order_relaxed), nullptr);
194c4568449SPavel Kumbrasev         g_threading_control = nullptr;
195c4568449SPavel Kumbrasev     }
196c4568449SPavel Kumbrasev 
197c4568449SPavel Kumbrasev     return is_last_ref;
198c4568449SPavel Kumbrasev }
199c4568449SPavel Kumbrasev 
get_threading_control(bool is_public)200c4568449SPavel Kumbrasev threading_control* threading_control::get_threading_control(bool is_public) {
201c4568449SPavel Kumbrasev     threading_control* control = g_threading_control;
202c4568449SPavel Kumbrasev     if (control) {
203c4568449SPavel Kumbrasev         control->add_ref(is_public);
204c4568449SPavel Kumbrasev     }
205c4568449SPavel Kumbrasev 
206c4568449SPavel Kumbrasev     return control;
207c4568449SPavel Kumbrasev }
208c4568449SPavel Kumbrasev 
create_threading_control()209c4568449SPavel Kumbrasev threading_control* threading_control::create_threading_control() {
210c4568449SPavel Kumbrasev     // Global control should be locked before threading_control_impl
211c4568449SPavel Kumbrasev     global_control_lock();
212c4568449SPavel Kumbrasev 
213c4568449SPavel Kumbrasev     threading_control* thr_control{ nullptr };
214c4568449SPavel Kumbrasev     try_call([&] {
215c4568449SPavel Kumbrasev         global_mutex_type::scoped_lock lock(g_threading_control_mutex);
216c4568449SPavel Kumbrasev 
217c4568449SPavel Kumbrasev         thr_control = get_threading_control(/*public = */ true);
218c4568449SPavel Kumbrasev         if (thr_control == nullptr) {
219c4568449SPavel Kumbrasev             thr_control =  new (cache_aligned_allocate(sizeof(threading_control))) threading_control(/*public_ref = */ 1, /*private_ref = */ 1);
220c4568449SPavel Kumbrasev             thr_control->my_pimpl = make_cache_aligned_unique<threading_control_impl>(thr_control);
221c4568449SPavel Kumbrasev 
222c4568449SPavel Kumbrasev             __TBB_InitOnce::add_ref();
223c4568449SPavel Kumbrasev 
224c4568449SPavel Kumbrasev             if (global_control_active_value_unsafe(global_control::scheduler_handle)) {
225c4568449SPavel Kumbrasev                 ++thr_control->my_public_ref_count;
226c4568449SPavel Kumbrasev                 ++thr_control->my_ref_count;
227c4568449SPavel Kumbrasev             }
228c4568449SPavel Kumbrasev 
229c4568449SPavel Kumbrasev             g_threading_control = thr_control;
230c4568449SPavel Kumbrasev         }
231c4568449SPavel Kumbrasev     }).on_exception([&] {
232c4568449SPavel Kumbrasev         global_control_unlock();
233c4568449SPavel Kumbrasev 
234c4568449SPavel Kumbrasev         cache_aligned_deleter deleter{};
235c4568449SPavel Kumbrasev         deleter(thr_control);
236c4568449SPavel Kumbrasev     });
237c4568449SPavel Kumbrasev 
238c4568449SPavel Kumbrasev     global_control_unlock();
239c4568449SPavel Kumbrasev     return thr_control;
240c4568449SPavel Kumbrasev }
241c4568449SPavel Kumbrasev 
destroy()242c4568449SPavel Kumbrasev void threading_control::destroy () {
243c4568449SPavel Kumbrasev     cache_aligned_deleter deleter;
244c4568449SPavel Kumbrasev     deleter(this);
245c4568449SPavel Kumbrasev     __TBB_InitOnce::remove_ref();
246c4568449SPavel Kumbrasev }
247c4568449SPavel Kumbrasev 
wait_last_reference(global_mutex_type::scoped_lock & lock)248c4568449SPavel Kumbrasev void threading_control::wait_last_reference(global_mutex_type::scoped_lock& lock) {
249c4568449SPavel Kumbrasev     while (my_public_ref_count.load(std::memory_order_relaxed) == 1 && my_ref_count.load(std::memory_order_relaxed) > 1) {
250c4568449SPavel Kumbrasev         lock.release();
251c4568449SPavel Kumbrasev         // To guarantee that request_close_connection() is called by the last external thread, we need to wait till all
252c4568449SPavel Kumbrasev         // references are released. Re-read my_public_ref_count to limit waiting if new external threads are created.
253c4568449SPavel Kumbrasev         // Theoretically, new private references to the threading control can be added during waiting making it potentially
254c4568449SPavel Kumbrasev         // endless.
255c4568449SPavel Kumbrasev         // TODO: revise why the weak scheduler needs threading control's pointer and try to remove this wait.
256c4568449SPavel Kumbrasev         // Note that the threading control should know about its schedulers for cancellation/exception/priority propagation,
257c4568449SPavel Kumbrasev         // see e.g. task_group_context::cancel_group_execution()
258c4568449SPavel Kumbrasev         while (my_public_ref_count.load(std::memory_order_acquire) == 1 && my_ref_count.load(std::memory_order_acquire) > 1) {
259c4568449SPavel Kumbrasev             yield();
260c4568449SPavel Kumbrasev         }
261c4568449SPavel Kumbrasev         lock.acquire(g_threading_control_mutex);
262c4568449SPavel Kumbrasev     }
263c4568449SPavel Kumbrasev }
264c4568449SPavel Kumbrasev 
release(bool is_public,bool blocking_terminate)265c4568449SPavel Kumbrasev bool threading_control::release(bool is_public, bool blocking_terminate) {
266c4568449SPavel Kumbrasev     bool do_release = false;
267c4568449SPavel Kumbrasev     {
268c4568449SPavel Kumbrasev         global_mutex_type::scoped_lock lock(g_threading_control_mutex);
269c4568449SPavel Kumbrasev         if (blocking_terminate) {
270c4568449SPavel Kumbrasev             __TBB_ASSERT(is_public, "Only an object with a public reference can request the blocking terminate");
271c4568449SPavel Kumbrasev             wait_last_reference(lock);
272c4568449SPavel Kumbrasev         }
273c4568449SPavel Kumbrasev         do_release = remove_ref(is_public);
274c4568449SPavel Kumbrasev     }
275c4568449SPavel Kumbrasev 
276c4568449SPavel Kumbrasev     if (do_release) {
277c4568449SPavel Kumbrasev         __TBB_ASSERT(!my_public_ref_count.load(std::memory_order_relaxed), "No public references must remain if we remove the threading control.");
278c4568449SPavel Kumbrasev         // inform RML that blocking termination is required
279c4568449SPavel Kumbrasev         my_pimpl->release(blocking_terminate);
280c4568449SPavel Kumbrasev         return blocking_terminate;
281c4568449SPavel Kumbrasev     }
282c4568449SPavel Kumbrasev     return false;
283c4568449SPavel Kumbrasev }
284c4568449SPavel Kumbrasev 
threading_control(unsigned public_ref,unsigned ref)285c4568449SPavel Kumbrasev threading_control::threading_control(unsigned public_ref, unsigned ref) : my_public_ref_count(public_ref), my_ref_count(ref)
286c4568449SPavel Kumbrasev {}
287c4568449SPavel Kumbrasev 
register_public_reference()288c4568449SPavel Kumbrasev threading_control* threading_control::register_public_reference() {
289c4568449SPavel Kumbrasev     threading_control* control{nullptr};
290c4568449SPavel Kumbrasev     global_mutex_type::scoped_lock lock(g_threading_control_mutex);
291c4568449SPavel Kumbrasev     control = get_threading_control(/*public = */ true);
292c4568449SPavel Kumbrasev     if (!control) {
293c4568449SPavel Kumbrasev         // We are going to create threading_control_impl, we should acquire mutexes in right order
294c4568449SPavel Kumbrasev         lock.release();
295c4568449SPavel Kumbrasev         control = create_threading_control();
296c4568449SPavel Kumbrasev     }
297c4568449SPavel Kumbrasev 
298c4568449SPavel Kumbrasev     return control;
299c4568449SPavel Kumbrasev }
300c4568449SPavel Kumbrasev 
unregister_public_reference(bool blocking_terminate)301c4568449SPavel Kumbrasev bool threading_control::unregister_public_reference(bool blocking_terminate) {
302c4568449SPavel Kumbrasev     __TBB_ASSERT(g_threading_control, "Threading control should exist until last public reference");
303c4568449SPavel Kumbrasev     __TBB_ASSERT(g_threading_control->my_public_ref_count.load(std::memory_order_relaxed), nullptr);
304c4568449SPavel Kumbrasev     return g_threading_control->release(/*public = */ true, /*blocking_terminate = */ blocking_terminate);
305c4568449SPavel Kumbrasev }
306c4568449SPavel Kumbrasev 
create_client(arena & a)307c4568449SPavel Kumbrasev threading_control_client threading_control::create_client(arena& a) {
308c4568449SPavel Kumbrasev     {
309c4568449SPavel Kumbrasev         global_mutex_type::scoped_lock lock(g_threading_control_mutex);
310c4568449SPavel Kumbrasev         add_ref(/*public = */ false);
311c4568449SPavel Kumbrasev     }
312c4568449SPavel Kumbrasev 
313c4568449SPavel Kumbrasev     return my_pimpl->create_client(a);
314c4568449SPavel Kumbrasev }
315c4568449SPavel Kumbrasev 
publish_client(threading_control_client client,d1::constraints & constraints)316*71e1bb8eSPavel Kumbrasev void threading_control::publish_client(threading_control_client client, d1::constraints& constraints) {
317*71e1bb8eSPavel Kumbrasev     return my_pimpl->publish_client(client, constraints);
318c4568449SPavel Kumbrasev }
319c4568449SPavel Kumbrasev 
prepare_client_destruction(threading_control_client client)320c4568449SPavel Kumbrasev threading_control::client_snapshot threading_control::prepare_client_destruction(threading_control_client client) {
321c4568449SPavel Kumbrasev     return my_pimpl->prepare_client_destruction(client);
322c4568449SPavel Kumbrasev }
323c4568449SPavel Kumbrasev 
try_destroy_client(threading_control::client_snapshot deleter)324c4568449SPavel Kumbrasev bool threading_control::try_destroy_client(threading_control::client_snapshot deleter) {
325c4568449SPavel Kumbrasev     bool res = my_pimpl->try_destroy_client(deleter);
326c4568449SPavel Kumbrasev     if (res) {
327c4568449SPavel Kumbrasev         release(/*public = */ false, /*blocking_terminate = */ false);
328c4568449SPavel Kumbrasev     }
329c4568449SPavel Kumbrasev     return res;
330c4568449SPavel Kumbrasev }
331c4568449SPavel Kumbrasev 
set_active_num_workers(unsigned soft_limit)332c4568449SPavel Kumbrasev void threading_control::set_active_num_workers(unsigned soft_limit) {
333c4568449SPavel Kumbrasev     threading_control* thr_control = get_threading_control(/*public = */ false);
334c4568449SPavel Kumbrasev     if (thr_control != nullptr) {
335c4568449SPavel Kumbrasev         thr_control->my_pimpl->set_active_num_workers(soft_limit);
336c4568449SPavel Kumbrasev         thr_control->release(/*is_public=*/false, /*blocking_terminate=*/false);
337c4568449SPavel Kumbrasev     }
338c4568449SPavel Kumbrasev }
339c4568449SPavel Kumbrasev 
is_present()340c4568449SPavel Kumbrasev bool threading_control::is_present() {
341c4568449SPavel Kumbrasev     global_mutex_type::scoped_lock lock(g_threading_control_mutex);
342c4568449SPavel Kumbrasev     return g_threading_control != nullptr;
343c4568449SPavel Kumbrasev }
344c4568449SPavel Kumbrasev 
register_lifetime_control()345c4568449SPavel Kumbrasev bool threading_control::register_lifetime_control() {
346c4568449SPavel Kumbrasev     global_mutex_type::scoped_lock lock(g_threading_control_mutex);
347c4568449SPavel Kumbrasev     return get_threading_control(/*public = */ true) != nullptr;
348c4568449SPavel Kumbrasev }
349c4568449SPavel Kumbrasev 
unregister_lifetime_control(bool blocking_terminate)350c4568449SPavel Kumbrasev bool threading_control::unregister_lifetime_control(bool blocking_terminate) {
351c4568449SPavel Kumbrasev     threading_control* thr_control{nullptr};
352c4568449SPavel Kumbrasev     {
353c4568449SPavel Kumbrasev         global_mutex_type::scoped_lock lock(g_threading_control_mutex);
354c4568449SPavel Kumbrasev         thr_control = g_threading_control;
355c4568449SPavel Kumbrasev     }
356c4568449SPavel Kumbrasev 
357c4568449SPavel Kumbrasev     bool released{true};
358c4568449SPavel Kumbrasev     if (thr_control) {
359c4568449SPavel Kumbrasev         released = thr_control->release(/*public = */ true, /*blocking_terminate = */ blocking_terminate);
360c4568449SPavel Kumbrasev     }
361c4568449SPavel Kumbrasev 
362c4568449SPavel Kumbrasev     return released;
363c4568449SPavel Kumbrasev }
364c4568449SPavel Kumbrasev 
register_thread(thread_data & td)365c4568449SPavel Kumbrasev void threading_control::register_thread(thread_data& td) {
366c4568449SPavel Kumbrasev     my_pimpl->register_thread(td);
367c4568449SPavel Kumbrasev }
368c4568449SPavel Kumbrasev 
unregister_thread(thread_data & td)369c4568449SPavel Kumbrasev void threading_control::unregister_thread(thread_data& td) {
370c4568449SPavel Kumbrasev     my_pimpl->unregister_thread(td);
371c4568449SPavel Kumbrasev }
372c4568449SPavel Kumbrasev 
propagate_task_group_state(std::atomic<uint32_t> d1::task_group_context::* mptr_state,d1::task_group_context & src,uint32_t new_state)373c4568449SPavel Kumbrasev void threading_control::propagate_task_group_state(std::atomic<uint32_t> d1::task_group_context::*mptr_state,
374c4568449SPavel Kumbrasev                                                    d1::task_group_context& src, uint32_t new_state)
375c4568449SPavel Kumbrasev {
376c4568449SPavel Kumbrasev     my_pimpl->propagate_task_group_state(mptr_state, src, new_state);
377c4568449SPavel Kumbrasev }
378c4568449SPavel Kumbrasev 
worker_stack_size()379c4568449SPavel Kumbrasev std::size_t threading_control::worker_stack_size() {
380c4568449SPavel Kumbrasev     return my_pimpl->worker_stack_size();
381c4568449SPavel Kumbrasev }
382c4568449SPavel Kumbrasev 
max_num_workers()383c4568449SPavel Kumbrasev unsigned threading_control::max_num_workers() {
384c4568449SPavel Kumbrasev     global_mutex_type::scoped_lock lock(g_threading_control_mutex);
385c4568449SPavel Kumbrasev     return g_threading_control ? g_threading_control->my_pimpl->max_num_workers() : 0;
386c4568449SPavel Kumbrasev }
387c4568449SPavel Kumbrasev 
adjust_demand(threading_control_client client,int mandatory_delta,int workers_delta)388c4568449SPavel Kumbrasev void threading_control::adjust_demand(threading_control_client client, int mandatory_delta, int workers_delta) {
389c4568449SPavel Kumbrasev     my_pimpl->adjust_demand(client, mandatory_delta, workers_delta);
390c4568449SPavel Kumbrasev }
391c4568449SPavel Kumbrasev 
get_waiting_threads_monitor()392c4568449SPavel Kumbrasev thread_control_monitor& threading_control::get_waiting_threads_monitor() {
393c4568449SPavel Kumbrasev     return my_pimpl->get_waiting_threads_monitor();
394c4568449SPavel Kumbrasev }
395c4568449SPavel Kumbrasev 
396c4568449SPavel Kumbrasev } // r1
397c4568449SPavel Kumbrasev } // detail
398c4568449SPavel Kumbrasev } // tbb
399