1 /*
2 Copyright (c) 2005-2023 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 #include "governor.h"
18 #include "threading_control.h"
19 #include "main.h"
20 #include "thread_data.h"
21 #include "market.h"
22 #include "arena.h"
23 #include "dynamic_link.h"
24 #include "concurrent_monitor.h"
25 #include "thread_dispatcher.h"
26
27 #include "oneapi/tbb/task_group.h"
28 #include "oneapi/tbb/global_control.h"
29 #include "oneapi/tbb/tbb_allocator.h"
30 #include "oneapi/tbb/info.h"
31
32 #include "task_dispatcher.h"
33
34 #include <cstdio>
35 #include <cstdlib>
36 #include <cstring>
37 #include <atomic>
38 #include <algorithm>
39
40 namespace tbb {
41 namespace detail {
42 namespace r1 {
43
44 void clear_address_waiter_table();
45
46 //! global_control.cpp contains definition
47 bool remove_and_check_if_empty(d1::global_control& gc);
48 bool is_present(d1::global_control& gc);
49
50 namespace rml {
51 tbb_server* make_private_server( tbb_client& client );
52 } // namespace rml
53
54 namespace system_topology {
55 void destroy();
56 }
57
58 //------------------------------------------------------------------------
59 // governor
60 //------------------------------------------------------------------------
61
acquire_resources()62 void governor::acquire_resources () {
63 #if __TBB_USE_POSIX
64 int status = theTLS.create(auto_terminate);
65 #else
66 int status = theTLS.create();
67 #endif
68 if( status )
69 handle_perror(status, "TBB failed to initialize task scheduler TLS\n");
70 detect_cpu_features(cpu_features);
71
72 is_rethrow_broken = gcc_rethrow_exception_broken();
73 }
74
release_resources()75 void governor::release_resources () {
76 theRMLServerFactory.close();
77 destroy_process_mask();
78
79 __TBB_ASSERT(!(__TBB_InitOnce::initialization_done() && theTLS.get()), "TBB is unloaded while thread data still alive?");
80
81 int status = theTLS.destroy();
82 if( status )
83 runtime_warning("failed to destroy task scheduler TLS: %s", std::strerror(status));
84 clear_address_waiter_table();
85
86 system_topology::destroy();
87 dynamic_unlink_all();
88 }
89
create_rml_server(rml::tbb_client & client)90 rml::tbb_server* governor::create_rml_server ( rml::tbb_client& client ) {
91 rml::tbb_server* server = nullptr;
92 if( !UsePrivateRML ) {
93 ::rml::factory::status_type status = theRMLServerFactory.make_server( server, client );
94 if( status != ::rml::factory::st_success ) {
95 UsePrivateRML = true;
96 runtime_warning( "rml::tbb_factory::make_server failed with status %x, falling back on private rml", status );
97 }
98 }
99 if ( !server ) {
100 __TBB_ASSERT( UsePrivateRML, nullptr);
101 server = rml::make_private_server( client );
102 }
103 __TBB_ASSERT( server, "Failed to create RML server" );
104 return server;
105 }
106
one_time_init()107 void governor::one_time_init() {
108 if ( !__TBB_InitOnce::initialization_done() ) {
109 DoOneTimeInitialization();
110 }
111 }
112
does_client_join_workers(const rml::tbb_client & client)113 bool governor::does_client_join_workers(const rml::tbb_client &client) {
114 return ((const thread_dispatcher&)client).must_join_workers();
115 }
116
117 /*
118 There is no portable way to get stack base address in Posix, however the modern
119 Linux versions provide pthread_attr_np API that can be used to obtain thread's
120 stack size and base address. Unfortunately even this function does not provide
121 enough information for the main thread on IA-64 architecture (RSE spill area
122 and memory stack are allocated as two separate discontinuous chunks of memory),
123 and there is no portable way to discern the main and the secondary threads.
124 Thus for macOS* and IA-64 architecture for Linux* OS we use the TBB worker stack size for
125 all threads and use the current stack top as the stack base. This simplified
126 approach is based on the following assumptions:
127 1) If the default stack size is insufficient for the user app needs, the
128 required amount will be explicitly specified by the user at the point of the
129 TBB scheduler initialization (as an argument to tbb::task_scheduler_init
130 constructor).
131 2) When an external thread initializes the scheduler, it has enough space on its
132 stack. Here "enough" means "at least as much as worker threads have".
133 3) If the user app strives to conserve the memory by cutting stack size, it
134 should do this for TBB workers too (as in the #1).
135 */
get_stack_base(std::size_t stack_size)136 static std::uintptr_t get_stack_base(std::size_t stack_size) {
137 // Stacks are growing top-down. Highest address is called "stack base",
138 // and the lowest is "stack limit".
139 #if __TBB_USE_WINAPI
140 suppress_unused_warning(stack_size);
141 NT_TIB* pteb = (NT_TIB*)NtCurrentTeb();
142 __TBB_ASSERT(&pteb < pteb->StackBase && &pteb > pteb->StackLimit, "invalid stack info in TEB");
143 return reinterpret_cast<std::uintptr_t>(pteb->StackBase);
144 #else
145 // There is no portable way to get stack base address in Posix, so we use
146 // non-portable method (on all modern Linux) or the simplified approach
147 // based on the common sense assumptions. The most important assumption
148 // is that the main thread's stack size is not less than that of other threads.
149
150 // Points to the lowest addressable byte of a stack.
151 void* stack_limit = nullptr;
152 #if __linux__ && !__bg__
153 size_t np_stack_size = 0;
154 pthread_attr_t np_attr_stack;
155 if (0 == pthread_getattr_np(pthread_self(), &np_attr_stack)) {
156 if (0 == pthread_attr_getstack(&np_attr_stack, &stack_limit, &np_stack_size)) {
157 __TBB_ASSERT( &stack_limit > stack_limit, "stack size must be positive" );
158 }
159 pthread_attr_destroy(&np_attr_stack);
160 }
161 #endif /* __linux__ */
162 std::uintptr_t stack_base{};
163 if (stack_limit) {
164 stack_base = reinterpret_cast<std::uintptr_t>(stack_limit) + stack_size;
165 } else {
166 // Use an anchor as a base stack address.
167 int anchor{};
168 stack_base = reinterpret_cast<std::uintptr_t>(&anchor);
169 }
170 return stack_base;
171 #endif /* __TBB_USE_WINAPI */
172 }
173
174 #if (_WIN32||_WIN64) && !__TBB_DYNAMIC_LOAD_ENABLED
register_external_thread_destructor()175 static void register_external_thread_destructor() {
176 struct thread_destructor {
177 ~thread_destructor() {
178 governor::terminate_external_thread();
179 }
180 };
181 // ~thread_destructor() will be call during the calling thread termination
182 static thread_local thread_destructor thr_destructor;
183 }
184 #endif // (_WIN32||_WIN64) && !__TBB_DYNAMIC_LOAD_ENABLED
185
init_external_thread()186 void governor::init_external_thread() {
187 one_time_init();
188 // Create new scheduler instance with arena
189 int num_slots = default_num_threads();
190 // TODO_REVAMP: support an external thread without an implicit arena
191 int num_reserved_slots = 1;
192 unsigned arena_priority_level = 1; // corresponds to tbb::task_arena::priority::normal
193 std::size_t stack_size = 0;
194 threading_control* thr_control = threading_control::register_public_reference();
195 arena& a = arena::create(thr_control, num_slots, num_reserved_slots, arena_priority_level);
196 // External thread always occupies the first slot
197 thread_data& td = *new(cache_aligned_allocate(sizeof(thread_data))) thread_data(0, false);
198 td.attach_arena(a, /*slot index*/ 0);
199 __TBB_ASSERT(td.my_inbox.is_idle_state(false), nullptr);
200
201 stack_size = a.my_threading_control->worker_stack_size();
202 std::uintptr_t stack_base = get_stack_base(stack_size);
203 task_dispatcher& task_disp = td.my_arena_slot->default_task_dispatcher();
204 td.enter_task_dispatcher(task_disp, calculate_stealing_threshold(stack_base, stack_size));
205
206 td.my_arena_slot->occupy();
207 thr_control->register_thread(td);
208 set_thread_data(td);
209 #if (_WIN32||_WIN64) && !__TBB_DYNAMIC_LOAD_ENABLED
210 // The external thread destructor is called from dllMain but it is not available with a static build.
211 // Therefore, we need to register the current thread to call the destructor during thread termination.
212 register_external_thread_destructor();
213 #endif
214 }
215
auto_terminate(void * tls)216 void governor::auto_terminate(void* tls) {
217 __TBB_ASSERT(get_thread_data_if_initialized() == nullptr ||
218 get_thread_data_if_initialized() == tls, nullptr);
219 if (tls) {
220 thread_data* td = static_cast<thread_data*>(tls);
221
222 auto clear_tls = [td] {
223 td->~thread_data();
224 cache_aligned_deallocate(td);
225 clear_thread_data();
226 };
227
228 // Only external thread can be inside an arena during termination.
229 if (td->my_arena_slot) {
230 arena* a = td->my_arena;
231 threading_control* thr_control = a->my_threading_control;
232
233 // If the TLS slot is already cleared by OS or underlying concurrency
234 // runtime, restore its value to properly clean up arena
235 if (!is_thread_data_set(td)) {
236 set_thread_data(*td);
237 }
238
239 a->my_observers.notify_exit_observers(td->my_last_observer, td->my_is_worker);
240
241 td->leave_task_dispatcher();
242 td->my_arena_slot->release();
243 // Release an arena
244 a->on_thread_leaving(arena::ref_external);
245
246 thr_control->unregister_thread(*td);
247
248 // The tls should be cleared before market::release because
249 // market can destroy the tls key if we keep the last reference
250 clear_tls();
251
252 // If there was an associated arena, it added a public market reference
253 thr_control->unregister_public_reference(/* blocking terminate =*/ false);
254 } else {
255 clear_tls();
256 }
257 }
258 __TBB_ASSERT(get_thread_data_if_initialized() == nullptr, nullptr);
259 }
260
initialize_rml_factory()261 void governor::initialize_rml_factory () {
262 ::rml::factory::status_type res = theRMLServerFactory.open();
263 UsePrivateRML = res != ::rml::factory::st_success;
264 }
265
get(d1::task_scheduler_handle & handle)266 void __TBB_EXPORTED_FUNC get(d1::task_scheduler_handle& handle) {
267 handle.m_ctl = new(allocate_memory(sizeof(global_control))) global_control(global_control::scheduler_handle, 1);
268 }
269
release_impl(d1::task_scheduler_handle & handle)270 void release_impl(d1::task_scheduler_handle& handle) {
271 if (handle.m_ctl != nullptr) {
272 handle.m_ctl->~global_control();
273 deallocate_memory(handle.m_ctl);
274 handle.m_ctl = nullptr;
275 }
276 }
277
finalize_impl(d1::task_scheduler_handle & handle)278 bool finalize_impl(d1::task_scheduler_handle& handle) {
279 __TBB_ASSERT_RELEASE(handle, "trying to finalize with null handle");
280 __TBB_ASSERT(is_present(*handle.m_ctl), "finalize or release was already called on this object");
281
282 bool ok = true; // ok if threading_control does not exist yet
283 if (threading_control::is_present()) {
284 thread_data* td = governor::get_thread_data_if_initialized();
285 if (td) {
286 task_dispatcher* task_disp = td->my_task_dispatcher;
287 __TBB_ASSERT(task_disp, nullptr);
288 if (task_disp->m_properties.outermost && !td->my_is_worker) { // is not inside a parallel region
289 governor::auto_terminate(td);
290 }
291 }
292
293 if (remove_and_check_if_empty(*handle.m_ctl)) {
294 ok = threading_control::unregister_lifetime_control(/*blocking_terminate*/ true);
295 } else {
296 ok = false;
297 }
298 }
299
300 return ok;
301 }
302
finalize(d1::task_scheduler_handle & handle,std::intptr_t mode)303 bool __TBB_EXPORTED_FUNC finalize(d1::task_scheduler_handle& handle, std::intptr_t mode) {
304 if (mode == d1::release_nothrowing) {
305 release_impl(handle);
306 return true;
307 } else {
308 bool ok = finalize_impl(handle);
309 // TODO: it is unsafe when finalize is called concurrently and further library unload
310 release_impl(handle);
311 if (mode == d1::finalize_throwing && !ok) {
312 throw_exception(exception_id::unsafe_wait);
313 }
314 return ok;
315 }
316 }
317
318 #if __TBB_ARENA_BINDING
319
320 #if __TBB_WEAK_SYMBOLS_PRESENT
321 #pragma weak __TBB_internal_initialize_system_topology
322 #pragma weak __TBB_internal_destroy_system_topology
323 #pragma weak __TBB_internal_allocate_binding_handler
324 #pragma weak __TBB_internal_deallocate_binding_handler
325 #pragma weak __TBB_internal_apply_affinity
326 #pragma weak __TBB_internal_restore_affinity
327 #pragma weak __TBB_internal_get_default_concurrency
328
329 extern "C" {
330 void __TBB_internal_initialize_system_topology(
331 size_t groups_num,
332 int& numa_nodes_count, int*& numa_indexes_list,
333 int& core_types_count, int*& core_types_indexes_list
334 );
335 void __TBB_internal_destroy_system_topology( );
336
337 //TODO: consider renaming to `create_binding_handler` and `destroy_binding_handler`
338 binding_handler* __TBB_internal_allocate_binding_handler( int slot_num, int numa_id, int core_type_id, int max_threads_per_core );
339 void __TBB_internal_deallocate_binding_handler( binding_handler* handler_ptr );
340
341 void __TBB_internal_apply_affinity( binding_handler* handler_ptr, int slot_num );
342 void __TBB_internal_restore_affinity( binding_handler* handler_ptr, int slot_num );
343
344 int __TBB_internal_get_default_concurrency( int numa_id, int core_type_id, int max_threads_per_core );
345 }
346 #endif /* __TBB_WEAK_SYMBOLS_PRESENT */
347
348 // Stubs that will be used if TBBbind library is unavailable.
dummy_destroy_system_topology()349 static void dummy_destroy_system_topology ( ) { }
dummy_allocate_binding_handler(int,int,int,int)350 static binding_handler* dummy_allocate_binding_handler ( int, int, int, int ) { return nullptr; }
dummy_deallocate_binding_handler(binding_handler *)351 static void dummy_deallocate_binding_handler ( binding_handler* ) { }
dummy_apply_affinity(binding_handler *,int)352 static void dummy_apply_affinity ( binding_handler*, int ) { }
dummy_restore_affinity(binding_handler *,int)353 static void dummy_restore_affinity ( binding_handler*, int ) { }
dummy_get_default_concurrency(int,int,int)354 static int dummy_get_default_concurrency( int, int, int ) { return governor::default_num_threads(); }
355
356 // Handlers for communication with TBBbind
357 static void (*initialize_system_topology_ptr)(
358 size_t groups_num,
359 int& numa_nodes_count, int*& numa_indexes_list,
360 int& core_types_count, int*& core_types_indexes_list
361 ) = nullptr;
362 static void (*destroy_system_topology_ptr)( ) = dummy_destroy_system_topology;
363
364 static binding_handler* (*allocate_binding_handler_ptr)( int slot_num, int numa_id, int core_type_id, int max_threads_per_core )
365 = dummy_allocate_binding_handler;
366 static void (*deallocate_binding_handler_ptr)( binding_handler* handler_ptr )
367 = dummy_deallocate_binding_handler;
368 static void (*apply_affinity_ptr)( binding_handler* handler_ptr, int slot_num )
369 = dummy_apply_affinity;
370 static void (*restore_affinity_ptr)( binding_handler* handler_ptr, int slot_num )
371 = dummy_restore_affinity;
372 int (*get_default_concurrency_ptr)( int numa_id, int core_type_id, int max_threads_per_core )
373 = dummy_get_default_concurrency;
374
375 #if _WIN32 || _WIN64 || __unix__ || __APPLE__
376
377 // Table describing how to link the handlers.
378 static const dynamic_link_descriptor TbbBindLinkTable[] = {
379 DLD(__TBB_internal_initialize_system_topology, initialize_system_topology_ptr),
380 DLD(__TBB_internal_destroy_system_topology, destroy_system_topology_ptr),
381 #if __TBB_CPUBIND_PRESENT
382 DLD(__TBB_internal_allocate_binding_handler, allocate_binding_handler_ptr),
383 DLD(__TBB_internal_deallocate_binding_handler, deallocate_binding_handler_ptr),
384 DLD(__TBB_internal_apply_affinity, apply_affinity_ptr),
385 DLD(__TBB_internal_restore_affinity, restore_affinity_ptr),
386 #endif
387 DLD(__TBB_internal_get_default_concurrency, get_default_concurrency_ptr)
388 };
389
390 static const unsigned LinkTableSize = sizeof(TbbBindLinkTable) / sizeof(dynamic_link_descriptor);
391
392 #if TBB_USE_DEBUG
393 #define DEBUG_SUFFIX "_debug"
394 #else
395 #define DEBUG_SUFFIX
396 #endif /* TBB_USE_DEBUG */
397
398 #if _WIN32 || _WIN64
399 #define LIBRARY_EXTENSION ".dll"
400 #define LIBRARY_PREFIX
401 #elif __APPLE__
402 #define LIBRARY_EXTENSION __TBB_STRING(.3.dylib)
403 #define LIBRARY_PREFIX "lib"
404 #elif __unix__
405 #define LIBRARY_EXTENSION __TBB_STRING(.so.3)
406 #define LIBRARY_PREFIX "lib"
407 #endif /* __unix__ */
408
409 #define TBBBIND_NAME LIBRARY_PREFIX "tbbbind" DEBUG_SUFFIX LIBRARY_EXTENSION
410 #define TBBBIND_2_0_NAME LIBRARY_PREFIX "tbbbind_2_0" DEBUG_SUFFIX LIBRARY_EXTENSION
411
412 #define TBBBIND_2_5_NAME LIBRARY_PREFIX "tbbbind_2_5" DEBUG_SUFFIX LIBRARY_EXTENSION
413 #endif /* _WIN32 || _WIN64 || __unix__ */
414
415 // Representation of system hardware topology information on the TBB side.
416 // System topology may be initialized by third-party component (e.g. hwloc)
417 // or just filled in with default stubs.
418 namespace system_topology {
419
420 constexpr int automatic = -1;
421
422 static std::atomic<do_once_state> initialization_state;
423
424 namespace {
425 int numa_nodes_count = 0;
426 int* numa_nodes_indexes = nullptr;
427
428 int core_types_count = 0;
429 int* core_types_indexes = nullptr;
430
load_tbbbind_shared_object()431 const char* load_tbbbind_shared_object() {
432 #if _WIN32 || _WIN64 || __unix__ || __APPLE__
433 #if _WIN32 && !_WIN64
434 // For 32-bit Windows applications, process affinity masks can only support up to 32 logical CPUs.
435 SYSTEM_INFO si;
436 GetNativeSystemInfo(&si);
437 if (si.dwNumberOfProcessors > 32) return nullptr;
438 #endif /* _WIN32 && !_WIN64 */
439 for (const auto& tbbbind_version : {TBBBIND_2_5_NAME, TBBBIND_2_0_NAME, TBBBIND_NAME}) {
440 if (dynamic_link(tbbbind_version, TbbBindLinkTable, LinkTableSize, nullptr, DYNAMIC_LINK_LOCAL_BINDING)) {
441 return tbbbind_version;
442 }
443 }
444 #endif /* _WIN32 || _WIN64 || __unix__ || __APPLE__ */
445 return nullptr;
446 }
447
processor_groups_num()448 int processor_groups_num() {
449 #if _WIN32
450 return NumberOfProcessorGroups();
451 #else
452 // Stub to improve code readability by reducing number of the compile-time conditions
453 return 1;
454 #endif
455 }
456 } // internal namespace
457
458 // Tries to load TBBbind library API, if success, gets NUMA topology information from it,
459 // in another case, fills NUMA topology by stubs.
initialization_impl()460 void initialization_impl() {
461 governor::one_time_init();
462
463 if (const char* tbbbind_name = load_tbbbind_shared_object()) {
464 initialize_system_topology_ptr(
465 processor_groups_num(),
466 numa_nodes_count, numa_nodes_indexes,
467 core_types_count, core_types_indexes
468 );
469
470 PrintExtraVersionInfo("TBBBIND", tbbbind_name);
471 return;
472 }
473
474 static int dummy_index = automatic;
475
476 numa_nodes_count = 1;
477 numa_nodes_indexes = &dummy_index;
478
479 core_types_count = 1;
480 core_types_indexes = &dummy_index;
481
482 PrintExtraVersionInfo("TBBBIND", "UNAVAILABLE");
483 }
484
initialize()485 void initialize() {
486 atomic_do_once(initialization_impl, initialization_state);
487 }
488
destroy()489 void destroy() {
490 destroy_system_topology_ptr();
491 }
492 } // namespace system_topology
493
construct_binding_handler(int slot_num,int numa_id,int core_type_id,int max_threads_per_core)494 binding_handler* construct_binding_handler(int slot_num, int numa_id, int core_type_id, int max_threads_per_core) {
495 system_topology::initialize();
496 return allocate_binding_handler_ptr(slot_num, numa_id, core_type_id, max_threads_per_core);
497 }
498
destroy_binding_handler(binding_handler * handler_ptr)499 void destroy_binding_handler(binding_handler* handler_ptr) {
500 __TBB_ASSERT(deallocate_binding_handler_ptr, "tbbbind loading was not performed");
501 deallocate_binding_handler_ptr(handler_ptr);
502 }
503
apply_affinity_mask(binding_handler * handler_ptr,int slot_index)504 void apply_affinity_mask(binding_handler* handler_ptr, int slot_index) {
505 __TBB_ASSERT(slot_index >= 0, "Negative thread index");
506 __TBB_ASSERT(apply_affinity_ptr, "tbbbind loading was not performed");
507 apply_affinity_ptr(handler_ptr, slot_index);
508 }
509
restore_affinity_mask(binding_handler * handler_ptr,int slot_index)510 void restore_affinity_mask(binding_handler* handler_ptr, int slot_index) {
511 __TBB_ASSERT(slot_index >= 0, "Negative thread index");
512 __TBB_ASSERT(restore_affinity_ptr, "tbbbind loading was not performed");
513 restore_affinity_ptr(handler_ptr, slot_index);
514 }
515
numa_node_count()516 unsigned __TBB_EXPORTED_FUNC numa_node_count() {
517 system_topology::initialize();
518 return system_topology::numa_nodes_count;
519 }
520
fill_numa_indices(int * index_array)521 void __TBB_EXPORTED_FUNC fill_numa_indices(int* index_array) {
522 system_topology::initialize();
523 std::memcpy(index_array, system_topology::numa_nodes_indexes, system_topology::numa_nodes_count * sizeof(int));
524 }
525
numa_default_concurrency(int node_id)526 int __TBB_EXPORTED_FUNC numa_default_concurrency(int node_id) {
527 if (node_id >= 0) {
528 system_topology::initialize();
529 int result = get_default_concurrency_ptr(
530 node_id,
531 /*core_type*/system_topology::automatic,
532 /*threads_per_core*/system_topology::automatic
533 );
534 if (result > 0) return result;
535 }
536 return governor::default_num_threads();
537 }
538
core_type_count(intptr_t)539 unsigned __TBB_EXPORTED_FUNC core_type_count(intptr_t /*reserved*/) {
540 system_topology::initialize();
541 return system_topology::core_types_count;
542 }
543
fill_core_type_indices(int * index_array,intptr_t)544 void __TBB_EXPORTED_FUNC fill_core_type_indices(int* index_array, intptr_t /*reserved*/) {
545 system_topology::initialize();
546 std::memcpy(index_array, system_topology::core_types_indexes, system_topology::core_types_count * sizeof(int));
547 }
548
constraints_assertion(d1::constraints c)549 void constraints_assertion(d1::constraints c) {
550 bool is_topology_initialized = system_topology::initialization_state == do_once_state::initialized;
551 __TBB_ASSERT_RELEASE(c.max_threads_per_core == system_topology::automatic || c.max_threads_per_core > 0,
552 "Wrong max_threads_per_core constraints field value.");
553
554 auto numa_nodes_begin = system_topology::numa_nodes_indexes;
555 auto numa_nodes_end = system_topology::numa_nodes_indexes + system_topology::numa_nodes_count;
556 __TBB_ASSERT_RELEASE(
557 c.numa_id == system_topology::automatic ||
558 (is_topology_initialized && std::find(numa_nodes_begin, numa_nodes_end, c.numa_id) != numa_nodes_end),
559 "The constraints::numa_id value is not known to the library. Use tbb::info::numa_nodes() to get the list of possible values.");
560
561 int* core_types_begin = system_topology::core_types_indexes;
562 int* core_types_end = system_topology::core_types_indexes + system_topology::core_types_count;
563 __TBB_ASSERT_RELEASE(c.core_type == system_topology::automatic ||
564 (is_topology_initialized && std::find(core_types_begin, core_types_end, c.core_type) != core_types_end),
565 "The constraints::core_type value is not known to the library. Use tbb::info::core_types() to get the list of possible values.");
566 }
567
constraints_default_concurrency(const d1::constraints & c,intptr_t)568 int __TBB_EXPORTED_FUNC constraints_default_concurrency(const d1::constraints& c, intptr_t /*reserved*/) {
569 constraints_assertion(c);
570
571 if (c.numa_id >= 0 || c.core_type >= 0 || c.max_threads_per_core > 0) {
572 system_topology::initialize();
573 return get_default_concurrency_ptr(c.numa_id, c.core_type, c.max_threads_per_core);
574 }
575 return governor::default_num_threads();
576 }
577
constraints_threads_per_core(const d1::constraints &,intptr_t)578 int __TBB_EXPORTED_FUNC constraints_threads_per_core(const d1::constraints&, intptr_t /*reserved*/) {
579 return system_topology::automatic;
580 }
581 #endif /* __TBB_ARENA_BINDING */
582
583 } // namespace r1
584 } // namespace detail
585 } // namespace tbb
586