Intel(R) Threading Building Blocks Doxygen Documentation  version 4.2.3
scheduler.cpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 2005-2018 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 "custom_scheduler.h"
22 #include "scheduler_utility.h"
23 #include "governor.h"
24 #include "market.h"
25 #include "arena.h"
26 #include "mailbox.h"
27 #include "observer_proxy.h"
28 #include "tbb/tbb_machine.h"
29 #include "tbb/atomic.h"
30 
31 namespace tbb {
32 namespace internal {
33 
34 //------------------------------------------------------------------------
35 // Library initialization
36 //------------------------------------------------------------------------
37 
39 extern generic_scheduler* (*AllocateSchedulerPtr)( market& );
40 
42  return AllocateSchedulerPtr( m );
43 }
44 
45 #if __TBB_TASK_GROUP_CONTEXT
46 context_state_propagation_mutex_type the_context_state_propagation_mutex;
47 
48 uintptr_t the_context_state_propagation_epoch = 0;
49 
51 
53 static task_group_context the_dummy_context(task_group_context::isolated);
54 #endif /* __TBB_TASK_GROUP_CONTEXT */
55 
56 void Scheduler_OneTimeInitialization ( bool itt_present ) {
59 #if __TBB_TASK_GROUP_CONTEXT
60  // There must be no tasks belonging to this fake task group. Mark invalid for the assert
62  the_dummy_context.my_state = task_group_context::low_unused_state_bit;
63 #if __TBB_TASK_PRIORITY
64  // It should never prevent tasks from being passed to execution.
65  the_dummy_context.my_priority = num_priority_levels - 1;
66 #endif /* __TBB_TASK_PRIORITY */
67 #endif /* __TBB_TASK_GROUP_CONTEXT */
68 }
69 
70 //------------------------------------------------------------------------
71 // scheduler interface
72 //------------------------------------------------------------------------
73 
74 // A pure virtual destructor should still have a body
75 // so the one for tbb::internal::scheduler::~scheduler() is provided here
77 
78 //------------------------------------------------------------------------
79 // generic_scheduler
80 //------------------------------------------------------------------------
81 
82 #if _MSC_VER && !defined(__INTEL_COMPILER)
83  // Suppress overzealous compiler warning about using 'this' in base initializer list.
84  #pragma warning(push)
85  #pragma warning(disable:4355)
86 #endif
87 
89  : my_market(&m)
90  , my_random(this)
91  , my_ref_count(1)
92  , my_small_task_count(1) // Extra 1 is a guard reference
93 #if __TBB_SURVIVE_THREAD_SWITCH && TBB_USE_ASSERT
94  , my_cilk_state(cs_none)
95 #endif /* __TBB_SURVIVE_THREAD_SWITCH && TBB_USE_ASSERT */
96 {
97  __TBB_ASSERT( !my_arena_index, "constructor expects the memory being zero-initialized" );
98  __TBB_ASSERT( governor::is_set(NULL), "scheduler is already initialized for this thread" );
99 
100  my_innermost_running_task = my_dummy_task = &allocate_task( sizeof(task), __TBB_CONTEXT_ARG(NULL, &the_dummy_context) );
101  my_properties.outermost = true;
102 #if __TBB_TASK_PRIORITY
103  my_ref_top_priority = &m.my_global_top_priority;
104  my_ref_reload_epoch = &m.my_global_reload_epoch;
105 #endif /* __TBB_TASK_PRIORITY */
106 #if __TBB_TASK_GROUP_CONTEXT
107  // Sync up the local cancellation state with the global one. No need for fence here.
108  my_context_state_propagation_epoch = the_context_state_propagation_epoch;
109  my_context_list_head.my_prev = &my_context_list_head;
110  my_context_list_head.my_next = &my_context_list_head;
111  ITT_SYNC_CREATE(&my_context_list_mutex, SyncType_Scheduler, SyncObj_ContextsList);
112 #endif /* __TBB_TASK_GROUP_CONTEXT */
113  ITT_SYNC_CREATE(&my_dummy_task->prefix().ref_count, SyncType_Scheduler, SyncObj_WorkerLifeCycleMgmt);
114  ITT_SYNC_CREATE(&my_return_list, SyncType_Scheduler, SyncObj_TaskReturnList);
115 }
116 
117 #if _MSC_VER && !defined(__INTEL_COMPILER)
118  #pragma warning(pop)
119 #endif // warning 4355 is back
120 
121 #if TBB_USE_ASSERT > 1
123  if ( !my_arena_slot )
124  return;
129  const size_t H = __TBB_load_relaxed(my_arena_slot->head); // mirror
130  const size_t T = __TBB_load_relaxed(my_arena_slot->tail); // mirror
131  __TBB_ASSERT( H <= T, NULL );
132  for ( size_t i = 0; i < H; ++i )
133  __TBB_ASSERT( tp[i] == poisoned_ptr, "Task pool corrupted" );
134  for ( size_t i = H; i < T; ++i ) {
135  if ( tp[i] ) {
136  assert_task_valid( tp[i] );
137  __TBB_ASSERT( tp[i]->prefix().state == task::ready ||
138  tp[i]->prefix().extra_state == es_task_proxy, "task in the deque has invalid state" );
139  }
140  }
141  for ( size_t i = T; i < my_arena_slot->my_task_pool_size; ++i )
142  __TBB_ASSERT( tp[i] == poisoned_ptr, "Task pool corrupted" );
144 }
145 #endif /* TBB_USE_ASSERT > 1 */
146 
148  // Stacks are growing top-down. Highest address is called "stack base",
149  // and the lowest is "stack limit".
150  __TBB_ASSERT( !my_stealing_threshold, "Stealing threshold has already been calculated" );
151  size_t stack_size = my_market->worker_stack_size();
152 #if USE_WINTHREAD
153 #if defined(_MSC_VER)&&_MSC_VER<1400 && !_WIN64
154  NT_TIB *pteb;
155  __asm mov eax, fs:[0x18]
156  __asm mov pteb, eax
157 #else
158  NT_TIB *pteb = (NT_TIB*)NtCurrentTeb();
159 #endif
160  __TBB_ASSERT( &pteb < pteb->StackBase && &pteb > pteb->StackLimit, "invalid stack info in TEB" );
161  __TBB_ASSERT( stack_size >0, "stack_size not initialized?" );
162  // When a thread is created with the attribute STACK_SIZE_PARAM_IS_A_RESERVATION, stack limit
163  // in the TIB points to the committed part of the stack only. This renders the expression
164  // "(uintptr_t)pteb->StackBase / 2 + (uintptr_t)pteb->StackLimit / 2" virtually useless.
165  // Thus for worker threads we use the explicit stack size we used while creating them.
166  // And for master threads we rely on the following fact and assumption:
167  // - the default stack size of a master thread on Windows is 1M;
168  // - if it was explicitly set by the application it is at least as large as the size of a worker stack.
169  if ( is_worker() || stack_size < MByte )
170  my_stealing_threshold = (uintptr_t)pteb->StackBase - stack_size / 2;
171  else
172  my_stealing_threshold = (uintptr_t)pteb->StackBase - MByte / 2;
173 #else /* USE_PTHREAD */
174  // There is no portable way to get stack base address in Posix, so we use
175  // non-portable method (on all modern Linux) or the simplified approach
176  // based on the common sense assumptions. The most important assumption
177  // is that the main thread's stack size is not less than that of other threads.
178  // See also comment 3 at the end of this file
179  void *stack_base = &stack_size;
180 #if __linux__ && !__bg__
181 #if __TBB_ipf
182  void *rsb_base = __TBB_get_bsp();
183 #endif
184  size_t np_stack_size = 0;
185  void *stack_limit = NULL;
186  pthread_attr_t np_attr_stack;
187  if( 0 == pthread_getattr_np(pthread_self(), &np_attr_stack) ) {
188  if ( 0 == pthread_attr_getstack(&np_attr_stack, &stack_limit, &np_stack_size) ) {
189 #if __TBB_ipf
190  pthread_attr_t attr_stack;
191  if ( 0 == pthread_attr_init(&attr_stack) ) {
192  if ( 0 == pthread_attr_getstacksize(&attr_stack, &stack_size) ) {
193  if ( np_stack_size < stack_size ) {
194  // We are in a secondary thread. Use reliable data.
195  // IA-64 architecture stack is split into RSE backup and memory parts
196  rsb_base = stack_limit;
197  stack_size = np_stack_size/2;
198  // Limit of the memory part of the stack
199  stack_limit = (char*)stack_limit + stack_size;
200  }
201  // We are either in the main thread or this thread stack
202  // is bigger that that of the main one. As we cannot discern
203  // these cases we fall back to the default (heuristic) values.
204  }
205  pthread_attr_destroy(&attr_stack);
206  }
207  // IA-64 architecture stack is split into RSE backup and memory parts
208  my_rsb_stealing_threshold = (uintptr_t)((char*)rsb_base + stack_size/2);
209 #endif /* __TBB_ipf */
210  // Size of the stack free part
211  stack_size = size_t((char*)stack_base - (char*)stack_limit);
212  }
213  pthread_attr_destroy(&np_attr_stack);
214  }
215 #endif /* __linux__ */
216  __TBB_ASSERT( stack_size>0, "stack size must be positive" );
217  my_stealing_threshold = (uintptr_t)((char*)stack_base - stack_size/2);
218 #endif /* USE_PTHREAD */
219 }
220 
221 #if __TBB_TASK_GROUP_CONTEXT
222 
227 void generic_scheduler::cleanup_local_context_list () {
228  // Detach contexts remaining in the local list
229  bool wait_for_concurrent_destroyers_to_leave = false;
230  uintptr_t local_count_snapshot = my_context_state_propagation_epoch;
231  my_local_ctx_list_update.store<relaxed>(1);
232  {
233  // This is just a definition. Actual lock is acquired only in case of conflict.
235  // Full fence prevents reordering of store to my_local_ctx_list_update with
236  // load from my_nonlocal_ctx_list_update.
237  atomic_fence();
238  // Check for the conflict with concurrent destroyer or cancellation propagator
239  if ( my_nonlocal_ctx_list_update.load<relaxed>() || local_count_snapshot != the_context_state_propagation_epoch )
240  lock.acquire(my_context_list_mutex);
241  // No acquire fence is necessary for loading my_context_list_head.my_next,
242  // as the list can be updated by this thread only.
243  context_list_node_t *node = my_context_list_head.my_next;
244  while ( node != &my_context_list_head ) {
246  __TBB_ASSERT( __TBB_load_relaxed(ctx.my_kind) != task_group_context::binding_required, "Only a context bound to a root task can be detached" );
247  node = node->my_next;
248  __TBB_ASSERT( is_alive(ctx.my_version_and_traits), "Walked into a destroyed context while detaching contexts from the local list" );
249  // Synchronizes with ~task_group_context(). TODO: evaluate and perhaps relax
251  wait_for_concurrent_destroyers_to_leave = true;
252  }
253  }
254  my_local_ctx_list_update.store<release>(0);
255  // Wait until other threads referencing this scheduler object finish with it
256  if ( wait_for_concurrent_destroyers_to_leave )
257  spin_wait_until_eq( my_nonlocal_ctx_list_update, 0u );
258 }
259 #endif /* __TBB_TASK_GROUP_CONTEXT */
260 
262  __TBB_ASSERT( !my_arena_slot, NULL );
263 #if __TBB_TASK_GROUP_CONTEXT
264  cleanup_local_context_list();
265 #endif /* __TBB_TASK_GROUP_CONTEXT */
266  free_task<small_local_task>( *my_dummy_task );
267 
268 #if __TBB_HOARD_NONLOCAL_TASKS
269  while( task* t = my_nonlocal_free_list ) {
270  task_prefix& p = t->prefix();
271  my_nonlocal_free_list = p.next;
272  __TBB_ASSERT( p.origin && p.origin!=this, NULL );
274  }
275 #endif
276  // k accounts for a guard reference and each task that we deallocate.
277  intptr_t k = 1;
278  for(;;) {
279  while( task* t = my_free_list ) {
280  my_free_list = t->prefix().next;
281  deallocate_task(*t);
282  ++k;
283  }
285  break;
286  my_free_list = (task*)__TBB_FetchAndStoreW( &my_return_list, (intptr_t)plugged_return_list() );
287  }
288 #if __TBB_COUNT_TASK_NODES
289  my_market->update_task_node_count( my_task_node_count );
290 #endif /* __TBB_COUNT_TASK_NODES */
291  // Update my_small_task_count last. Doing so sooner might cause another thread to free *this.
292  __TBB_ASSERT( my_small_task_count>=k, "my_small_task_count corrupted" );
293  governor::sign_off(this);
294  if( __TBB_FetchAndAddW( &my_small_task_count, -k )==k )
295  NFS_Free( this );
296 }
297 
298 task& generic_scheduler::allocate_task( size_t number_of_bytes,
300  GATHER_STATISTIC(++my_counters.active_tasks);
301  task *t;
302  if( number_of_bytes<=quick_task_size ) {
303 #if __TBB_HOARD_NONLOCAL_TASKS
304  if( (t = my_nonlocal_free_list) ) {
305  GATHER_STATISTIC(--my_counters.free_list_length);
306  __TBB_ASSERT( t->state()==task::freed, "free list of tasks is corrupted" );
307  my_nonlocal_free_list = t->prefix().next;
308  } else
309 #endif
310  if( (t = my_free_list) ) {
311  GATHER_STATISTIC(--my_counters.free_list_length);
312  __TBB_ASSERT( t->state()==task::freed, "free list of tasks is corrupted" );
313  my_free_list = t->prefix().next;
314  } else if( my_return_list ) {
315  // No fence required for read of my_return_list above, because __TBB_FetchAndStoreW has a fence.
316  t = (task*)__TBB_FetchAndStoreW( &my_return_list, 0 ); // with acquire
317  __TBB_ASSERT( t, "another thread emptied the my_return_list" );
318  __TBB_ASSERT( t->prefix().origin==this, "task returned to wrong my_return_list" );
319  ITT_NOTIFY( sync_acquired, &my_return_list );
320  my_free_list = t->prefix().next;
321  } else {
323 #if __TBB_COUNT_TASK_NODES
324  ++my_task_node_count;
325 #endif /* __TBB_COUNT_TASK_NODES */
326  t->prefix().origin = this;
327  t->prefix().next = 0;
329  }
330 #if __TBB_PREFETCHING
331  task *t_next = t->prefix().next;
332  if( !t_next ) { // the task was last in the list
333 #if __TBB_HOARD_NONLOCAL_TASKS
334  if( my_free_list )
335  t_next = my_free_list;
336  else
337 #endif
338  if( my_return_list ) // enable prefetching, gives speedup
339  t_next = my_free_list = (task*)__TBB_FetchAndStoreW( &my_return_list, 0 );
340  }
341  if( t_next ) { // gives speedup for both cache lines
342  __TBB_cl_prefetch(t_next);
343  __TBB_cl_prefetch(&t_next->prefix());
344  }
345 #endif /* __TBB_PREFETCHING */
346  } else {
347  GATHER_STATISTIC(++my_counters.big_tasks);
348  t = (task*)((char*)NFS_Allocate( 1, task_prefix_reservation_size+number_of_bytes, NULL ) + task_prefix_reservation_size );
349 #if __TBB_COUNT_TASK_NODES
350  ++my_task_node_count;
351 #endif /* __TBB_COUNT_TASK_NODES */
352  t->prefix().origin = NULL;
353  }
354  task_prefix& p = t->prefix();
355 #if __TBB_TASK_GROUP_CONTEXT
356  p.context = context;
357 #endif /* __TBB_TASK_GROUP_CONTEXT */
358  // Obsolete. But still in use, so has to be assigned correct value here.
359  p.owner = this;
360  p.ref_count = 0;
361  // Obsolete. Assign some not outrageously out-of-place value for a while.
362  p.depth = 0;
363  p.parent = parent;
364  // In TBB 2.1 and later, the constructor for task sets extra_state to indicate the version of the tbb/task.h header.
365  // In TBB 2.0 and earlier, the constructor leaves extra_state as zero.
366  p.extra_state = 0;
367  p.affinity = 0;
368  p.state = task::allocated;
369  __TBB_ISOLATION_EXPR( p.isolation = no_isolation );
370  return *t;
371 }
372 
374  __TBB_ASSERT( t.state()==task::freed, NULL );
375  generic_scheduler& s = *static_cast<generic_scheduler*>(t.prefix().origin);
376  __TBB_ASSERT( &s!=this, NULL );
377  for(;;) {
378  task* old = s.my_return_list;
379  if( old==plugged_return_list() )
380  break;
381  // Atomically insert t at head of s.my_return_list
382  t.prefix().next = old;
383  ITT_NOTIFY( sync_releasing, &s.my_return_list );
384  if( as_atomic(s.my_return_list).compare_and_swap(&t, old )==old ) {
385 #if __TBB_PREFETCHING
386  __TBB_cl_evict(&t.prefix());
387  __TBB_cl_evict(&t);
388 #endif
389  return;
390  }
391  }
392  deallocate_task(t);
393  if( __TBB_FetchAndDecrementWrelease( &s.my_small_task_count )==1 ) {
394  // We freed the last task allocated by scheduler s, so it's our responsibility
395  // to free the scheduler.
396  NFS_Free( &s );
397  }
398 }
399 
400 inline size_t generic_scheduler::prepare_task_pool ( size_t num_tasks ) {
401  size_t T = __TBB_load_relaxed(my_arena_slot->tail); // mirror
402  if ( T + num_tasks <= my_arena_slot->my_task_pool_size )
403  return T;
404 
405  size_t new_size = num_tasks;
406 
410  if ( num_tasks < min_task_pool_size ) new_size = min_task_pool_size;
412  return 0;
413  }
414 
416  size_t H = __TBB_load_relaxed( my_arena_slot->head ); // mirror
417  task** task_pool = my_arena_slot->task_pool_ptr;;
419  // Count not skipped tasks. Consider using std::count_if.
420  for ( size_t i = H; i < T; ++i )
421  if ( task_pool[i] ) ++new_size;
422  // If the free space at the beginning of the task pool is too short, we
423  // are likely facing a pathological single-producer-multiple-consumers
424  // scenario, and thus it's better to expand the task pool
426  if ( allocate ) {
427  // Grow task pool. As this operation is rare, and its cost is asymptotically
428  // amortizable, we can tolerate new task pool allocation done under the lock.
429  if ( new_size < 2 * my_arena_slot->my_task_pool_size )
431  my_arena_slot->allocate_task_pool( new_size ); // updates my_task_pool_size
432  }
433  // Filter out skipped tasks. Consider using std::copy_if.
434  size_t T1 = 0;
435  for ( size_t i = H; i < T; ++i )
436  if ( task_pool[i] )
437  my_arena_slot->task_pool_ptr[T1++] = task_pool[i];
438  // Deallocate the previous task pool if a new one has been allocated.
439  if ( allocate )
440  NFS_Free( task_pool );
441  else
443  // Publish the new state.
446  return T1;
447 }
448 
455  if ( !is_task_pool_published() )
456  return; // we are not in arena - nothing to lock
457  bool sync_prepare_done = false;
458  for( atomic_backoff b;;b.pause() ) {
459 #if TBB_USE_ASSERT
460  __TBB_ASSERT( my_arena_slot == my_arena->my_slots + my_arena_index, "invalid arena slot index" );
461  // Local copy of the arena slot task pool pointer is necessary for the next
462  // assertion to work correctly to exclude asynchronous state transition effect.
463  task** tp = my_arena_slot->task_pool;
464  __TBB_ASSERT( tp == LockedTaskPool || tp == my_arena_slot->task_pool_ptr, "slot ownership corrupt?" );
465 #endif
468  {
469  // We acquired our own slot
470  ITT_NOTIFY(sync_acquired, my_arena_slot);
471  break;
472  }
473  else if( !sync_prepare_done ) {
474  // Start waiting
475  ITT_NOTIFY(sync_prepare, my_arena_slot);
476  sync_prepare_done = true;
477  }
478  // Someone else acquired a lock, so pause and do exponential backoff.
479  }
480  __TBB_ASSERT( my_arena_slot->task_pool == LockedTaskPool, "not really acquired task pool" );
481 } // generic_scheduler::acquire_task_pool
482 
484  if ( !is_task_pool_published() )
485  return; // we are not in arena - nothing to unlock
486  __TBB_ASSERT( my_arena_slot, "we are not in arena" );
487  __TBB_ASSERT( my_arena_slot->task_pool == LockedTaskPool, "arena slot is not locked" );
490 }
491 
498 inline task** generic_scheduler::lock_task_pool( arena_slot* victim_arena_slot ) const {
499  task** victim_task_pool;
500  bool sync_prepare_done = false;
501  for( atomic_backoff backoff;; /*backoff pause embedded in the loop*/) {
502  victim_task_pool = victim_arena_slot->task_pool;
503  // NOTE: Do not use comparison of head and tail indices to check for
504  // the presence of work in the victim's task pool, as they may give
505  // incorrect indication because of task pool relocations and resizes.
506  if ( victim_task_pool == EmptyTaskPool ) {
507  // The victim thread emptied its task pool - nothing to lock
508  if( sync_prepare_done )
509  ITT_NOTIFY(sync_cancel, victim_arena_slot);
510  break;
511  }
512  if( victim_task_pool != LockedTaskPool &&
513  as_atomic(victim_arena_slot->task_pool).compare_and_swap(LockedTaskPool, victim_task_pool ) == victim_task_pool )
514  {
515  // We've locked victim's task pool
516  ITT_NOTIFY(sync_acquired, victim_arena_slot);
517  break;
518  }
519  else if( !sync_prepare_done ) {
520  // Start waiting
521  ITT_NOTIFY(sync_prepare, victim_arena_slot);
522  sync_prepare_done = true;
523  }
524  GATHER_STATISTIC( ++my_counters.thieves_conflicts );
525  // Someone else acquired a lock, so pause and do exponential backoff.
526 #if __TBB_STEALING_ABORT_ON_CONTENTION
527  if(!backoff.bounded_pause()) {
528  // the 16 was acquired empirically and a theory behind it supposes
529  // that number of threads becomes much bigger than number of
530  // tasks which can be spawned by one thread causing excessive contention.
531  // TODO: However even small arenas can benefit from the abort on contention
532  // if preemption of a thief is a problem
533  if(my_arena->my_limit >= 16)
534  return EmptyTaskPool;
535  __TBB_Yield();
536  }
537 #else
538  backoff.pause();
539 #endif
540  }
541  __TBB_ASSERT( victim_task_pool == EmptyTaskPool ||
542  (victim_arena_slot->task_pool == LockedTaskPool && victim_task_pool != LockedTaskPool),
543  "not really locked victim's task pool?" );
544  return victim_task_pool;
545 } // generic_scheduler::lock_task_pool
546 
547 inline void generic_scheduler::unlock_task_pool( arena_slot* victim_arena_slot,
548  task** victim_task_pool ) const {
549  __TBB_ASSERT( victim_arena_slot, "empty victim arena slot pointer" );
550  __TBB_ASSERT( victim_arena_slot->task_pool == LockedTaskPool, "victim arena slot is not locked" );
551  ITT_NOTIFY(sync_releasing, victim_arena_slot);
552  __TBB_store_with_release( victim_arena_slot->task_pool, victim_task_pool );
553 }
554 
555 
557  __TBB_ASSERT( t->state()==task::allocated, "attempt to spawn task that is not in 'allocated' state" );
558  t->prefix().state = task::ready;
559 #if TBB_USE_ASSERT
560  if( task* parent = t->parent() ) {
561  internal::reference_count ref_count = parent->prefix().ref_count;
562  __TBB_ASSERT( ref_count>=0, "attempt to spawn task whose parent has a ref_count<0" );
563  __TBB_ASSERT( ref_count!=0, "attempt to spawn task whose parent has a ref_count==0 (forgot to set_ref_count?)" );
564  parent->prefix().extra_state |= es_ref_count_active;
565  }
566 #endif /* TBB_USE_ASSERT */
567  affinity_id dst_thread = t->prefix().affinity;
568  __TBB_ASSERT( dst_thread == 0 || is_version_3_task(*t),
569  "backwards compatibility to TBB 2.0 tasks is broken" );
570 #if __TBB_TASK_ISOLATION
571  isolation_tag isolation = my_innermost_running_task->prefix().isolation;
572  t->prefix().isolation = isolation;
573 #endif /* __TBB_TASK_ISOLATION */
574  if( dst_thread != 0 && dst_thread != my_affinity_id ) {
575  task_proxy& proxy = (task_proxy&)allocate_task( sizeof(task_proxy),
576  __TBB_CONTEXT_ARG(NULL, NULL) );
577  // Mark as a proxy
578  proxy.prefix().extra_state = es_task_proxy;
579  proxy.outbox = &my_arena->mailbox(dst_thread);
580  // Mark proxy as present in both locations (sender's task pool and destination mailbox)
581  proxy.task_and_tag = intptr_t(t) | task_proxy::location_mask;
582 #if __TBB_TASK_PRIORITY
583  poison_pointer( proxy.prefix().context );
584 #endif /* __TBB_TASK_PRIORITY */
585  __TBB_ISOLATION_EXPR( proxy.prefix().isolation = isolation );
586  ITT_NOTIFY( sync_releasing, proxy.outbox );
587  // Mail the proxy - after this point t may be destroyed by another thread at any moment.
588  proxy.outbox->push(&proxy);
589  return &proxy;
590  }
591  return t;
592 }
593 
597  __TBB_ASSERT( first, NULL );
598  __TBB_ASSERT( governor::is_set(this), NULL );
599 #if __TBB_TODO
600  // We need to consider capping the max task pool size and switching
601  // to in-place task execution whenever it is reached.
602 #endif
603  if ( &first->prefix().next == &next ) {
604  // Single task is being spawned
605 #if __TBB_TODO
606  // TODO:
607  // In the future we need to add overloaded spawn method for a single task,
608  // and a method accepting an array of task pointers (we may also want to
609  // change the implementation of the task_list class). But since such changes
610  // may affect the binary compatibility, we postpone them for a while.
611 #endif
612  size_t T = prepare_task_pool( 1 );
614  commit_spawned_tasks( T + 1 );
615  }
616  else {
617  // Task list is being spawned
618 #if __TBB_TODO
619  // TODO: add task_list::front() and implement&document the local execution ordering which is
620  // opposite to the current implementation. The idea is to remove hackish fast_reverse_vector
621  // and use push_back/push_front when accordingly LIFO and FIFO order of local execution is
622  // desired. It also requires refactoring of the reload_tasks method and my_offloaded_tasks list.
623  // Additional benefit may come from adding counter to the task_list so that it can reserve enough
624  // space in the task pool in advance and move all the tasks directly without any intermediate
625  // storages. But it requires dealing with backward compatibility issues and still supporting
626  // counter-less variant (though not necessarily fast implementation).
627 #endif
628  task *arr[min_task_pool_size];
630  task *t_next = NULL;
631  for( task* t = first; ; t = t_next ) {
632  // If t is affinitized to another thread, it may already be executed
633  // and destroyed by the time prepare_for_spawning returns.
634  // So milk it while it is alive.
635  bool end = &t->prefix().next == &next;
636  t_next = t->prefix().next;
637  tasks.push_back( prepare_for_spawning(t) );
638  if( end )
639  break;
640  }
641  size_t num_tasks = tasks.size();
642  size_t T = prepare_task_pool( num_tasks );
644  commit_spawned_tasks( T + num_tasks );
645  }
646  if ( !is_task_pool_published() )
650 }
651 
653  __TBB_ASSERT( governor::is_set(this), NULL );
654  __TBB_ASSERT( first, NULL );
655  auto_empty_task dummy( __TBB_CONTEXT_ARG(this, first->prefix().context) );
657  for( task* t=first; ; t=t->prefix().next ) {
658  ++n;
659  __TBB_ASSERT( !t->prefix().parent, "not a root task, or already running" );
660  t->prefix().parent = &dummy;
661  if( &t->prefix().next==&next ) break;
662 #if __TBB_TASK_GROUP_CONTEXT
663  __TBB_ASSERT( t->prefix().context == t->prefix().next->prefix().context,
664  "all the root tasks in list must share the same context");
665 #endif /* __TBB_TASK_GROUP_CONTEXT */
666  }
667  dummy.prefix().ref_count = n+1;
668  if( n>1 )
669  local_spawn( first->prefix().next, next );
670  local_wait_for_all( dummy, first );
671 }
672 
675 }
676 
679 }
680 
683  // these redirections are due to bw-compatibility, consider reworking some day
684  __TBB_ASSERT( s->my_arena, "thread is not in any arena" );
685  s->my_arena->enqueue_task(t, (intptr_t)prio, s->my_random );
686 }
687 
688 #if __TBB_TASK_PRIORITY
689 class auto_indicator : no_copy {
690  volatile bool& my_indicator;
691 public:
692  auto_indicator ( volatile bool& indicator ) : my_indicator(indicator) { my_indicator = true ;}
693  ~auto_indicator () { my_indicator = false; }
694 };
695 
696 task *generic_scheduler::get_task_and_activate_task_pool( size_t H0, __TBB_ISOLATION_ARG( size_t T0, isolation_tag isolation ) ) {
698 
699  // Go through the task pool to find an available task for execution.
700  task *t = NULL;
701 #if __TBB_TASK_ISOLATION
702  size_t T = T0;
703  bool tasks_omitted = false;
704  while ( !t && T>H0 ) {
705  t = get_task( --T, isolation, tasks_omitted );
706  if ( !tasks_omitted ) {
708  --T0;
709  }
710  }
711  // Make a hole if some tasks have been skipped.
712  if ( t && tasks_omitted ) {
713  my_arena_slot->task_pool_ptr[T] = NULL;
714  if ( T == H0 ) {
715  // The obtained task is on the head. So we can move the head instead of making a hole.
716  ++H0;
718  }
719  }
720 #else
721  while ( !t && T0 ) {
722  t = get_task( --T0 );
724  }
725 #endif /* __TBB_TASK_ISOLATION */
726 
727  if ( H0 < T0 ) {
728  // There are some tasks in the task pool. Publish them.
731  if ( is_task_pool_published() )
733  else
735  } else {
738  if ( is_task_pool_published() )
739  leave_task_pool();
740  }
741 
742 #if __TBB_TASK_ISOLATION
743  // Now it is safe to call note_affinity because the task pool is restored.
744  if ( tasks_omitted && my_innermost_running_task == t ) {
745  assert_task_valid( t );
746  t->note_affinity( my_affinity_id );
747  }
748 #endif /* __TBB_TASK_ISOLATION */
749 
751  return t;
752 }
753 
754 task* generic_scheduler::winnow_task_pool( __TBB_ISOLATION_EXPR( isolation_tag isolation ) ) {
755  GATHER_STATISTIC( ++my_counters.prio_winnowings );
757  __TBB_ASSERT( my_offloaded_tasks, "At least one task is expected to be already offloaded" );
758  // To eliminate possible sinking of the store to the indicator below the subsequent
759  // store to my_arena_slot->tail, the stores should have either been separated
760  // by full fence or both use release fences. And resetting indicator should have
761  // been done with release fence. But since this is just an optimization, and
762  // the corresponding checking sequence in arena::is_out_of_work() is not atomic
763  // anyway, fences aren't used, so that not to penalize warmer path.
764  auto_indicator indicator( my_pool_reshuffling_pending );
765 
766  // Locking the task pool unconditionally produces simpler code,
767  // scalability of which should not suffer unless priority jitter takes place.
768  // TODO: consider the synchronization algorithm here is for the owner thread
769  // to avoid locking task pool most of the time.
771  size_t T0 = __TBB_load_relaxed( my_arena_slot->tail );
772  size_t H0 = __TBB_load_relaxed( my_arena_slot->head );
773  size_t T1 = 0;
774  for ( size_t src = H0; src<T0; ++src ) {
775  if ( task *t = my_arena_slot->task_pool_ptr[src] ) {
776  // We cannot offload a proxy task (check the priority of it) because it can be already consumed.
777  if ( !is_proxy( *t ) ) {
778  intptr_t p = priority( *t );
779  if ( p<*my_ref_top_priority ) {
780  offload_task( *t, p );
781  continue;
782  }
783  }
784  my_arena_slot->task_pool_ptr[T1++] = t;
785  }
786  }
787  __TBB_ASSERT( T1<=T0, NULL );
788 
789  // Choose max(T1, H0) because ranges [0, T1) and [H0, T0) can overlap.
790  my_arena_slot->fill_with_canary_pattern( max( T1, H0 ), T0 );
791  return get_task_and_activate_task_pool( 0, __TBB_ISOLATION_ARG( T1, isolation ) );
792 }
793 
794 task* generic_scheduler::reload_tasks ( task*& offloaded_tasks, task**& offloaded_task_list_link, __TBB_ISOLATION_ARG( intptr_t top_priority, isolation_tag isolation ) ) {
795  GATHER_STATISTIC( ++my_counters.prio_reloads );
796 #if __TBB_TASK_ISOLATION
797  // In many cases, locking the task pool is no-op here because the task pool is in the empty
798  // state. However, isolation allows entering stealing loop with non-empty task pool.
799  // In principle, it is possible to process reloaded tasks without locking but it will
800  // complicate the logic of get_task_and_activate_task_pool (TODO: evaluate).
802 #else
804 #endif
805  task *arr[min_task_pool_size];
806  fast_reverse_vector<task*> tasks(arr, min_task_pool_size);
807  task **link = &offloaded_tasks;
808  while ( task *t = *link ) {
809  task** next_ptr = &t->prefix().next_offloaded;
810  __TBB_ASSERT( !is_proxy(*t), "The proxy tasks cannot be offloaded" );
811  if ( priority(*t) >= top_priority ) {
812  tasks.push_back( t );
813  // Note that owner is an alias of next_offloaded. Thus the following
814  // assignment overwrites *next_ptr
815  task* next = *next_ptr;
816  t->prefix().owner = this;
817  __TBB_ASSERT( t->prefix().state == task::ready, NULL );
818  *link = next;
819  }
820  else {
821  link = next_ptr;
822  }
823  }
824  if ( link == &offloaded_tasks ) {
825  offloaded_tasks = NULL;
826 #if TBB_USE_ASSERT
827  offloaded_task_list_link = NULL;
828 #endif /* TBB_USE_ASSERT */
829  }
830  else {
831  __TBB_ASSERT( link, NULL );
832  // Mark end of list
833  *link = NULL;
834  offloaded_task_list_link = link;
835  }
836  __TBB_ASSERT( link, NULL );
837  size_t num_tasks = tasks.size();
838  if ( !num_tasks ) {
840  return NULL;
841  }
842 
843  // Copy found tasks into the task pool.
844  GATHER_STATISTIC( ++my_counters.prio_tasks_reloaded );
845  size_t T = prepare_task_pool( num_tasks );
846  tasks.copy_memory( my_arena_slot->task_pool_ptr + T );
847 
848  // Find a task available for execution.
849  task *t = get_task_and_activate_task_pool( __TBB_load_relaxed( my_arena_slot->head ), __TBB_ISOLATION_ARG( T + num_tasks, isolation ) );
850  if ( t ) --num_tasks;
851  if ( num_tasks )
853 
854  return t;
855 }
856 
857 task* generic_scheduler::reload_tasks( __TBB_ISOLATION_EXPR( isolation_tag isolation ) ) {
858  uintptr_t reload_epoch = *my_ref_reload_epoch;
859  __TBB_ASSERT( my_offloaded_tasks, NULL );
860  __TBB_ASSERT( my_local_reload_epoch <= reload_epoch
861  || my_local_reload_epoch - reload_epoch > uintptr_t(-1)/2,
862  "Reload epoch counter overflow?" );
863  if ( my_local_reload_epoch == reload_epoch )
864  return NULL;
865  __TBB_ASSERT( my_offloaded_tasks, NULL );
866  intptr_t top_priority = effective_reference_priority();
867  __TBB_ASSERT( (uintptr_t)top_priority < (uintptr_t)num_priority_levels, NULL );
868  task *t = reload_tasks( my_offloaded_tasks, my_offloaded_task_list_tail_link, __TBB_ISOLATION_ARG( top_priority, isolation ) );
869  if ( my_offloaded_tasks && (my_arena->my_bottom_priority >= top_priority || !my_arena->my_num_workers_requested) ) {
870  // Safeguard against deliberately relaxed synchronization while checking
871  // for the presence of work in arena (so that not to impact hot paths).
872  // Arena may be reset to empty state when offloaded low priority tasks
873  // are still present. This results in both bottom and top priority bounds
874  // becoming 'normal', which makes offloaded low priority tasks unreachable.
875  // Update arena's bottom priority to accommodate them.
876  // NOTE: If the number of priority levels is increased, we may want
877  // to calculate minimum of priorities in my_offloaded_tasks.
878 
879  // First indicate the presence of lower-priority tasks
880  my_market->update_arena_priority( *my_arena, priority(*my_offloaded_tasks) );
881  // Then mark arena as full to unlock arena priority level adjustment
882  // by arena::is_out_of_work(), and ensure worker's presence
884  }
885  my_local_reload_epoch = reload_epoch;
886  return t;
887 }
888 #endif /* __TBB_TASK_PRIORITY */
889 
890 #if __TBB_TASK_ISOLATION
891 inline task* generic_scheduler::get_task( size_t T, isolation_tag isolation, bool& tasks_omitted )
892 #else
894 #endif /* __TBB_TASK_ISOLATION */
895 {
896  __TBB_ASSERT( __TBB_load_relaxed( my_arena_slot->tail ) <= T
897  || is_local_task_pool_quiescent(), "Is it safe to get a task at position T?" );
898 
899  task* result = my_arena_slot->task_pool_ptr[T];
900  __TBB_ASSERT( !is_poisoned( result ), "The poisoned task is going to be processed" );
901 #if __TBB_TASK_ISOLATION
902  if ( !result )
903  return NULL;
904 
905  bool omit = isolation != no_isolation && isolation != result->prefix().isolation;
906  if ( !omit && !is_proxy( *result ) )
907  return result;
908  else if ( omit ) {
909  tasks_omitted = true;
910  return NULL;
911  }
912 #else
913  poison_pointer( my_arena_slot->task_pool_ptr[T] );
914  if ( !result || !is_proxy( *result ) )
915  return result;
916 #endif /* __TBB_TASK_ISOLATION */
917 
918  task_proxy& tp = static_cast<task_proxy&>(*result);
919  if ( task *t = tp.extract_task<task_proxy::pool_bit>() ) {
920  GATHER_STATISTIC( ++my_counters.proxies_executed );
921  // Following assertion should be true because TBB 2.0 tasks never specify affinity, and hence are not proxied.
922  __TBB_ASSERT( is_version_3_task( *t ), "backwards compatibility with TBB 2.0 broken" );
923  __TBB_ASSERT( my_innermost_running_task != t, NULL );
924  my_innermost_running_task = t; // prepare for calling note_affinity()
925 #if __TBB_TASK_ISOLATION
926  // Task affinity has changed. Postpone calling note_affinity because the task pool is in invalid state.
927  if ( !tasks_omitted )
928 #endif /* __TBB_TASK_ISOLATION */
929  {
930  poison_pointer( my_arena_slot->task_pool_ptr[T] );
931  t->note_affinity( my_affinity_id );
932  }
933  return t;
934  }
935 
936  // Proxy was empty, so it's our responsibility to free it
937  free_task<small_task>( tp );
938 #if __TBB_TASK_ISOLATION
939  if ( tasks_omitted )
940  my_arena_slot->task_pool_ptr[T] = NULL;
941 #endif /* __TBB_TASK_ISOLATION */
942  return NULL;
943 }
944 
947  // The current task position in the task pool.
948  size_t T0 = __TBB_load_relaxed( my_arena_slot->tail );
949  // The bounds of available tasks in the task pool. H0 is only used when the head bound is reached.
950  size_t H0 = (size_t)-1, T = T0;
951  task* result = NULL;
952  bool task_pool_empty = false;
953  __TBB_ISOLATION_EXPR( bool tasks_omitted = false );
954  do {
955  __TBB_ASSERT( !result, NULL );
957  atomic_fence();
958  if ( (intptr_t)__TBB_load_relaxed( my_arena_slot->head ) > (intptr_t)T ) {
961  if ( (intptr_t)H0 > (intptr_t)T ) {
962  // The thief has not backed off - nothing to grab.
965  && H0 == T + 1, "victim/thief arbitration algorithm failure" );
967  // No tasks in the task pool.
968  task_pool_empty = true;
969  break;
970  } else if ( H0 == T ) {
971  // There is only one task in the task pool.
973  task_pool_empty = true;
974  } else {
975  // Release task pool if there are still some tasks.
976  // After the release, the tail will be less than T, thus a thief
977  // will not attempt to get a task at position T.
979  }
980  }
981  __TBB_control_consistency_helper(); // on my_arena_slot->head
982 #if __TBB_TASK_ISOLATION
983  result = get_task( T, isolation, tasks_omitted );
984  if ( result ) {
986  break;
987  } else if ( !tasks_omitted ) {
989  __TBB_ASSERT( T0 == T+1, NULL );
990  T0 = T;
991  }
992 #else
993  result = get_task( T );
994 #endif /* __TBB_TASK_ISOLATION */
995  } while ( !result && !task_pool_empty );
996 
997 #if __TBB_TASK_ISOLATION
998  if ( tasks_omitted ) {
999  if ( task_pool_empty ) {
1000  // All tasks have been checked. The task pool should be in reset state.
1001  // We just restore the bounds for the available tasks.
1002  // TODO: Does it have sense to move them to the beginning of the task pool?
1004  if ( result ) {
1005  // If we have a task, it should be at H0 position.
1006  __TBB_ASSERT( H0 == T, NULL );
1007  ++H0;
1008  }
1009  __TBB_ASSERT( H0 <= T0, NULL );
1010  if ( H0 < T0 ) {
1011  // Restore the task pool if there are some tasks.
1014  // The release fence is used in publish_task_pool.
1016  // Synchronize with snapshot as we published some tasks.
1018  }
1019  } else {
1020  // A task has been obtained. We need to make a hole in position T.
1022  __TBB_ASSERT( result, NULL );
1023  my_arena_slot->task_pool_ptr[T] = NULL;
1025  // Synchronize with snapshot as we published some tasks.
1026  // TODO: consider some approach not to call wakeup for each time. E.g. check if the tail reached the head.
1028  }
1029 
1030  // Now it is safe to call note_affinity because the task pool is restored.
1031  if ( my_innermost_running_task == result ) {
1032  assert_task_valid( result );
1033  result->note_affinity( my_affinity_id );
1034  }
1035  }
1036 #endif /* __TBB_TASK_ISOLATION */
1037  __TBB_ASSERT( (intptr_t)__TBB_load_relaxed( my_arena_slot->tail ) >= 0, NULL );
1038  __TBB_ASSERT( result || __TBB_ISOLATION_EXPR( tasks_omitted || ) is_quiescent_local_task_pool_reset(), NULL );
1039  return result;
1040 } // generic_scheduler::get_task
1041 
1043  task** victim_pool = lock_task_pool( &victim_slot );
1044  if ( !victim_pool )
1045  return NULL;
1046  task* result = NULL;
1047  size_t H = __TBB_load_relaxed(victim_slot.head); // mirror
1048  size_t H0 = H;
1049  bool tasks_omitted = false;
1050  do {
1051  __TBB_store_relaxed( victim_slot.head, ++H );
1052  atomic_fence();
1053  if ( (intptr_t)H > (intptr_t)__TBB_load_relaxed( victim_slot.tail ) ) {
1054  // Stealing attempt failed, deque contents has not been changed by us
1055  GATHER_STATISTIC( ++my_counters.thief_backoffs );
1056  __TBB_store_relaxed( victim_slot.head, /*dead: H = */ H0 );
1057  __TBB_ASSERT( !result, NULL );
1058  goto unlock;
1059  }
1060  __TBB_control_consistency_helper(); // on victim_slot.tail
1061  result = victim_pool[H-1];
1062  __TBB_ASSERT( !is_poisoned( result ), NULL );
1063 
1064  if ( result ) {
1065  __TBB_ISOLATION_EXPR( if ( isolation == no_isolation || isolation == result->prefix().isolation ) )
1066  {
1067  if ( !is_proxy( *result ) )
1068  break;
1069  task_proxy& tp = *static_cast<task_proxy*>(result);
1070  // If mailed task is likely to be grabbed by its destination thread, skip it.
1072  break;
1073  GATHER_STATISTIC( ++my_counters.proxies_bypassed );
1074  }
1075  // The task cannot be executed either due to isolation or proxy contraints.
1076  result = NULL;
1077  tasks_omitted = true;
1078  } else if ( !tasks_omitted ) {
1079  // Cleanup the task pool from holes until a task is skipped.
1080  __TBB_ASSERT( H0 == H-1, NULL );
1081  poison_pointer( victim_pool[H0] );
1082  H0 = H;
1083  }
1084  } while ( !result );
1085  __TBB_ASSERT( result, NULL );
1086 
1087  // emit "task was consumed" signal
1088  ITT_NOTIFY( sync_acquired, (void*)((uintptr_t)&victim_slot+sizeof( uintptr_t )) );
1089  poison_pointer( victim_pool[H-1] );
1090  if ( tasks_omitted ) {
1091  // Some proxies in the task pool have been omitted. Set the stolen task to NULL.
1092  victim_pool[H-1] = NULL;
1093  __TBB_store_relaxed( victim_slot.head, /*dead: H = */ H0 );
1094  }
1095 unlock:
1096  unlock_task_pool( &victim_slot, victim_pool );
1097 #if __TBB_PREFETCHING
1098  __TBB_cl_evict(&victim_slot.head);
1099  __TBB_cl_evict(&victim_slot.tail);
1100 #endif
1101  if ( tasks_omitted )
1102  // Synchronize with snapshot as the head and tail can be bumped which can falsely trigger EMPTY state
1104  return result;
1105 }
1106 
1108  __TBB_ASSERT( my_affinity_id>0, "not in arena" );
1109  while ( task_proxy* const tp = my_inbox.pop( __TBB_ISOLATION_EXPR( isolation ) ) ) {
1110  if ( task* result = tp->extract_task<task_proxy::mailbox_bit>() ) {
1111  ITT_NOTIFY( sync_acquired, my_inbox.outbox() );
1112  result->prefix().extra_state |= es_task_is_stolen;
1113  return result;
1114  }
1115  // We have exclusive access to the proxy, and can destroy it.
1116  free_task<no_cache_small_task>(*tp);
1117  }
1118  return NULL;
1119 }
1120 
1122  __TBB_ASSERT ( my_arena, "no arena: initialization not completed?" );
1123  __TBB_ASSERT ( my_arena_index < my_arena->my_num_slots, "arena slot index is out-of-bound" );
1125  __TBB_ASSERT ( my_arena_slot->task_pool == EmptyTaskPool, "someone else grabbed my arena slot?" );
1127  "entering arena without tasks to share" );
1128  // Release signal on behalf of previously spawned tasks (when this thread was not in arena yet)
1131 }
1132 
1134  __TBB_ASSERT( is_task_pool_published(), "Not in arena" );
1135  // Do not reset my_arena_index. It will be used to (attempt to) re-acquire the slot next time
1136  __TBB_ASSERT( &my_arena->my_slots[my_arena_index] == my_arena_slot, "arena slot and slot index mismatch" );
1137  __TBB_ASSERT ( my_arena_slot->task_pool == LockedTaskPool, "Task pool must be locked when leaving arena" );
1138  __TBB_ASSERT ( is_quiescent_local_task_pool_empty(), "Cannot leave arena when the task pool is not empty" );
1140  // No release fence is necessary here as this assignment precludes external
1141  // accesses to the local task pool when becomes visible. Thus it is harmless
1142  // if it gets hoisted above preceding local bookkeeping manipulations.
1144 }
1145 
1148  __TBB_ASSERT(index, "workers should have index > 0");
1149  s->my_arena_index = index; // index is not a real slot in arena yet
1150  s->my_dummy_task->prefix().ref_count = 2;
1151  s->my_properties.type = scheduler_properties::worker;
1152  // Do not call init_stack_info before the scheduler is set as master or worker.
1153  s->init_stack_info();
1155  return s;
1156 }
1157 
1158 // TODO: make it a member method
1160  // add an internal market reference; the public reference is possibly added in create_arena
1161  generic_scheduler* s = allocate_scheduler( market::global_market(/*is_public=*/false) );
1162  __TBB_ASSERT( !s->my_arena, NULL );
1163  __TBB_ASSERT( s->my_market, NULL );
1164  task& t = *s->my_dummy_task;
1165  s->my_properties.type = scheduler_properties::master;
1166  t.prefix().ref_count = 1;
1167 #if __TBB_TASK_GROUP_CONTEXT
1168  t.prefix().context = new ( NFS_Allocate(1, sizeof(task_group_context), NULL) )
1170 #if __TBB_FP_CONTEXT
1171  s->default_context()->capture_fp_settings();
1172 #endif
1173  // Do not call init_stack_info before the scheduler is set as master or worker.
1174  s->init_stack_info();
1175  context_state_propagation_mutex_type::scoped_lock lock(the_context_state_propagation_mutex);
1176  s->my_market->my_masters.push_front( *s );
1177  lock.release();
1178 #endif /* __TBB_TASK_GROUP_CONTEXT */
1179  if( a ) {
1180  // Master thread always occupies the first slot
1181  s->attach_arena( a, /*index*/0, /*is_master*/true );
1182  s->my_arena_slot->my_scheduler = s;
1183  a->my_default_ctx = s->default_context(); // also transfers implied ownership
1184  }
1185  __TBB_ASSERT( s->my_arena_index == 0, "Master thread must occupy the first slot in its arena" );
1187 
1188 #if _WIN32||_WIN64
1189  s->my_market->register_master( s->master_exec_resource );
1190 #endif /* _WIN32||_WIN64 */
1191  // Process any existing observers.
1192 #if __TBB_ARENA_OBSERVER
1193  __TBB_ASSERT( !a || a->my_observers.empty(), "Just created arena cannot have any observers associated with it" );
1194 #endif
1195 #if __TBB_SCHEDULER_OBSERVER
1196  the_global_observer_list.notify_entry_observers( s->my_last_global_observer, /*worker=*/false );
1197 #endif /* __TBB_SCHEDULER_OBSERVER */
1198  return s;
1199 }
1200 
1201 void generic_scheduler::cleanup_worker( void* arg, bool worker ) {
1203  __TBB_ASSERT( !s.my_arena_slot, "cleaning up attached worker" );
1204 #if __TBB_SCHEDULER_OBSERVER
1205  if ( worker ) // can be called by master for worker, do not notify master twice
1206  the_global_observer_list.notify_exit_observers( s.my_last_global_observer, /*worker=*/true );
1207 #endif /* __TBB_SCHEDULER_OBSERVER */
1208  s.free_scheduler();
1209 }
1210 
1211 bool generic_scheduler::cleanup_master( bool blocking_terminate ) {
1212  arena* const a = my_arena;
1213  market * const m = my_market;
1214  __TBB_ASSERT( my_market, NULL );
1215  if( a && is_task_pool_published() ) {
1219  {
1220  // Local task pool is empty
1221  leave_task_pool();
1222  }
1223  else {
1224  // Master's local task pool may e.g. contain proxies of affinitized tasks.
1226  __TBB_ASSERT ( governor::is_set(this), "TLS slot is cleared before the task pool cleanup" );
1229  __TBB_ASSERT ( governor::is_set(this), "Other thread reused our TLS key during the task pool cleanup" );
1230  }
1231  }
1232 #if __TBB_ARENA_OBSERVER
1233  if( a )
1234  a->my_observers.notify_exit_observers( my_last_local_observer, /*worker=*/false );
1235 #endif
1236 #if __TBB_SCHEDULER_OBSERVER
1237  the_global_observer_list.notify_exit_observers( my_last_global_observer, /*worker=*/false );
1238 #endif /* __TBB_SCHEDULER_OBSERVER */
1239 #if _WIN32||_WIN64
1240  m->unregister_master( master_exec_resource );
1241 #endif /* _WIN32||_WIN64 */
1242  if( a ) {
1243  __TBB_ASSERT(a->my_slots+0 == my_arena_slot, NULL);
1244 #if __TBB_STATISTICS
1245  *my_arena_slot->my_counters += my_counters;
1246 #endif /* __TBB_STATISTICS */
1248  }
1249 #if __TBB_TASK_GROUP_CONTEXT
1250  else { // task_group_context ownership was not transferred to arena
1251  default_context()->~task_group_context();
1252  NFS_Free(default_context());
1253  }
1254  context_state_propagation_mutex_type::scoped_lock lock(the_context_state_propagation_mutex);
1255  my_market->my_masters.remove( *this );
1256  lock.release();
1257 #endif /* __TBB_TASK_GROUP_CONTEXT */
1258  my_arena_slot = NULL; // detached from slot
1259  free_scheduler(); // do not use scheduler state after this point
1260 
1261  if( a )
1263  // If there was an associated arena, it added a public market reference
1264  return m->release( /*is_public*/ a != NULL, blocking_terminate );
1265 }
1266 
1267 } // namespace internal
1268 } // namespace tbb
1269 
1270 /*
1271  Comments:
1272 
1273 1. The premise of the cancellation support implementation is that cancellations are
1274  not part of the hot path of the program execution. Therefore all changes in its
1275  implementation in order to reduce the overhead of the cancellation control flow
1276  should be done only in ways that do not increase overhead of the normal execution.
1277 
1278  In general contexts are used by all threads and their descendants are created in
1279  different threads as well. In order to minimize impact of the cross-thread tree
1280  maintenance (first of all because of the synchronization), the tree of contexts
1281  is split into pieces, each of which is handled by the only thread. Such pieces
1282  are represented as lists of contexts, members of which are contexts that were
1283  bound to their parents in the given thread.
1284 
1285  The context tree maintenance and cancellation propagation algorithms is designed
1286  in such a manner that cross-thread access to a context list will take place only
1287  when cancellation signal is sent (by user or when an exception happens), and
1288  synchronization is necessary only then. Thus the normal execution flow (without
1289  exceptions and cancellation) remains free from any synchronization done on
1290  behalf of exception handling and cancellation support.
1291 
1292 2. Consider parallel cancellations at the different levels of the context tree:
1293 
1294  Ctx1 <- Cancelled by Thread1 |- Thread2 started processing
1295  | |
1296  Ctx2 |- Thread1 started processing
1297  | T1 |- Thread2 finishes and syncs up local counters
1298  Ctx3 <- Cancelled by Thread2 |
1299  | |- Ctx5 is bound to Ctx2
1300  Ctx4 |
1301  T2 |- Thread1 reaches Ctx2
1302 
1303  Thread-propagator of each cancellation increments global counter. However the thread
1304  propagating the cancellation from the outermost context (Thread1) may be the last
1305  to finish. Which means that the local counters may be synchronized earlier (by Thread2,
1306  at Time1) than it propagated cancellation into Ctx2 (at time Time2). If a new context
1307  (Ctx5) is created and bound to Ctx2 between Time1 and Time2, checking its parent only
1308  (Ctx2) may result in cancellation request being lost.
1309 
1310  This issue is solved by doing the whole propagation under the lock.
1311 
1312  If we need more concurrency while processing parallel cancellations, we could try
1313  the following modification of the propagation algorithm:
1314 
1315  advance global counter and remember it
1316  for each thread:
1317  scan thread's list of contexts
1318  for each thread:
1319  sync up its local counter only if the global counter has not been changed
1320 
1321  However this version of the algorithm requires more analysis and verification.
1322 
1323 3. There is no portable way to get stack base address in Posix, however the modern
1324  Linux versions provide pthread_attr_np API that can be used to obtain thread's
1325  stack size and base address. Unfortunately even this function does not provide
1326  enough information for the main thread on IA-64 architecture (RSE spill area
1327  and memory stack are allocated as two separate discontinuous chunks of memory),
1328  and there is no portable way to discern the main and the secondary threads.
1329  Thus for macOS* and IA-64 architecture for Linux* OS we use the TBB worker stack size for
1330  all threads and use the current stack top as the stack base. This simplified
1331  approach is based on the following assumptions:
1332  1) If the default stack size is insufficient for the user app needs, the
1333  required amount will be explicitly specified by the user at the point of the
1334  TBB scheduler initialization (as an argument to tbb::task_scheduler_init
1335  constructor).
1336  2) When a master thread initializes the scheduler, it has enough space on its
1337  stack. Here "enough" means "at least as much as worker threads have".
1338  3) If the user app strives to conserve the memory by cutting stack size, it
1339  should do this for TBB workers too (as in the #1).
1340 */
static generic_scheduler * create_worker(market &m, size_t index)
Initialize a scheduler for a worker thread.
Definition: scheduler.cpp:1146
#define __TBB_ISOLATION_ARG(arg1, isolation)
void allocate_task_pool(size_t n)
task object is freshly allocated or recycled.
Definition: task.h:604
const size_t task_prefix_reservation_size
Number of bytes reserved for a task prefix.
#define __TBB_control_consistency_helper()
Definition: gcc_generic.h:64
void deallocate_task(task &t)
Return task object to the memory allocator.
Definition: scheduler.h:580
uintptr_t my_stealing_threshold
Position in the call stack specifying its maximal filling when stealing is still allowed.
Definition: scheduler.h:129
#define LockedTaskPool
Definition: scheduler.h:47
arena_slot * my_arena_slot
Pointer to the slot in the arena we own at the moment.
Definition: scheduler.h:67
static const intptr_t mailbox_bit
Definition: mailbox.h:35
void on_thread_leaving()
Notification that worker or master leaves its arena.
Definition: arena.h:287
int my_num_workers_requested
The number of workers that are currently requested from the resource manager.
Definition: arena.h:79
void poison_pointer(T *__TBB_atomic &)
Definition: tbb_stddef.h:309
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task * task
static void sign_on(generic_scheduler *s)
Register TBB scheduler instance in thread-local storage.
Definition: governor.cpp:128
void spin_wait_until_eq(const volatile T &location, const U value)
Spin UNTIL the value of the variable is equal to a given value.
Definition: tbb_machine.h:403
void fill_with_canary_pattern(size_t, size_t)
generic_scheduler *(* AllocateSchedulerPtr)(market &)
Pointer to the scheduler factory function.
Definition: tbb_main.cpp:78
Class that implements exponential backoff.
Definition: tbb_machine.h:349
Base class for user-defined tasks.
Definition: task.h:576
void local_spawn(task *first, task *&next)
Definition: scheduler.cpp:596
Work stealing task scheduler.
Definition: scheduler.h:115
task & allocate_task(size_t number_of_bytes, __TBB_CONTEXT_ARG(task *parent, task_group_context *context))
Allocate task object, either from the heap or a free list.
Definition: scheduler.cpp:298
bool recipient_is_idle()
True if thread that owns this mailbox is looking for work.
Definition: mailbox.h:183
static const kind_type detached
Definition: task.h:552
static bool is_shared(intptr_t tat)
True if the proxy is stored both in its sender's pool and in the destination mailbox.
Definition: mailbox.h:50
void __TBB_store_with_release(volatile T &location, V value)
Definition: tbb_machine.h:717
atomic< T > & as_atomic(T &t)
Definition: atomic.h:547
generic_scheduler * allocate_scheduler(market &m)
Definition: scheduler.cpp:41
void const char const char int ITT_FORMAT __itt_group_sync s
task * my_free_list
Free list of small tasks that can be reused.
Definition: scheduler.h:152
#define __TBB_get_object_ref(class_name, member_name, member_addr)
Returns address of the object containing a member with the given name and address.
Definition: tbb_stddef.h:274
affinity_id my_affinity_id
The mailbox id assigned to this scheduler.
Definition: scheduler.h:84
bool release(bool is_public, bool blocking_terminate)
Decrements market's refcount and destroys it in the end.
Definition: market.cpp:179
static const intptr_t num_priority_levels
task * get_task(__TBB_ISOLATION_EXPR(isolation_tag isolation))
Get a task from the local pool.
Definition: scheduler.cpp:945
Memory prefix to a task object.
Definition: task.h:180
Base class for types that should not be copied or assigned.
Definition: tbb_stddef.h:335
void * __TBB_get_bsp()
Retrieves the current RSE backing store pointer. IA64 specific.
Smart holder for the empty task class with automatic destruction.
task * extract_task()
Returns a pointer to the encapsulated task or NULL, and frees proxy if necessary.
Definition: mailbox.h:61
virtual void __TBB_EXPORTED_METHOD note_affinity(affinity_id id)
Invoked by scheduler to notify task that it ran on unexpected thread.
Definition: task.cpp:249
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id parent
__TBB_atomic intptr_t my_small_task_count
Number of small tasks that have been allocated by this scheduler.
Definition: scheduler.h:358
void free_scheduler()
Destroy and deallocate this scheduler object.
Definition: scheduler.cpp:261
A scheduler with a customized evaluation loop.
void spawn_root_and_wait(task &first, task *&next) __TBB_override
For internal use only.
Definition: scheduler.cpp:677
#define __TBB_ASSERT(predicate, comment)
No-op version of __TBB_ASSERT.
Definition: tbb_stddef.h:169
Release.
Definition: atomic.h:49
auto first(Container &c) -> decltype(begin(c))
task * prepare_for_spawning(task *t)
Checks if t is affinitized to another thread, and if so, bundles it as proxy.
Definition: scheduler.cpp:556
void const char const char int ITT_FORMAT __itt_group_sync p
scheduler_properties my_properties
Definition: scheduler.h:86
task_group_context * context()
This method is deprecated and will be removed in the future.
Definition: task.h:822
task * my_dummy_task
Fake root task created by slave threads.
Definition: scheduler.h:160
void init_stack_info()
Sets up the data necessary for the stealing limiting heuristics.
Definition: scheduler.cpp:147
static market & global_market(bool is_public, unsigned max_num_workers=0, size_t stack_size=0)
Factory method creating new market object.
Definition: market.cpp:100
void local_spawn_root_and_wait(task *first, task *&next)
Definition: scheduler.cpp:652
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p sync_releasing
static const size_t min_task_pool_size
Definition: scheduler.h:269
size_t my_task_pool_size
Capacity of the primary task pool (number of elements - pointers to task).
bool is_quiescent_local_task_pool_reset() const
Definition: scheduler.h:541
static generic_scheduler * local_scheduler()
Obtain the thread-local instance of the TBB scheduler.
Definition: governor.h:126
static const size_t quick_task_size
If sizeof(task) is <=quick_task_size, it is handled on a free list instead of malloc'd.
Definition: scheduler.h:122
static const unsigned ref_external
Reference increment values for externals and workers.
Definition: arena.h:213
void push(task_proxy *t)
Push task_proxy onto the mailbox queue of another thread.
Definition: mailbox.h:144
#define ITT_NOTIFY(name, obj)
Definition: itt_notify.h:117
void commit_spawned_tasks(size_t new_tail)
Makes newly spawned tasks visible to thieves.
Definition: scheduler.h:607
static const kind_type binding_required
Definition: task.h:550
size_t prepare_task_pool(size_t n)
Makes sure that the task pool can accommodate at least n more elements.
Definition: scheduler.cpp:400
static bool is_set(generic_scheduler *s)
Used to check validity of the local scheduler TLS contents.
Definition: governor.cpp:124
#define __TBB_cl_evict(p)
Definition: mic_common.h:38
task is in ready pool, or is going to be put there, or was just taken off.
Definition: task.h:602
void pause()
Pause for a while.
Definition: tbb_machine.h:364
void release_task_pool() const
Unlocks the local task pool.
Definition: scheduler.cpp:483
Represents acquisition of a mutex.
Definition: spin_mutex.h:54
#define __TBB_cl_prefetch(p)
Definition: mic_common.h:37
task ** lock_task_pool(arena_slot *victim_arena_slot) const
Locks victim's task pool, and returns pointer to it. The pointer can be NULL.
Definition: scheduler.cpp:498
void copy_memory(T *dst) const
Copies the contents of the vector into the dst array.
__TBB_atomic size_t head
Index of the first ready task in the deque.
No ordering.
Definition: atomic.h:51
Used to form groups of tasks.
Definition: task.h:319
#define ITT_SYNC_CREATE(obj, type, name)
Definition: itt_notify.h:120
void spawn(task &first, task *&next) __TBB_override
For internal use only.
Definition: scheduler.cpp:673
void free_nonlocal_small_task(task &t)
Free a small task t that that was allocated by a different scheduler.
Definition: scheduler.cpp:373
static task * plugged_return_list()
Special value used to mark my_return_list as not taking any more entries.
Definition: scheduler.h:355
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p sync_cancel
virtual ~scheduler()=0
Pure virtual destructor;.
Definition: scheduler.cpp:76
intptr_t isolation_tag
A tag for task isolation.
Definition: task.h:126
T max(const T &val1, const T &val2)
Utility template function returning greater of the two values.
Definition: tbb_misc.h:118
#define __TBB_ISOLATION_EXPR(isolation)
task * parent() const
task on whose behalf this task is working, or NULL if this is a root.
Definition: task.h:809
void advertise_new_work()
If necessary, raise a flag that there is new job in arena.
Definition: arena.h:376
void acquire_task_pool() const
Locks the local task pool.
Definition: scheduler.cpp:454
uintptr_t my_version_and_traits
Version for run-time checks and behavioral traits of the context.
Definition: task.h:407
atomic< unsigned > my_limit
The maximal number of currently busy slots.
Definition: arena.h:65
void *__TBB_EXPORTED_FUNC NFS_Allocate(size_t n_element, size_t element_size, void *hint)
Allocate memory on cache/sector line boundary.
void __TBB_store_relaxed(volatile T &location, V value)
Definition: tbb_machine.h:743
state_type state() const
Current execution state.
Definition: task.h:838
bool is_local_task_pool_quiescent() const
Definition: scheduler.h:530
__TBB_atomic size_t tail
Index of the element following the last ready task in the deque.
task * steal_task(__TBB_ISOLATION_ARG(arena_slot &victim_arena_slot, isolation_tag isolation))
Steal task from another scheduler's ready pool.
Definition: scheduler.cpp:1042
void assert_task_valid(const task *)
mail_outbox * outbox
Mailbox to which this was mailed.
Definition: mailbox.h:47
task * get_mailbox_task(__TBB_ISOLATION_EXPR(isolation_tag isolation))
Attempt to get a task from the mailbox.
Definition: scheduler.cpp:1107
static const intptr_t location_mask
Definition: mailbox.h:36
void commit_relocated_tasks(size_t new_tail)
Makes relocated tasks visible to thieves and releases the local task pool.
Definition: scheduler.h:616
The graph class.
#define __TBB_CONTEXT_ARG(arg1, context)
Set if ref_count might be changed by another thread. Used for debugging.
void Scheduler_OneTimeInitialization(bool itt_present)
Defined in scheduler.cpp.
Definition: scheduler.cpp:56
market * my_market
The market I am in.
Definition: scheduler.h:146
Tag for v3 task_proxy.
Set if the task has been stolen.
#define __TBB_Yield()
Definition: ibm_aix51.h:48
const size_t MByte
Definition: tbb_misc.h:44
static void cleanup_worker(void *arg, bool worker)
Perform necessary cleanup when a worker thread finishes.
Definition: scheduler.cpp:1201
unsigned short affinity_id
An id as used for specifying affinity.
Definition: task.h:122
#define __TBB_FetchAndDecrementWrelease(P)
Definition: tbb_machine.h:315
virtual void local_wait_for_all(task &parent, task *child)=0
__TBB_atomic reference_count ref_count
Reference count used for synchronization.
Definition: task.h:240
arena * my_arena
The arena that I own (if master) or am servicing at the moment (if worker)
Definition: scheduler.h:70
#define TBB_USE_ASSERT
Definition: tbb_config.h:441
task object is on free list, or is going to be put there, or was just taken off.
Definition: task.h:606
static void sign_off(generic_scheduler *s)
Unregister TBB scheduler instance from thread-local storage.
Definition: governor.cpp:149
void atomic_fence()
Sequentially consistent full memory fence.
Definition: tbb_machine.h:343
__TBB_atomic kind_type my_kind
Flavor of this context: bound or isolated.
Definition: task.h:366
mail_outbox & mailbox(affinity_id id)
Get reference to mailbox corresponding to given affinity_id.
Definition: arena.h:191
void __TBB_EXPORTED_FUNC NFS_Free(void *)
Free memory allocated by NFS_Allocate.
bool outermost
Indicates that a scheduler is on outermost level.
Definition: scheduler.h:57
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp end
static const kind_type dying
Definition: task.h:553
size_t worker_stack_size() const
Returns the requested stack size of worker threads.
Definition: market.h:298
T __TBB_load_relaxed(const volatile T &location)
Definition: tbb_machine.h:739
void leave_task_pool()
Leave the task pool.
Definition: scheduler.cpp:1133
void publish_task_pool()
Used by workers to enter the task pool.
Definition: scheduler.cpp:1121
static bool is_proxy(const task &t)
True if t is a task_proxy.
Definition: scheduler.h:260
bool cleanup_master(bool blocking_terminate)
Perform necessary cleanup when a master thread stops using TBB.
Definition: scheduler.cpp:1211
void unlock_task_pool(arena_slot *victim_arena_slot, task **victim_task_pool) const
Unlocks victim's task pool.
Definition: scheduler.cpp:547
generic_scheduler * my_scheduler
Scheduler of the thread attached to the slot.
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void * lock
size_t my_arena_index
Index of the arena slot the scheduler occupies now, or occupied last time.
Definition: scheduler.h:64
internal::task_prefix & prefix(internal::version_tag *=NULL) const
Get reference to corresponding task_prefix.
Definition: task.h:920
void reset_task_pool_and_leave()
Resets head and tail indices to 0, and leaves task pool.
Definition: scheduler.h:599
bool is_quiescent_local_task_pool_empty() const
Definition: scheduler.h:536
bool is_worker() const
True if running on a worker thread, false otherwise.
Definition: scheduler.h:570
task_proxy * pop(__TBB_ISOLATION_EXPR(isolation_tag isolation))
Get next piece of mail, or NULL if mailbox is empty.
Definition: mailbox.h:206
intptr_t reference_count
A reference count.
Definition: task.h:119
static bool is_version_3_task(task &t)
Definition: scheduler.h:124
task * my_return_list
List of small tasks that have been returned to this scheduler by other schedulers.
Definition: scheduler.h:362
const isolation_tag no_isolation
Definition: task.h:127
void enqueue(task &, void *reserved) __TBB_override
For internal use only.
Definition: scheduler.cpp:681
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t new_size
Vector that grows without reallocations, and stores items in the reverse order.
arena_slot my_slots[1]
Definition: arena.h:283
static const intptr_t pool_bit
Definition: mailbox.h:34
task * my_innermost_running_task
Innermost task whose task::execute() is running. A dummy task on the outermost level.
Definition: scheduler.h:73
#define EmptyTaskPool
Definition: scheduler.h:46
static generic_scheduler * create_master(arena *a)
Initialize a scheduler for a master thread.
Definition: scheduler.cpp:1159
context_list_node_t * my_next
Definition: task.h:134
#define GATHER_STATISTIC(x)

Copyright © 2005-2018 Intel Corporation. All Rights Reserved.

Intel, Pentium, Intel Xeon, Itanium, Intel XScale and VTune are registered trademarks or trademarks of Intel Corporation or its subsidiaries in the United States and other countries.

* Other names and brands may be claimed as the property of others.