Make sure tesselate_lambda has lock before modifying zone.

Also implements locking for upating pad & tracks' zoneConnectionCaches.

Fixes https://gitlab.com/kicad/code/kicad/issues/13531
This commit is contained in:
Jeff Young 2023-01-15 15:46:32 +00:00
parent 45a71a205c
commit 0c8f95aa02
5 changed files with 86 additions and 33 deletions

View File

@ -740,11 +740,17 @@ public:
m_zoneLayerConnections[ ii ] = ZLC_UNRESOLVED;
}
ZONE_LAYER_CONNECTION& ZoneConnectionCache( PCB_LAYER_ID aLayer ) const
const ZONE_LAYER_CONNECTION& ZoneConnectionCache( PCB_LAYER_ID aLayer ) const
{
return m_zoneLayerConnections[ aLayer ];
}
void SetZoneConnectionCache( PCB_LAYER_ID aLayer, ZONE_LAYER_CONNECTION aConnection )
{
std::unique_lock<std::mutex> cacheLock( m_zoneLayerConnectionsMutex );
m_zoneLayerConnections[ aLayer ] = aConnection;
}
#if defined(DEBUG)
virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
#endif
@ -863,7 +869,8 @@ private:
// while 90° will produce a +.
int m_thermalGap;
mutable ZONE_LAYER_CONNECTION m_zoneLayerConnections[B_Cu + 1];
std::mutex m_zoneLayerConnectionsMutex;
ZONE_LAYER_CONNECTION m_zoneLayerConnections[B_Cu + 1];
};
#endif // PAD_H

View File

@ -96,6 +96,36 @@ PCB_VIA::PCB_VIA( BOARD_ITEM* aParent ) :
}
PCB_VIA::PCB_VIA( const PCB_VIA& aOther ) :
PCB_TRACK( aOther.GetParent(), PCB_VIA_T )
{
PCB_VIA::operator=( aOther );
const_cast<KIID&>( m_Uuid ) = aOther.m_Uuid;
}
PCB_VIA& PCB_VIA::operator=( const PCB_VIA &aOther )
{
BOARD_CONNECTED_ITEM::operator=( aOther );
m_Width = aOther.m_Width;
m_Start = aOther.m_Start;
m_End = aOther.m_End;
m_CachedLOD = aOther.m_CachedLOD;
m_CachedScale = aOther.m_CachedScale;
m_bottomLayer = aOther.m_bottomLayer;
m_viaType = aOther.m_viaType;
m_drill = aOther.m_drill;
m_removeUnconnectedLayer = aOther.m_removeUnconnectedLayer;
m_keepStartEndLayer = aOther.m_keepStartEndLayer;
m_isFree = aOther.m_isFree;
return *this;
}
EDA_ITEM* PCB_VIA::Clone() const
{
return new PCB_VIA( *this );

View File

@ -368,7 +368,8 @@ public:
return aItem && PCB_VIA_T == aItem->Type();
}
// Do not create a copy constructor. The one generated by the compiler is adequate.
PCB_VIA( const PCB_VIA& aOther );
PCB_VIA& operator=( const PCB_VIA &aOther );
bool IsType( const std::vector<KICAD_T>& aScanTypes ) const override
{
@ -557,11 +558,17 @@ public:
m_zoneLayerConnections[ ii ] = ZLC_UNRESOLVED;
}
ZONE_LAYER_CONNECTION& ZoneConnectionCache( PCB_LAYER_ID aLayer ) const
const ZONE_LAYER_CONNECTION& ZoneConnectionCache( PCB_LAYER_ID aLayer ) const
{
return m_zoneLayerConnections[ aLayer ];
}
void SetZoneConnectionCache( PCB_LAYER_ID aLayer, ZONE_LAYER_CONNECTION aConnection )
{
std::unique_lock<std::mutex> cacheLock( m_zoneLayerConnectionsMutex );
m_zoneLayerConnections[ aLayer ] = aConnection;
}
protected:
void swapData( BOARD_ITEM* aImage ) override;
@ -579,7 +586,8 @@ private:
bool m_keepStartEndLayer; ///< Keep the start and end annular rings
bool m_isFree; ///< "Free" vias don't get their nets auto-updated
mutable ZONE_LAYER_CONNECTION m_zoneLayerConnections[B_Cu + 1];
std::mutex m_zoneLayerConnectionsMutex;
ZONE_LAYER_CONNECTION m_zoneLayerConnections[B_Cu + 1];
};

View File

@ -5026,7 +5026,7 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
if( layer < F_Cu || layer > B_Cu )
Expecting( "copper layer name" );
pad->ZoneConnectionCache( layer ) = ZLC_CONNECTED;
pad->SetZoneConnectionCache( layer, ZLC_CONNECTED );
}
break;
@ -5450,7 +5450,7 @@ PCB_VIA* PCB_PARSER::parsePCB_VIA()
if( layer < F_Cu || layer > B_Cu )
Expecting( "copper layer name" );
via->ZoneConnectionCache( layer ) = ZLC_CONNECTED;
via->SetZoneConnectionCache( layer, ZLC_CONNECTED );
}
break;

View File

@ -263,20 +263,20 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
if( !canFill )
return 0;
// Now we're ready to fill.
std::unique_lock<std::mutex> zoneLock( zone->GetLock(), std::try_to_lock );
{
std::unique_lock<std::mutex> zoneLock( zone->GetLock(), std::try_to_lock );
if( !zoneLock.owns_lock() )
return 0;
if( !zoneLock.owns_lock() )
return 0;
SHAPE_POLY_SET fillPolys;
SHAPE_POLY_SET fillPolys;
if( !fillSingleZone( zone, layer, fillPolys ) )
return 0;
if( !fillSingleZone( zone, layer, fillPolys ) )
return 0;
zone->SetFilledPolysList( layer, fillPolys );
zone->SetFilledPolysList( layer, fillPolys );
}
if( m_progressReporter )
m_progressReporter->AdvanceProgress();
@ -314,9 +314,9 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
if( zone->Outline()->Collide( viaShape.get() ) )
{
if( zone->GetFill( layer )->Collide( flashedShape.get() ) )
via->ZoneConnectionCache( layer ) = ZLC_CONNECTED;
via->SetZoneConnectionCache( layer, ZLC_CONNECTED );
else
via->ZoneConnectionCache( layer ) = ZLC_UNCONNECTED;
via->SetZoneConnectionCache( layer, ZLC_UNCONNECTED );
}
}
}
@ -341,9 +341,9 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
if( zone->Outline()->Collide( padShape.get() ) )
{
if( zone->GetFill( layer )->Collide( flashedShape.get() ) )
pad->ZoneConnectionCache( layer ) = ZLC_CONNECTED;
pad->SetZoneConnectionCache( layer, ZLC_CONNECTED );
else
pad->ZoneConnectionCache( layer ) = ZLC_UNCONNECTED;
pad->SetZoneConnectionCache( layer, ZLC_UNCONNECTED );
}
}
}
@ -353,19 +353,26 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
auto tesselate_lambda =
[&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) -> int
{
if( m_progressReporter && m_progressReporter->IsCancelled() )
return 0;
PCB_LAYER_ID layer = aFillItem.second;
ZONE* zone = aFillItem.first;
zone->CacheTriangulation( layer );
{
std::unique_lock<std::mutex> zoneLock( zone->GetLock(), std::try_to_lock );
if( zone->IsOnCopperLayer() )
cache_optionally_flashed_connections( zone, layer );
if( !zoneLock.owns_lock() )
return 0;
zone->CacheTriangulation( layer );
if( zone->IsOnCopperLayer() )
cache_optionally_flashed_connections( zone, layer );
zone->SetFillFlag( layer, true );
}
zone->SetFillFlag( layer, true );
return 1;
};
@ -394,18 +401,19 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
if( status == std::future_status::ready )
{
if( ret.first.get() )
if( ret.first.get() ) // lambda completed
{
++finished;
if( !cancelled && ret.second == 0 )
returns[ii].first = tp.submit( tesselate_lambda, toFill[ii] );
ret.second++;
ret.second++; // go to next step
}
else if( !cancelled )
if( !cancelled )
{
returns[ii].first = tp.submit( fill_lambda, toFill[ii] );
// Queue the next step (will re-queue the existing step if it didn't complete)
if( ret.second == 0 )
returns[ii].first = tp.submit( fill_lambda, toFill[ii] );
else if( ret.second == 1 )
returns[ii].first = tp.submit( tesselate_lambda, toFill[ii] );
}
}
}