pcbnew: Connectivity threads update

This is a melded cherry-pick of the following commits from 5.1

59fb6d8851
ed1c8eee9e
4a730e6c54

This implements the std::async connecitivty and locking for the 5.0
branch
This commit is contained in:
Seth Hillbrand 2018-11-04 17:11:43 -07:00
parent 0b5ca1a883
commit a77caa6baa
2 changed files with 57 additions and 52 deletions

View File

@ -28,6 +28,8 @@
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <future>
#include <algorithm>
#ifdef PROFILE #ifdef PROFILE
#include <profile.h> #include <profile.h>
@ -325,58 +327,63 @@ void CN_CONNECTIVITY_ALGO::searchConnections()
PROF_COUNTER search_basic( "search-basic" ); PROF_COUNTER search_basic( "search-basic" );
#endif #endif
size_t numDirty = std::count_if( m_itemList.begin(), m_itemList.end(), [] ( CN_ITEM* aItem ) std::vector<CN_ITEM*> dirtyItems;
{ return aItem->Dirty(); } ); std::copy_if( m_itemList.begin(), m_itemList.end(), std::back_inserter( dirtyItems ),
[] ( CN_ITEM* aItem ) { return aItem->Dirty(); } );
if( m_progressReporter ) if( m_progressReporter )
{ {
m_progressReporter->SetMaxProgress( numDirty ); m_progressReporter->SetMaxProgress( dirtyItems.size() );
m_progressReporter->KeepRefreshing(); m_progressReporter->KeepRefreshing();
} }
if( m_itemList.IsDirty() ) if( m_itemList.IsDirty() )
{ {
std::atomic<int> nextItem( 0 ); size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
std::atomic<size_t> threadsFinished( 0 ); ( dirtyItems.size() + 7 ) / 8 );
size_t parallelThreadCount = std::min<size_t>( std::atomic<size_t> nextItem( 0 );
std::max<size_t>( std::thread::hardware_concurrency(), 2 ), std::vector<std::future<size_t>> returns( parallelThreadCount );
numDirty );
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 ); CN_VISITOR visitor( dirtyItems[i] );
i < m_itemList.Size(); aItemList->FindNearby( dirtyItems[i], visitor );
i = nextItem.fetch_add( 1 ) )
{
auto item = m_itemList[i];
if( item->Dirty() )
{
CN_VISITOR visitor( item, &m_listLock );
m_itemList.FindNearby( item, visitor );
if( m_progressReporter ) if( aReporter )
m_progressReporter->AdvanceProgress(); aReporter->AdvanceProgress();
} }
}
threadsFinished++; return 1;
} ); };
t.detach(); if( parallelThreadCount <= 1 )
} conn_lambda( &m_itemList, m_progressReporter );
else
// Finalize the connectivity threads
while( threadsFinished < parallelThreadCount )
{ {
if( m_progressReporter ) for( size_t ii = 0; ii < parallelThreadCount; ++ii )
m_progressReporter->KeepRefreshing(); 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 for( size_t ii = 0; ii < parallelThreadCount; ++ii )
std::this_thread::sleep_for( std::chrono::milliseconds( 1 ) ); {
// 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 #ifdef PROFILE
@ -766,8 +773,8 @@ void CN_VISITOR::checkZoneItemConnection( CN_ZONE* aZone, CN_ITEM* aItem )
( aItem->Parent()->Type() == PCB_TRACE_T && ( aItem->Parent()->Type() == PCB_TRACE_T &&
zoneItem->ContainsPoint( aItem->GetAnchor( 1 ) ) ) ) zoneItem->ContainsPoint( aItem->GetAnchor( 1 ) ) ) )
{ {
std::lock_guard<std::mutex> lock( *m_listLock ); zoneItem->Connect( aItem );
CN_ITEM::Connect( zoneItem, aItem ); aItem->Connect( zoneItem );
} }
} }
@ -791,8 +798,8 @@ void CN_VISITOR::checkZoneZoneConnection( CN_ZONE* aZoneA, CN_ZONE* aZoneB )
{ {
if( aZoneB->ContainsPoint( outline.CPoint( i ) ) ) if( aZoneB->ContainsPoint( outline.CPoint( i ) ) )
{ {
std::lock_guard<std::mutex> lock( *m_listLock ); aZoneA->Connect( aZoneB );
CN_ITEM::Connect( aZoneA, aZoneB ); aZoneB->Connect( aZoneA );
return; return;
} }
} }
@ -803,8 +810,8 @@ void CN_VISITOR::checkZoneZoneConnection( CN_ZONE* aZoneA, CN_ZONE* aZoneB )
{ {
if( aZoneA->ContainsPoint( outline2.CPoint( i ) ) ) if( aZoneA->ContainsPoint( outline2.CPoint( i ) ) )
{ {
std::lock_guard<std::mutex> lock( *m_listLock ); aZoneA->Connect( aZoneB );
CN_ITEM::Connect( aZoneA, aZoneB ); aZoneB->Connect( aZoneA );
return; return;
} }
} }
@ -862,8 +869,8 @@ bool CN_VISITOR::operator()( CN_ITEM* aCandidate )
( parentA->Type() == PCB_TRACE_T && parentB->HitTest( ptA2 ) ) || ( parentA->Type() == PCB_TRACE_T && parentB->HitTest( ptA2 ) ) ||
( parentB->Type() == PCB_TRACE_T && parentA->HitTest( ptB2 ) ) ) ( parentB->Type() == PCB_TRACE_T && parentA->HitTest( ptB2 ) ) )
{ {
std::lock_guard<std::mutex> lock( *m_listLock ); m_item->Connect( aCandidate );
CN_ITEM::Connect( m_item, aCandidate ); aCandidate->Connect( m_item );
} }
return true; return true;

View File

@ -284,6 +284,9 @@ private:
///> valid flag, used to identify garbage items (we use lazy removal) ///> valid flag, used to identify garbage items (we use lazy removal)
bool m_valid; bool m_valid;
///> mutex protecting this item's connected_items set to allow parallel connection threads
std::mutex m_listLock;
protected: protected:
///> dirty flag, used to identify recently added item not yet scanned into the connectivity search ///> dirty flag, used to identify recently added item not yet scanned into the connectivity search
bool m_dirty; bool m_dirty;
@ -436,10 +439,10 @@ public:
return ( m_connected.find( aItem ) != m_connected.end() ); 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 ); std::lock_guard<std::mutex> lock( m_listLock );
b->m_connected.insert( a ); m_connected.insert( b );
} }
void RemoveInvalidRefs(); void RemoveInvalidRefs();
@ -835,9 +838,8 @@ class CN_VISITOR {
public: public:
CN_VISITOR( CN_ITEM* aItem, std::mutex* aListLock ) : CN_VISITOR( CN_ITEM* aItem ) :
m_item( aItem ), m_item( aItem )
m_listLock( aListLock )
{} {}
bool operator()( CN_ITEM* aCandidate ); bool operator()( CN_ITEM* aCandidate );
@ -850,10 +852,6 @@ protected:
///> the item we are looking for connections to ///> the item we are looking for connections to
CN_ITEM* m_item; CN_ITEM* m_item;
///> the mutex protecting our connection list
std::mutex* m_listLock;
}; };
#endif #endif