Intel(R) Threading Building Blocks Doxygen Documentation  version 4.2.3
custom_scheduler.h
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 #ifndef _TBB_custom_scheduler_H
22 #define _TBB_custom_scheduler_H
23 
24 #include "scheduler.h"
25 #include "observer_proxy.h"
26 #include "itt_notify.h"
27 
28 namespace tbb {
29 namespace internal {
30 
31 //------------------------------------------------------------------------
33 //------------------------------------------------------------------------
34 
36  static const bool itt_possible = true;
37  static const bool has_slow_atomic = false;
38 };
39 
41  static const bool itt_possible = false;
42 #if __TBB_x86_32||__TBB_x86_64
43  static const bool has_slow_atomic = true;
44 #else
45  static const bool has_slow_atomic = false;
46 #endif /* __TBB_x86_32||__TBB_x86_64 */
47 };
48 
49 //------------------------------------------------------------------------
50 // custom_scheduler
51 //------------------------------------------------------------------------
52 
54 
55 template<typename SchedulerTraits>
58 
60 
62 
65 
67 
70  static_cast<custom_scheduler*>(governor::local_scheduler())->scheduler_type::local_wait_for_all( parent, child );
71  }
72 
74 
77  task_prefix& p = s.prefix();
78  if( SchedulerTraits::itt_possible )
79  ITT_NOTIFY(sync_releasing, &p.ref_count);
80  if( SchedulerTraits::has_slow_atomic && p.ref_count==1 )
81  p.ref_count=0;
82  else if( __TBB_FetchAndDecrementWrelease(&p.ref_count) > 1 ) {// more references exist
83  // '__TBB_cl_evict(&p)' degraded performance of parallel_preorder example
84  return;
85  }
86 
87  // Ordering on p.ref_count (superfluous if SchedulerTraits::has_slow_atomic)
89  __TBB_ASSERT(p.ref_count==0, "completion of task caused predecessor's reference count to underflow");
90  if( SchedulerTraits::itt_possible )
91  ITT_NOTIFY(sync_acquired, &p.ref_count);
92 #if TBB_USE_ASSERT
93  p.extra_state &= ~es_ref_count_active;
94 #endif /* TBB_USE_ASSERT */
95 #if __TBB_TASK_ISOLATION
96  if ( isolation != no_isolation ) {
97  // The parent is allowed not to have isolation (even if a child has isolation) because it has never spawned.
98  __TBB_ASSERT(p.isolation == no_isolation || p.isolation == isolation, NULL);
99  p.isolation = isolation;
100  }
101 #endif /* __TBB_TASK_ISOLATION */
102 
103 #if __TBB_RECYCLE_TO_ENQUEUE
104  if (p.state==task::to_enqueue) {
105  // related to __TBB_TASK_ARENA TODO: try keep priority of the task
106  // e.g. rework task_prefix to remember priority of received task and use here
108  } else
109 #endif /*__TBB_RECYCLE_TO_ENQUEUE*/
110  if( bypass_slot==NULL )
111  bypass_slot = &s;
112  else
113  local_spawn( &s, s.prefix().next );
114  }
115 
116 public:
118  void* p = NFS_Allocate(1, sizeof(scheduler_type), NULL);
119  std::memset(p, 0, sizeof(scheduler_type));
120  scheduler_type* s = new( p ) scheduler_type( m );
121  s->assert_task_pool_valid();
122  ITT_SYNC_CREATE(s, SyncType_Scheduler, SyncObj_TaskPoolSpinning);
123  return s;
124  }
125 
127 
129 
130 }; // class custom_scheduler<>
131 
132 //------------------------------------------------------------------------
133 // custom_scheduler methods
134 //------------------------------------------------------------------------
135 template<typename SchedulerTraits>
137  task* t = NULL;
138  bool outermost_worker_level = worker_outermost_level();
139  bool outermost_dispatch_level = outermost_worker_level || master_outermost_level();
140  bool can_steal_here = can_steal();
141  my_inbox.set_is_idle( true );
142 #if __TBB_HOARD_NONLOCAL_TASKS
143  __TBB_ASSERT(!my_nonlocal_free_list, NULL);
144 #endif
145 #if __TBB_TASK_PRIORITY
146  if ( outermost_dispatch_level ) {
147  if ( intptr_t skipped_priority = my_arena->my_skipped_fifo_priority ) {
148  // This thread can dequeue FIFO tasks, and some priority levels of
149  // FIFO tasks have been bypassed (to prevent deadlock caused by
150  // dynamic priority changes in nested task group hierarchy).
151  if ( my_arena->my_skipped_fifo_priority.compare_and_swap(0, skipped_priority) == skipped_priority
152  && skipped_priority > my_arena->my_top_priority )
153  {
154  my_market->update_arena_priority( *my_arena, skipped_priority );
155  }
156  }
157  }
158 #endif /* !__TBB_TASK_PRIORITY */
159  // TODO: Try to find a place to reset my_limit (under market's lock)
160  // The number of slots potentially used in the arena. Updated once in a while, as my_limit changes rarely.
161  size_t n = my_arena->my_limit-1;
162  int yield_count = 0;
163  // The state "failure_count==-1" is used only when itt_possible is true,
164  // and denotes that a sync_prepare has not yet been issued.
165  for( int failure_count = -static_cast<int>(SchedulerTraits::itt_possible);; ++failure_count) {
166  __TBB_ASSERT( my_arena->my_limit > 0, NULL );
167  __TBB_ASSERT( my_arena_index <= n, NULL );
168  if( completion_ref_count==1 ) {
169  if( SchedulerTraits::itt_possible ) {
170  if( failure_count!=-1 ) {
171  ITT_NOTIFY(sync_prepare, &completion_ref_count);
172  // Notify Intel(R) Thread Profiler that thread has stopped spinning.
173  ITT_NOTIFY(sync_acquired, this);
174  }
175  ITT_NOTIFY(sync_acquired, &completion_ref_count);
176  }
177  __TBB_ASSERT( !t, NULL );
178  // A worker thread in its outermost dispatch loop (i.e. its execution stack is empty) should
179  // exit it either when there is no more work in the current arena, or when revoked by the market.
180  __TBB_ASSERT( !outermost_worker_level, NULL );
181  __TBB_control_consistency_helper(); // on ref_count
182  break; // exit stealing loop and return;
183  }
184  // Check if the resource manager requires our arena to relinquish some threads
185  if ( outermost_worker_level && (my_arena->my_num_workers_allotted < my_arena->num_workers_active()
186 #if __TBB_ENQUEUE_ENFORCED_CONCURRENCY
187  || my_arena->recall_by_mandatory_request()
188 #endif
189  ) ) {
190  if( SchedulerTraits::itt_possible && failure_count != -1 )
191  ITT_NOTIFY(sync_cancel, this);
192  return NULL;
193  }
194 #if __TBB_TASK_PRIORITY
195  const int p = int(my_arena->my_top_priority);
196 #else /* !__TBB_TASK_PRIORITY */
197  static const int p = 0;
198 #endif
199  // Check if there are tasks mailed to this thread via task-to-thread affinity mechanism.
200  __TBB_ASSERT(my_affinity_id, NULL);
201  if ( n && !my_inbox.empty() ) {
202  t = get_mailbox_task( __TBB_ISOLATION_EXPR( isolation ) );
203 #if __TBB_TASK_ISOLATION
204  // There is a race with a thread adding a new task (possibly with suitable isolation)
205  // to our mailbox, so the below conditions might result in a false positive.
206  // Then set_is_idle(false) allows that task to be stolen; it's OK.
207  if ( isolation != no_isolation && !t && !my_inbox.empty()
208  && my_inbox.is_idle_state( true ) ) {
209  // We have proxy tasks in our mailbox but the isolation blocks their execution.
210  // So publish the proxy tasks in mailbox to be available for stealing from owner's task pool.
211  my_inbox.set_is_idle( false );
212  }
213 #endif /* __TBB_TASK_ISOLATION */
214  }
215  if ( t ) {
216  GATHER_STATISTIC( ++my_counters.mails_received );
217  }
218  // Check if there are tasks in starvation-resistant stream.
219  // Only allowed at the outermost dispatch level without isolation.
220  else if (__TBB_ISOLATION_EXPR(isolation == no_isolation &&) outermost_dispatch_level &&
221  !my_arena->my_task_stream.empty(p) && (t = my_arena->my_task_stream.pop( p, my_arena_slot->hint_for_pop)) ) {
222  ITT_NOTIFY(sync_acquired, &my_arena->my_task_stream);
223  // just proceed with the obtained task
224  }
225 #if __TBB_TASK_PRIORITY
226  // Check if any earlier offloaded non-top priority tasks become returned to the top level
227  else if ( my_offloaded_tasks && (t = reload_tasks( __TBB_ISOLATION_EXPR( isolation ) )) ) {
228  __TBB_ASSERT( !is_proxy(*t), "The proxy task cannot be offloaded" );
229  // just proceed with the obtained task
230  }
231 #endif /* __TBB_TASK_PRIORITY */
232  else if ( can_steal_here && n ) {
233  // Try to steal a task from a random victim.
234  size_t k = my_random.get() % n;
235  arena_slot* victim = &my_arena->my_slots[k];
236  // The following condition excludes the master that might have
237  // already taken our previous place in the arena from the list .
238  // of potential victims. But since such a situation can take
239  // place only in case of significant oversubscription, keeping
240  // the checks simple seems to be preferable to complicating the code.
241  if( k >= my_arena_index )
242  ++victim; // Adjusts random distribution to exclude self
243  task **pool = victim->task_pool;
244  if( pool == EmptyTaskPool || !(t = steal_task( __TBB_ISOLATION_ARG(*victim, isolation) )) )
245  goto fail;
246  if( is_proxy(*t) ) {
247  task_proxy &tp = *(task_proxy*)t;
249  if ( !t ) {
250  // Proxy was empty, so it's our responsibility to free it
251  free_task<no_cache_small_task>(tp);
252  goto fail;
253  }
254  GATHER_STATISTIC( ++my_counters.proxies_stolen );
255  }
256  t->prefix().extra_state |= es_task_is_stolen;
257  if( is_version_3_task(*t) ) {
258  my_innermost_running_task = t;
259  t->prefix().owner = this;
260  t->note_affinity( my_affinity_id );
261  }
262  GATHER_STATISTIC( ++my_counters.steals_committed );
263  } // end of stealing branch
264  else
265  goto fail;
266  // A task was successfully obtained somewhere
267  __TBB_ASSERT(t,NULL);
268 #if __TBB_ARENA_OBSERVER
269  my_arena->my_observers.notify_entry_observers( my_last_local_observer, is_worker() );
270 #endif
271 #if __TBB_SCHEDULER_OBSERVER
272  the_global_observer_list.notify_entry_observers( my_last_global_observer, is_worker() );
273 #endif /* __TBB_SCHEDULER_OBSERVER */
274  if ( SchedulerTraits::itt_possible && failure_count != -1 ) {
275  // FIXME - might be victim, or might be selected from a mailbox
276  // Notify Intel(R) Thread Profiler that thread has stopped spinning.
277  ITT_NOTIFY(sync_acquired, this);
278  }
279  break; // exit stealing loop and return
280 fail:
281  GATHER_STATISTIC( ++my_counters.steals_failed );
282  if( SchedulerTraits::itt_possible && failure_count==-1 ) {
283  // The first attempt to steal work failed, so notify Intel(R) Thread Profiler that
284  // the thread has started spinning. Ideally, we would do this notification
285  // *before* the first failed attempt to steal, but at that point we do not
286  // know that the steal will fail.
287  ITT_NOTIFY(sync_prepare, this);
288  failure_count = 0;
289  }
290  // Pause, even if we are going to yield, because the yield might return immediately.
291  prolonged_pause();
292  const int failure_threshold = 2*int(n+1);
293  if( failure_count>=failure_threshold ) {
294 #if __TBB_YIELD2P
295  failure_count = 0;
296 #else
297  failure_count = failure_threshold;
298 #endif
299  __TBB_Yield();
300 #if __TBB_TASK_PRIORITY
301  // Check if there are tasks abandoned by other workers
302  if ( my_arena->my_orphaned_tasks ) {
303  // Epoch must be advanced before seizing the list pointer
304  ++my_arena->my_abandonment_epoch;
305  task* orphans = (task*)__TBB_FetchAndStoreW( &my_arena->my_orphaned_tasks, 0 );
306  if ( orphans ) {
307  task** link = NULL;
308  // Get local counter out of the way (we've just brought in external tasks)
309  my_local_reload_epoch--;
310  t = reload_tasks( orphans, link, __TBB_ISOLATION_ARG( effective_reference_priority(), isolation ) );
311  if ( orphans ) {
312  *link = my_offloaded_tasks;
313  if ( !my_offloaded_tasks )
314  my_offloaded_task_list_tail_link = link;
315  my_offloaded_tasks = orphans;
316  }
317  __TBB_ASSERT( !my_offloaded_tasks == !my_offloaded_task_list_tail_link, NULL );
318  if ( t ) {
319  if( SchedulerTraits::itt_possible )
320  ITT_NOTIFY(sync_cancel, this);
321  __TBB_ASSERT( !is_proxy(*t), "The proxy task cannot be offloaded" );
322  break; // exit stealing loop and return
323  }
324  }
325  }
326 #endif /* __TBB_TASK_PRIORITY */
327  const int yield_threshold = 100;
328  if( yield_count++ >= yield_threshold ) {
329  // When a worker thread has nothing to do, return it to RML.
330  // For purposes of affinity support, the thread is considered idle while in RML.
331 #if __TBB_TASK_PRIORITY
332  if( outermost_worker_level || my_arena->my_top_priority > my_arena->my_bottom_priority ) {
333  if ( my_arena->is_out_of_work() && outermost_worker_level ) {
334 #else /* !__TBB_TASK_PRIORITY */
335  if ( outermost_worker_level && my_arena->is_out_of_work() ) {
336 #endif /* !__TBB_TASK_PRIORITY */
337  if( SchedulerTraits::itt_possible )
338  ITT_NOTIFY(sync_cancel, this);
339  return NULL;
340  }
341 #if __TBB_TASK_PRIORITY
342  }
343  if ( my_offloaded_tasks ) {
344  // Safeguard against any sloppiness in managing reload epoch
345  // counter (e.g. on the hot path because of performance reasons).
346  my_local_reload_epoch--;
347  // Break the deadlock caused by a higher priority dispatch loop
348  // stealing and offloading a lower priority task. Priority check
349  // at the stealing moment cannot completely preclude such cases
350  // because priorities can changes dynamically.
351  if ( !outermost_worker_level && *my_ref_top_priority > my_arena->my_top_priority ) {
352  GATHER_STATISTIC( ++my_counters.prio_ref_fixups );
353  my_ref_top_priority = &my_arena->my_top_priority;
354  // it's expected that only outermost workers can use global reload epoch
355  __TBB_ASSERT(my_ref_reload_epoch == &my_arena->my_reload_epoch, NULL);
356  }
357  }
358 #endif /* __TBB_TASK_PRIORITY */
359  } // end of arena snapshot branch
360  // If several attempts did not find work, re-read the arena limit.
361  n = my_arena->my_limit-1;
362  } // end of yielding branch
363  } // end of nonlocal task retrieval loop
364  if ( my_inbox.is_idle_state( true ) )
365  my_inbox.set_is_idle( false );
366  return t;
367 }
368 
369 template<typename SchedulerTraits>
371  __TBB_ASSERT( governor::is_set(this), NULL );
372  __TBB_ASSERT( parent.ref_count() >= (child && child->parent() == &parent ? 2 : 1), "ref_count is too small" );
373  __TBB_ASSERT( my_innermost_running_task, NULL );
374  assert_task_pool_valid();
375  // Using parent's refcount in sync_prepare (in the stealing loop below) is
376  // a workaround for TP. We need to name it here to display correctly in Ampl.
377  if( SchedulerTraits::itt_possible )
378  ITT_SYNC_CREATE(&parent.prefix().ref_count, SyncType_Scheduler, SyncObj_TaskStealingLoop);
379 #if __TBB_TASK_GROUP_CONTEXT
380  __TBB_ASSERT( parent.prefix().context, "parent task does not have context" );
381 #endif /* __TBB_TASK_GROUP_CONTEXT */
382  task* t = child;
383  // Constant all_local_work_done is an unreachable refcount value that prevents
384  // early quitting the dispatch loop. It is defined to be in the middle of the range
385  // of negative values representable by the reference_count type.
386  static const reference_count
387  // For normal dispatch loops
388  parents_work_done = 1,
389  // For termination dispatch loops in masters
390  all_local_work_done = (reference_count)3 << (sizeof(reference_count) * 8 - 2);
391  reference_count quit_point;
392 #if __TBB_TASK_PRIORITY
393  __TBB_ASSERT( (uintptr_t)*my_ref_top_priority < (uintptr_t)num_priority_levels, NULL );
394  volatile intptr_t *old_ref_top_priority = my_ref_top_priority;
395  // When entering nested parallelism level market level counter
396  // must be replaced with the one local to this arena.
397  volatile uintptr_t *old_ref_reload_epoch = my_ref_reload_epoch;
398 #endif /* __TBB_TASK_PRIORITY */
399  task* old_innermost_running_task = my_innermost_running_task;
400  scheduler_properties old_properties = my_properties;
401  // Remove outermost property to indicate nested level.
402  __TBB_ASSERT( my_properties.outermost || my_innermost_running_task!=my_dummy_task, "The outermost property should be set out of a dispatch loop" );
403  my_properties.outermost &= my_innermost_running_task==my_dummy_task;
404 #if __TBB_TASK_ISOLATION
405  isolation_tag isolation = my_innermost_running_task->prefix().isolation;
406 #endif /* __TBB_TASK_ISOLATION */
407  if( master_outermost_level() ) {
408  // We are in the outermost task dispatch loop of a master thread or a worker which mimics master
409  quit_point = &parent == my_dummy_task ? all_local_work_done : parents_work_done;
410  } else {
411  quit_point = parents_work_done;
412 #if __TBB_TASK_PRIORITY
413  if ( &parent != my_dummy_task ) {
414  // We are in a nested dispatch loop.
415  // Market or arena priority must not prevent child tasks from being
416  // executed so that dynamic priority changes did not cause deadlock.
417  my_ref_top_priority = &parent.prefix().context->my_priority;
418  my_ref_reload_epoch = &my_arena->my_reload_epoch;
419  if(my_ref_reload_epoch != old_ref_reload_epoch)
420  my_local_reload_epoch = *my_ref_reload_epoch-1;
421  }
422 #endif /* __TBB_TASK_PRIORITY */
423  }
424 
425  context_guard_helper</*report_tasks=*/SchedulerTraits::itt_possible> context_guard;
426  if ( t ) {
427  context_guard.set_ctx( __TBB_CONTEXT_ARG1(t->prefix().context) );
428 #if __TBB_TASK_ISOLATION
429  if ( isolation != no_isolation ) {
430  __TBB_ASSERT( t->prefix().isolation == no_isolation, NULL );
431  // Propagate the isolation to the task executed without spawn.
432  t->prefix().isolation = isolation;
433  }
434 #endif /* __TBB_TASK_ISOLATION */
435  }
436 
437 #if TBB_USE_EXCEPTIONS
438  // Infinite safeguard EH loop
439  for (;;) {
440  try {
441 #endif /* TBB_USE_EXCEPTIONS */
442  // Outer loop receives tasks from global environment (via mailbox, FIFO queue(s),
443  // and by stealing from other threads' task pools).
444  // All exit points from the dispatch loop are located in its immediate scope.
445  for(;;) {
446  // Middle loop retrieves tasks from the local task pool.
447  for(;;) {
448  // Inner loop evaluates tasks coming from nesting loops and those returned
449  // by just executed tasks (bypassing spawn or enqueue calls).
450  while(t) {
451  __TBB_ASSERT( my_inbox.is_idle_state(false), NULL );
452  __TBB_ASSERT(!is_proxy(*t),"unexpected proxy");
453  __TBB_ASSERT( t->prefix().owner, NULL );
454 #if __TBB_TASK_ISOLATION
455  __TBB_ASSERT( isolation == no_isolation || isolation == t->prefix().isolation,
456  "A task from another isolated region is going to be executed" );
457 #endif /* __TBB_TASK_ISOLATION */
459 #if __TBB_TASK_GROUP_CONTEXT && TBB_USE_ASSERT
460  assert_context_valid(t->prefix().context);
461  if ( !t->prefix().context->my_cancellation_requested )
462 #endif
463  // TODO: make the assert stronger by prohibiting allocated state.
464  __TBB_ASSERT( 1L<<t->state() & (1L<<task::allocated|1L<<task::ready|1L<<task::reexecute), NULL );
465  assert_task_pool_valid();
466 #if __TBB_TASK_PRIORITY
467  intptr_t p = priority(*t);
468  if ( p != *my_ref_top_priority && (t->prefix().extra_state & es_task_enqueued) == 0) {
469  assert_priority_valid(p);
470  if ( p != my_arena->my_top_priority ) {
471  my_market->update_arena_priority( *my_arena, p );
472  }
473  if ( p < effective_reference_priority() ) {
474  if ( !my_offloaded_tasks ) {
475  my_offloaded_task_list_tail_link = &t->prefix().next_offloaded;
476  // Erase possible reference to the owner scheduler (next_offloaded is a union member)
477  *my_offloaded_task_list_tail_link = NULL;
478  }
479  offload_task( *t, p );
480  if ( is_task_pool_published() ) {
481  t = winnow_task_pool( __TBB_ISOLATION_EXPR( isolation ) );
482  if ( t )
483  continue;
484  } else {
485  // Mark arena as full to unlock arena priority level adjustment
486  // by arena::is_out_of_work(), and ensure worker's presence.
487  my_arena->advertise_new_work<arena::wakeup>();
488  }
489  goto stealing_ground;
490  }
491  }
492 #endif /* __TBB_TASK_PRIORITY */
493  task* t_next = NULL;
494  my_innermost_running_task = t;
495  t->prefix().owner = this;
496  t->prefix().state = task::executing;
497 #if __TBB_TASK_GROUP_CONTEXT
498  if ( !t->prefix().context->my_cancellation_requested )
499 #endif
500  {
501  GATHER_STATISTIC( ++my_counters.tasks_executed );
502  GATHER_STATISTIC( my_counters.avg_arena_concurrency += my_arena->num_workers_active() );
503  GATHER_STATISTIC( my_counters.avg_assigned_workers += my_arena->my_num_workers_allotted );
504 #if __TBB_TASK_PRIORITY
505  GATHER_STATISTIC( my_counters.avg_arena_prio += p );
506  GATHER_STATISTIC( my_counters.avg_market_prio += my_market->my_global_top_priority );
507 #endif /* __TBB_TASK_PRIORITY */
508  ITT_STACK(SchedulerTraits::itt_possible, callee_enter, t->prefix().context->itt_caller);
509  t_next = t->execute();
510  ITT_STACK(SchedulerTraits::itt_possible, callee_leave, t->prefix().context->itt_caller);
511  if (t_next) {
512  __TBB_ASSERT( t_next->state()==task::allocated,
513  "if task::execute() returns task, it must be marked as allocated" );
514  reset_extra_state(t_next);
515  __TBB_ISOLATION_EXPR( t_next->prefix().isolation = t->prefix().isolation );
516 #if TBB_USE_ASSERT
517  affinity_id next_affinity=t_next->prefix().affinity;
518  if (next_affinity != 0 && next_affinity != my_affinity_id)
519  GATHER_STATISTIC( ++my_counters.affinity_ignored );
520 #endif
521  }
522  }
523  assert_task_pool_valid();
524  switch( t->state() ) {
525  case task::executing: {
526  task* s = t->parent();
527  __TBB_ASSERT( my_innermost_running_task==t, NULL );
528  __TBB_ASSERT( t->prefix().ref_count==0, "Task still has children after it has been executed" );
529  t->~task();
530  if( s )
531  tally_completion_of_predecessor( *s, __TBB_ISOLATION_ARG( t_next, t->prefix().isolation ) );
532  free_task<no_hint>( *t );
533  poison_pointer( my_innermost_running_task );
534  assert_task_pool_valid();
535  break;
536  }
537 
538  case task::recycle: // set by recycle_as_safe_continuation()
539  t->prefix().state = task::allocated;
540 #if __TBB_RECYCLE_TO_ENQUEUE
542  case task::to_enqueue: // set by recycle_to_enqueue()
543 #endif
544  __TBB_ASSERT( t_next != t, "a task returned from method execute() can not be recycled in another way" );
546  // for safe continuation, need atomically decrement ref_count;
547  tally_completion_of_predecessor(*t, __TBB_ISOLATION_ARG( t_next, t->prefix().isolation ) );
548  assert_task_pool_valid();
549  break;
550 
551  case task::reexecute: // set by recycle_to_reexecute()
552  __TBB_ASSERT( t_next, "reexecution requires that method execute() return another task" );
553  __TBB_ASSERT( t_next != t, "a task returned from method execute() can not be recycled in another way" );
554  t->prefix().state = task::allocated;
556  local_spawn( t, t->prefix().next );
557  assert_task_pool_valid();
558  break;
559  case task::allocated:
561  break;
562 #if TBB_USE_ASSERT
563  case task::ready:
564  __TBB_ASSERT( false, "task is in READY state upon return from method execute()" );
565  break;
566  default:
567  __TBB_ASSERT( false, "illegal state" );
568 #else
569  default: // just to shut up some compilation warnings
570  break;
571 #endif /* TBB_USE_ASSERT */
572  }
573  GATHER_STATISTIC( t_next ? ++my_counters.spawns_bypassed : 0 );
574  t = t_next;
575  } // end of scheduler bypass loop
576 
577  assert_task_pool_valid();
578  if ( parent.prefix().ref_count == quit_point ) {
579  __TBB_ASSERT( quit_point != all_local_work_done, NULL );
580  __TBB_control_consistency_helper(); // on ref_count
581  ITT_NOTIFY(sync_acquired, &parent.prefix().ref_count);
582  goto done;
583  }
584  if ( is_task_pool_published() ) {
585  t = get_task( __TBB_ISOLATION_EXPR( isolation ) );
586  } else {
587  __TBB_ASSERT( is_quiescent_local_task_pool_reset(), NULL );
588  break;
589  }
590  assert_task_pool_valid();
591 
592  if ( !t ) break;
593 
594  context_guard.set_ctx( __TBB_CONTEXT_ARG1(t->prefix().context) );
595  }; // end of local task pool retrieval loop
596 
597 #if __TBB_TASK_PRIORITY
598 stealing_ground:
599 #endif /* __TBB_TASK_PRIORITY */
600 #if __TBB_HOARD_NONLOCAL_TASKS
601  // before stealing, previously stolen task objects are returned
602  for (; my_nonlocal_free_list; my_nonlocal_free_list = t ) {
603  t = my_nonlocal_free_list->prefix().next;
604  free_nonlocal_small_task( *my_nonlocal_free_list );
605  }
606 #endif
607  if ( quit_point == all_local_work_done ) {
608  __TBB_ASSERT( !is_task_pool_published() && is_quiescent_local_task_pool_reset(), NULL );
609  __TBB_ASSERT( !worker_outermost_level(), NULL );
610  my_innermost_running_task = old_innermost_running_task;
611  my_properties = old_properties;
612 #if __TBB_TASK_PRIORITY
613  my_ref_top_priority = old_ref_top_priority;
614  if(my_ref_reload_epoch != old_ref_reload_epoch)
615  my_local_reload_epoch = *old_ref_reload_epoch-1;
616  my_ref_reload_epoch = old_ref_reload_epoch;
617 #endif /* __TBB_TASK_PRIORITY */
618  return;
619  }
620 
621  t = receive_or_steal_task( __TBB_ISOLATION_ARG( parent.prefix().ref_count, isolation ) );
622  if ( !t )
623  goto done;
624 
625  // The user can capture another the FPU settings to the context so the
626  // cached data in the helper can be out-of-date and we cannot do fast
627  // check.
628  context_guard.set_ctx( __TBB_CONTEXT_ARG1(t->prefix().context) );
629  } // end of infinite stealing loop
630 #if TBB_USE_EXCEPTIONS
631  __TBB_ASSERT( false, "Must never get here" );
632  } // end of try-block
633  TbbCatchAll( t->prefix().context );
634  // Complete post-processing ...
635  if( t->state() == task::recycle
636 #if __TBB_RECYCLE_TO_ENQUEUE
637  // TODO: the enqueue semantics gets lost below, consider reimplementing
638  || t->state() == task::to_enqueue
639 #endif
640  ) {
641  // ... for recycled tasks to atomically decrement ref_count
642  t->prefix().state = task::allocated;
643  if( SchedulerTraits::itt_possible )
644  ITT_NOTIFY(sync_releasing, &t->prefix().ref_count);
645  if( __TBB_FetchAndDecrementWrelease(&t->prefix().ref_count)==1 ) {
646  if( SchedulerTraits::itt_possible )
647  ITT_NOTIFY(sync_acquired, &t->prefix().ref_count);
648  }else{
649  t = NULL;
650  }
651  }
652  } // end of infinite EH loop
653  __TBB_ASSERT( false, "Must never get here too" );
654 #endif /* TBB_USE_EXCEPTIONS */
655 done:
656  my_innermost_running_task = old_innermost_running_task;
657  my_properties = old_properties;
658 #if __TBB_TASK_PRIORITY
659  my_ref_top_priority = old_ref_top_priority;
660  if(my_ref_reload_epoch != old_ref_reload_epoch)
661  my_local_reload_epoch = *old_ref_reload_epoch-1;
662  my_ref_reload_epoch = old_ref_reload_epoch;
663 #endif /* __TBB_TASK_PRIORITY */
664  if ( !ConcurrentWaitsEnabled(parent) ) {
665  if ( parent.prefix().ref_count != parents_work_done ) {
666  // This is a worker that was revoked by the market.
667  __TBB_ASSERT( worker_outermost_level(),
668  "Worker thread exits nested dispatch loop prematurely" );
669  return;
670  }
671  parent.prefix().ref_count = 0;
672  }
673 #if TBB_USE_ASSERT
674  parent.prefix().extra_state &= ~es_ref_count_active;
675 #endif /* TBB_USE_ASSERT */
676 #if __TBB_TASK_GROUP_CONTEXT
677  __TBB_ASSERT(parent.prefix().context && default_context(), NULL);
678  task_group_context* parent_ctx = parent.prefix().context;
679  if ( parent_ctx->my_cancellation_requested ) {
680  task_group_context::exception_container_type *pe = parent_ctx->my_exception;
681  if ( master_outermost_level() && parent_ctx == default_context() ) {
682  // We are in the outermost task dispatch loop of a master thread, and
683  // the whole task tree has been collapsed. So we may clear cancellation data.
684  parent_ctx->my_cancellation_requested = 0;
685  // TODO: Add assertion that master's dummy task context does not have children
686  parent_ctx->my_state &= ~(uintptr_t)task_group_context::may_have_children;
687  }
688  if ( pe ) {
689  // On Windows, FPU control settings changed in the helper destructor are not visible
690  // outside a catch block. So restore the default settings manually before rethrowing
691  // the exception.
692  context_guard.restore_default();
693  TbbRethrowException( pe );
694  }
695  }
696  __TBB_ASSERT(!is_worker() || !CancellationInfoPresent(*my_dummy_task),
697  "Worker's dummy task context modified");
698  __TBB_ASSERT(!master_outermost_level() || !CancellationInfoPresent(*my_dummy_task),
699  "Unexpected exception or cancellation data in the master's dummy task");
700 #endif /* __TBB_TASK_GROUP_CONTEXT */
701  assert_task_pool_valid();
702 }
703 
704 } // namespace internal
705 } // namespace tbb
706 
707 #endif /* _TBB_custom_scheduler_H */
#define __TBB_ISOLATION_ARG(arg1, isolation)
task object is freshly allocated or recycled.
Definition: task.h:604
#define __TBB_control_consistency_helper()
Definition: gcc_generic.h:64
bool ConcurrentWaitsEnabled(task &t)
void poison_pointer(T *__TBB_atomic &)
Definition: tbb_stddef.h:309
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
void const char const char int ITT_FORMAT __itt_group_sync s
#define __TBB_CONTEXT_ARG1(context)
static const intptr_t num_priority_levels
Memory prefix to a task object.
Definition: task.h:180
internal::tbb_exception_ptr exception_container_type
Definition: task.h:328
FastRandom my_random
Random number generator used for picking a random victim from which to steal.
Definition: scheduler.h:149
void wait_for_all(task &parent, task *child) __TBB_override
Entry point from client code to the scheduler loop that dispatches tasks.
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
A scheduler with a customized evaluation loop.
static generic_scheduler * allocate_scheduler(market &m)
task is running, and will be destroyed after method execute() completes.
Definition: task.h:598
#define __TBB_ASSERT(predicate, comment)
No-op version of __TBB_ASSERT.
Definition: tbb_stddef.h:169
task * receive_or_steal_task(__TBB_ISOLATION_ARG(__TBB_atomic reference_count &completion_ref_count, isolation_tag isolation)) __TBB_override
Try getting a task from the mailbox or stealing from another scheduler.
#define __TBB_override
Definition: tbb_stddef.h:244
void const char const char int ITT_FORMAT __itt_group_sync p
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 generic_scheduler * local_scheduler()
Obtain the thread-local instance of the TBB scheduler.
Definition: governor.h:126
custom_scheduler< SchedulerTraits > scheduler_type
#define ITT_NOTIFY(name, obj)
Definition: itt_notify.h:117
static bool is_set(generic_scheduler *s)
Used to check validity of the local scheduler TLS contents.
Definition: governor.cpp:124
task is in ready pool, or is going to be put there, or was just taken off.
Definition: task.h:602
Used to form groups of tasks.
Definition: task.h:319
Bit-field representing properties of a sheduler.
Definition: scheduler.h:50
#define ITT_SYNC_CREATE(obj, type, name)
Definition: itt_notify.h:120
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
intptr_t isolation_tag
A tag for task isolation.
Definition: task.h:126
#define ITT_STACK(precond, name, obj)
Definition: itt_notify.h:123
void tally_completion_of_predecessor(task &s, __TBB_ISOLATION_ARG(task *&bypass_slot, isolation_tag isolation))
Decrements ref_count of a predecessor.
#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 *__TBB_EXPORTED_FUNC NFS_Allocate(size_t n_element, size_t element_size, void *hint)
Allocate memory on cache/sector line boundary.
Traits classes for scheduler.
void assert_task_valid(const task *)
task to be recycled as continuation
Definition: task.h:608
The graph class.
Set if ref_count might be changed by another thread. Used for debugging.
Set if the task has been stolen.
#define __TBB_Yield()
Definition: ibm_aix51.h:48
unsigned short affinity_id
An id as used for specifying affinity.
Definition: task.h:122
#define __TBB_FetchAndDecrementWrelease(P)
Definition: tbb_machine.h:315
arena * my_arena
The arena that I own (if master) or am servicing at the moment (if worker)
Definition: scheduler.h:70
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 ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_id __itt_string_handle __itt_metadata_type size_t void ITT_FORMAT p const __itt_domain __itt_id __itt_string_handle const wchar_t size_t ITT_FORMAT lu const __itt_domain __itt_id __itt_relation __itt_id ITT_FORMAT p const wchar_t int ITT_FORMAT __itt_group_mark d int
void local_wait_for_all(task &parent, task *child) __TBB_override
Scheduler loop that dispatches tasks.
#define __TBB_fallthrough
Definition: tbb_stddef.h:254
void set_ctx(__TBB_CONTEXT_ARG1(task_group_context *))
Definition: scheduler.h:769
virtual task * execute()=0
Should be overridden by derived classes.
task to be rescheduled.
Definition: task.h:600
internal::task_prefix & prefix(internal::version_tag *=NULL) const
Get reference to corresponding task_prefix.
Definition: task.h:920
intptr_t reference_count
A reference count.
Definition: task.h:119
const isolation_tag no_isolation
Definition: task.h:127
#define __TBB_atomic
Definition: tbb_stddef.h:241
void enqueue_task(task &, intptr_t, FastRandom &)
enqueue a task into starvation-resistance queue
Definition: arena.cpp:544
static const intptr_t pool_bit
Definition: mailbox.h:34
#define EmptyTaskPool
Definition: scheduler.h:46
void reset_extra_state(task *t)
#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.