Update BS Threadpool to 5.0

This commit is contained in:
Seth Hillbrand 2025-09-10 12:46:19 -07:00
parent 2f1a91279f
commit 6e2b20ed0e
30 changed files with 2371 additions and 869 deletions

View File

@ -277,7 +277,7 @@ void RENDER_3D_RAYTRACE_BASE::renderTracing( uint8_t* ptrPBO, REPORTER* aStatusR
BS::multi_future<void> futures;
for( size_t i = 0; i < tp.get_thread_count(); ++i )
futures.push_back( tp.submit( processBlocks ) );
futures.push_back( tp.submit_task( processBlocks ) );
futures.wait();

View File

@ -190,7 +190,7 @@ void DESIGN_BLOCK_LIST_IMPL::loadDesignBlocks()
};
for( size_t ii = 0; ii < num_elements; ++ii )
returns[ii] = tp.submit( db_thread );
returns[ii] = tp.submit_task( db_thread );
for( const std::future<size_t>& ret : returns )
{

View File

@ -1614,11 +1614,10 @@ void CONNECTION_GRAPH::resolveAllDrivers()
thread_pool& tp = GetKiCadThreadPool();
auto results = tp.parallelize_loop( dirty_graphs.size(),
[&]( const int a, const int b)
auto results = tp.submit_loop( 0, dirty_graphs.size(),
[&]( const int ii )
{
for( int ii = a; ii < b; ++ii )
update_lambda( dirty_graphs[ii] );
update_lambda( dirty_graphs[ii] );
});
results.wait();
@ -2257,11 +2256,10 @@ void CONNECTION_GRAPH::buildConnectionGraph( std::function<void( SCH_ITEM* )>* a
thread_pool& tp = GetKiCadThreadPool();
auto results = tp.parallelize_loop( m_driver_subgraphs.size(),
[&]( const int a, const int b)
auto results = tp.submit_loop( 0, m_driver_subgraphs.size(),
[&]( const int ii )
{
for( int ii = a; ii < b; ++ii )
m_driver_subgraphs[ii]->UpdateItemConnections();
m_driver_subgraphs[ii]->UpdateItemConnections();
});
results.wait();
@ -2464,12 +2462,11 @@ void CONNECTION_GRAPH::buildConnectionGraph( std::function<void( SCH_ITEM* )>* a
return 1;
};
auto results2 = tp.parallelize_loop( m_driver_subgraphs.size(),
[&]( const int a, const int b)
auto results2 = tp.submit_loop( 0, m_driver_subgraphs.size(),
[&]( const int ii )
{
for( int ii = a; ii < b; ++ii )
updateItemConnectionsTask( m_driver_subgraphs[ii] );
});
updateItemConnectionsTask( m_driver_subgraphs[ii] );
} );
results2.wait();
m_net_code_to_subgraphs_map.clear();

View File

@ -140,11 +140,10 @@ void SPICE_LIBRARY_PARSER::ReadFile( const wxString& aFilePath, REPORTER& aRepor
// Read all self-contained models in parallel
thread_pool& tp = GetKiCadThreadPool();
auto results = tp.parallelize_loop( modelQueue.size(),
[&]( const int a, const int b )
auto results = tp.submit_loop( 0, modelQueue.size(),
[&]( const int ii )
{
for( int ii = a; ii < b; ++ii )
createModel( ii, true );
createModel( ii, true );
} );
results.wait();

View File

@ -110,7 +110,7 @@ public:
*/
void BuildArgvUtf8();
BS::thread_pool& GetThreadPool() { return *m_singleton.m_ThreadPool; }
BS::thread_pool<0>& GetThreadPool() { return *m_singleton.m_ThreadPool; }
GL_CONTEXT_MANAGER* GetGLContextManager() { return m_singleton.m_GLContextManager; }

View File

@ -25,6 +25,7 @@
class GL_CONTEXT_MANAGER;
namespace BS
{
template <std::uint8_t>
class thread_pool;
}
@ -42,7 +43,7 @@ public:
void Init();
public:
BS::thread_pool* m_ThreadPool;
BS::thread_pool<0>* m_ThreadPool;
GL_CONTEXT_MANAGER* m_GLContextManager;
};

View File

@ -28,7 +28,7 @@
#include <bs_thread_pool.hpp>
#include <import_export.h>
using thread_pool = BS::thread_pool;
using thread_pool = BS::thread_pool<0>;
/**
* Get a reference to the current thread pool. N.B., you cannot copy the thread pool

View File

@ -636,7 +636,11 @@ void PROJECT_TREE_PANE::ReCreateTreePrj()
std::lock_guard<std::mutex> lock2( m_gitTreeCacheMutex );
thread_pool& tp = GetKiCadThreadPool();
tp.wait_for_tasks();
while( tp.get_tasks_running() )
{
tp.wait_for( std::chrono::milliseconds( 250 ) );
}
m_gitStatusTimer.Stop();
m_gitSyncTimer.Stop();
m_gitTreeCache.clear();
@ -2293,25 +2297,21 @@ void PROJECT_TREE_PANE::onGitSyncTimer( wxTimerEvent& aEvent )
thread_pool& tp = GetKiCadThreadPool();
tp.push_task(
[this]()
{
KIGIT_COMMON* gitCommon = m_TreeProject->GitCommon();
tp.submit_task( [this]()
{
KIGIT_COMMON* gitCommon = m_TreeProject->GitCommon();
if( !gitCommon )
{
wxLogTrace( traceGit, "onGitSyncTimer: No git repository found" );
return;
}
if( !gitCommon )
{
wxLogTrace( traceGit, "onGitSyncTimer: No git repository found" );
return;
}
GIT_PULL_HANDLER handler( gitCommon );
handler.PerformFetch();
GIT_PULL_HANDLER handler( gitCommon );
handler.PerformFetch();
CallAfter( [this]()
{
gitStatusTimerHandler();
} );
} );
CallAfter( [this]() { gitStatusTimerHandler(); } );
} );
if( gitSettings.updatInterval > 0 )
{
@ -2327,11 +2327,7 @@ void PROJECT_TREE_PANE::gitStatusTimerHandler()
updateTreeCache();
thread_pool& tp = GetKiCadThreadPool();
tp.push_task(
[this]()
{
updateGitStatusIconMap();
} );
tp.submit_task( [this]() { updateGitStatusIconMap(); } );
}
void PROJECT_TREE_PANE::onGitStatusTimer( wxTimerEvent& aEvent )

View File

@ -274,5 +274,5 @@ void UPDATE_MANAGER::CheckForUpdate( wxWindow* aNoticeParent )
};
thread_pool& tp = GetKiCadThreadPool();
m_updateTask = tp.submit( update_check );
m_updateTask = tp.submit_task( update_check );
}

View File

@ -1114,7 +1114,7 @@ void BOARD::CacheTriangulation( PROGRESS_REPORTER* aReporter, const std::vector<
};
for( ZONE* zone : zones )
returns.emplace_back( tp.submit( cache_zones, zone ) );
returns.emplace_back( tp.submit_task( [cache_zones, zone] { return cache_zones( zone ); } ) );
// Finalize the triangulation threads
for( const std::future<size_t>& ret : returns )

View File

@ -270,24 +270,21 @@ void CN_CONNECTIVITY_ALGO::searchConnections()
{
std::vector<std::future<size_t>> returns( dirtyItems.size() );
auto conn_lambda =
[&dirtyItems]( size_t aItem, CN_LIST* aItemList,
PROGRESS_REPORTER* aReporter) -> size_t
{
if( aReporter && aReporter->IsCancelled() )
for( size_t ii = 0; ii < dirtyItems.size(); ++ii )
{
returns[ii] = tp.submit_task(
[&dirtyItems, ii, this] () ->size_t {
if( m_progressReporter && m_progressReporter->IsCancelled() )
return 0;
CN_VISITOR visitor( dirtyItems[aItem] );
aItemList->FindNearby( dirtyItems[aItem], visitor );
CN_VISITOR visitor( dirtyItems[ii] );
m_itemList.FindNearby( dirtyItems[ii], visitor );
if( aReporter )
aReporter->AdvanceProgress();
if( m_progressReporter )
m_progressReporter->AdvanceProgress();
return 1;
};
for( size_t ii = 0; ii < dirtyItems.size(); ++ii )
returns[ii] = tp.submit( conn_lambda, ii, &m_itemList, m_progressReporter );
return 1; } );
}
for( const std::future<size_t>& ret : returns )
{
@ -490,7 +487,11 @@ void CN_CONNECTIVITY_ALGO::Build( BOARD* aBoard, PROGRESS_REPORTER* aReporter )
};
for( size_t ii = 0; ii < zitems.size(); ++ii )
returns[ii] = tp.submit( cache_zones, zitems[ii] );
{
CN_ZONE_LAYER* ptr = zitems[ii];
returns[ii] = tp.submit_task(
[cache_zones, ptr] { return cache_zones( ptr ); } );
}
for( const std::future<size_t>& ret : returns )
{

View File

@ -191,19 +191,17 @@ void CONNECTIVITY_DATA::updateRatsnest()
thread_pool& tp = GetKiCadThreadPool();
auto results = tp.parallelize_loop( dirty_nets.size(),
[&]( const int a, const int b )
auto results = tp.submit_loop( 0, dirty_nets.size(),
[&]( const int ii )
{
for( int ii = a; ii < b; ++ii )
dirty_nets[ii]->UpdateNet();
dirty_nets[ii]->UpdateNet();
} );
results.wait();
auto results2 = tp.parallelize_loop( dirty_nets.size(),
[&]( const int a, const int b )
auto results2 = tp.submit_loop( 0, dirty_nets.size(),
[&]( const int ii )
{
for( int ii = a; ii < b; ++ii )
dirty_nets[ii]->OptimizeRNEdges();
dirty_nets[ii]->OptimizeRNEdges();
} );
results2.wait();
@ -370,11 +368,10 @@ void CONNECTIVITY_DATA::ComputeLocalRatsnest( const std::vector<BOARD_ITEM*>& aI
thread_pool& tp = GetKiCadThreadPool();
size_t num_nets = std::min( m_nets.size(), aDynamicData->m_nets.size() );
auto results = tp.parallelize_loop( 1, num_nets,
[&]( const int a, const int b)
auto results = tp.submit_loop( 1, num_nets,
[&]( const int ii )
{
for( int ii = a; ii < b; ++ii )
update_lambda( ii );
update_lambda( ii );
});
results.wait();

View File

@ -425,7 +425,7 @@ void DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BO
};
thread_pool& tp = GetKiCadThreadPool();
auto ret = tp.submit( saveFile );
auto ret = tp.submit_task( saveFile );
std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );

View File

@ -158,7 +158,7 @@ bool DRC_CACHE_GENERATOR::Run()
forEachGeometryItem( itemTypes, boardCopperLayers, countItems );
std::future<void> retn = tp.submit(
std::future<void> retn = tp.submit_task(
[&]()
{
std::unique_lock<std::shared_mutex> writeLock( m_board->m_CachesMutex );
@ -225,7 +225,7 @@ bool DRC_CACHE_GENERATOR::Run()
};
for( ZONE* zone : allZones )
returns.emplace_back( tp.submit( cache_zones, zone ) );
returns.emplace_back( tp.submit_task( [cache_zones, zone] { return cache_zones( zone ); } ) );
done.store( 1 );

View File

@ -2317,56 +2317,54 @@ void CREEPAGE_GRAPH::GeneratePaths( double aMaxWeight, PCB_LAYER_ID aLayer )
}
}
auto processWorkItems = [&]( size_t start_idx, size_t end_idx ) -> bool
auto processWorkItems = [&]( size_t idx ) -> bool
{
for( size_t idx = start_idx; idx < end_idx; ++idx )
auto [gn1, gn2] = work_items[idx];
for( PATH_CONNECTION pc : GetPaths( gn1->m_parent, gn2->m_parent, aMaxWeight ) )
{
auto [gn1, gn2] = work_items[idx];
std::vector<const BOARD_ITEM*> IgnoreForTest = {
gn1->m_parent->GetParent(), gn2->m_parent->GetParent()
};
for( PATH_CONNECTION pc : GetPaths( gn1->m_parent, gn2->m_parent, aMaxWeight ) )
if( !pc.isValid( m_board, aLayer, m_boardEdge, IgnoreForTest, m_boardOutline,
{ false, true }, m_minGrooveWidth ) )
continue;
std::shared_ptr<GRAPH_NODE> connect1 = gn1, connect2 = gn2;
std::lock_guard<std::mutex> lock( nodes_lock );
// Handle non-point node1
if( gn1->m_parent->GetType() != CREEP_SHAPE::TYPE::POINT )
{
std::vector<const BOARD_ITEM*> IgnoreForTest = {
gn1->m_parent->GetParent(), gn2->m_parent->GetParent()
};
auto gnt1 = AddNode( GRAPH_NODE::POINT, gn1->m_parent, pc.a1 );
gnt1->m_connectDirectly = false;
connect1 = gnt1;
if( !pc.isValid( m_board, aLayer, m_boardEdge, IgnoreForTest, m_boardOutline,
{ false, true }, m_minGrooveWidth ) )
continue;
std::shared_ptr<GRAPH_NODE> connect1 = gn1, connect2 = gn2;
std::lock_guard<std::mutex> lock( nodes_lock );
// Handle non-point node1
if( gn1->m_parent->GetType() != CREEP_SHAPE::TYPE::POINT )
if( gn1->m_parent->IsConductive() )
{
auto gnt1 = AddNode( GRAPH_NODE::POINT, gn1->m_parent, pc.a1 );
gnt1->m_connectDirectly = false;
connect1 = gnt1;
if( gn1->m_parent->IsConductive() )
{
if( auto gc = AddConnection( gn1, gnt1 ) )
gc->m_path.m_show = false;
}
if( auto gc = AddConnection( gn1, gnt1 ) )
gc->m_path.m_show = false;
}
// Handle non-point node2
if( gn2->m_parent->GetType() != CREEP_SHAPE::TYPE::POINT )
{
auto gnt2 = AddNode( GRAPH_NODE::POINT, gn2->m_parent, pc.a2 );
gnt2->m_connectDirectly = false;
connect2 = gnt2;
if( gn2->m_parent->IsConductive() )
{
if( auto gc = AddConnection( gn2, gnt2 ) )
gc->m_path.m_show = false;
}
}
AddConnection( connect1, connect2, pc );
}
// Handle non-point node2
if( gn2->m_parent->GetType() != CREEP_SHAPE::TYPE::POINT )
{
auto gnt2 = AddNode( GRAPH_NODE::POINT, gn2->m_parent, pc.a2 );
gnt2->m_connectDirectly = false;
connect2 = gnt2;
if( gn2->m_parent->IsConductive() )
{
if( auto gc = AddConnection( gn2, gnt2 ) )
gc->m_path.m_show = false;
}
}
AddConnection( connect1, connect2, pc );
}
return true;
};
@ -2374,15 +2372,16 @@ void CREEPAGE_GRAPH::GeneratePaths( double aMaxWeight, PCB_LAYER_ID aLayer )
// has already parallelized the work, so we can process all items in one go.
if( tp.get_tasks_total() >= tp.get_thread_count() - 4 )
{
processWorkItems( 0, work_items.size() );
for( size_t ii = 0; ii < work_items.size(); ii++ )
processWorkItems( ii );
}
else
{
auto ret = tp.parallelize_loop( work_items.size(), processWorkItems );
auto ret = tp.submit_loop( 0, work_items.size(), processWorkItems );
for( size_t ii = 0; ii < ret.size(); ii++ )
{
std::future<bool>& r = ret[ii];
auto& r = ret[ii];
if( !r.valid() )
continue;

View File

@ -505,7 +505,6 @@ bool DRC_TEST_PROVIDER_CONNECTION_WIDTH::Run()
}
thread_pool& tp = GetKiCadThreadPool();
std::vector<std::future<size_t>> returns;
size_t total_effort = 0;
for( const auto& [ netLayer, itemsPoly ] : dataset )
@ -513,14 +512,16 @@ bool DRC_TEST_PROVIDER_CONNECTION_WIDTH::Run()
total_effort += std::max( (size_t) 1, total_effort ) * distinctMinWidths.size();
std::vector<std::future<size_t>> returns;
returns.reserve( dataset.size() );
for( const auto& [ netLayer, itemsPoly ] : dataset )
{
returns.emplace_back( tp.submit( build_netlayer_polys, netLayer.Netcode, netLayer.Layer ) );
int netcode = netLayer.Netcode;
PCB_LAYER_ID layer = netLayer.Layer;
returns.emplace_back( tp.submit_task( [&, netcode, layer]() { return build_netlayer_polys( netcode, layer ); } ) );
}
for( std::future<size_t>& ret : returns )
for( auto& ret : returns )
{
std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
@ -541,11 +542,13 @@ bool DRC_TEST_PROVIDER_CONNECTION_WIDTH::Run()
if( minWidth - epsilon <= 0 )
continue;
returns.emplace_back( tp.submit( min_checker, itemsPoly, netLayer.Layer, minWidth ) );
returns.emplace_back( tp.submit_task( [min_checker, &itemsPoly, &netLayer, minWidth]() {
return min_checker( itemsPoly, netLayer.Layer, minWidth );
} ) );
}
}
for( std::future<size_t>& ret : returns )
for( auto& ret : returns )
{
std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );

View File

@ -594,115 +594,112 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackClearances()
LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
auto testTrack = [&]( const int start_idx, const int end_idx )
auto testTrack = [&]( const int trackIdx )
{
for( int trackIdx = start_idx; trackIdx < end_idx; ++trackIdx )
PCB_TRACK* track = m_board->Tracks()[trackIdx];
for( PCB_LAYER_ID layer : LSET( track->GetLayerSet() & boardCopperLayers ) )
{
PCB_TRACK* track = m_board->Tracks()[trackIdx];
std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer );
for( PCB_LAYER_ID layer : LSET( track->GetLayerSet() & boardCopperLayers ) )
m_board->m_CopperItemRTreeCache->QueryColliding( track, layer, layer,
// Filter:
[&]( BOARD_ITEM* other ) -> bool
{
BOARD_CONNECTED_ITEM* otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
if( otherCItem && otherCItem->GetNetCode() == track->GetNetCode() )
return false;
BOARD_ITEM* a = track;
BOARD_ITEM* b = other;
// store canonical order so we don't collide in both directions
// (a:b and b:a)
if( static_cast<void*>( a ) > static_cast<void*>( b ) )
std::swap( a, b );
std::lock_guard<std::mutex> lock( checkedPairsMutex );
auto it = checkedPairs.find( { a, b } );
if( it != checkedPairs.end()
&& ( it->second.layers.test( layer ) || ( it->second.has_error ) ) )
{
return false;
}
else
{
checkedPairs[ { a, b } ].layers.set( layer );
return true;
}
},
// Visitor:
[&]( BOARD_ITEM* other ) -> bool
{
if( m_drcEngine->IsCancelled() )
return false;
if( other->Type() == PCB_PAD_T && static_cast<PAD*>( other )->IsFreePad() )
{
if( other->GetEffectiveShape( layer )->Collide( trackShape.get() ) )
{
std::lock_guard<std::mutex> lock( freePadsUsageMapMutex );
auto it = freePadsUsageMap.find( other );
if( it == freePadsUsageMap.end() )
{
freePadsUsageMap[ other ] = track->GetNetCode();
return true; // Continue colliding tests
}
else if( it->second == track->GetNetCode() )
{
return true; // Continue colliding tests
}
}
}
// If we get an error, mark the pair as having a clearance error already
if( !testSingleLayerItemAgainstItem( track, trackShape.get(), layer, other ) )
{
if( !m_drcEngine->GetReportAllTrackErrors() )
{
BOARD_ITEM* a = track;
BOARD_ITEM* b = other;
// store canonical order so we don't collide in both directions
// (a:b and b:a)
if( static_cast<void*>( a ) > static_cast<void*>( b ) )
std::swap( a, b );
std::lock_guard<std::mutex> lock( checkedPairsMutex );
auto it = checkedPairs.find( { a, b } );
if( it != checkedPairs.end() )
it->second.has_error = true;
return false; // We're done with this track
}
}
return !m_drcEngine->IsCancelled();
},
m_board->m_DRCMaxClearance );
for( ZONE* zone : m_board->m_DRCCopperZones )
{
std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer );
testItemAgainstZone( track, zone, layer );
m_board->m_CopperItemRTreeCache->QueryColliding( track, layer, layer,
// Filter:
[&]( BOARD_ITEM* other ) -> bool
{
BOARD_CONNECTED_ITEM* otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
if( otherCItem && otherCItem->GetNetCode() == track->GetNetCode() )
return false;
BOARD_ITEM* a = track;
BOARD_ITEM* b = other;
// store canonical order so we don't collide in both directions
// (a:b and b:a)
if( static_cast<void*>( a ) > static_cast<void*>( b ) )
std::swap( a, b );
std::lock_guard<std::mutex> lock( checkedPairsMutex );
auto it = checkedPairs.find( { a, b } );
if( it != checkedPairs.end()
&& ( it->second.layers.test( layer ) || ( it->second.has_error ) ) )
{
return false;
}
else
{
checkedPairs[ { a, b } ].layers.set( layer );
return true;
}
},
// Visitor:
[&]( BOARD_ITEM* other ) -> bool
{
if( m_drcEngine->IsCancelled() )
return false;
if( other->Type() == PCB_PAD_T && static_cast<PAD*>( other )->IsFreePad() )
{
if( other->GetEffectiveShape( layer )->Collide( trackShape.get() ) )
{
std::lock_guard<std::mutex> lock( freePadsUsageMapMutex );
auto it = freePadsUsageMap.find( other );
if( it == freePadsUsageMap.end() )
{
freePadsUsageMap[ other ] = track->GetNetCode();
return true; // Continue colliding tests
}
else if( it->second == track->GetNetCode() )
{
return true; // Continue colliding tests
}
}
}
// If we get an error, mark the pair as having a clearance error already
if( !testSingleLayerItemAgainstItem( track, trackShape.get(), layer, other ) )
{
if( !m_drcEngine->GetReportAllTrackErrors() )
{
BOARD_ITEM* a = track;
BOARD_ITEM* b = other;
// store canonical order so we don't collide in both directions
// (a:b and b:a)
if( static_cast<void*>( a ) > static_cast<void*>( b ) )
std::swap( a, b );
std::lock_guard<std::mutex> lock( checkedPairsMutex );
auto it = checkedPairs.find( { a, b } );
if( it != checkedPairs.end() )
it->second.has_error = true;
return false; // We're done with this track
}
}
return !m_drcEngine->IsCancelled();
},
m_board->m_DRCMaxClearance );
for( ZONE* zone : m_board->m_DRCCopperZones )
{
testItemAgainstZone( track, zone, layer );
if( m_drcEngine->IsCancelled() )
break;
}
if( m_drcEngine->IsCancelled() )
break;
}
done.fetch_add( 1 );
}
done.fetch_add( 1 );
};
thread_pool& tp = GetKiCadThreadPool();
tp.push_loop( m_board->Tracks().size(), testTrack );
auto track_futures = tp.submit_loop( 0, m_board->Tracks().size(), testTrack );
while( done < count )
{
@ -710,7 +707,8 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackClearances()
if( m_drcEngine->IsCancelled() )
{
tp.wait_for_tasks();
// Wait for the submitted loop tasks to finish
track_futures.wait();
break;
}
@ -967,82 +965,79 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadClearances( )
LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
const auto fp_check = [&]( size_t aFromIdx, size_t aToIdx )
const auto fp_check = [&]( size_t ii )
{
for( size_t ii = aFromIdx; ii < aToIdx; ++ii )
FOOTPRINT* footprint = m_board->Footprints()[ ii ];
for( PAD* pad : footprint->Pads() )
{
FOOTPRINT* footprint = m_board->Footprints()[ ii ];
for( PAD* pad : footprint->Pads() )
for( PCB_LAYER_ID layer : LSET( pad->GetLayerSet() & boardCopperLayers ) )
{
for( PCB_LAYER_ID layer : LSET( pad->GetLayerSet() & boardCopperLayers ) )
{
if( m_drcEngine->IsCancelled() )
return;
if( m_drcEngine->IsCancelled() )
return;
std::shared_ptr<SHAPE> padShape = pad->GetEffectiveShape( layer );
std::shared_ptr<SHAPE> padShape = pad->GetEffectiveShape( layer );
m_board->m_CopperItemRTreeCache->QueryColliding( pad, layer, layer,
// Filter:
[&]( BOARD_ITEM* other ) -> bool
m_board->m_CopperItemRTreeCache->QueryColliding( pad, layer, layer,
// Filter:
[&]( BOARD_ITEM* other ) -> bool
{
BOARD_ITEM* a = pad;
BOARD_ITEM* b = other;
// store canonical order so we don't collide in both
// directions (a:b and b:a)
if( static_cast<void*>( a ) > static_cast<void*>( b ) )
std::swap( a, b );
std::lock_guard<std::mutex> lock( checkedPairsMutex );
auto it = checkedPairs.find( { a, b } );
if( it != checkedPairs.end()
&& ( it->second.layers.test( layer ) || it->second.has_error ) )
{
return false;
}
else
{
checkedPairs[ { a, b } ].layers.set( layer );
return true;
}
},
// Visitor
[&]( BOARD_ITEM* other ) -> bool
{
if( !testPadAgainstItem( pad, padShape.get(), layer, other ) )
{
BOARD_ITEM* a = pad;
BOARD_ITEM* b = other;
// store canonical order so we don't collide in both
// directions (a:b and b:a)
if( static_cast<void*>( a ) > static_cast<void*>( b ) )
std::swap( a, b );
std::lock_guard<std::mutex> lock( checkedPairsMutex );
auto it = checkedPairs.find( { a, b } );
if( it != checkedPairs.end()
&& ( it->second.layers.test( layer ) || it->second.has_error ) )
{
return false;
}
else
{
checkedPairs[ { a, b } ].layers.set( layer );
return true;
}
},
// Visitor
[&]( BOARD_ITEM* other ) -> bool
{
if( !testPadAgainstItem( pad, padShape.get(), layer, other ) )
{
BOARD_ITEM* a = pad;
BOARD_ITEM* b = other;
if( it != checkedPairs.end() )
it->second.has_error = true;
}
std::lock_guard<std::mutex> lock( checkedPairsMutex );
auto it = checkedPairs.find( { a, b } );
return !m_drcEngine->IsCancelled();
},
m_board->m_DRCMaxClearance );
if( it != checkedPairs.end() )
it->second.has_error = true;
}
for( ZONE* zone : m_board->m_DRCCopperZones )
{
testItemAgainstZone( pad, zone, layer );
return !m_drcEngine->IsCancelled();
},
m_board->m_DRCMaxClearance );
for( ZONE* zone : m_board->m_DRCCopperZones )
{
testItemAgainstZone( pad, zone, layer );
if( m_drcEngine->IsCancelled() )
return;
}
if( m_drcEngine->IsCancelled() )
return;
}
}
done.fetch_add( 1 );
}
done.fetch_add( 1 );
};
size_t numFootprints = m_board->Footprints().size();
auto returns = tp.parallelize_loop( numFootprints, fp_check );
auto returns = tp.submit_loop( 0, numFootprints, fp_check );
// Wait for all threads to finish
for( size_t ii = 0; ii < returns.size(); ++ii )
@ -1152,7 +1147,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testGraphicClearances()
m_board->m_DRCMaxClearance );
};
std::future<void> retn = tp.submit(
std::future<void> retn = tp.submit_task(
[&]()
{
for( BOARD_ITEM* item : m_board->Drawings() )
@ -1370,7 +1365,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones()
continue;
count++;
tp.push_task( checkZones, ia, ia2, sameNet, layer );
tp.submit_task( [checkZones, ia, ia2, sameNet, layer]() { checkZones(ia, ia2, sameNet, layer); } );
}
}
}
@ -1382,7 +1377,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones()
if( m_drcEngine->IsCancelled() )
break;
if( tp.wait_for_tasks_duration( std::chrono::milliseconds( 250 ) ) )
if( tp.wait_for( std::chrono::milliseconds( 250 ) ) )
break;
}

View File

@ -110,11 +110,11 @@ bool DRC_TEST_PROVIDER_DISALLOW::Run()
}
auto query_areas =
[&]( std::pair<ZONE* /* rule area */, ZONE* /* copper zone */> areaZonePair ) -> size_t
[&]( const int idx ) -> size_t
{
if( m_drcEngine->IsCancelled() )
return 0;
const auto& areaZonePair = toCache[idx];
ZONE* ruleArea = areaZonePair.first;
ZONE* copperZone = areaZonePair.second;
BOX2I areaBBox = ruleArea->GetBoundingBox();
@ -169,14 +169,9 @@ bool DRC_TEST_PROVIDER_DISALLOW::Run()
};
thread_pool& tp = GetKiCadThreadPool();
std::vector<std::future<size_t>> returns;
auto futures = tp.submit_loop( 0, toCache.size(), query_areas );
returns.reserve( toCache.size() );
for( const std::pair<ZONE*, ZONE*>& areaZonePair : toCache )
returns.emplace_back( tp.submit( query_areas, areaZonePair ) );
for( const std::future<size_t>& ret : returns )
for( auto& ret : futures )
{
std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );

View File

@ -149,14 +149,10 @@ bool DRC_TEST_PROVIDER_SLIVER_CHECKER::Run()
};
thread_pool& tp = GetKiCadThreadPool();
std::vector<std::future<size_t>> returns;
returns.reserve( copperLayers.size() );
auto returns = tp.submit_loop( 0, copperLayers.size(), build_layer_polys );
for( size_t ii = 0; ii < copperLayers.size(); ++ii )
returns.emplace_back( tp.submit( build_layer_polys, ii ) );
for( const std::future<size_t>& ret : returns )
for( auto& ret : returns )
{
std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );

View File

@ -737,50 +737,47 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testMaskBridges()
thread_pool& tp = GetKiCadThreadPool();
auto returns = tp.parallelize_loop( test_items.size(), [&]( size_t a, size_t b ) -> bool
auto returns = tp.submit_loop( 0, test_items.size(), [&]( size_t i ) -> bool
{
BOARD_ITEM* item = test_items[ i ];
if( m_drcEngine->IsErrorLimitExceeded( DRCE_SOLDERMASK_BRIDGE ) )
return false;
BOX2I itemBBox = item->GetBoundingBox();
if( item->IsOnLayer( F_Mask ) && !isNullAperture( item ) )
{
for( size_t i = a; i < b; ++i )
{
BOARD_ITEM* item = test_items[ i ];
// Test for aperture-to-aperture collisions
testItemAgainstItems( item, itemBBox, F_Mask, F_Mask );
if( m_drcEngine->IsErrorLimitExceeded( DRCE_SOLDERMASK_BRIDGE ) )
return false;
// Test for aperture-to-zone collisions
testMaskItemAgainstZones( item, itemBBox, F_Mask, F_Cu );
}
else if( item->IsOnLayer( PADSTACK::ALL_LAYERS ) )
{
// Test for copper-item-to-aperture collisions
testItemAgainstItems( item, itemBBox, F_Cu, F_Mask );
}
BOX2I itemBBox = item->GetBoundingBox();
if( item->IsOnLayer( B_Mask ) && !isNullAperture( item ) )
{
// Test for aperture-to-aperture collisions
testItemAgainstItems( item, itemBBox, B_Mask, B_Mask );
if( item->IsOnLayer( F_Mask ) && !isNullAperture( item ) )
{
// Test for aperture-to-aperture collisions
testItemAgainstItems( item, itemBBox, F_Mask, F_Mask );
// Test for aperture-to-zone collisions
testMaskItemAgainstZones( item, itemBBox, B_Mask, B_Cu );
}
else if( item->IsOnLayer( B_Cu ) )
{
// Test for copper-item-to-aperture collisions
testItemAgainstItems( item, itemBBox, B_Cu, B_Mask );
}
// Test for aperture-to-zone collisions
testMaskItemAgainstZones( item, itemBBox, F_Mask, F_Cu );
}
else if( item->IsOnLayer( PADSTACK::ALL_LAYERS ) )
{
// Test for copper-item-to-aperture collisions
testItemAgainstItems( item, itemBBox, F_Cu, F_Mask );
}
++count;
if( item->IsOnLayer( B_Mask ) && !isNullAperture( item ) )
{
// Test for aperture-to-aperture collisions
testItemAgainstItems( item, itemBBox, B_Mask, B_Mask );
// Test for aperture-to-zone collisions
testMaskItemAgainstZones( item, itemBBox, B_Mask, B_Cu );
}
else if( item->IsOnLayer( B_Cu ) )
{
// Test for copper-item-to-aperture collisions
testItemAgainstItems( item, itemBBox, B_Cu, B_Mask );
}
++count;
}
return true;
} );
return true;
} );
for( size_t i = 0; i < returns.size(); ++i )
{

View File

@ -70,13 +70,15 @@ bool DRC_TEST_PROVIDER_TRACK_ANGLE::Run()
return false; // DRC cancelled
auto checkTrackAngle =
[&]( PCB_TRACK* item ) -> bool
[&]( const int ind ) -> bool
{
if( m_drcEngine->IsErrorLimitExceeded( DRCE_TRACK_ANGLE ) )
{
return false;
}
PCB_TRACK* item = m_drcEngine->GetBoard()->Tracks()[ind];
if( item->Type() != PCB_TRACE_T )
{
return true;
@ -195,17 +197,10 @@ bool DRC_TEST_PROVIDER_TRACK_ANGLE::Run()
const int progressDelta = 250;
int ii = 0;
thread_pool& tp = GetKiCadThreadPool();
std::vector<std::future<bool>> returns;
thread_pool& tp = GetKiCadThreadPool();
auto futures = tp.submit_loop( 0, m_drcEngine->GetBoard()->Tracks().size(), checkTrackAngle );
returns.reserve( m_drcEngine->GetBoard()->Tracks().size() );
for( PCB_TRACK* item : m_drcEngine->GetBoard()->Tracks() )
{
returns.emplace_back( tp.submit( checkTrackAngle, item ) );
}
for( std::future<bool>& ret : returns )
for( auto& ret : futures )
{
std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );

View File

@ -67,8 +67,9 @@ bool DRC_TEST_PROVIDER_TRACK_SEGMENT_LENGTH::Run()
return false; // DRC cancelled
auto checkTrackSegmentLength =
[&]( BOARD_ITEM* item ) -> bool
[&]( const int idx ) -> bool
{
BOARD_ITEM* item = m_drcEngine->GetBoard()->Tracks()[idx];
if( m_drcEngine->IsErrorLimitExceeded( DRCE_TRACK_SEGMENT_LENGTH ) )
return false;
@ -153,16 +154,9 @@ bool DRC_TEST_PROVIDER_TRACK_SEGMENT_LENGTH::Run()
int ii = 0;
thread_pool& tp = GetKiCadThreadPool();
std::vector<std::future<bool>> returns;
auto futures = tp.submit_loop( 0, m_drcEngine->GetBoard()->Tracks().size(), checkTrackSegmentLength );
returns.reserve( m_drcEngine->GetBoard()->Tracks().size() );
for( PCB_TRACK* item : m_drcEngine->GetBoard()->Tracks() )
{
returns.emplace_back( tp.submit( checkTrackSegmentLength, item ) );
}
for( std::future<bool>& ret : returns )
for( auto& ret : futures )
{
std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );

View File

@ -319,27 +319,17 @@ bool DRC_TEST_PROVIDER_ZONE_CONNECTIONS::Run()
total_effort = std::max( (size_t) 1, total_effort );
thread_pool& tp = GetKiCadThreadPool();
std::vector<std::future<int>> returns;
auto returns = tp.submit_loop( 0, zoneLayers.size(),
[&]( const int ii )
{
if( !m_drcEngine->IsCancelled() )
{
testZoneLayer( zoneLayers[ii].first, zoneLayers[ii].second );
done.fetch_add( zoneLayers[ii].first->GetFilledPolysList( zoneLayers[ii].second )->FullPointCount() );
}
} );
returns.reserve( zoneLayers.size() );
for( const std::pair<ZONE*, PCB_LAYER_ID>& zonelayer : zoneLayers )
{
returns.emplace_back( tp.submit(
[&]( ZONE* aZone, PCB_LAYER_ID aLayer ) -> int
{
if( !m_drcEngine->IsCancelled() )
{
testZoneLayer( aZone, aLayer );
done.fetch_add( aZone->GetFilledPolysList( aLayer )->FullPointCount() );
}
return 0;
},
zonelayer.first, zonelayer.second ) );
}
for( const std::future<int>& ret : returns )
for( auto& ret : returns )
{
std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );

View File

@ -2026,76 +2026,73 @@ bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, const VECTOR2D& aOrigi
{
std::mutex mutex;
auto subtractLoopFn = [&]( const int a, const int b )
auto subtractLoopFn = [&]( const int shapeId )
{
for( int shapeId = a; shapeId < b; shapeId++ )
TopoDS_Shape& shape = vec[shapeId];
Bnd_Box shapeBbox;
BRepBndLib::Add( shape, shapeBbox );
TopTools_ListOfShape holelist;
{
TopoDS_Shape& shape = vec[shapeId];
std::unique_lock lock( mutex );
Bnd_Box shapeBbox;
BRepBndLib::Add( shape, shapeBbox );
const TColStd_ListOfInteger& indices = aBSBHoles.Compare( shapeBbox );
TopTools_ListOfShape holelist;
{
std::unique_lock lock( mutex );
const TColStd_ListOfInteger& indices = aBSBHoles.Compare( shapeBbox );
for( const Standard_Integer& index : indices )
holelist.Append( aHolesList[index] );
}
if( holelist.IsEmpty() )
continue;
TopTools_ListOfShape cutArgs;
cutArgs.Append( shape );
BRepAlgoAPI_Cut cut;
cut.SetRunParallel( true );
cut.SetToFillHistory( false );
cut.SetArguments( cutArgs );
cut.SetTools( holelist );
cut.Build();
if( cut.HasErrors() || cut.HasWarnings() )
{
m_reporter->Report( wxString::Format( _( "** Got problems while cutting "
"%s net '%s' **" ),
aWhat,
UnescapeString( netname ) ),
RPT_SEVERITY_ERROR );
shapeBbox.Dump();
if( cut.HasErrors() )
{
wxString msg = _( "Errors:\n" );
wxStringOutputStream os_stream( &msg );
wxStdOutputStream out( os_stream );
cut.DumpErrors( out );
m_reporter->Report( msg, RPT_SEVERITY_ERROR );
}
if( cut.HasWarnings() )
{
wxString msg = _( "Warnings:\n" );
wxStringOutputStream os_stream( &msg );
wxStdOutputStream out( os_stream );
cut.DumpWarnings( out );
m_reporter->Report( msg, RPT_SEVERITY_WARNING );
}
}
shape = cut.Shape();
for( const Standard_Integer& index : indices )
holelist.Append( aHolesList[index] );
}
if( holelist.IsEmpty() )
return; // nothing to cut for this shape
TopTools_ListOfShape cutArgs;
cutArgs.Append( shape );
BRepAlgoAPI_Cut cut;
cut.SetRunParallel( true );
cut.SetToFillHistory( false );
cut.SetArguments( cutArgs );
cut.SetTools( holelist );
cut.Build();
if( cut.HasErrors() || cut.HasWarnings() )
{
m_reporter->Report( wxString::Format( _( "** Got problems while cutting "
"%s net '%s' **" ),
aWhat,
UnescapeString( netname ) ),
RPT_SEVERITY_ERROR );
shapeBbox.Dump();
if( cut.HasErrors() )
{
wxString msg = _( "Errors:\n" );
wxStringOutputStream os_stream( &msg );
wxStdOutputStream out( os_stream );
cut.DumpErrors( out );
m_reporter->Report( msg, RPT_SEVERITY_ERROR );
}
if( cut.HasWarnings() )
{
wxString msg = _( "Warnings:\n" );
wxStringOutputStream os_stream( &msg );
wxStdOutputStream out( os_stream );
cut.DumpWarnings( out );
m_reporter->Report( msg, RPT_SEVERITY_WARNING );
}
}
shape = cut.Shape();
};
tp.parallelize_loop( vec.size(), subtractLoopFn ).wait();
tp.submit_loop( 0, vec.size(), subtractLoopFn ).wait();
}
};
@ -2172,7 +2169,7 @@ bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, const VECTOR2D& aOrigi
BS::multi_future<void> mf;
for( const auto& [netname, _] : shapesToFuseMap )
mf.push_back( tp.submit( fuseLoopFn, netname ) );
mf.push_back( tp.submit_task( [&, netname]() { fuseLoopFn( netname ); } ) );
mf.wait();
}

View File

@ -207,7 +207,7 @@ void FOOTPRINT_LIST_IMPL::loadFootprints()
};
for( size_t ii = 0; ii < num_elements; ++ii )
returns[ii] = tp.submit( fp_thread );
returns[ii] = tp.submit_task( fp_thread );
for( const std::future<size_t>& ret : returns )
{

View File

@ -581,7 +581,7 @@ void TRACKS_CLEANER::cleanup( bool aDeleteDuplicateVias, bool aDeleteNullSegment
// and extract all of the pairs of segments that might be merged. Then, perform
// the actual merge in the main loop.
thread_pool& tp = GetKiCadThreadPool();
auto merge_returns = tp.parallelize_loop( 0, m_brd->Tracks().size(), track_loop );
auto merge_returns = tp.submit_blocks( 0, m_brd->Tracks().size(), track_loop );
bool retval = false;
for( size_t ii = 0; ii < merge_returns.size(); ++ii )

View File

@ -654,49 +654,46 @@ PCB_NET_INSPECTOR_PANEL::calculateNets( const std::vector<NETINFO_ITEM*>& aNetCo
std::mutex resultsMutex;
thread_pool& tp = GetKiCadThreadPool();
auto resultsFuture = tp.parallelize_loop(
0, foundNets.size(),
[&, this, calc]( const int start, const int end )
auto resultsFuture = tp.submit_loop(
0, foundNets.size(),
[&, this, calc]( const int i )
{
int netCode = foundNets[i]->GetNetCode();
constexpr PATH_OPTIMISATIONS opts = { .OptimiseViaLayers = true,
.MergeTracks = true,
.OptimiseTracesInPads = true,
.InferViaInPad = false };
LENGTH_DELAY_STATS lengthDetails = calc->CalculateLengthDetails(
netItemsMap[netCode],
opts,
nullptr,
nullptr,
LENGTH_DELAY_LAYER_OPT::WITH_LAYER_DETAIL,
m_showTimeDomainDetails ? LENGTH_DELAY_DOMAIN_OPT::WITH_DELAY_DETAIL
: LENGTH_DELAY_DOMAIN_OPT::NO_DELAY_DETAIL );
if( aIncludeZeroPadNets || lengthDetails.NumPads > 0 )
{
for( int i = start; i < end; ++i )
{
int netCode = foundNets[i]->GetNetCode();
std::unique_ptr<LIST_ITEM> new_item = std::make_unique<LIST_ITEM>( foundNets[i] );
constexpr PATH_OPTIMISATIONS opts = { .OptimiseViaLayers = true,
.MergeTracks = true,
.OptimiseTracesInPads = true,
.InferViaInPad = false };
new_item->SetPadCount( lengthDetails.NumPads );
new_item->SetLayerCount( m_board->GetCopperLayerCount() );
new_item->SetPadDieLength( lengthDetails.PadToDieLength );
new_item->SetPadDieDelay( lengthDetails.PadToDieDelay );
new_item->SetViaCount( lengthDetails.NumVias );
new_item->SetViaLength( lengthDetails.ViaLength );
new_item->SetViaDelay( lengthDetails.ViaDelay );
new_item->SetLayerWireLengths( *lengthDetails.LayerLengths );
LENGTH_DELAY_STATS lengthDetails = calc->CalculateLengthDetails(
netItemsMap[netCode],
opts,
nullptr,
nullptr,
LENGTH_DELAY_LAYER_OPT::WITH_LAYER_DETAIL,
m_showTimeDomainDetails ? LENGTH_DELAY_DOMAIN_OPT::WITH_DELAY_DETAIL
: LENGTH_DELAY_DOMAIN_OPT::NO_DELAY_DETAIL );
if( m_showTimeDomainDetails )
new_item->SetLayerWireDelays( *lengthDetails.LayerDelays );
if( aIncludeZeroPadNets || lengthDetails.NumPads > 0 )
{
std::unique_ptr<LIST_ITEM> new_item = std::make_unique<LIST_ITEM>( foundNets[i] );
new_item->SetPadCount( lengthDetails.NumPads );
new_item->SetLayerCount( m_board->GetCopperLayerCount() );
new_item->SetPadDieLength( lengthDetails.PadToDieLength );
new_item->SetPadDieDelay( lengthDetails.PadToDieDelay );
new_item->SetViaCount( lengthDetails.NumVias );
new_item->SetViaLength( lengthDetails.ViaLength );
new_item->SetViaDelay( lengthDetails.ViaDelay );
new_item->SetLayerWireLengths( *lengthDetails.LayerLengths );
if( m_showTimeDomainDetails )
new_item->SetLayerWireDelays( *lengthDetails.LayerDelays );
std::scoped_lock lock( resultsMutex );
results.emplace_back( std::move( new_item ) );
}
}
} );
std::scoped_lock lock( resultsMutex );
results.emplace_back( std::move( new_item ) );
}
} );
resultsFuture.get();

View File

@ -611,7 +611,7 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE*>& aZones, bool aCheck, wxWindow*
thread_pool& tp = GetKiCadThreadPool();
for( const std::pair<ZONE*, PCB_LAYER_ID>& fillItem : toFill )
returns.emplace_back( std::make_pair( tp.submit( fill_lambda, fillItem ), 0 ) );
returns.emplace_back( std::make_pair( tp.submit_task( [&, fillItem]() { return fill_lambda( fillItem ); } ), 0 ) );
while( !cancelled && finished != 2 * toFill.size() )
{
@ -636,9 +636,9 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE*>& aZones, bool aCheck, wxWindow*
{
// 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] );
returns[ii].first = tp.submit_task( [&, idx = ii]() { return fill_lambda( toFill[idx] ); } );
else if( ret.second == 1 )
returns[ii].first = tp.submit( tesselate_lambda, toFill[ii] );
returns[ii].first = tp.submit_task( [&, idx = ii]() { return tesselate_lambda( toFill[idx] ); } );
}
}
}
@ -827,7 +827,7 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE*>& aZones, bool aCheck, wxWindow*
return retval;
};
auto island_returns = tp.parallelize_loop( 0, polys_to_check.size(), island_lambda );
auto island_returns = tp.submit_blocks( 0, polys_to_check.size(), island_lambda );
cancelled = false;
// Allow island removal threads to finish

View File

@ -97,7 +97,7 @@ long PYTHON_MANAGER::Execute( const std::vector<wxString>& aArgs,
PYTHON_PROCESS* process = new PYTHON_PROCESS( aCallback );
process->Redirect();
auto monitor =
auto monitor =
[]( PYTHON_PROCESS* aProcess )
{
wxInputStream* processOut = aProcess->GetInputStream();
@ -147,7 +147,7 @@ long PYTHON_MANAGER::Execute( const std::vector<wxString>& aArgs,
if( !aSaveOutput )
{
thread_pool& tp = GetKiCadThreadPool();
auto ret = tp.submit( monitor, process );
auto ret = tp.submit_task( [monitor, process] { monitor( process ); } );
}
}
@ -236,7 +236,7 @@ std::optional<wxString> PYTHON_MANAGER::GetVirtualPython( const wxString& aNames
return std::nullopt;
wxFileName python( *envPath, wxEmptyString );
#ifdef _WIN32
python.AppendDir( "Scripts" );
python.SetFullName( "pythonw.exe" );

File diff suppressed because it is too large Load Diff