1 /*
2     Copyright (c) 2019-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 #ifndef __TBB_test_common_arena_constraints_H_
18 #define __TBB_test_common_arena_constraints_H_
19 
20 #if _WIN32 || _WIN64
21 #define _CRT_SECURE_NO_WARNINGS
22 #endif
23 
24 #include "common/test.h"
25 #include "common/spin_barrier.h"
26 #include "common/utils.h"
27 #include "common/memory_usage.h"
28 #include "common/utils_concurrency_limit.h"
29 
30 #include "oneapi/tbb/task_arena.h"
31 #include "oneapi/tbb/spin_mutex.h"
32 
33 #include <vector>
34 #include <unordered_set>
35 
36 #if (_WIN32 || _WIN64) && __TBB_HWLOC_VALID_ENVIRONMENT
37 #include <windows.h>
get_processors_group_count()38 int get_processors_group_count() {
39     SYSTEM_INFO si;
40     GetNativeSystemInfo(&si);
41     DWORD_PTR pam, sam, m = 1;
42     GetProcessAffinityMask( GetCurrentProcess(), &pam, &sam );
43     int nproc = 0;
44     for ( std::size_t i = 0; i < sizeof(DWORD_PTR) * CHAR_BIT; ++i, m <<= 1 ) {
45         if ( pam & m )
46             ++nproc;
47     }
48     // Setting up processor groups in case the process does not restrict affinity mask and more than one processor group is present
49     if ( nproc == (int)si.dwNumberOfProcessors  ) {
50         // The process does not have restricting affinity mask and multiple processor groups are possible
51         return (int)GetActiveProcessorGroupCount();
52     } else {
53         return 1;
54     }
55 }
56 #else
get_processors_group_count()57 int get_processors_group_count() { return 1; }
58 #endif
59 
60 //TODO: Write a test that checks for memory leaks during dynamic link/unlink of TBBbind.
61 #if __TBB_HWLOC_VALID_ENVIRONMENT
62 #include "oneapi/tbb/concurrent_unordered_set.h"
63 
64 #include <atomic>
65 #include <algorithm>
66 
67 #if _MSC_VER
68 #if __clang__
69 #pragma GCC diagnostic push
70 #pragma GCC diagnostic ignored "-Wunused-parameter"
71 #else
72 #pragma warning( push )
73 #pragma warning( disable : 4100 )
74 #endif
75 #endif
76 #include <hwloc.h>
77 #if _MSC_VER
78 #if __clang__
79 #pragma GCC diagnostic pop
80 #else
81 #pragma warning( pop )
82 #endif
83 #endif
84 
85 #define __HWLOC_HYBRID_CPUS_INTERFACES_PRESENT (HWLOC_API_VERSION >= 0x20400)
86 #define __HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING_PRESENT (HWLOC_API_VERSION >= 0x20500)
87 // At this moment the hybrid CPUs HWLOC interfaces returns unexpected results on some Windows machines
88 // in the 32-bit arch mode.
89 #define __HWLOC_HYBRID_CPUS_INTERFACES_VALID (!_WIN32 || _WIN64)
90 
91 #define __HYBRID_CPUS_TESTING __HWLOC_HYBRID_CPUS_INTERFACES_PRESENT && __HWLOC_HYBRID_CPUS_INTERFACES_VALID
92 #define __HWLOC_CPUBIND_PRESENT (!__APPLE__)
93 
94 // Macro to check hwloc interfaces return codes
95 #define hwloc_require_ex(command, ...)                                          \
96         REQUIRE_MESSAGE(command(__VA_ARGS__) >= 0, "Error occurred inside hwloc call.");
97 
98 struct index_info {
99     int index{-1};
100     int concurrency{-1};
101     hwloc_bitmap_t cpuset{nullptr};
102 
103     index_info() = default;
index_infoindex_info104     index_info(const index_info& src)
105         : index{src.index}
106         , concurrency{src.concurrency}
107         , cpuset{hwloc_bitmap_dup(src.cpuset)}
108     {}
109 
110     index_info& operator=(index_info src) {
111         index = src.index;
112         concurrency = src.concurrency;
113         std::swap(cpuset, src.cpuset);
114         return *this;
115     }
116 
~index_infoindex_info117     ~index_info() {
118         hwloc_bitmap_free(cpuset);
119     }
120 };
121 
122 struct core_info {
123     hwloc_bitmap_t cpuset{nullptr};
124 
125     core_info() = default;
core_infocore_info126     core_info(hwloc_bitmap_t _cpuset)
127         : cpuset{hwloc_bitmap_dup(_cpuset)}
128     {}
core_infocore_info129     core_info(const core_info& src)
130         : cpuset{hwloc_bitmap_dup(src.cpuset)}
131     {}
132 
133     core_info& operator=(core_info src) {
134         std::swap(cpuset, src.cpuset);
135         return *this;
136     }
137 
~core_infocore_info138     ~core_info() {
139         hwloc_bitmap_free(cpuset);
140     }
141 };
142 
143 class system_info {
144     hwloc_topology_t topology;
145     hwloc_cpuset_t process_cpuset{nullptr};
146 
147     std::vector<index_info> numa_node_infos{};
148     std::vector<index_info> cpu_kind_infos{};
149     std::vector<core_info> core_infos{};
150 
151     // hwloc_cpuset_t and hwloc_nodeset_t (inherited from hwloc_bitmap_t ) is pointers,
152     // so we must manage memory allocation and deallocation
153     using memory_handler_t = tbb::concurrent_unordered_set<hwloc_bitmap_t>;
154     memory_handler_t memory_handler{};
155 
156     static system_info* system_info_ptr;
157 
158 public:
initialize()159     static void initialize() {
160         static system_info topology_instance;
161         system_info_ptr = &topology_instance;
162     }
163 
164 private:
instance()165     static system_info& instance() {
166         REQUIRE_MESSAGE(system_info_ptr, "Get access to the uninitialize system info.(reference)");
167         return *system_info_ptr;
168     }
169 
system_info()170     system_info() {
171         hwloc_require_ex(hwloc_topology_init, &topology);
172 #if __HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING_PRESENT
173         if ( get_processors_group_count() == 1 ) {
174             REQUIRE(
175                 hwloc_topology_set_flags(topology,
176                     HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM |
177                     HWLOC_TOPOLOGY_FLAG_RESTRICT_TO_CPUBINDING) == 0
178             );
179         }
180 #endif
181         hwloc_require_ex(hwloc_topology_load, topology);
182 
183 #if __HWLOC_CPUBIND_PRESENT
184         if ( get_processors_group_count() > 1 ) {
185             process_cpuset = hwloc_bitmap_dup(hwloc_topology_get_complete_cpuset(topology));
186         } else {
187             process_cpuset = hwloc_bitmap_alloc();
188             hwloc_require_ex(hwloc_get_cpubind, topology, process_cpuset, 0);
189         }
190 #else
191         process_cpuset = hwloc_bitmap_dup(hwloc_topology_get_complete_cpuset(topology));
192 #endif
193 
194         hwloc_obj_t current_numa_node = nullptr;
195         index_info current_node_info{};
196         while ((current_numa_node = hwloc_get_next_obj_by_type(topology,
197                                                                HWLOC_OBJ_NUMANODE,
198                                                                current_numa_node)) != nullptr) {
199             current_node_info.index = static_cast<int>(current_numa_node->logical_index);
200             current_node_info.cpuset = hwloc_bitmap_dup(current_numa_node->cpuset);
201             hwloc_bitmap_and(current_node_info.cpuset, current_node_info.cpuset, process_cpuset);
202             current_node_info.concurrency = hwloc_bitmap_weight(current_node_info.cpuset);
203             if(current_node_info.concurrency) {
204                 numa_node_infos.push_back(current_node_info);
205             }
206         }
207 
208         if (numa_node_infos.empty()) {
209             current_node_info.index = 0;
210             current_node_info.cpuset = hwloc_bitmap_dup(process_cpuset);
211             current_node_info.concurrency = hwloc_bitmap_weight(process_cpuset);
212             numa_node_infos.push_back(current_node_info);
213         }
214 
215         std::sort(numa_node_infos.begin(), numa_node_infos.end(),
216             [](const index_info& a, const index_info& b) {
217                 return a.index < b.index;
218             }
219         );
220 
221         bool core_types_parsing_broken = true;
222 #if __HYBRID_CPUS_TESTING
223         // Getting CPU kinds info
224         auto num_cpu_kinds = hwloc_cpukinds_get_nr(topology, 0);
225         REQUIRE_MESSAGE(num_cpu_kinds >= 0,
226             "HWLOC cannot detect the number of cpukinds.(reference)");
227 
228         core_types_parsing_broken = num_cpu_kinds == 0;
229         int current_efficiency = -1;
230         cpu_kind_infos.resize(num_cpu_kinds);
231         for (auto kind_index = 0; kind_index < num_cpu_kinds; ++kind_index) {
232             auto& cki = cpu_kind_infos[kind_index];
233 
234             cki.cpuset = hwloc_bitmap_alloc();
235             CHECK_MESSAGE(
236                 cki.cpuset,
237                 "HWLOC was unable to allocate bitmap. Following checks might fail.(reference)"
238             );
239 
240             hwloc_require_ex(
241                 hwloc_cpukinds_get_info, topology, kind_index, cki.cpuset, &current_efficiency,
242                 /*nr_infos*/nullptr, /*infos*/nullptr, /*flags*/0
243             );
244             if (current_efficiency < 0) {
245                 core_types_parsing_broken = true;
246                 break;
247             }
248             hwloc_bitmap_and(cki.cpuset, cki.cpuset, process_cpuset);
249 
250             cki.index = hwloc_cpukinds_get_by_cpuset(topology, cki.cpuset, /*flags*/0);
251             REQUIRE_MESSAGE(cki.index >= 0,
252                 "hwloc failed obtaining kind index via cpuset.(reference)");
253 
254             cki.concurrency = hwloc_bitmap_weight(cki.cpuset);
255         }
256 #endif /*__HYBRID_CPUS_TESTING*/
257 
258         if (core_types_parsing_broken) {
259             cpu_kind_infos.resize(1);
260             cpu_kind_infos[0].index = -1;
261             cpu_kind_infos[0].cpuset = hwloc_bitmap_dup(process_cpuset);
262             cpu_kind_infos[0].concurrency = hwloc_bitmap_weight(process_cpuset);
263         }
264 
265         hwloc_bitmap_t core_affinity = hwloc_bitmap_alloc();
266         hwloc_obj_t current_core = nullptr;
267         while ((current_core = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_CORE, current_core)) != nullptr) {
268             hwloc_bitmap_and(core_affinity, process_cpuset, current_core->cpuset);
269             if (hwloc_bitmap_weight(core_affinity) > 0) {
270                 core_infos.emplace_back(core_affinity);
271             }
272         }
273         hwloc_bitmap_free(core_affinity);
274 
275         testing_reference_topology_parsing_validation();
276     }
277 
~system_info()278     ~system_info() {
279         for (auto& allocated_mask: memory_handler) {
280             hwloc_bitmap_free(allocated_mask);
281         }
282         hwloc_topology_destroy(topology);
283 
284         hwloc_bitmap_free(process_cpuset);
285     }
286 
testing_reference_topology_parsing_validation()287     void testing_reference_topology_parsing_validation() {
288         hwloc_cpuset_t buffer_cpu_set = hwloc_bitmap_alloc();
289 
290         REQUIRE_MESSAGE(numa_node_infos.size() > 0, "Negative NUMA nodes count.(reference)");
291         REQUIRE_MESSAGE(cpu_kind_infos.size() > 0, "Negative core types count.(reference)");
292         REQUIRE_MESSAGE(core_infos.size() > 0, "Negative available cores count.(reference)");
293 #if NUMA_NODES_NUMBER
294         REQUIRE_MESSAGE(numa_node_infos.size() == NUMA_NODES_NUMBER,
295             "Manual NUMA nodes count validation fails.(reference)");
296 #endif /*NUMA_NODES_NUMBER*/
297 #if CORE_TYPES_NUMBER
298         REQUIRE_MESSAGE(cpu_kind_infos.size() == CORE_TYPES_NUMBER,
299             "Manual core types count validation fails.(reference)");
300 #endif /*CORE_TYPES_NUMBER*/
301 
302         // NUMA topology verification
303         hwloc_bitmap_zero(buffer_cpu_set);
304         for (const auto& numa_node_info: numa_node_infos) {
305             REQUIRE_MESSAGE(!hwloc_bitmap_intersects(buffer_cpu_set, numa_node_info.cpuset),
306                 "NUMA nodes related CPUset have the same bits. "
307                 "It seems like error during HWLOC topology parsing.(reference)");
308             hwloc_bitmap_or(buffer_cpu_set, buffer_cpu_set, numa_node_info.cpuset);
309         }
310         REQUIRE_MESSAGE( hwloc_bitmap_isequal(buffer_cpu_set, process_cpuset),
311             "Intersected NUMA nodes masks should be equal to process affinity.(reference)");
312 
313         // Core types topology verification
314         hwloc_bitmap_zero(buffer_cpu_set);
315         for (const auto& cpu_kind_info: cpu_kind_infos) {
316             REQUIRE_FALSE_MESSAGE(hwloc_bitmap_intersects(buffer_cpu_set, cpu_kind_info.cpuset),
317                 "core types related CPUset have the same bits. "
318                 "It seems like error during HWLOC topology parsing.(reference)");
319             hwloc_bitmap_or(buffer_cpu_set, buffer_cpu_set, cpu_kind_info.cpuset);
320         }
321         REQUIRE_MESSAGE(hwloc_bitmap_isequal(buffer_cpu_set, process_cpuset),
322             "Intersected core type masks should be equal to process affinity.(reference)");
323 
324         hwloc_bitmap_free(buffer_cpu_set);
325     }
326 
327 public:
328     typedef hwloc_bitmap_t affinity_mask;
329     typedef hwloc_const_bitmap_t const_affinity_mask;
330 
get_process_affinity_mask()331     static hwloc_const_bitmap_t get_process_affinity_mask() {
332         return instance().process_cpuset;
333     }
334 
get_maximal_threads_per_core()335     static std::size_t get_maximal_threads_per_core() {
336         auto max_threads_it =
337             std::max_element(
338                 std::begin(instance().core_infos), std::end(instance().core_infos),
339                 [](const core_info& first, const core_info& second) {
340                     return hwloc_bitmap_weight(first.cpuset) < hwloc_bitmap_weight(second.cpuset);
341                 }
342             );
343         __TBB_ASSERT(hwloc_bitmap_weight(max_threads_it->cpuset) > 0,
344             "Not positive maximal threads per core value.(reference)");
345         return hwloc_bitmap_weight(max_threads_it->cpuset);
346     }
347 
allocate_empty_affinity_mask()348     static affinity_mask allocate_empty_affinity_mask() {
349         affinity_mask result = hwloc_bitmap_alloc();
350         instance().memory_handler.insert(result);
351         return result;
352     }
353 
allocate_current_affinity_mask()354     static affinity_mask allocate_current_affinity_mask() {
355         affinity_mask result = hwloc_bitmap_alloc();
356         instance().memory_handler.insert(result);
357 #if __HWLOC_CPUBIND_PRESENT
358         hwloc_require_ex(hwloc_get_cpubind, instance().topology, result, HWLOC_CPUBIND_THREAD);
359 #else
360         hwloc_bitmap_copy(result, hwloc_topology_get_complete_cpuset(instance().topology));
361 #endif
362         REQUIRE_MESSAGE(!hwloc_bitmap_iszero(result), "Empty current affinity mask.");
363         return result;
364     }
365 
get_cpu_kinds_info()366     static std::vector<index_info> get_cpu_kinds_info() {
367         return instance().cpu_kind_infos;
368     }
369 
get_numa_nodes_info()370     static std::vector<index_info> get_numa_nodes_info() {
371         return instance().numa_node_infos;
372     }
373 
get_cores_info()374     static std::vector<core_info> get_cores_info() {
375         return instance().core_infos;
376     }
377 
get_available_max_threads_values()378     static std::vector<int> get_available_max_threads_values() {
379         std::vector<int> result{};
380         for (int value = -1; value <= (int)get_maximal_threads_per_core(); ++value) {
381             if (value != 0) {
382                 result.push_back(value);
383             }
384         }
385         return result;
386     }
387 }; // class system_info
388 
389 system_info* system_info::system_info_ptr{nullptr};
390 
prepare_reference_affinity_mask(const tbb::task_arena::constraints & c)391 system_info::affinity_mask prepare_reference_affinity_mask(const tbb::task_arena::constraints& c) {
392     auto reference_affinity = system_info::allocate_empty_affinity_mask();
393     hwloc_bitmap_copy(reference_affinity, system_info::get_process_affinity_mask());
394 
395     if (c.numa_id != tbb::task_arena::automatic) {
396         const auto& numa_nodes_info = system_info::get_numa_nodes_info();
397         auto required_info = std::find_if(numa_nodes_info.begin(), numa_nodes_info.end(),
398             [&](const index_info& info) { return info.index == c.numa_id; }
399         );
400 
401         REQUIRE_MESSAGE(required_info != numa_nodes_info.end(), "Constraints instance has wrong NUMA index.");
402         hwloc_bitmap_and(reference_affinity, reference_affinity, required_info->cpuset);
403     }
404 
405     if (c.core_type != tbb::task_arena::automatic) {
406         const auto& core_types_info = system_info::get_cpu_kinds_info();
407         auto required_info = std::find_if(core_types_info.begin(), core_types_info.end(),
408             [&](index_info info) { return info.index == c.core_type; }
409         );
410         REQUIRE_MESSAGE(required_info != core_types_info.end(), "Constraints instance has wrong core type index.");
411         hwloc_bitmap_and(reference_affinity, reference_affinity, required_info->cpuset);
412     }
413 
414     return reference_affinity;
415 }
416 
test_constraints_affinity_and_concurrency(tbb::task_arena::constraints constraints,system_info::affinity_mask arena_affinity)417 void test_constraints_affinity_and_concurrency(tbb::task_arena::constraints constraints,
418                                                system_info::affinity_mask arena_affinity) {
419     int default_concurrency = tbb::info::default_concurrency(constraints);
420     system_info::affinity_mask reference_affinity = prepare_reference_affinity_mask(constraints);
421     int max_threads_per_core = static_cast<int>(system_info::get_maximal_threads_per_core());
422 
423     if (constraints.max_threads_per_core == tbb::task_arena::automatic || constraints.max_threads_per_core == max_threads_per_core) {
424         REQUIRE_MESSAGE(hwloc_bitmap_isequal(reference_affinity, arena_affinity),
425             "Wrong affinity mask was applied for the constraints instance.");
426         REQUIRE_MESSAGE(hwloc_bitmap_weight(reference_affinity) == default_concurrency,
427             "Wrong default_concurrency was returned for the constraints instance.");
428     } else {
429         REQUIRE_MESSAGE(constraints.max_threads_per_core < max_threads_per_core,
430             "Constraints instance has wrong max_threads_per_core value.");
431         REQUIRE_MESSAGE(hwloc_bitmap_isincluded(arena_affinity, reference_affinity),
432             "If custom threads per core value is applied then the applied affinity"
433             "should be a sub-set of the affinity applied to constraints without such restriction.");
434 
435         system_info::affinity_mask core_affinity = system_info::allocate_empty_affinity_mask();
436         int threads_per_current_core = 0, valid_concurrency = 0;
437         for (const auto& current_core : system_info::get_cores_info()) {
438             hwloc_bitmap_and(core_affinity, reference_affinity, current_core.cpuset);
439             threads_per_current_core = hwloc_bitmap_weight(core_affinity);
440             if (threads_per_current_core > 0) { // current core should exist in the valid affinity mask
441                 hwloc_bitmap_and(core_affinity, arena_affinity, current_core.cpuset);
442                 threads_per_current_core = std::min(threads_per_current_core, constraints.max_threads_per_core);
443                 valid_concurrency += threads_per_current_core;
444                 REQUIRE_MESSAGE(hwloc_bitmap_weight(core_affinity) == threads_per_current_core ,
445                     "Wrong number of threads may be scheduled to some core.");
446             }
447         }
448         REQUIRE_MESSAGE(valid_concurrency == default_concurrency,
449             "Wrong default_concurrency was returned for the constraints instance.");
450         REQUIRE_MESSAGE(valid_concurrency == hwloc_bitmap_weight(arena_affinity),
451             "Wrong number of bits inside the affinity mask.");
452     }
453 }
454 
get_arena_affinity(tbb::task_arena & ta)455 system_info::affinity_mask get_arena_affinity(tbb::task_arena& ta) {
456     system_info::affinity_mask arena_affinity;
457     ta.execute([&]{
458         arena_affinity = system_info::allocate_current_affinity_mask();
459     });
460 
461     utils::SpinBarrier exit_barrier(ta.max_concurrency());
462     tbb::spin_mutex affinity_mutex{};
463     for (int i = 0; i < ta.max_concurrency() - 1; ++i) {
464         ta.enqueue([&] {
465             {
466                 tbb::spin_mutex::scoped_lock lock(affinity_mutex);
467                 system_info::affinity_mask thread_affinity = system_info::allocate_current_affinity_mask();
468                 if (get_processors_group_count() == 1) {
469                     REQUIRE_MESSAGE(hwloc_bitmap_isequal(thread_affinity, arena_affinity),
470                         "Threads have different masks on machine without several processors groups.");
471                 }
472                 hwloc_bitmap_or(arena_affinity, arena_affinity, thread_affinity);
473             }
474             exit_barrier.wait();
475         });
476     }
477     exit_barrier.wait();
478     return arena_affinity;
479 }
480 
481 #else /*__TBB_HWLOC_VALID_ENVIRONMENT*/
482 
483 namespace system_info {
484     // The values that seems meaningful for the most systems that exists at this moment
485     // Should be used when we cannot parse the system topology
get_available_max_threads_values()486     std::vector<int> get_available_max_threads_values() { return {tbb::task_arena::automatic, 1, 2}; }
487 }
488 
489 #endif /*!__TBB_HWLOC_VALID_ENVIRONMENT*/
490 
491 struct constraints_hash {
operatorconstraints_hash492     std::size_t operator()(const tbb::task_arena::constraints& c) const {
493       return (std::hash<int>{}(c.numa_id) ^ std::hash<int>{}(c.core_type) ^ std::hash<int>{}(c.max_threads_per_core));
494     }
495 };
496 
497 struct constraints_equal {
operatorconstraints_equal498     bool operator()(const tbb::task_arena::constraints& c1, const tbb::task_arena::constraints& c2) const {
499         return (c1.numa_id == c2.numa_id &&
500                 c1.core_type == c2.core_type &&
501                 c1.max_threads_per_core == c2.max_threads_per_core);
502   }
503 };
504 
505 using constraints_container = std::unordered_set<tbb::task_arena::constraints, constraints_hash, constraints_equal>;
506 
507 // Using unordered_set to get rid of duplicated elements
generate_constraints_variety()508 constraints_container generate_constraints_variety() {
509     static constraints_container constraints_variety = [](){
510         using constraints = tbb::task_arena::constraints;
511         constraints_container results{};
512 
513         std::vector<tbb::numa_node_id> numa_nodes = tbb::info::numa_nodes();
514         numa_nodes.push_back((tbb::numa_node_id)tbb::task_arena::automatic);
515 
516 #if __HYBRID_CPUS_TESTING
517         std::vector<tbb::core_type_id> core_types = tbb::info::core_types();
518         core_types.push_back((tbb::core_type_id)tbb::task_arena::automatic);
519 #endif
520 
521         results.insert(constraints{});
522         for (const auto& numa_node : numa_nodes) {
523             results.insert(constraints{}.set_numa_id(numa_node));
524 #if __HYBRID_CPUS_TESTING
525             for (const auto& core_type : core_types) {
526                 results.insert(constraints{}.set_core_type(core_type));
527 
528                 results.insert(
529                     constraints{}
530                         .set_numa_id(numa_node)
531                         .set_core_type(core_type)
532                 );
533 
534                 for (const auto& max_threads_per_core: system_info::get_available_max_threads_values()) {
535                     results.insert(
536                         constraints{}
537                             .set_max_threads_per_core(max_threads_per_core)
538                     );
539 
540                     results.insert(
541                         constraints{}
542                             .set_numa_id(numa_node)
543                             .set_max_threads_per_core(max_threads_per_core)
544                     );
545 
546                     results.insert(
547                         constraints{}
548                             .set_core_type(core_type)
549                             .set_max_threads_per_core(max_threads_per_core)
550                     );
551 
552                     results.insert(
553                         constraints{}
554                             .set_numa_id(numa_node)
555                             .set_core_type(core_type)
556                             .set_max_threads_per_core(max_threads_per_core)
557                     );
558                 }
559             }
560 #endif /*__HYBRID_CPUS_TESTING*/
561         }
562 
563         // Some constraints may cause unexpected behavior, which would be fixed later.
564         if (get_processors_group_count() > 1) {
565             for(auto it = results.begin(); it != results.end(); ++it) {
566                 if (it->max_threads_per_core != tbb::task_arena::automatic
567                    && (it->numa_id == tbb::task_arena::automatic || tbb::info::numa_nodes().size() == 1)
568 #if __HYBRID_CPUS_TESTING
569                    && (it->core_type == tbb::task_arena::automatic || tbb::info::core_types().size() == 1)
570 #endif /*__HYBRID_CPUS_TESTING*/
571                 ) {
572                     it = results.erase(it);
573                 }
574             }
575         }
576         return results;
577     }();
578 
579     return constraints_variety;
580 }
581 #endif // __TBB_test_common_arena_constraints_H_
582