//! Regression test for a bug that caused premature arena destruction void TestCascadedEnqueue () { REMARK("Testing cascaded enqueue\n"); tbb::task_scheduler_init init(tbb::task_scheduler_init::default_num_threads() + 1); int minNumThreads = min(tbb::task_scheduler_init::default_num_threads(), MaxNumThreads) / 2; int maxNumThreads = min(tbb::task_scheduler_init::default_num_threads() * 2, MaxNumThreads); for ( int numThreads = minNumThreads; numThreads <= maxNumThreads; ++numThreads ) { for ( int i = 0; i < NumRepeats; ++i ) { CanStart = false; __TBB_Yield(); NativeParallelFor( numThreads, EnqueuerBody() ); CanStart = true; int j = 0; while ( j < numThreads ) { if ( Finished[j] ) ++j; else __TBB_Yield(); } for ( j = 0; j < numThreads; ++j ) Finished[j] = false; REMARK("%02d threads; Iteration %03d\r", numThreads, i); } } REMARK( " \r" ); }
static void apply( YourTable& table, int i ) { YourTable::accessor a1, a2; if(i&1) __TBB_Yield(); table.insert( a1, MyKey::make(1) ); __TBB_Yield(); table.insert( a2, MyKey::make(1 + (1<<30)) ); // the same chain table.erase( a2 ); // if erase by key it would lead to deadlock for single thread }
//Look for discussion of the issue at http://software.intel.com/en-us/forums/showthread.php?t=102159 void TestEnqueueOrder () { REMARK("Testing order of enqueued tasks\n"); tbb::task_scheduler_init init(1); // to simplify transition checks only one extra worker for enqueue g_order = tbb::priority_low; g_order_established = false; for( int i = 0; i < 1000; i++) OrderedTask::start(i); while( g_order == tbb::priority_low ) __TBB_Yield(); while( g_order != tbb::priority_low ) __TBB_Yield(); }
void operator() () const { Harness::ConcurrencyTracker ct; AssertLive(); if ( g_Throw ) { if ( ++m_TaskCount == SKIP_CHORES ) __TBB_THROW( test_exception(EXCEPTION_DESCR1) ); __TBB_Yield(); } else { ++g_TaskCount; while( !Concurrency::is_current_task_group_canceling() ) __TBB_Yield(); } }
void operator () () const { if ( m_pOwner->m_sharingMode & ParallelWait ) { while ( Harness::ConcurrencyTracker::PeakParallelism() < m_pOwner->m_numThreads ) __TBB_Yield(); } ++s_tasksExecuted; }
void TBBWork() { if( AvailWork>=0 ) { int k = --AvailWork; if( k==-1 ) { TBB_RunTime.server->adjust_job_count_estimate(-(TBB_MaxThread-1)); ++CompletionCount; } else if( k>=0 ) { for( int k=0; k<4; ++k ) { OMP_Team team( *OMP_RunTime.server ); int n = OMP_RunTime.server->try_increase_load( OMP_ParallelRegionSize-1, /*strict=*/false ); team.barrier = 0; ::rml::job* array[OMP_ParallelRegionSize-1]; if( n>0) OMP_RunTime.server->get_threads( n, &team, array ); // Master does work inside parallel region too. OMPWork(); // Master waits for workers to finish if( n>0 ) while( team.barrier!=unsigned(n) ) { __TBB_Yield(); } } ++CompletionCount; } } }
void Run ( uint_t idx ) { #if TBBTEST_USE_TBB tbb::task_scheduler_init init; #endif AssertLive(); if ( idx == 0 ) { ASSERT ( !m_taskGroup && !m_tasksSpawned, "SharedGroupBody must be reset before reuse"); m_taskGroup = new Concurrency::task_group; Spawn( c_numTasks0 ); Wait(); if ( m_sharingMode & VagabondGroup ) m_barrier.wait(); else DeleteTaskGroup(); } else { while ( m_tasksSpawned == 0 ) __TBB_Yield(); ASSERT ( m_taskGroup, "Task group is not initialized"); Spawn (c_numTasks1); if ( m_sharingMode & ParallelWait ) Wait(); if ( m_sharingMode & VagabondGroup ) { ASSERT ( idx == 1, "In vagabond mode SharedGroupBody must be used with 2 threads only" ); m_barrier.wait(); DeleteTaskGroup(); } } AssertLive(); }
void task_scheduler_observer_v3::observe( bool state ) { if( state ) { if( !my_proxy ) { if( !__TBB_InitOnce::initialization_done() ) DoOneTimeInitializations(); my_busy_count = 0; my_proxy = new observer_proxy(*this); if( generic_scheduler* s = governor::local_scheduler_if_initialized() ) { // Notify newly created observer of its own thread. // Any other pending observers are notified too. s->notify_entry_observers(); } } } else { if( observer_proxy* proxy = my_proxy ) { my_proxy = NULL; __TBB_ASSERT( proxy->gc_ref_count>=1, "reference for observer missing" ); { task_scheduler_observer_mutex_scoped_lock lock(the_task_scheduler_observer_mutex.begin()[0],/*is_writer=*/true); proxy->observer = NULL; } proxy->remove_ref_slow(); while( my_busy_count ) { __TBB_Yield(); } } } }
void operator()( int ) const { for( int i=0; i<my_m; ++i ) { // Busy wait to synchronize threads int j = 0; while( Counter<i ) { if( ++j==1000000 ) { // If Counter<i after a million iterations, then we almost surely have // more logical threads than physical threads, and should yield in // order to let suspended logical threads make progress. j = 0; __TBB_Yield(); } } // Now all threads attempt to simultaneously insert a key. int k; { MyTable::accessor a; MyKey key = MyKey::make(i); if( my_table.insert( a, key ) ) a->second.set_value( 1 ); else a->second.set_value( a->second.value_of()+1 ); k = a->second.value_of(); } if( k==my_nthread ) Counter=i+1; } }
void observer_list::clear () { __TBB_ASSERT( this != &the_global_observer_list, "Method clear() cannot be used on the list of global observers" ); // Though the method will work fine for the empty list, we require the caller // to check for the list emptiness before invoking it to avoid extra overhead. __TBB_ASSERT( !empty(), NULL ); { scoped_lock lock(mutex(), /*is_writer=*/true); observer_proxy *next = my_head; while ( observer_proxy *p = next ) { __TBB_ASSERT( p->my_version >= 6, NULL ); next = p->my_next; // Both proxy p and observer p->my_observer (if non-null) are guaranteed // to be alive while the list is locked. task_scheduler_observer_v3 *obs = p->my_observer; // Make sure that possible concurrent observer destruction does not // conflict with the proxy list cleanup. if ( !obs || !(p = (observer_proxy*)__TBB_FetchAndStoreW(&obs->my_proxy, 0)) ) continue; __TBB_ASSERT( !next || p == next->my_prev, NULL ); __TBB_ASSERT( is_alive(p->my_ref_count), "Observer's proxy died prematurely" ); __TBB_ASSERT( p->my_ref_count == 1, "Reference for observer is missing" ); __TBB_ASSERT( !obs->my_busy_count, "Local observer in an empty arena cannot be marked as busy" ); store<relaxed>( obs->my_busy_count, interface6::task_scheduler_observer::v6_trait ); #if TBB_USE_ASSERT p->my_observer = NULL; p->my_ref_count = 0; #endif /* TBB_USE_ASSERT */ remove(p); delete p; } } while( my_head ) __TBB_Yield(); }
server_thread& wait_for_thread() const { for(;;) { server_thread* ptr=const_cast<server_thread*volatile&>(my_thread); if( ptr ) return *ptr; __TBB_Yield(); } }
/** An interval is the code between two operations on the job_automaton that we are testing. */ void Cover( int k ) { ASSERT( k<N, NULL ); ++Coverage[k]; if( DelayMask>>k&1 ) { // Introduce delay (and possibly a thread context switch) __TBB_Yield(); } }
void market::wait_workers () { // usable for this kind of scheduler only __TBB_ASSERT(governor::needsWaitWorkers(), NULL); // wait till terminating last worker decresed my_ref_count while (__TBB_load_with_acquire(my_ref_count) > 1) __TBB_Yield(); __TBB_ASSERT(1 == my_ref_count, NULL); release(); }
// Tests task_group_context state propagation, also for cancellation. void TestSetPriority() { REMARK("Testing set_priority() with existing forest\n"); const int workers = last*2+1; // +1 is worker thread executing the first task tbb::task_scheduler_init init(workers+1); // +1 is master thread g_barrier = workers; is_finished = false; tbb::task::spawn(*new(tbb::task::allocate_root()) TestSetPriorityTask(0,0)); while(g_barrier) __TBB_Yield(); g_trees[0][2]->set_priority(tbb::priority_high); g_trees[0][4]->set_priority(tbb::priority_normal); g_trees[1][3]->set_priority(tbb::priority_high); // Regression test: it must not set priority_high to g_trees[0][4] // - 1 2 3 4 5 6 7 const int expected_priority[2][last+1] = {{0, 0, 1, 0, 0, 1, 0, 0}, {0, 0, 0, 1, 0, 0, 1, 1}}; for (int t = 0; t < 2; ++t) for (int i = first; i <= last; ++i) { REMARK("\r \rTask %i... ", i); ASSERT(g_trees[t][i]->priority() == expected_priority[t][i]? tbb::priority_high : tbb::priority_normal, NULL); REMARK("OK"); } REMARK("\r \r"); REMARK("Also testing cancel_group_execution()\n"); // cancellation shares propagation logic with set_priority() but there are also differences g_trees[0][4]->cancel_group_execution(); g_trees[0][5]->cancel_group_execution(); g_trees[1][3]->cancel_group_execution(); // - 1 2 3 4 5 6 7 const int expected_cancellation[2][last+1] = {{0, 0, 0, 0, 1, 1, 0, 0}, {0, 0, 0, 1, 0, 0, 1, 1}}; for (int t = 0; t < 2; ++t) for (int i = first; i <= last; ++i) { REMARK("\r \rTask %i... ", i); ASSERT( g_trees[t][i]->is_group_execution_cancelled() == (expected_cancellation[t][i]==1), NULL); REMARK("OK"); } REMARK("\r \r"); g_barrier = workers; is_finished = true; REMARK("waiting tasks to terminate\n"); while(g_barrier) __TBB_Yield(); for (int t = 0; t < 2; ++t) for (int i = first; i <= last; ++i) delete g_trees[t][i]; }
void State::exercise( bool is_owner ) { barrier.wait(); if( is_owner ) { Cover(0); if( ja.try_acquire() ) { Cover(1); ++job_created; ja.set_and_release(job); Cover(2); if( ja.try_acquire() ) { Cover(3); ja.release(); Cover(4); if( ja.try_acquire() ) { Cover(5); ja.release(); } } Cover(6); } else { Cover(7); } if( DelayMask&1<<N ) { while( !job_received ) __TBB_Yield(); } } else { // Using extra bit of DelayMask for choosing whether to run wait_for_job or not. if( DelayMask&1<<N ) { rml::job* j= &ja.wait_for_job(); if( j!=&job ) REPORT("%p\n",j); ASSERT( j==&job, NULL ); job_received = true; } Cover(8); } rml::job* j; if( ja.try_plug(j) ) { ASSERT( j==&job || !j, NULL ); if( j ) { Cover(9+is_owner); ++job_destroyed; } else { __TBB_ASSERT( !is_owner, "owner failed to create job but plugged self" ); Cover(11); } } else { Cover(12+is_owner); } }
void RunTaskForest ( int id ) const { ASSERT( id < g_NumMasters, NULL ); g_LeavesExecuted[id] = 0; int d = BaseDepth + id; tbb::task_scheduler_init init(P-1); tbb::task_group_context ctx (tbb::task_group_context::isolated); tbb::empty_task &r = *new( tbb::task::allocate_root(ctx) ) tbb::empty_task; const int R = 4; r.set_ref_count( R * P + 1 ); // Only PreemptionActivator thread changes its task tree priority in preemption test mode const uintptr_t opts = (id == PreemptionActivatorId) ? m_opts : (m_opts & ~(uintptr_t)TestPreemption); for ( int i = 0; i < R; ++i ) { for ( int j = 1; j < P; ++j ) r.spawn( *new(r.allocate_child()) NodeType(id, MinBaseDepth + id, opts, &r) ); r.spawn( *new(r.allocate_child()) NodeType(id, d, opts, &r) ); } int count = 1; intptr_t lastExecuted = 0; while ( r.ref_count() > 1 ) { // Give workers time to make some progress. for ( int i = 0; i < 10 * count; ++i ) __TBB_Yield(); #if __TBB_TASK_PRIORITY if ( lastExecuted == g_LeavesExecuted[id] ) { // No progress. Likely all workers left to higher priority arena, // and then returned to RML. Request workers back from RML. tbb::task::enqueue( *new(tbb::task::allocate_root() ) tbb::empty_task, id == 0 ? Low : High ); Harness::Sleep(count); #if __TBB_ipf // Increased sleep periods are required on systems with unfair hyperthreading (Itanium(R) 2 processor) count += 10; #endif } else { count = 1; lastExecuted = g_LeavesExecuted[id]; } #else /* !__TBB_TASK_PRIORITY */ (void)lastExecuted; tbb::task::enqueue( *new(tbb::task::allocate_root() ) tbb::empty_task ); #endif /* !__TBB_TASK_PRIORITY */ } ASSERT( g_LeavesExecuted[id] == R * ((1 << d) + ((P - 1) * (1 << (MinBaseDepth + id)))), NULL ); g_LeavesExecuted[id] = -1; tbb::task::destroy(r); }
void operator()( long k ) const { tbb::tick_count t0 = tbb::tick_count::now(); for( long i=0; i<10000; ++i ) { // Wait for previous thread to notify us for( int j=0; CyclicCounter!=k && !Quit; ++j ) { __TBB_Yield(); if( j%100==0 ) { tbb::tick_count t1 = tbb::tick_count::now(); if( (t1-t0).seconds()>=1.0*number_of_threads ) { REPORT("Warning: __TBB_Yield failing to yield with %d threads (or system is heavily loaded)\n",number_of_threads); Quit = true; return; } } } // Notify next thread that it can run CyclicCounter = (k+1)%number_of_threads; } }
void operator()( int k ) const { if( k==0 ) { int number_of_pops = nthread-1; // Wait for all pops to pend. while( queue.size()>-number_of_pops ) { __TBB_Yield(); } for( int i=0; ; ++i ) { ASSERT( queue.size()==i-number_of_pops, NULL ); ASSERT( queue.empty()==(queue.size()<=0), NULL ); if( i==number_of_pops ) break; // Satisfy another pop queue.push( T() ); } } else { // Pop item from queue T item; queue.pop(item); } }
void Wait () { while ( m_threadsReady != m_numThreads ) __TBB_Yield(); const uint_t numSpawned = c_numTasks0 + c_numTasks1 * (m_numThreads - 1); ASSERT ( m_tasksSpawned == numSpawned, "Wrong number of spawned tasks. The test is broken" ); REMARK("Max spawning parallelism is %u out of %u\n", Harness::ConcurrencyTracker::PeakParallelism(), g_MaxConcurrency); if ( m_sharingMode & ParallelWait ) { m_barrier.wait( &Harness::ConcurrencyTracker::Reset ); { Harness::ConcurrencyTracker ct; m_taskGroup->wait(); } if ( Harness::ConcurrencyTracker::PeakParallelism() == 1 ) REPORT ( "Warning: No parallel waiting detected in TestParallelWait\n" ); m_barrier.wait(); } else m_taskGroup->wait(); ASSERT ( m_tasksSpawned == numSpawned, "No tasks should be spawned after wait starts. The test is broken" ); ASSERT ( s_tasksExecuted == numSpawned, "Not all spawned tasks were executed" ); }
void SpawnChildren ( task* parent_node ) { ASSERT( m_depth > 0, NULL ); if ( g_LeavesExecuted[m_tid] % (100 / m_depth) == 0 ) { if ( m_opts & Flog ) { #if __TBB_TASK_PRIORITY task *r = m_opts & FlogEncloser ? this : m_root; tbb::priority_t p = r->group_priority(); r->set_group_priority( p == Low ? High : Low ); #endif /* __TBB_TASK_PRIORITY */ } else __TBB_Yield(); } parent_node->set_ref_count(NumLeafTasks + 1); --m_depth; for ( int i = 0; i < NumLeafTasks; ++i ) { task *t = m_depth ? (task*) new(parent_node->allocate_child()) NodeType(m_tid, m_depth, m_opts, m_root) : (task*) new(parent_node->allocate_child()) LeafTask(m_tid, m_opts); task::spawn(*t); } }
tbb::task* execute() { if( !m_i ) { // the first task creates two trees g_default_ctx = group(); for( int i = 0; i <= 1; ++i ) { g_trees[i][1] = new tbb::task_group_context( tbb::task_group_context::isolated ); tbb::task::spawn(*new(tbb::task::allocate_root(*g_trees[i][1])) TestSetPriorityTask(i, 1)); } } else if( m_i <= last/2 ) { // is divisible for( int i = 0; i <= 1; ++i ) { const int index = 2*m_i + i; g_trees[m_tree][index] = new tbb::task_group_context ( tbb::task_group_context::bound ); tbb::task::spawn(*new(tbb::task::allocate_root(*g_trees[m_tree][index])) TestSetPriorityTask(m_tree, index)); } } --g_barrier; //REMARK("Task %i executing\n", m_i); while (!is_finished) __TBB_Yield(); change_group(*g_default_ctx); // avoid races with destruction of custom contexts --g_barrier; return NULL; }
void operator()( int i ) const { theLocalState->m_isMaster = true; uintptr_t f = i <= MaxFlagIndex ? 1<<i : 0; MyObserver o(f); if ( theTestMode & tmSynchronized ) theMasterBarrier.wait(); // when mode is local observation but not synchronized and when num threads == default if ( theTestMode & tmAutoinitialization ) o.observe(true); // test autoinitialization can be done by observer // when mode is local synchronized observation and when num threads == default if ( theTestMode & tmLeavingControl ) o.test_leaving(); // Observer in enabled state must outlive the scheduler to ensure that // all exit notifications are called. tbb::task_scheduler_init init(m_numThreads); // when local & non-autoinitialized observation mode if ( theTestMode & tmLocalObservation ) o.observe(true); for ( int j = 0; j < 2; ++j ) { tbb::task &t = *new( tbb::task::allocate_root() ) FibTask(m_numThreads, f, o); tbb::task::spawn_root_and_wait(t); thePrevMode = theTestMode; } if( o.is_leaving_test() ) { REMARK( "Testing on_scheduler_leaving()\n"); ASSERT(o.m_workerEntries > 0, "Unbelievable"); // TODO: start from 0? for ( int j = o.m_workerExits; j < o.m_workerEntries; j++ ) { REMARK( "Round %d: entries %d, exits %d\n", j, (int)o.m_workerEntries, (int)o.m_workerExits ); ASSERT_WARNING(o.m_workerExits == j, "Workers unexpectedly leaved arena"); o.dismiss_one(); double n_seconds = 5; (Harness::TimedWaitWhileEq(n_seconds))(o.m_workerExits, j); ASSERT( n_seconds >= 0, "Time out while waiting for a worker to leave arena"); __TBB_Yield(); } } }
void market::process( job& j ) { generic_scheduler& s = static_cast<generic_scheduler&>(j); arena *a = NULL; __TBB_ASSERT( governor::is_set(&s), NULL ); #if !__TBB_SLEEP_PERMISSION while ( (a = arena_in_need(a)) ) a->process(s); #else//__TBB_SLEEP_PERMISSION enum { query_interval = 1000, first_interval = 1, pause_time = 100 // similar to PauseTime used for the stealing loop }; for(int i = first_interval; ; i--) { while ( (a = arena_in_need(a)) ) { a->process(s); i = first_interval; } if( i == 0 ) { #if __TBB_TASK_PRIORITY arena_list_type &al = my_priority_levels[my_global_top_priority].arenas; #else /* __TBB_TASK_PRIORITY */ arena_list_type &al = my_arenas; #endif /* __TBB_TASK_PRIORITY */ if( al.empty() ) // races if any are innocent TODO: replace by an RML query interface break; // no arenas left, perhaps going to shut down if( the_global_observer_list.ask_permission_to_leave() ) break; // go sleep __TBB_Yield(); i = query_interval; } else __TBB_Pause(pause_time); } #endif//__TBB_SLEEP_PERMISSION GATHER_STATISTIC( ++s.my_counters.market_roundtrips ); }
void TimedYield( double pause_time ) { tbb::tick_count start = tbb::tick_count::now(); while( (tbb::tick_count::now()-start).seconds() < pause_time ) __TBB_Yield(); }
inline void WaitForException () { int n = 0; while ( ++n < c_Timeout && !__TBB_load_with_acquire(g_ExceptionCaught) ) __TBB_Yield(); ASSERT_WARNING( n < c_Timeout, "WaitForException failed" ); }
/*override*/ void yield() {__TBB_Yield();}
void FireUpJobs( MyServer& server, MyClient& client, int max_thread, int n_extra, Checker* checker ) { ASSERT( max_thread>=0, NULL ); #if _WIN32||_WIN64 ::rml::server::execution_resource_t me; server.register_master( me ); #endif /* _WIN32||_WIN64 */ client.server = &server; MyTeam team(server,size_t(max_thread)); MyServer::size_type n_thread = 0; for( int iteration=0; iteration<4; ++iteration ) { for( size_t i=0; i<team.max_thread; ++i ) team.info[i].ran = false; switch( iteration ) { default: n_thread = int(max_thread); break; case 1: // No change in number of threads break; case 2: // Decrease number of threads. n_thread = int(max_thread)/2; break; // Case 3 is same code as the default, but has effect of increasing the number of threads. } team.barrier = 0; REMARK("client %d: server.run with n_thread=%d\n", client.client_id(), int(n_thread) ); server.independent_thread_number_changed( n_extra ); if( checker ) { // Give RML time to respond to change in number of threads. Harness::Sleep(1); } int n_delivered = server.try_increase_load( n_thread, StrictTeam ); ASSERT( !StrictTeam || n_delivered==int(n_thread), "server failed to satisfy strict request" ); if( n_delivered<0 ) { REMARK( "client %d: oversubscription occurred (by %d)\n", client.client_id(), -n_delivered ); server.independent_thread_number_changed( -n_extra ); n_delivered = 0; } else { team.n_thread = n_delivered; ::rml::job* job_array[JobArraySize]; job_array[n_delivered] = (::rml::job*)intptr_t(-1); server.get_threads( n_delivered, &team, job_array ); __TBB_ASSERT( job_array[n_delivered]== (::rml::job*)intptr_t(-1), NULL ); for( int i=0; i<n_delivered; ++i ) { MyJob* j = static_cast<MyJob*>(job_array[i]); int s = j->state; ASSERT( s==MyJob::idle||s==MyJob::busy, NULL ); } server.independent_thread_number_changed( -n_extra ); REMARK("client %d: team size is %d\n", client.client_id(), n_delivered); if( checker ) { checker->check_number_of_threads_delivered( n_delivered, n_thread, n_extra ); } // Protocol requires that master wait until workers have called "done_processing" while( team.barrier!=n_delivered ) { ASSERT( team.barrier>=0, NULL ); ASSERT( team.barrier<=n_delivered, NULL ); __TBB_Yield(); } REMARK("client %d: team completed\n", client.client_id() ); for( int i=0; i<n_delivered; ++i ) { ASSERT( team.info[i].ran, "thread on team allegedly delivered, but did not run?" ); } } for( MyServer::size_type i=n_delivered; i<MyServer::size_type(max_thread); ++i ) { ASSERT( !team.info[i].ran, "thread on team ran with illegal index" ); } } #if _WIN32||_WIN64 server.unregister_master( me ); #endif }
void Execute() { while ( !CanStart ) __TBB_Yield(); tbb::task::enqueue( *new( tbb::task::allocate_root() ) CarrierTask(m_taskToSpawn) ); }
void EmulateWork( int ) { for ( int i = 0; i < 1000; ++i ) __TBB_Yield(); }
void thread_yield_v3() { __TBB_Yield(); }