1 /*
2 Copyright (c) 2005-2017 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
18
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include "governor.h"
24 #include "tbb_main.h"
25 #include "scheduler.h"
26 #include "market.h"
27 #include "arena.h"
28
29 #include "tbb/task_scheduler_init.h"
30
31 #include "dynamic_link.h"
32
33 namespace tbb {
34 namespace internal {
35
36 //------------------------------------------------------------------------
37 // governor
38 //------------------------------------------------------------------------
39
40 #if __TBB_SURVIVE_THREAD_SWITCH
41 // Support for interoperability with Intel(R) Cilk(TM) Plus.
42
43 #if _WIN32
44 #define CILKLIB_NAME "cilkrts20.dll"
45 #else
46 #define CILKLIB_NAME "libcilkrts.so"
47 #endif
48
49 //! Handler for interoperation with cilkrts library.
50 static __cilk_tbb_retcode (*watch_stack_handler)(struct __cilk_tbb_unwatch_thunk* u,
51 struct __cilk_tbb_stack_op_thunk o);
52
53 //! Table describing how to link the handlers.
54 static const dynamic_link_descriptor CilkLinkTable[] = {
55 { "__cilkrts_watch_stack", (pointer_to_handler*)(void*)(&watch_stack_handler)
56 #if __TBB_WEAK_SYMBOLS_PRESENT
57 ,
58 nullptr
59 #endif
60 }
61 };
62
63 static atomic<do_once_state> cilkrts_load_state;
64
initialize_cilk_interop()65 bool initialize_cilk_interop() {
66 // Pinning can fail. This is a normal situation, and means that the current
67 // thread does not use cilkrts and consequently does not need interop.
68 return dynamic_link( CILKLIB_NAME, CilkLinkTable, 1, /*handle=*/0, DYNAMIC_LINK_GLOBAL );
69 }
70 #endif /* __TBB_SURVIVE_THREAD_SWITCH */
71
72 namespace rml {
73 tbb_server* make_private_server( tbb_client& client );
74 }
75
acquire_resources()76 void governor::acquire_resources () {
77 #if USE_PTHREAD
78 int status = theTLS.create(auto_terminate);
79 #else
80 int status = theTLS.create();
81 #endif
82 if( status )
83 handle_perror(status, "TBB failed to initialize task scheduler TLS\n");
84 is_speculation_enabled = cpu_has_speculation();
85 is_rethrow_broken = gcc_rethrow_exception_broken();
86 }
87
release_resources()88 void governor::release_resources () {
89 theRMLServerFactory.close();
90 destroy_process_mask();
91 #if TBB_USE_ASSERT
92 if( __TBB_InitOnce::initialization_done() && theTLS.get() )
93 runtime_warning( "TBB is unloaded while tbb::task_scheduler_init object is alive?" );
94 #endif
95 int status = theTLS.destroy();
96 if( status )
97 runtime_warning("failed to destroy task scheduler TLS: %s", strerror(status));
98 dynamic_unlink_all();
99 }
100
create_rml_server(rml::tbb_client & client)101 rml::tbb_server* governor::create_rml_server ( rml::tbb_client& client ) {
102 rml::tbb_server* server = NULL;
103 if( !UsePrivateRML ) {
104 ::rml::factory::status_type status = theRMLServerFactory.make_server( server, client );
105 if( status != ::rml::factory::st_success ) {
106 UsePrivateRML = true;
107 runtime_warning( "rml::tbb_factory::make_server failed with status %x, falling back on private rml", status );
108 }
109 }
110 if ( !server ) {
111 __TBB_ASSERT( UsePrivateRML, NULL );
112 server = rml::make_private_server( client );
113 }
114 __TBB_ASSERT( server, "Failed to create RML server" );
115 return server;
116 }
117
118
tls_value_of(generic_scheduler * s)119 uintptr_t governor::tls_value_of( generic_scheduler* s ) {
120 __TBB_ASSERT( (uintptr_t(s)&1) == 0, "Bad pointer to the scheduler" );
121 // LSB marks the scheduler initialized with arena
122 return uintptr_t(s) | uintptr_t((s && (s->my_arena || s->is_worker()))? 1 : 0);
123 }
124
assume_scheduler(generic_scheduler * s)125 void governor::assume_scheduler( generic_scheduler* s ) {
126 theTLS.set( tls_value_of(s) );
127 }
128
is_set(generic_scheduler * s)129 bool governor::is_set( generic_scheduler* s ) {
130 return theTLS.get() == tls_value_of(s);
131 }
132
sign_on(generic_scheduler * s)133 void governor::sign_on(generic_scheduler* s) {
134 __TBB_ASSERT( is_set(NULL) && s, NULL );
135 assume_scheduler( s );
136 #if __TBB_SURVIVE_THREAD_SWITCH
137 if( watch_stack_handler ) {
138 __cilk_tbb_stack_op_thunk o;
139 o.routine = &stack_op_handler;
140 o.data = s;
141 if( (*watch_stack_handler)(&s->my_cilk_unwatch_thunk, o) ) {
142 // Failed to register with cilkrts, make sure we are clean
143 s->my_cilk_unwatch_thunk.routine = NULL;
144 }
145 #if TBB_USE_ASSERT
146 else
147 s->my_cilk_state = generic_scheduler::cs_running;
148 #endif /* TBB_USE_ASSERT */
149 }
150 #endif /* __TBB_SURVIVE_THREAD_SWITCH */
151 __TBB_ASSERT( is_set(s), NULL );
152 }
153
sign_off(generic_scheduler * s)154 void governor::sign_off(generic_scheduler* s) {
155 suppress_unused_warning(s);
156 __TBB_ASSERT( is_set(s), "attempt to unregister a wrong scheduler instance" );
157 assume_scheduler(NULL);
158 #if __TBB_SURVIVE_THREAD_SWITCH
159 __cilk_tbb_unwatch_thunk &ut = s->my_cilk_unwatch_thunk;
160 if ( ut.routine )
161 (*ut.routine)(ut.data);
162 #endif /* __TBB_SURVIVE_THREAD_SWITCH */
163 }
164
one_time_init()165 void governor::one_time_init() {
166 if( !__TBB_InitOnce::initialization_done() )
167 DoOneTimeInitializations();
168 #if __TBB_SURVIVE_THREAD_SWITCH
169 atomic_do_once( &initialize_cilk_interop, cilkrts_load_state );
170 #endif /* __TBB_SURVIVE_THREAD_SWITCH */
171 }
172
init_scheduler_weak()173 generic_scheduler* governor::init_scheduler_weak() {
174 one_time_init();
175 __TBB_ASSERT( is_set(NULL), "TLS contains a scheduler?" );
176 generic_scheduler* s = generic_scheduler::create_master( NULL ); // without arena
177 s->my_auto_initialized = true;
178 return s;
179 }
180
init_scheduler(int num_threads,stack_size_type stack_size,bool auto_init)181 generic_scheduler* governor::init_scheduler( int num_threads, stack_size_type stack_size, bool auto_init ) {
182 one_time_init();
183 if ( uintptr_t v = theTLS.get() ) {
184 generic_scheduler* s = tls_scheduler_of( v );
185 if ( (v&1) == 0 ) { // TLS holds scheduler instance without arena
186 __TBB_ASSERT( s->my_ref_count == 1, "weakly initialized scheduler must have refcount equal to 1" );
187 __TBB_ASSERT( !s->my_arena, "weakly initialized scheduler must have no arena" );
188 __TBB_ASSERT( s->my_auto_initialized, "weakly initialized scheduler is supposed to be auto-initialized" );
189 s->attach_arena( market::create_arena( default_num_threads(), 1, 0 ), 0, /*is_master*/true );
190 __TBB_ASSERT( s->my_arena_index == 0, "Master thread must occupy the first slot in its arena" );
191 s->my_arena_slot->my_scheduler = s;
192 s->my_arena->my_default_ctx = s->default_context(); // it also transfers implied ownership
193 // Mark the scheduler as fully initialized
194 assume_scheduler( s );
195 }
196 // Increment refcount only for explicit instances of task_scheduler_init.
197 if ( !auto_init ) s->my_ref_count += 1;
198 __TBB_ASSERT( s->my_arena, "scheduler is not initialized fully" );
199 return s;
200 }
201 // Create new scheduler instance with arena
202 if( num_threads == task_scheduler_init::automatic )
203 num_threads = default_num_threads();
204 arena *a = market::create_arena( num_threads, 1, stack_size );
205 generic_scheduler* s = generic_scheduler::create_master( a );
206 __TBB_ASSERT(s, "Somehow a local scheduler creation for a master thread failed");
207 __TBB_ASSERT( is_set(s), NULL );
208 s->my_auto_initialized = auto_init;
209 return s;
210 }
211
terminate_scheduler(generic_scheduler * s,const task_scheduler_init *,bool blocking)212 bool governor::terminate_scheduler( generic_scheduler* s, const task_scheduler_init* /*tsi_ptr*/, bool blocking ) {
213 bool ok = false;
214 __TBB_ASSERT( is_set(s), "Attempt to terminate non-local scheduler instance" );
215 if (0 == --(s->my_ref_count)) {
216 ok = s->cleanup_master( blocking );
217 __TBB_ASSERT( is_set(NULL), "cleanup_master has not cleared its TLS slot" );
218 }
219 return ok;
220 }
221
auto_terminate(void * arg)222 void governor::auto_terminate(void* arg){
223 generic_scheduler* s = tls_scheduler_of( uintptr_t(arg) ); // arg is equivalent to theTLS.get()
224 if( s && s->my_auto_initialized ) {
225 if( !--(s->my_ref_count) ) {
226 // If the TLS slot is already cleared by OS or underlying concurrency
227 // runtime, restore its value.
228 if( !is_set(s) )
229 assume_scheduler(s);
230 s->cleanup_master( /*blocking_terminate=*/false );
231 __TBB_ASSERT( is_set(NULL), "cleanup_master has not cleared its TLS slot" );
232 }
233 }
234 }
235
print_version_info()236 void governor::print_version_info () {
237 if ( UsePrivateRML )
238 PrintExtraVersionInfo( "RML", "private" );
239 else {
240 PrintExtraVersionInfo( "RML", "shared" );
241 theRMLServerFactory.call_with_server_info( PrintRMLVersionInfo, (void*)"" );
242 }
243 #if __TBB_SURVIVE_THREAD_SWITCH
244 if( watch_stack_handler )
245 PrintExtraVersionInfo( "CILK", CILKLIB_NAME );
246 #endif /* __TBB_SURVIVE_THREAD_SWITCH */
247 }
248
initialize_rml_factory()249 void governor::initialize_rml_factory () {
250 ::rml::factory::status_type res = theRMLServerFactory.open();
251 UsePrivateRML = res != ::rml::factory::st_success;
252 }
253
254 #if __TBB_SURVIVE_THREAD_SWITCH
stack_op_handler(__cilk_tbb_stack_op op,void * data)255 __cilk_tbb_retcode governor::stack_op_handler( __cilk_tbb_stack_op op, void* data ) {
256 __TBB_ASSERT(data,NULL);
257 generic_scheduler* s = static_cast<generic_scheduler*>(data);
258 #if TBB_USE_ASSERT
259 void* current = local_scheduler_if_initialized();
260 #if _WIN32||_WIN64
261 uintptr_t thread_id = GetCurrentThreadId();
262 #else
263 uintptr_t thread_id = uintptr_t(pthread_self());
264 #endif
265
266 #endif /* TBB_USE_ASSERT */
267 switch( op ) {
268 default:
269 __TBB_ASSERT( 0, "invalid op" );
270 case CILK_TBB_STACK_ADOPT: {
271 __TBB_ASSERT( !current && s->my_cilk_state==generic_scheduler::cs_limbo ||
272 current==s && s->my_cilk_state==generic_scheduler::cs_running, "invalid adoption" );
273 #if TBB_USE_ASSERT
274 if( current==s )
275 runtime_warning( "redundant adoption of %p by thread %p\n", s, (void*)thread_id );
276 s->my_cilk_state = generic_scheduler::cs_running;
277 #endif /* TBB_USE_ASSERT */
278 assume_scheduler( s );
279 break;
280 }
281 case CILK_TBB_STACK_ORPHAN: {
282 __TBB_ASSERT( current==s && s->my_cilk_state==generic_scheduler::cs_running, "invalid orphaning" );
283 #if TBB_USE_ASSERT
284 s->my_cilk_state = generic_scheduler::cs_limbo;
285 #endif /* TBB_USE_ASSERT */
286 assume_scheduler(NULL);
287 break;
288 }
289 case CILK_TBB_STACK_RELEASE: {
290 __TBB_ASSERT( !current && s->my_cilk_state==generic_scheduler::cs_limbo ||
291 current==s && s->my_cilk_state==generic_scheduler::cs_running, "invalid release" );
292 #if TBB_USE_ASSERT
293 s->my_cilk_state = generic_scheduler::cs_freed;
294 #endif /* TBB_USE_ASSERT */
295 s->my_cilk_unwatch_thunk.routine = NULL;
296 auto_terminate( s );
297 }
298 }
299 return 0;
300 }
301 #endif /* __TBB_SURVIVE_THREAD_SWITCH */
302
303 } // namespace internal
304
305 //------------------------------------------------------------------------
306 // task_scheduler_init
307 //------------------------------------------------------------------------
308
309 using namespace internal;
310
311 /** Left out-of-line for the sake of the backward binary compatibility **/
initialize(int number_of_threads)312 void task_scheduler_init::initialize( int number_of_threads ) {
313 initialize( number_of_threads, 0 );
314 }
315
initialize(int number_of_threads,stack_size_type thread_stack_size)316 void task_scheduler_init::initialize( int number_of_threads, stack_size_type thread_stack_size ) {
317 #if __TBB_TASK_GROUP_CONTEXT && TBB_USE_EXCEPTIONS
318 uintptr_t new_mode = thread_stack_size & propagation_mode_mask;
319 #endif
320 thread_stack_size &= ~(stack_size_type)propagation_mode_mask;
321 if( number_of_threads!=deferred ) {
322 __TBB_ASSERT_RELEASE( !my_scheduler, "task_scheduler_init already initialized" );
323 __TBB_ASSERT_RELEASE( number_of_threads==automatic || number_of_threads > 0,
324 "number_of_threads for task_scheduler_init must be automatic or positive" );
325 internal::generic_scheduler *s = governor::init_scheduler( number_of_threads, thread_stack_size, /*auto_init=*/false );
326 #if __TBB_TASK_GROUP_CONTEXT && TBB_USE_EXCEPTIONS
327 if ( s->master_outermost_level() ) {
328 uintptr_t &vt = s->default_context()->my_version_and_traits;
329 uintptr_t prev_mode = vt & task_group_context::exact_exception ? propagation_mode_exact : 0;
330 vt = new_mode & propagation_mode_exact ? vt | task_group_context::exact_exception
331 : new_mode & propagation_mode_captured ? vt & ~task_group_context::exact_exception : vt;
332 // Use least significant bit of the scheduler pointer to store previous mode.
333 // This is necessary when components compiled with different compilers and/or
334 // TBB versions initialize the
335 my_scheduler = static_cast<scheduler*>((generic_scheduler*)((uintptr_t)s | prev_mode));
336 }
337 else
338 #endif /* __TBB_TASK_GROUP_CONTEXT && TBB_USE_EXCEPTIONS */
339 my_scheduler = s;
340 } else {
341 __TBB_ASSERT_RELEASE( !thread_stack_size, "deferred initialization ignores stack size setting" );
342 }
343 }
344
internal_terminate(bool blocking)345 bool task_scheduler_init::internal_terminate( bool blocking ) {
346 #if __TBB_TASK_GROUP_CONTEXT && TBB_USE_EXCEPTIONS
347 uintptr_t prev_mode = (uintptr_t)my_scheduler & propagation_mode_exact;
348 my_scheduler = (scheduler*)((uintptr_t)my_scheduler & ~(uintptr_t)propagation_mode_exact);
349 #endif /* __TBB_TASK_GROUP_CONTEXT && TBB_USE_EXCEPTIONS */
350 generic_scheduler* s = static_cast<generic_scheduler*>(my_scheduler);
351 my_scheduler = NULL;
352 __TBB_ASSERT_RELEASE( s, "task_scheduler_init::terminate without corresponding task_scheduler_init::initialize()");
353 #if __TBB_TASK_GROUP_CONTEXT && TBB_USE_EXCEPTIONS
354 if ( s->master_outermost_level() ) {
355 uintptr_t &vt = s->default_context()->my_version_and_traits;
356 vt = prev_mode & propagation_mode_exact ? vt | task_group_context::exact_exception
357 : vt & ~task_group_context::exact_exception;
358 }
359 #endif /* __TBB_TASK_GROUP_CONTEXT && TBB_USE_EXCEPTIONS */
360 return governor::terminate_scheduler(s, this, blocking);
361 }
362
terminate()363 void task_scheduler_init::terminate() {
364 internal_terminate(/*blocking_terminate=*/false);
365 }
366
367 #if __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE
internal_blocking_terminate(bool throwing)368 bool task_scheduler_init::internal_blocking_terminate( bool throwing ) {
369 bool ok = internal_terminate( /*blocking_terminate=*/true );
370 #if TBB_USE_EXCEPTIONS
371 if( throwing && !ok )
372 throw_exception( eid_blocking_thread_join_impossible );
373 #else
374 suppress_unused_warning( throwing );
375 #endif
376 return ok;
377 }
378 #endif // __TBB_SUPPORTS_WORKERS_WAITING_IN_TERMINATE
379
default_num_threads()380 int task_scheduler_init::default_num_threads() {
381 return governor::default_num_threads();
382 }
383
384 } // namespace tbb
385