diff --git a/pcbnew/connectivity_algo.cpp b/pcbnew/connectivity_algo.cpp index 4c722ba8f2..c55e375129 100644 --- a/pcbnew/connectivity_algo.cpp +++ b/pcbnew/connectivity_algo.cpp @@ -28,6 +28,8 @@ #include #include +#include +#include #ifdef PROFILE #include @@ -325,58 +327,63 @@ void CN_CONNECTIVITY_ALGO::searchConnections() PROF_COUNTER search_basic( "search-basic" ); #endif - size_t numDirty = std::count_if( m_itemList.begin(), m_itemList.end(), [] ( CN_ITEM* aItem ) - { return aItem->Dirty(); } ); + std::vector dirtyItems; + std::copy_if( m_itemList.begin(), m_itemList.end(), std::back_inserter( dirtyItems ), + [] ( CN_ITEM* aItem ) { return aItem->Dirty(); } ); if( m_progressReporter ) { - m_progressReporter->SetMaxProgress( numDirty ); + m_progressReporter->SetMaxProgress( dirtyItems.size() ); m_progressReporter->KeepRefreshing(); } if( m_itemList.IsDirty() ) { - std::atomic nextItem( 0 ); - std::atomic threadsFinished( 0 ); + size_t parallelThreadCount = std::min( std::thread::hardware_concurrency(), + ( dirtyItems.size() + 7 ) / 8 ); - size_t parallelThreadCount = std::min( - std::max( std::thread::hardware_concurrency(), 2 ), - numDirty ); + std::atomic nextItem( 0 ); + std::vector> returns( parallelThreadCount ); - for( size_t ii = 0; ii < parallelThreadCount; ++ii ) + auto conn_lambda = [&nextItem, &dirtyItems] + ( CN_LIST* aItemList, PROGRESS_REPORTER* aReporter) -> size_t { - std::thread t = std::thread( [&nextItem, &threadsFinished, this]() + for( size_t i = nextItem++; i < dirtyItems.size(); i = nextItem++ ) { - for( int i = nextItem.fetch_add( 1 ); - i < m_itemList.Size(); - i = nextItem.fetch_add( 1 ) ) - { - auto item = m_itemList[i]; - if( item->Dirty() ) - { - CN_VISITOR visitor( item, &m_listLock ); - m_itemList.FindNearby( item, visitor ); + CN_VISITOR visitor( dirtyItems[i] ); + aItemList->FindNearby( dirtyItems[i], visitor ); - if( m_progressReporter ) - m_progressReporter->AdvanceProgress(); - } - } + if( aReporter ) + aReporter->AdvanceProgress(); + } - threadsFinished++; - } ); + return 1; + }; - t.detach(); - } - - // Finalize the connectivity threads - while( threadsFinished < parallelThreadCount ) + if( parallelThreadCount <= 1 ) + conn_lambda( &m_itemList, m_progressReporter ); + else { - if( m_progressReporter ) - m_progressReporter->KeepRefreshing(); + for( size_t ii = 0; ii < parallelThreadCount; ++ii ) + returns[ii] = std::async( std::launch::async, conn_lambda, + &m_itemList, m_progressReporter ); - // This routine is called every click while routing so keep the sleep time minimal - std::this_thread::sleep_for( std::chrono::milliseconds( 1 ) ); + for( size_t ii = 0; ii < parallelThreadCount; ++ii ) + { + // Here we balance returns with a 100ms timeout to allow UI updating + std::future_status status; + do + { + if( m_progressReporter ) + m_progressReporter->KeepRefreshing(); + + status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) ); + } while( status != std::future_status::ready ); + } } + + if( m_progressReporter ) + m_progressReporter->KeepRefreshing(); } #ifdef PROFILE @@ -766,8 +773,8 @@ void CN_VISITOR::checkZoneItemConnection( CN_ZONE* aZone, CN_ITEM* aItem ) ( aItem->Parent()->Type() == PCB_TRACE_T && zoneItem->ContainsPoint( aItem->GetAnchor( 1 ) ) ) ) { - std::lock_guard lock( *m_listLock ); - CN_ITEM::Connect( zoneItem, aItem ); + zoneItem->Connect( aItem ); + aItem->Connect( zoneItem ); } } @@ -791,8 +798,8 @@ void CN_VISITOR::checkZoneZoneConnection( CN_ZONE* aZoneA, CN_ZONE* aZoneB ) { if( aZoneB->ContainsPoint( outline.CPoint( i ) ) ) { - std::lock_guard lock( *m_listLock ); - CN_ITEM::Connect( aZoneA, aZoneB ); + aZoneA->Connect( aZoneB ); + aZoneB->Connect( aZoneA ); return; } } @@ -803,8 +810,8 @@ void CN_VISITOR::checkZoneZoneConnection( CN_ZONE* aZoneA, CN_ZONE* aZoneB ) { if( aZoneA->ContainsPoint( outline2.CPoint( i ) ) ) { - std::lock_guard lock( *m_listLock ); - CN_ITEM::Connect( aZoneA, aZoneB ); + aZoneA->Connect( aZoneB ); + aZoneB->Connect( aZoneA ); return; } } @@ -862,8 +869,8 @@ bool CN_VISITOR::operator()( CN_ITEM* aCandidate ) ( parentA->Type() == PCB_TRACE_T && parentB->HitTest( ptA2 ) ) || ( parentB->Type() == PCB_TRACE_T && parentA->HitTest( ptB2 ) ) ) { - std::lock_guard lock( *m_listLock ); - CN_ITEM::Connect( m_item, aCandidate ); + m_item->Connect( aCandidate ); + aCandidate->Connect( m_item ); } return true; diff --git a/pcbnew/connectivity_algo.h b/pcbnew/connectivity_algo.h index 91f439b05e..c43ee000eb 100644 --- a/pcbnew/connectivity_algo.h +++ b/pcbnew/connectivity_algo.h @@ -284,6 +284,9 @@ private: ///> valid flag, used to identify garbage items (we use lazy removal) bool m_valid; + ///> mutex protecting this item's connected_items set to allow parallel connection threads + std::mutex m_listLock; + protected: ///> dirty flag, used to identify recently added item not yet scanned into the connectivity search bool m_dirty; @@ -436,10 +439,10 @@ public: return ( m_connected.find( aItem ) != m_connected.end() ); } - static void Connect( CN_ITEM* a, CN_ITEM* b ) + void Connect( CN_ITEM* b ) { - a->m_connected.insert( b ); - b->m_connected.insert( a ); + std::lock_guard lock( m_listLock ); + m_connected.insert( b ); } void RemoveInvalidRefs(); @@ -835,9 +838,8 @@ class CN_VISITOR { public: - CN_VISITOR( CN_ITEM* aItem, std::mutex* aListLock ) : - m_item( aItem ), - m_listLock( aListLock ) + CN_VISITOR( CN_ITEM* aItem ) : + m_item( aItem ) {} bool operator()( CN_ITEM* aCandidate ); @@ -850,10 +852,6 @@ protected: ///> the item we are looking for connections to CN_ITEM* m_item; - - ///> the mutex protecting our connection list - std::mutex* m_listLock; - }; #endif