Further improvements to cache locking.
In particular, don't hold cache lock while doing computations.
This commit is contained in:
parent
0f8dac81cb
commit
6ced4f72d0
|
@ -263,7 +263,7 @@ void BOARD::IncrementTimeStamp()
|
||||||
|| !m_ZoneBBoxCache.empty()
|
|| !m_ZoneBBoxCache.empty()
|
||||||
|| m_CopperItemRTreeCache )
|
|| m_CopperItemRTreeCache )
|
||||||
{
|
{
|
||||||
std::unique_lock<std::shared_mutex> cacheLock( m_CachesMutex );
|
std::unique_lock<std::shared_mutex> writeLock( m_CachesMutex );
|
||||||
|
|
||||||
m_IntersectsAreaCache.clear();
|
m_IntersectsAreaCache.clear();
|
||||||
m_EnclosedByAreaCache.clear();
|
m_EnclosedByAreaCache.clear();
|
||||||
|
|
|
@ -1227,7 +1227,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------ Run-time caches -------------
|
// ------------ Run-time caches -------------
|
||||||
std::shared_mutex m_CachesMutex;
|
mutable std::shared_mutex m_CachesMutex;
|
||||||
std::unordered_map<PTR_PTR_CACHE_KEY, bool> m_IntersectsCourtyardCache;
|
std::unordered_map<PTR_PTR_CACHE_KEY, bool> m_IntersectsCourtyardCache;
|
||||||
std::unordered_map<PTR_PTR_CACHE_KEY, bool> m_IntersectsFCourtyardCache;
|
std::unordered_map<PTR_PTR_CACHE_KEY, bool> m_IntersectsFCourtyardCache;
|
||||||
std::unordered_map<PTR_PTR_CACHE_KEY, bool> m_IntersectsBCourtyardCache;
|
std::unordered_map<PTR_PTR_CACHE_KEY, bool> m_IntersectsBCourtyardCache;
|
||||||
|
|
|
@ -137,7 +137,7 @@ bool DRC_CACHE_GENERATOR::Run()
|
||||||
std::future<void> retn = tp.submit(
|
std::future<void> retn = tp.submit(
|
||||||
[&]()
|
[&]()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::shared_mutex> cacheLock( m_board->m_CachesMutex );
|
std::unique_lock<std::shared_mutex> writeLock( m_board->m_CachesMutex );
|
||||||
|
|
||||||
if( !m_board->m_CopperItemRTreeCache )
|
if( !m_board->m_CopperItemRTreeCache )
|
||||||
m_board->m_CopperItemRTreeCache = std::make_shared<DRC_RTREE>();
|
m_board->m_CopperItemRTreeCache = std::make_shared<DRC_RTREE>();
|
||||||
|
@ -185,8 +185,10 @@ bool DRC_CACHE_GENERATOR::Run()
|
||||||
rtree->Insert( aZone, layer );
|
rtree->Insert( aZone, layer );
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_lock<std::shared_mutex> cacheLock( m_board->m_CachesMutex );
|
{
|
||||||
m_board->m_CopperZoneRTreeCache[ aZone ] = std::move( rtree );
|
std::unique_lock<std::shared_mutex> writeLock( m_board->m_CachesMutex );
|
||||||
|
m_board->m_CopperZoneRTreeCache[ aZone ] = std::move( rtree );
|
||||||
|
}
|
||||||
|
|
||||||
done.fetch_add( 1 );
|
done.fetch_add( 1 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,7 +154,7 @@ bool DRC_TEST_PROVIDER_DISALLOW::Run()
|
||||||
PTR_PTR_LAYER_CACHE_KEY key = { ruleArea, copperZone, UNDEFINED_LAYER };
|
PTR_PTR_LAYER_CACHE_KEY key = { ruleArea, copperZone, UNDEFINED_LAYER };
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock<std::shared_mutex> cacheLock( board->m_CachesMutex );
|
std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
|
||||||
board->m_IntersectsAreaCache[ key ] = isInside;
|
board->m_IntersectsAreaCache[ key ] = isInside;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,28 +58,32 @@ public:
|
||||||
// in the ENUM_MAP: one for the canonical layer name and one for the user layer name.
|
// in the ENUM_MAP: one for the canonical layer name and one for the user layer name.
|
||||||
// We need to check against both.
|
// We need to check against both.
|
||||||
|
|
||||||
wxPGChoices& layerMap = ENUM_MAP<PCB_LAYER_ID>::Instance().Choices();
|
wxPGChoices& layerMap = ENUM_MAP<PCB_LAYER_ID>::Instance().Choices();
|
||||||
const wxString& layerName = b->AsString();
|
const wxString& layerName = b->AsString();
|
||||||
BOARD* board = static_cast<PCBEXPR_CONTEXT*>( aCtx )->GetBoard();
|
BOARD* board = static_cast<PCBEXPR_CONTEXT*>( aCtx )->GetBoard();
|
||||||
std::unique_lock<std::shared_mutex> cacheLock( board->m_CachesMutex );
|
|
||||||
auto i = board->m_LayerExpressionCache.find( layerName );
|
|
||||||
LSET mask;
|
|
||||||
|
|
||||||
if( i == board->m_LayerExpressionCache.end() )
|
|
||||||
{
|
{
|
||||||
for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
|
std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
|
||||||
{
|
|
||||||
wxPGChoiceEntry& entry = layerMap[ii];
|
|
||||||
|
|
||||||
if( entry.GetText().Matches( layerName ) )
|
auto i = board->m_LayerExpressionCache.find( layerName );
|
||||||
mask.set( ToLAYER_ID( entry.GetValue() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
board->m_LayerExpressionCache[ layerName ] = mask;
|
if( i != board->m_LayerExpressionCache.end() )
|
||||||
|
return i->second.Contains( m_layer );
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
LSET mask;
|
||||||
|
|
||||||
|
for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
|
||||||
{
|
{
|
||||||
mask = i->second;
|
wxPGChoiceEntry& entry = layerMap[ii];
|
||||||
|
|
||||||
|
if( entry.GetText().Matches( layerName ) )
|
||||||
|
mask.set( ToLAYER_ID( entry.GetValue() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
|
||||||
|
board->m_LayerExpressionCache[ layerName ] = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mask.Contains( m_layer );
|
return mask.Contains( m_layer );
|
||||||
|
|
|
@ -128,6 +128,8 @@ static void existsOnLayerFunc( LIBEVAL::CONTEXT* aCtx, void *self )
|
||||||
aCtx->ReportError( wxString::Format( _( "Unrecognized layer '%s'" ),
|
aCtx->ReportError( wxString::Format( _( "Unrecognized layer '%s'" ),
|
||||||
layerName ) );
|
layerName ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -136,32 +138,33 @@ static void existsOnLayerFunc( LIBEVAL::CONTEXT* aCtx, void *self )
|
||||||
*/
|
*/
|
||||||
|
|
||||||
BOARD* board = item->GetBoard();
|
BOARD* board = item->GetBoard();
|
||||||
std::unique_lock<std::shared_mutex> cacheLock( board->m_CachesMutex );
|
|
||||||
auto i = board->m_LayerExpressionCache.find( layerName );
|
{
|
||||||
|
std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
|
||||||
|
|
||||||
|
auto i = board->m_LayerExpressionCache.find( layerName );
|
||||||
|
|
||||||
|
if( i != board->m_LayerExpressionCache.end() )
|
||||||
|
return ( item->GetLayerSet() & i->second ).any() ? 1.0 : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
LSET mask;
|
LSET mask;
|
||||||
|
|
||||||
if( i == board->m_LayerExpressionCache.end() )
|
for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
|
||||||
{
|
{
|
||||||
for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
|
wxPGChoiceEntry& entry = layerMap[ ii ];
|
||||||
{
|
|
||||||
wxPGChoiceEntry& entry = layerMap[ ii ];
|
|
||||||
|
|
||||||
if( entry.GetText().Matches( layerName ) )
|
if( entry.GetText().Matches( layerName ) )
|
||||||
mask.set( ToLAYER_ID( entry.GetValue() ) );
|
mask.set( ToLAYER_ID( entry.GetValue() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
|
||||||
board->m_LayerExpressionCache[ layerName ] = mask;
|
board->m_LayerExpressionCache[ layerName ] = mask;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
mask = i->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( ( item->GetLayerSet() & mask ).any() )
|
return ( item->GetLayerSet() & mask ).any() ? 1.0 : 0.0;
|
||||||
return 1.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0.0;
|
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,11 +275,12 @@ static void intersectsCourtyardFunc( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||||
if( searchFootprints( board, arg->AsString(), context,
|
if( searchFootprints( board, arg->AsString(), context,
|
||||||
[&]( FOOTPRINT* fp )
|
[&]( FOOTPRINT* fp )
|
||||||
{
|
{
|
||||||
PTR_PTR_CACHE_KEY key = { fp, item };
|
PTR_PTR_CACHE_KEY key = { fp, item };
|
||||||
std::unique_lock<std::shared_mutex> cacheLock( board->m_CachesMutex );
|
|
||||||
|
|
||||||
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
||||||
{
|
{
|
||||||
|
std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
|
||||||
|
|
||||||
auto i = board->m_IntersectsCourtyardCache.find( key );
|
auto i = board->m_IntersectsCourtyardCache.find( key );
|
||||||
|
|
||||||
if( i != board->m_IntersectsCourtyardCache.end() )
|
if( i != board->m_IntersectsCourtyardCache.end() )
|
||||||
|
@ -287,7 +291,10 @@ static void intersectsCourtyardFunc( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||||
|| collidesWithCourtyard( item, itemShape, context, fp, B_Cu );
|
|| collidesWithCourtyard( item, itemShape, context, fp, B_Cu );
|
||||||
|
|
||||||
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
||||||
|
{
|
||||||
|
std::unique_lock<std::shared_mutex> cacheLock( board->m_CachesMutex );
|
||||||
board->m_IntersectsCourtyardCache[ key ] = res;
|
board->m_IntersectsCourtyardCache[ key ] = res;
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
} ) )
|
} ) )
|
||||||
|
@ -332,11 +339,12 @@ static void intersectsFrontCourtyardFunc( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||||
if( searchFootprints( board, arg->AsString(), context,
|
if( searchFootprints( board, arg->AsString(), context,
|
||||||
[&]( FOOTPRINT* fp )
|
[&]( FOOTPRINT* fp )
|
||||||
{
|
{
|
||||||
PTR_PTR_CACHE_KEY key = { fp, item };
|
PTR_PTR_CACHE_KEY key = { fp, item };
|
||||||
std::unique_lock<std::shared_mutex> cacheLock( board->m_CachesMutex );
|
|
||||||
|
|
||||||
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
||||||
{
|
{
|
||||||
|
std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
|
||||||
|
|
||||||
auto i = board->m_IntersectsFCourtyardCache.find( key );
|
auto i = board->m_IntersectsFCourtyardCache.find( key );
|
||||||
|
|
||||||
if( i != board->m_IntersectsFCourtyardCache.end() )
|
if( i != board->m_IntersectsFCourtyardCache.end() )
|
||||||
|
@ -346,7 +354,10 @@ static void intersectsFrontCourtyardFunc( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||||
bool res = collidesWithCourtyard( item, itemShape, context, fp, F_Cu );
|
bool res = collidesWithCourtyard( item, itemShape, context, fp, F_Cu );
|
||||||
|
|
||||||
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
||||||
|
{
|
||||||
|
std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
|
||||||
board->m_IntersectsFCourtyardCache[ key ] = res;
|
board->m_IntersectsFCourtyardCache[ key ] = res;
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
} ) )
|
} ) )
|
||||||
|
@ -391,11 +402,12 @@ static void intersectsBackCourtyardFunc( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||||
if( searchFootprints( board, arg->AsString(), context,
|
if( searchFootprints( board, arg->AsString(), context,
|
||||||
[&]( FOOTPRINT* fp )
|
[&]( FOOTPRINT* fp )
|
||||||
{
|
{
|
||||||
PTR_PTR_CACHE_KEY key = { fp, item };
|
PTR_PTR_CACHE_KEY key = { fp, item };
|
||||||
std::unique_lock<std::shared_mutex> cacheLock( board->m_CachesMutex );
|
|
||||||
|
|
||||||
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
||||||
{
|
{
|
||||||
|
std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
|
||||||
|
|
||||||
auto i = board->m_IntersectsBCourtyardCache.find( key );
|
auto i = board->m_IntersectsBCourtyardCache.find( key );
|
||||||
|
|
||||||
if( i != board->m_IntersectsBCourtyardCache.end() )
|
if( i != board->m_IntersectsBCourtyardCache.end() )
|
||||||
|
@ -405,7 +417,10 @@ static void intersectsBackCourtyardFunc( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||||
bool res = collidesWithCourtyard( item, itemShape, context, fp, B_Cu );
|
bool res = collidesWithCourtyard( item, itemShape, context, fp, B_Cu );
|
||||||
|
|
||||||
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
||||||
|
{
|
||||||
|
std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
|
||||||
board->m_IntersectsBCourtyardCache[ key ] = res;
|
board->m_IntersectsBCourtyardCache[ key ] = res;
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
} ) )
|
} ) )
|
||||||
|
@ -649,8 +664,7 @@ static void intersectsAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||||
if( !aArea->GetBoundingBox().Intersects( itemBBox ) )
|
if( !aArea->GetBoundingBox().Intersects( itemBBox ) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
LSET testLayers;
|
LSET testLayers;
|
||||||
PTR_PTR_LAYER_CACHE_KEY key;
|
|
||||||
|
|
||||||
if( aLayer != UNDEFINED_LAYER )
|
if( aLayer != UNDEFINED_LAYER )
|
||||||
testLayers.set( aLayer );
|
testLayers.set( aLayer );
|
||||||
|
@ -659,10 +673,11 @@ static void intersectsAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||||
|
|
||||||
for( PCB_LAYER_ID layer : testLayers.UIOrder() )
|
for( PCB_LAYER_ID layer : testLayers.UIOrder() )
|
||||||
{
|
{
|
||||||
|
PTR_PTR_LAYER_CACHE_KEY key = { aArea, item, layer };
|
||||||
|
|
||||||
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
||||||
{
|
{
|
||||||
std::shared_lock<std::shared_mutex> cacheLock( board->m_CachesMutex );
|
std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
|
||||||
key = { aArea, item, layer };
|
|
||||||
|
|
||||||
auto i = board->m_IntersectsAreaCache.find( key );
|
auto i = board->m_IntersectsAreaCache.find( key );
|
||||||
|
|
||||||
|
@ -674,7 +689,7 @@ static void intersectsAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||||
|
|
||||||
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
||||||
{
|
{
|
||||||
std::unique_lock<std::shared_mutex> cacheLock( board->m_CachesMutex );
|
std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
|
||||||
board->m_IntersectsAreaCache[ key ] = collides;
|
board->m_IntersectsAreaCache[ key ] = collides;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -736,11 +751,12 @@ static void enclosedByAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||||
if( !aArea->GetBoundingBox().Intersects( itemBBox ) )
|
if( !aArea->GetBoundingBox().Intersects( itemBBox ) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::unique_lock<std::shared_mutex> cacheLock( board->m_CachesMutex );
|
PTR_PTR_LAYER_CACHE_KEY key = { aArea, item, layer };
|
||||||
PTR_PTR_LAYER_CACHE_KEY key = { aArea, item, layer };
|
|
||||||
|
|
||||||
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
||||||
{
|
{
|
||||||
|
std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
|
||||||
|
|
||||||
auto i = board->m_EnclosedByAreaCache.find( key );
|
auto i = board->m_EnclosedByAreaCache.find( key );
|
||||||
|
|
||||||
if( i != board->m_EnclosedByAreaCache.end() )
|
if( i != board->m_EnclosedByAreaCache.end() )
|
||||||
|
@ -767,7 +783,10 @@ static void enclosedByAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
if( ( item->GetFlags() & ROUTER_TRANSIENT ) == 0 )
|
||||||
|
{
|
||||||
|
std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
|
||||||
board->m_EnclosedByAreaCache[ key ] = enclosedByArea;
|
board->m_EnclosedByAreaCache[ key ] = enclosedByArea;
|
||||||
|
}
|
||||||
|
|
||||||
return enclosedByArea;
|
return enclosedByArea;
|
||||||
} ) )
|
} ) )
|
||||||
|
|
|
@ -345,25 +345,22 @@ const BOX2I ZONE::GetBoundingBox() const
|
||||||
if( const BOARD* board = GetBoard() )
|
if( const BOARD* board = GetBoard() )
|
||||||
{
|
{
|
||||||
std::unordered_map<const ZONE*, BOX2I>& cache = board->m_ZoneBBoxCache;
|
std::unordered_map<const ZONE*, BOX2I>& cache = board->m_ZoneBBoxCache;
|
||||||
std::shared_lock<std::shared_mutex> readLock( const_cast<BOARD*>( board )->m_CachesMutex );
|
|
||||||
auto cacheIter = cache.find( this );
|
|
||||||
|
|
||||||
if( cacheIter != cache.end() )
|
{
|
||||||
return cacheIter->second;
|
std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
|
||||||
|
|
||||||
readLock.unlock();
|
auto cacheIter = cache.find( this );
|
||||||
|
|
||||||
// if we get here we need an exclusive lock to cache the entry
|
if( cacheIter != cache.end() )
|
||||||
std::unique_lock<std::shared_mutex> writeLock( const_cast<BOARD*>( board )->m_CachesMutex );
|
return cacheIter->second;
|
||||||
|
}
|
||||||
// check again for the cached item; it might have been computed meanwhile by another thread
|
|
||||||
cacheIter = cache.find( this );
|
|
||||||
if( cacheIter != cache.end() )
|
|
||||||
return cacheIter->second;
|
|
||||||
|
|
||||||
BOX2I bbox = m_Poly->BBox();
|
BOX2I bbox = m_Poly->BBox();
|
||||||
|
|
||||||
cache[ this ] = bbox;
|
{
|
||||||
|
std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
|
||||||
|
cache[ this ] = bbox;
|
||||||
|
}
|
||||||
|
|
||||||
return bbox;
|
return bbox;
|
||||||
}
|
}
|
||||||
|
@ -374,18 +371,9 @@ const BOX2I ZONE::GetBoundingBox() const
|
||||||
|
|
||||||
void ZONE::CacheBoundingBox()
|
void ZONE::CacheBoundingBox()
|
||||||
{
|
{
|
||||||
BOARD* board = GetBoard();
|
// GetBoundingBox() will cache it for us, and there's no sense duplicating the somewhat tricky
|
||||||
std::unordered_map<const ZONE*, BOX2I>& cache = board->m_ZoneBBoxCache;
|
// locking code.
|
||||||
std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
|
GetBoundingBox();
|
||||||
auto cacheIter = cache.find( this );
|
|
||||||
|
|
||||||
if( cacheIter == cache.end() ) {
|
|
||||||
readLock.unlock();
|
|
||||||
std::unique_lock<std::shared_mutex> writeLock(board->m_CachesMutex);
|
|
||||||
// check again in case another thread has already calculated the entry while we were waiting for the exclusive lock
|
|
||||||
if ( cache.count( this ) == 0 )
|
|
||||||
cache[this] = m_Poly->BBox();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue