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