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 <mutex>
#include <future>
#include <algorithm>
#ifdef PROFILE
#include <profile.h>
@ -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<CN_ITEM*> 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<int> nextItem( 0 );
std::atomic<size_t> threadsFinished( 0 );
size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
( dirtyItems.size() + 7 ) / 8 );
size_t parallelThreadCount = std::min<size_t>(
std::max<size_t>( std::thread::hardware_concurrency(), 2 ),
numDirty );
std::atomic<size_t> nextItem( 0 );
std::vector<std::future<size_t>> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock( *m_listLock );
CN_ITEM::Connect( m_item, aCandidate );
m_item->Connect( aCandidate );
aCandidate->Connect( m_item );
}
return true;

View File

@ -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<std::mutex> 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