router: invalidate items freed by PNS::NODE in the clearance cache
This was causing intermittent shove hiccups/freezes as RULE_RESOLVER::GetClearance() could return a bogus clearance value, taking a cached value for an item that does not exist anymore. If the allocator/stack accidentally reclaimed such item's address - we were getting very nasty and difficult to reproduce misbehaviours of the shove algorithm. Hopefully this patch fixes this. More info in the comments. Note to self - I'm still not fully convinced I want PNS::ITEM pointers as the cache key, probably a unique counter would be better here.
This commit is contained in:
parent
14f6e32c74
commit
7966126040
|
@ -124,7 +124,7 @@ public:
|
||||||
|
|
||||||
int ClearanceEpsilon() const override { return m_clearanceEpsilon; }
|
int ClearanceEpsilon() const override { return m_clearanceEpsilon; }
|
||||||
|
|
||||||
void ClearCacheForItem( const PNS::ITEM* aItem ) override;
|
void ClearCacheForItems( std::vector<const PNS::ITEM*>& aItems ) override;
|
||||||
void ClearCaches() override;
|
void ClearCaches() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -410,13 +410,30 @@ bool PNS_PCBNEW_RULE_RESOLVER::QueryConstraint( PNS::CONSTRAINT_TYPE aType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PNS_PCBNEW_RULE_RESOLVER::ClearCacheForItem( const PNS::ITEM* aItem )
|
void PNS_PCBNEW_RULE_RESOLVER::ClearCacheForItems( std::vector<const PNS::ITEM*>& aItems )
|
||||||
{
|
{
|
||||||
CLEARANCE_CACHE_KEY key = { aItem, nullptr, false };
|
int n_pruned = 0;
|
||||||
m_clearanceCache.erase( key );
|
std::set<const PNS::ITEM*> remainingItems( aItems.begin(), aItems.end() );
|
||||||
|
|
||||||
key.Flag = true;
|
/* We need to carefully check both A and B item pointers in the cache against dirty/invalidated
|
||||||
m_clearanceCache.erase( key );
|
items in the set, as the clearance relation is commutative ( CL[a,b] == CL[b,a] ). The code
|
||||||
|
below is a bit ugly, but works in O(n*log(m)) and is run once or twice during ROUTER::Move() call
|
||||||
|
- so I hope it still gets better performance than no cache at all */
|
||||||
|
for( auto it = m_clearanceCache.begin(); it != m_clearanceCache.end(); )
|
||||||
|
{
|
||||||
|
bool dirty = remainingItems.find( it->first.A ) != remainingItems.end();
|
||||||
|
dirty |= remainingItems.find( it->first.B) != remainingItems.end();
|
||||||
|
|
||||||
|
if( dirty )
|
||||||
|
{
|
||||||
|
it = m_clearanceCache.erase( it );
|
||||||
|
n_pruned++;
|
||||||
|
} else
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
printf("ClearCache : n_pruned %d\n", n_pruned );
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -492,7 +509,15 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a
|
||||||
if( aUseClearanceEpsilon && rv > 0 )
|
if( aUseClearanceEpsilon && rv > 0 )
|
||||||
rv = std::max( 0, rv - m_clearanceEpsilon );
|
rv = std::max( 0, rv - m_clearanceEpsilon );
|
||||||
|
|
||||||
|
|
||||||
|
/* It makes no sense to put items that have no owning NODE in the cache - they can be allocated on stack
|
||||||
|
and we can't really invalidate them in the cache when they are destroyed. Probably a better idea would be
|
||||||
|
to use a static unique counter in PNS::ITEM constructor to generate the cache keys. */
|
||||||
|
if( aA && aB && aA->Owner() && aB->Owner() )
|
||||||
|
{
|
||||||
m_clearanceCache[ key ] = rv;
|
m_clearanceCache[ key ] = rv;
|
||||||
|
}
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,7 @@ NODE::~NODE()
|
||||||
|
|
||||||
m_joints.clear();
|
m_joints.clear();
|
||||||
|
|
||||||
std::vector<ITEM*> toDelete;
|
std::vector<const ITEM*> toDelete;
|
||||||
|
|
||||||
toDelete.reserve( m_index->Size() );
|
toDelete.reserve( m_index->Size() );
|
||||||
|
|
||||||
|
@ -97,12 +97,17 @@ NODE::~NODE()
|
||||||
toDelete.push_back( item );
|
toDelete.push_back( item );
|
||||||
}
|
}
|
||||||
|
|
||||||
for( ITEM* item : toDelete )
|
for( const ITEM* item : toDelete )
|
||||||
{
|
{
|
||||||
wxLogTrace( wxT( "PNS" ), wxT( "del item %p type %s" ), item, item->KindStr().c_str() );
|
wxLogTrace( wxT( "PNS" ), wxT( "del item %p type %s" ), item, item->KindStr().c_str() );
|
||||||
delete item;
|
delete item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( m_ruleResolver )
|
||||||
|
{
|
||||||
|
m_ruleResolver->ClearCacheForItems( toDelete );
|
||||||
|
}
|
||||||
|
|
||||||
releaseGarbage();
|
releaseGarbage();
|
||||||
unlinkParent();
|
unlinkParent();
|
||||||
|
|
||||||
|
@ -1389,6 +1394,9 @@ void NODE::releaseGarbage()
|
||||||
if( !isRoot() )
|
if( !isRoot() )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
std::vector<const ITEM*> cacheCheckItems;
|
||||||
|
cacheCheckItems.reserve( m_garbageItems.size() );
|
||||||
|
|
||||||
for( ITEM* item : m_garbageItems )
|
for( ITEM* item : m_garbageItems )
|
||||||
{
|
{
|
||||||
if( !item->BelongsTo( this ) )
|
if( !item->BelongsTo( this ) )
|
||||||
|
@ -1396,6 +1404,11 @@ void NODE::releaseGarbage()
|
||||||
}
|
}
|
||||||
|
|
||||||
m_garbageItems.clear();
|
m_garbageItems.clear();
|
||||||
|
|
||||||
|
if( m_ruleResolver )
|
||||||
|
{
|
||||||
|
m_ruleResolver->ClearCacheForItems( cacheCheckItems );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -155,7 +155,7 @@ public:
|
||||||
|
|
||||||
virtual wxString NetName( int aNet ) = 0;
|
virtual wxString NetName( int aNet ) = 0;
|
||||||
|
|
||||||
virtual void ClearCacheForItem( const ITEM* aItem ) {}
|
virtual void ClearCacheForItems( std::vector<const PNS::ITEM*>& aItems ) {}
|
||||||
virtual void ClearCaches() {}
|
virtual void ClearCaches() {}
|
||||||
|
|
||||||
virtual int ClearanceEpsilon() const { return 0; }
|
virtual int ClearanceEpsilon() const { return 0; }
|
||||||
|
|
|
@ -103,6 +103,7 @@ void ROUTER::ClearWorld()
|
||||||
{
|
{
|
||||||
if( m_world )
|
if( m_world )
|
||||||
{
|
{
|
||||||
|
m_world->SetRuleResolver( nullptr );
|
||||||
m_world->KillChildren();
|
m_world->KillChildren();
|
||||||
m_world.reset();
|
m_world.reset();
|
||||||
}
|
}
|
||||||
|
@ -716,9 +717,11 @@ void ROUTER::updateView( NODE* aNode, ITEM_SET& aCurrent, bool aDragging )
|
||||||
|
|
||||||
aNode->GetUpdatedItems( removed, added );
|
aNode->GetUpdatedItems( removed, added );
|
||||||
|
|
||||||
|
std::vector<const PNS::ITEM*> cacheCheckItems( added.begin(), added.end() );
|
||||||
|
GetRuleResolver()->ClearCacheForItems( cacheCheckItems );
|
||||||
|
|
||||||
for( ITEM* item : added )
|
for( ITEM* item : added )
|
||||||
{
|
{
|
||||||
GetRuleResolver()->ClearCacheForItem( item );
|
|
||||||
int clearance = GetRuleResolver()->Clearance( item, nullptr );
|
int clearance = GetRuleResolver()->Clearance( item, nullptr );
|
||||||
m_iface->DisplayItem( item, clearance, aDragging );
|
m_iface->DisplayItem( item, clearance, aDragging );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue