Committing TBB 2019 Update 9 source code
[platform/upstream/tbb.git] / src / tbb / tbb_main.cpp
1 /*
2     Copyright (c) 2005-2019 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 "tbb/tbb_config.h"
18 #include "tbb/global_control.h"
19 #include "tbb_main.h"
20 #include "governor.h"
21 #include "market.h"
22 #include "tbb_misc.h"
23 #include "itt_notify.h"
24
25 namespace tbb {
26 namespace internal {
27
28 //------------------------------------------------------------------------
29 // Begin shared data layout.
30 // The following global data items are mostly read-only after initialization.
31 //------------------------------------------------------------------------
32
33 //! Padding in order to prevent false sharing.
34 static const char _pad[NFS_MaxLineSize - sizeof(int)] = {};
35
36 //------------------------------------------------------------------------
37 // governor data
38 basic_tls<uintptr_t> governor::theTLS;
39 unsigned governor::DefaultNumberOfThreads;
40 size_t governor::DefaultPageSize;
41 rml::tbb_factory governor::theRMLServerFactory;
42 bool governor::UsePrivateRML;
43 bool governor::is_speculation_enabled;
44 bool governor::is_rethrow_broken;
45
46 //------------------------------------------------------------------------
47 // market data
48 market* market::theMarket;
49 market::global_market_mutex_type market::theMarketMutex;
50
51 //------------------------------------------------------------------------
52 // One time initialization data
53
54 //! Counter of references to global shared resources such as TLS.
55 atomic<int> __TBB_InitOnce::count;
56
57 __TBB_atomic_flag __TBB_InitOnce::InitializationLock;
58
59 //! Flag that is set to true after one-time initializations are done.
60 bool __TBB_InitOnce::InitializationDone;
61
62 #if DO_ITT_NOTIFY
63     static bool ITT_Present;
64     static atomic<bool> ITT_InitializationDone;
65 #endif
66
67 #if !(_WIN32||_WIN64) || __TBB_SOURCE_DIRECTLY_INCLUDED
68     static __TBB_InitOnce __TBB_InitOnceHiddenInstance;
69 #endif
70
71 //------------------------------------------------------------------------
72 // generic_scheduler data
73
74 //! Pointer to the scheduler factory function
75 generic_scheduler* (*AllocateSchedulerPtr)( market&, bool );
76
77 #if __TBB_OLD_PRIMES_RNG
78 //! Table of primes used by fast random-number generator (FastRandom).
79 /** Also serves to keep anything else from being placed in the same
80     cache line as the global data items preceding it. */
81 static const unsigned Primes[] = {
82     0x9e3779b1, 0xffe6cc59, 0x2109f6dd, 0x43977ab5,
83     0xba5703f5, 0xb495a877, 0xe1626741, 0x79695e6b,
84     0xbc98c09f, 0xd5bee2b3, 0x287488f9, 0x3af18231,
85     0x9677cd4d, 0xbe3a6929, 0xadc6a877, 0xdcf0674b,
86     0xbe4d6fe9, 0x5f15e201, 0x99afc3fd, 0xf3f16801,
87     0xe222cfff, 0x24ba5fdb, 0x0620452d, 0x79f149e3,
88     0xc8b93f49, 0x972702cd, 0xb07dd827, 0x6c97d5ed,
89     0x085a3d61, 0x46eb5ea7, 0x3d9910ed, 0x2e687b5b,
90     0x29609227, 0x6eb081f1, 0x0954c4e1, 0x9d114db9,
91     0x542acfa9, 0xb3e6bd7b, 0x0742d917, 0xe9f3ffa7,
92     0x54581edb, 0xf2480f45, 0x0bb9288f, 0xef1affc7,
93     0x85fa0ca7, 0x3ccc14db, 0xe6baf34b, 0x343377f7,
94     0x5ca19031, 0xe6d9293b, 0xf0a9f391, 0x5d2e980b,
95     0xfc411073, 0xc3749363, 0xb892d829, 0x3549366b,
96     0x629750ad, 0xb98294e5, 0x892d9483, 0xc235baf3,
97     0x3d2402a3, 0x6bdef3c9, 0xbec333cd, 0x40c9520f
98 };
99
100 //------------------------------------------------------------------------
101 // End of shared data layout
102 //------------------------------------------------------------------------
103
104 //------------------------------------------------------------------------
105 // Shared data accessors
106 //------------------------------------------------------------------------
107
108 unsigned GetPrime ( unsigned seed ) {
109     return Primes[seed%(sizeof(Primes)/sizeof(Primes[0]))];
110 }
111 #endif //__TBB_OLD_PRIMES_RNG
112
113 //------------------------------------------------------------------------
114 // __TBB_InitOnce
115 //------------------------------------------------------------------------
116
117 void __TBB_InitOnce::add_ref() {
118     if( ++count==1 )
119         governor::acquire_resources();
120 }
121
122 void __TBB_InitOnce::remove_ref() {
123     int k = --count;
124     __TBB_ASSERT(k>=0,"removed __TBB_InitOnce ref that was not added?");
125     if( k==0 ) {
126         governor::release_resources();
127         ITT_FINI_ITTLIB();
128     }
129 }
130
131 //------------------------------------------------------------------------
132 // One-time Initializations
133 //------------------------------------------------------------------------
134
135 //! Defined in cache_aligned_allocator.cpp
136 void initialize_cache_aligned_allocator();
137
138 //! Defined in scheduler.cpp
139 void Scheduler_OneTimeInitialization ( bool itt_present );
140
141 #if DO_ITT_NOTIFY
142
143 static __itt_domain *tbb_domains[ITT_NUM_DOMAINS] = {};
144
145 struct resource_string {
146     const char *str;
147     __itt_string_handle *itt_str_handle;
148 };
149
150 //
151 // populate resource strings
152 //
153 #define TBB_STRING_RESOURCE( index_name, str ) { str, NULL },
154 static resource_string strings_for_itt[] = {
155     #include "tbb/internal/_tbb_strings.h"
156     { "num_resource_strings", NULL }
157 };
158 #undef TBB_STRING_RESOURCE
159
160 static __itt_string_handle *ITT_get_string_handle(int idx) {
161     __TBB_ASSERT( idx >= 0 && idx < NUM_STRINGS, "string handle out of valid range");
162     return (idx >= 0 && idx < NUM_STRINGS) ? strings_for_itt[idx].itt_str_handle : NULL;
163 }
164
165 static void ITT_init_domains() {
166     tbb_domains[ITT_DOMAIN_MAIN] = __itt_domain_create( _T("tbb") );
167     tbb_domains[ITT_DOMAIN_MAIN]->flags = 1;
168     tbb_domains[ITT_DOMAIN_FLOW] = __itt_domain_create( _T("tbb.flow") );
169     tbb_domains[ITT_DOMAIN_FLOW]->flags = 1;
170     tbb_domains[ITT_DOMAIN_ALGO] = __itt_domain_create( _T("tbb.algorithm") );
171     tbb_domains[ITT_DOMAIN_ALGO]->flags = 1;
172 }
173
174 static void ITT_init_strings() {
175     for ( int i = 0; i < NUM_STRINGS; ++i ) {
176 #if _WIN32||_WIN64
177         strings_for_itt[i].itt_str_handle = __itt_string_handle_createA( strings_for_itt[i].str );
178 #else
179         strings_for_itt[i].itt_str_handle = __itt_string_handle_create( strings_for_itt[i].str );
180 #endif
181     }
182 }
183
184 static void ITT_init() {
185     ITT_init_domains();
186     ITT_init_strings();
187 }
188
189 /** Thread-unsafe lazy one-time initialization of tools interop.
190     Used by both dummy handlers and general TBB one-time initialization routine. **/
191 void ITT_DoUnsafeOneTimeInitialization () {
192     // Double check ITT_InitializationDone is necessary because the first check 
193     // in ITT_DoOneTimeInitialization is not guarded with the __TBB_InitOnce lock.
194     if ( !ITT_InitializationDone ) {
195         ITT_Present = (__TBB_load_ittnotify()!=0);
196         if (ITT_Present) ITT_init();
197         ITT_InitializationDone = true;
198         ITT_SYNC_CREATE(&market::theMarketMutex, SyncType_GlobalLock, SyncObj_SchedulerInitialization);
199     }
200 }
201
202 /** Thread-safe lazy one-time initialization of tools interop.
203     Used by dummy handlers only. **/
204 extern "C"
205 void ITT_DoOneTimeInitialization() {
206     if ( !ITT_InitializationDone ) {
207         __TBB_InitOnce::lock();
208         ITT_DoUnsafeOneTimeInitialization();
209         __TBB_InitOnce::unlock();
210     }
211 }
212 #endif /* DO_ITT_NOTIFY */
213
214 //! Performs thread-safe lazy one-time general TBB initialization.
215 void DoOneTimeInitializations() {
216     suppress_unused_warning(_pad);
217     __TBB_InitOnce::lock();
218     // No fence required for load of InitializationDone, because we are inside a critical section.
219     if( !__TBB_InitOnce::InitializationDone ) {
220         __TBB_InitOnce::add_ref();
221         if( GetBoolEnvironmentVariable("TBB_VERSION") )
222             PrintVersion();
223         bool itt_present = false;
224 #if DO_ITT_NOTIFY
225         ITT_DoUnsafeOneTimeInitialization();
226         itt_present = ITT_Present;
227 #endif /* DO_ITT_NOTIFY */
228         initialize_cache_aligned_allocator();
229         governor::initialize_rml_factory();
230         Scheduler_OneTimeInitialization( itt_present );
231         // Force processor groups support detection
232         governor::default_num_threads();
233         // Force OS regular page size detection
234         governor::default_page_size();
235         // Dump version data
236         governor::print_version_info();
237         PrintExtraVersionInfo( "Tools support", itt_present ? "enabled" : "disabled" );
238         __TBB_InitOnce::InitializationDone = true;
239     }
240     __TBB_InitOnce::unlock();
241 }
242
243 #if (_WIN32||_WIN64) && !__TBB_SOURCE_DIRECTLY_INCLUDED
244 //! Windows "DllMain" that handles startup and shutdown of dynamic library.
245 extern "C" bool WINAPI DllMain( HANDLE /*hinstDLL*/, DWORD reason, LPVOID lpvReserved ) {
246     switch( reason ) {
247         case DLL_PROCESS_ATTACH:
248             __TBB_InitOnce::add_ref();
249             break;
250         case DLL_PROCESS_DETACH:
251             // Since THREAD_DETACH is not called for the main thread, call auto-termination
252             // here as well - but not during process shutdown (due to risk of a deadlock).
253             if( lpvReserved==NULL ) // library unload
254                 governor::terminate_auto_initialized_scheduler();
255             __TBB_InitOnce::remove_ref();
256             // It is assumed that InitializationDone is not set after DLL_PROCESS_DETACH,
257             // and thus no race on InitializationDone is possible.
258             if( __TBB_InitOnce::initialization_done() ) {
259                 // Remove reference that we added in DoOneTimeInitializations.
260                 __TBB_InitOnce::remove_ref();
261             }
262             break;
263         case DLL_THREAD_DETACH:
264             governor::terminate_auto_initialized_scheduler();
265             break;
266     }
267     return true;
268 }
269 #endif /* (_WIN32||_WIN64) && !__TBB_SOURCE_DIRECTLY_INCLUDED */
270
271 void itt_store_pointer_with_release_v3( void* dst, void* src ) {
272     ITT_NOTIFY(sync_releasing, dst);
273     __TBB_store_with_release(*static_cast<void**>(dst),src);
274 }
275
276 void* itt_load_pointer_with_acquire_v3( const void* src ) {
277     void* result = __TBB_load_with_acquire(*static_cast<void*const*>(src));
278     ITT_NOTIFY(sync_acquired, const_cast<void*>(src));
279     return result;
280 }
281
282 #if DO_ITT_NOTIFY
283 void call_itt_notify_v5(int t, void *ptr) {
284     switch (t) {
285     case 0: ITT_NOTIFY(sync_prepare, ptr); break;
286     case 1: ITT_NOTIFY(sync_cancel, ptr); break;
287     case 2: ITT_NOTIFY(sync_acquired, ptr); break;
288     case 3: ITT_NOTIFY(sync_releasing, ptr); break;
289     }
290 }
291 #else
292 void call_itt_notify_v5(int /*t*/, void* /*ptr*/) {}
293 #endif
294
295 #if DO_ITT_NOTIFY
296 const __itt_id itt_null_id = {0, 0, 0};
297
298 static inline __itt_domain* get_itt_domain( itt_domain_enum idx ) {
299     if (tbb_domains[idx] == NULL) {
300         ITT_DoOneTimeInitialization();
301     }
302     return tbb_domains[idx];
303 }
304
305 static inline void itt_id_make(__itt_id *id, void* addr, unsigned long long extra) {
306     *id = __itt_id_make(addr, extra);
307 }
308
309 static inline void itt_id_create(const __itt_domain *domain, __itt_id id) {
310     ITTNOTIFY_VOID_D1(id_create, domain, id);
311 }
312
313 void itt_make_task_group_v7( itt_domain_enum domain, void *group, unsigned long long group_extra,
314                              void *parent, unsigned long long parent_extra, string_index name_index ) {
315     if ( __itt_domain *d = get_itt_domain( domain ) ) {
316         __itt_id group_id = itt_null_id;
317         __itt_id parent_id = itt_null_id;
318         itt_id_make( &group_id, group, group_extra );
319         itt_id_create( d, group_id );
320         if ( parent ) {
321             itt_id_make( &parent_id, parent, parent_extra );
322         }
323         __itt_string_handle *n = ITT_get_string_handle(name_index);
324         ITTNOTIFY_VOID_D3(task_group, d, group_id, parent_id, n);
325     }
326 }
327
328 void itt_metadata_str_add_v7( itt_domain_enum domain, void *addr, unsigned long long addr_extra,
329                               string_index key, const char *value ) {
330     if ( __itt_domain *d = get_itt_domain( domain ) ) {
331         __itt_id id = itt_null_id;
332         itt_id_make( &id, addr, addr_extra );
333         __itt_string_handle *k = ITT_get_string_handle(key);
334        size_t value_length = strlen( value );
335 #if _WIN32||_WIN64
336         ITTNOTIFY_VOID_D4(metadata_str_addA, d, id, k, value, value_length);
337 #else
338         ITTNOTIFY_VOID_D4(metadata_str_add, d, id, k, value, value_length);
339 #endif
340     }
341 }
342
343 void itt_metadata_ptr_add_v11( itt_domain_enum domain, void *addr, unsigned long long addr_extra,
344                               string_index key, void *value ) {
345     if ( __itt_domain *d = get_itt_domain( domain ) ) {
346         __itt_id id = itt_null_id;
347         itt_id_make( &id, addr, addr_extra );
348         __itt_string_handle *k = ITT_get_string_handle(key);
349 #if __TBB_x86_32
350         ITTNOTIFY_VOID_D5(metadata_add, d, id, k, __itt_metadata_u32, 1, value);
351 #else
352         ITTNOTIFY_VOID_D5(metadata_add, d, id, k, __itt_metadata_u64, 1, value);
353 #endif 
354     }
355 }
356
357
358 void itt_relation_add_v7( itt_domain_enum domain, void *addr0, unsigned long long addr0_extra,
359                           itt_relation relation, void *addr1, unsigned long long addr1_extra ) {
360     if ( __itt_domain *d = get_itt_domain( domain ) ) {
361         __itt_id id0 = itt_null_id;
362         __itt_id id1 = itt_null_id;
363         itt_id_make( &id0, addr0, addr0_extra );
364         itt_id_make( &id1, addr1, addr1_extra );
365         ITTNOTIFY_VOID_D3(relation_add, d, id0, (__itt_relation)relation, id1);
366     }
367 }
368
369 void itt_task_begin_v7( itt_domain_enum domain, void *task, unsigned long long task_extra,
370                         void *parent, unsigned long long parent_extra, string_index name_index ) {
371     if ( __itt_domain *d = get_itt_domain( domain ) ) {
372         __itt_id task_id = itt_null_id;
373         __itt_id parent_id = itt_null_id;
374         if ( task ) {
375             itt_id_make( &task_id, task, task_extra );
376         }
377         if ( parent ) {
378             itt_id_make( &parent_id, parent, parent_extra );
379         }
380         __itt_string_handle *n = ITT_get_string_handle(name_index);
381         ITTNOTIFY_VOID_D3(task_begin, d, task_id, parent_id, n );
382     }
383 }
384
385 void itt_task_end_v7( itt_domain_enum domain ) {
386     if ( __itt_domain *d = get_itt_domain( domain ) ) {
387         ITTNOTIFY_VOID_D0(task_end, d);
388     }
389 }
390
391 void itt_region_begin_v9( itt_domain_enum domain, void *region, unsigned long long region_extra,
392                           void *parent, unsigned long long parent_extra, string_index /* name_index */ ) {
393     if ( __itt_domain *d = get_itt_domain( domain ) ) {
394         __itt_id region_id = itt_null_id;
395         __itt_id parent_id = itt_null_id;
396         itt_id_make( &region_id, region, region_extra );
397         if ( parent ) {
398             itt_id_make( &parent_id, parent, parent_extra );
399         }
400         ITTNOTIFY_VOID_D3(region_begin, d, region_id, parent_id, NULL );
401     }
402 }
403
404 void itt_region_end_v9( itt_domain_enum domain, void *region, unsigned long long region_extra ) {
405     if ( __itt_domain *d = get_itt_domain( domain ) ) {
406         __itt_id region_id = itt_null_id;
407         itt_id_make( &region_id, region, region_extra );
408         ITTNOTIFY_VOID_D1( region_end, d, region_id );
409     }
410 }
411
412 #else // DO_ITT_NOTIFY
413
414 void itt_make_task_group_v7( itt_domain_enum /*domain*/, void* /*group*/, unsigned long long /*group_extra*/,
415                              void* /*parent*/, unsigned long long /*parent_extra*/, string_index /*name_index*/ ) { }
416
417 void itt_metadata_str_add_v7( itt_domain_enum /*domain*/, void* /*addr*/, unsigned long long /*addr_extra*/,
418                               string_index /*key*/, const char* /*value*/ ) { }
419
420 void itt_relation_add_v7( itt_domain_enum /*domain*/, void* /*addr0*/, unsigned long long /*addr0_extra*/,
421                           itt_relation /*relation*/, void* /*addr1*/, unsigned long long /*addr1_extra*/ ) { }
422
423 void itt_metadata_ptr_add_v11( itt_domain_enum /*domain*/, void * /*addr*/, unsigned long long /*addr_extra*/,
424                               string_index /*key*/, void * /*value*/ ) {}
425
426 void itt_task_begin_v7( itt_domain_enum /*domain*/, void* /*task*/, unsigned long long /*task_extra*/,
427                         void* /*parent*/, unsigned long long /*parent_extra*/, string_index /*name_index*/ ) { }
428
429 void itt_task_end_v7( itt_domain_enum /*domain*/ ) { }
430
431 void itt_region_begin_v9( itt_domain_enum /*domain*/, void* /*region*/, unsigned long long /*region_extra*/,
432                           void* /*parent*/, unsigned long long /*parent_extra*/, string_index /*name_index*/ ) { }
433
434 void itt_region_end_v9( itt_domain_enum /*domain*/, void* /*region*/, unsigned long long /*region_extra*/ ) { }
435
436 #endif // DO_ITT_NOTIFY
437
438 void* itt_load_pointer_v3( const void* src ) {
439     //TODO: replace this with __TBB_load_relaxed
440     void* result = *static_cast<void*const*>(src);
441     return result;
442 }
443
444 void itt_set_sync_name_v3( void* obj, const tchar* name) {
445     ITT_SYNC_RENAME(obj, name);
446     suppress_unused_warning(obj, name);
447 }
448
449
450 class control_storage {
451     friend class tbb::interface9::global_control;
452 protected:
453     size_t my_active_value;
454     atomic<global_control*> my_head;
455     spin_mutex my_list_mutex;
456
457     virtual size_t default_value() const = 0;
458     virtual void apply_active() const {}
459     virtual bool is_first_arg_preferred(size_t a, size_t b) const {
460         return a>b; // prefer max by default
461     }
462     virtual size_t active_value() const {
463         return my_head? my_active_value : default_value();
464     }
465 };
466
467 class allowed_parallelism_control : public padded<control_storage> {
468     virtual size_t default_value() const __TBB_override {
469         return max(1U, governor::default_num_threads());
470     }
471     virtual bool is_first_arg_preferred(size_t a, size_t b) const __TBB_override {
472         return a<b; // prefer min allowed parallelism
473     }
474     virtual void apply_active() const __TBB_override {
475         __TBB_ASSERT( my_active_value>=1, NULL );
476         // -1 to take master into account
477         market::set_active_num_workers( my_active_value-1 );
478     }
479     virtual size_t active_value() const __TBB_override {
480 /* Reading of my_active_value is not synchronized with possible updating
481    of my_head by other thread. It's ok, as value of my_active_value became
482    not invalid, just obsolete. */
483         if (!my_head)
484             return default_value();
485         // non-zero, if market is active
486         const size_t workers = market::max_num_workers();
487         // We can't exceed market's maximal number of workers.
488         // +1 to take master into account
489         return workers? min(workers+1, my_active_value): my_active_value;
490     }
491 public:
492     size_t active_value_if_present() const {
493         return my_head? my_active_value : 0;
494     }
495 };
496
497 class stack_size_control : public padded<control_storage> {
498     virtual size_t default_value() const __TBB_override {
499         return tbb::internal::ThreadStackSize;
500     }
501     virtual void apply_active() const __TBB_override {
502 #if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00)
503         __TBB_ASSERT( false, "For Windows 8 Store* apps we must not set stack size" );
504 #endif
505     }
506 };
507
508 static allowed_parallelism_control allowed_parallelism_ctl;
509 static stack_size_control stack_size_ctl;
510
511 static control_storage *controls[] = {&allowed_parallelism_ctl, &stack_size_ctl};
512
513 unsigned market::app_parallelism_limit() {
514     return allowed_parallelism_ctl.active_value_if_present();
515 }
516
517 } // namespace internal
518
519 namespace interface9 {
520
521 using namespace internal;
522 using namespace tbb::internal;
523
524 void global_control::internal_create() {
525     __TBB_ASSERT_RELEASE( my_param < global_control::parameter_max, NULL );
526     control_storage *const c = controls[my_param];
527
528     spin_mutex::scoped_lock lock(c->my_list_mutex);
529     if (!c->my_head || c->is_first_arg_preferred(my_value, c->my_active_value)) {
530         c->my_active_value = my_value;
531         // to guarantee that apply_active() is called with current active value,
532         // calls it here and in internal_destroy() under my_list_mutex
533         c->apply_active();
534     }
535     my_next = c->my_head;
536     // publish my_head, at this point my_active_value must be valid
537     c->my_head = this;
538 }
539
540 void global_control::internal_destroy() {
541     global_control *prev = 0;
542
543     __TBB_ASSERT_RELEASE( my_param < global_control::parameter_max, NULL );
544     control_storage *const c = controls[my_param];
545     __TBB_ASSERT( c->my_head, NULL );
546
547     // Concurrent reading and changing global parameter is possible.
548     // In this case, my_active_value may not match current state of parameters.
549     // This is OK because:
550     // 1) my_active_value is either current or previous
551     // 2) my_active_value is current on internal_destroy leave
552     spin_mutex::scoped_lock lock(c->my_list_mutex);
553     size_t new_active = (size_t)-1, old_active = c->my_active_value;
554
555     if ( c->my_head != this )
556         new_active = c->my_head->my_value;
557     else if ( c->my_head->my_next )
558         new_active = c->my_head->my_next->my_value;
559     // if there is only one element, new_active will be set later
560     for ( global_control *curr = c->my_head; curr; prev = curr, curr = curr->my_next )
561         if ( curr == this ) {
562             if ( prev )
563                 prev->my_next = my_next;
564             else
565                 c->my_head = my_next;
566         } else
567             if (c->is_first_arg_preferred(curr->my_value, new_active))
568                 new_active = curr->my_value;
569
570     if ( !c->my_head ) {
571         __TBB_ASSERT( new_active==(size_t)-1, NULL );
572         new_active = c->default_value();
573     }
574     if ( new_active != old_active ) {
575         c->my_active_value = new_active;
576         c->apply_active();
577     }
578 }
579
580 size_t global_control::active_value( int param ) {
581     __TBB_ASSERT_RELEASE( param < global_control::parameter_max, NULL );
582     return controls[param]->active_value();
583 }
584
585 } // tbb::interface9
586 } // namespace tbb