Implement progress reporter for cvpcb footprint loading.

Fixes: lp:1676910
* https://bugs.launchpad.net/kicad/+bug/1676910
This commit is contained in:
Jeff Young 2018-02-10 13:32:57 +00:00 committed by Wayne Stambaugh
parent 4bae901dc8
commit f68bf33cd3
9 changed files with 184 additions and 118 deletions

View File

@ -37,37 +37,34 @@ PROGRESS_REPORTER::PROGRESS_REPORTER( int aNumPhases ) :
void PROGRESS_REPORTER::BeginPhase( int aPhase )
{
m_phase = aPhase;
m_progress = 0;
updateUI();
m_phase.store( aPhase );
m_progress.store( 0 );
}
void PROGRESS_REPORTER::AdvancePhase( )
{
m_phase++;
m_progress = 0;
updateUI();
m_phase.fetch_add( 1 );
m_progress.store( 0 );
}
void PROGRESS_REPORTER::Report( const wxString& aMessage )
{
std::lock_guard<std::mutex> guard( m_mutex );
m_rptMessage = aMessage;
updateUI();
}
void PROGRESS_REPORTER::SetMaxProgress( int aMaxProgress )
{
m_maxProgress = aMaxProgress;
updateUI();
m_maxProgress.store( aMaxProgress );
}
void PROGRESS_REPORTER::AdvanceProgress()
{
m_progress++;
m_progress.fetch_add( 1 );
}
@ -80,14 +77,38 @@ int PROGRESS_REPORTER::currentProgress() const
}
// Please, *DO NOT* use wxPD_APP_MODAL style: it is not necessary
// (without this option the PROGRESS_REPORTER is modal for the parent frame)
// and PROGRESS_REPORTER works fine on OSX only without this style
// when called from a quasi modal dialog
WX_PROGRESS_REPORTER::WX_PROGRESS_REPORTER( wxWindow* aParent,
const wxString& aTitle, int aNumPhases ) :
bool PROGRESS_REPORTER::KeepRefreshing( bool aWait )
{
if( aWait )
{
while( m_progress < m_maxProgress && m_maxProgress > 0 )
{
if( !updateUI() )
return false;
wxMilliSleep( 20 );
}
return true;
}
else
{
wxMilliSleep( 20 );
return updateUI();
}
}
WX_PROGRESS_REPORTER::WX_PROGRESS_REPORTER( wxWindow* aParent, const wxString& aTitle,
int aNumPhases ) :
PROGRESS_REPORTER( aNumPhases ),
wxProgressDialog( aTitle, wxT( "" ), 1, aParent, wxPD_AUTO_HIDE | wxPD_CAN_ABORT |
wxProgressDialog( aTitle, wxT( "" ), 1, aParent,
// wxPD_APP_MODAL | // Don't use; messes up OSX when called from
// quasi-modal dialog
wxPD_AUTO_HIDE | // *MUST* use; otherwise wxWidgets will spin
// up another event loop on completion which
// causes all sorts of grief
wxPD_CAN_ABORT |
wxPD_ELAPSED_TIME )
{
}
@ -99,30 +120,21 @@ WX_PROGRESS_REPORTER::~WX_PROGRESS_REPORTER()
}
void WX_PROGRESS_REPORTER::updateUI()
bool WX_PROGRESS_REPORTER::updateUI()
{
int cur = currentProgress();
if( cur < 0 || cur > 1000 )
cur = 0;
wxString message;
{
std::lock_guard<std::mutex> guard( m_mutex );
message = m_rptMessage;
}
SetRange( 1000 );
wxProgressDialog::Update( cur, m_rptMessage );
return wxProgressDialog::Update( cur, message );
}
void PROGRESS_REPORTER::KeepRefreshing( bool aWait )
{
#ifdef USE_OPENMP
while( m_progress < m_maxProgress && m_maxProgress > 0 )
{
updateUI();
wxMilliSleep( 10 );
if( !aWait )
return;
}
#else
updateUI();
#endif
}

View File

@ -42,6 +42,7 @@
#include <fp_lib_table.h>
#include <netlist_reader.h>
#include <bitmaps.h>
#include <widgets/progress_reporter.h>
#include <cvpcb_mainframe.h>
#include <cvpcb.h>
@ -713,11 +714,9 @@ bool CVPCB_MAINFRAME::LoadFootprintFiles()
return false;
}
{
wxBusyCursor dummy; // Let the user know something is happening.
WX_PROGRESS_REPORTER progressReporter( this, _( "Loading Footprint Libraries" ), 2 );
m_FootprintsList->ReadFootprintFiles( fptbl );
}
m_FootprintsList->ReadFootprintFiles( fptbl, nullptr, &progressReporter );
if( m_FootprintsList->GetErrorCount() )
{

View File

@ -50,6 +50,7 @@ class FP_LIB_TABLE;
class FOOTPRINT_LIST;
class FOOTPRINT_LIST_IMPL;
class FOOTPRINT_ASYNC_LOADER;
class WX_PROGRESS_REPORTER;
class wxTopLevelWindow;
class KIWAY;
@ -247,11 +248,14 @@ public:
* @param aTable defines all the libraries.
* @param aNickname is the library to read from, or if NULL means read all
* footprints from all known libraries in aTable.
* @param aProgressReporter is an optional progress reporter. ReadFootprintFiles()
* will use 2 phases within the reporter.
* @return bool - true if it ran to completion, else false if it aborted after
* some number of errors. If true, it does not mean there were no errors, check
* GetErrorCount() for that, should be zero to indicate success.
*/
virtual bool ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname = NULL ) = 0;
virtual bool ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname = nullptr,
WX_PROGRESS_REPORTER* aProgressReporter = nullptr ) = 0;
void DisplayErrors( wxTopLevelWindow* aCaller = NULL );

View File

@ -30,6 +30,12 @@
#include <wx/progdlg.h>
/**
* A progress reporter for use in multi-threaded environments. The various advancement
* and message methods can be called from sub-threads. The KeepRefreshing method *MUST*
* be called only from the main thread (primarily a MSW requirement, which won't allow
* access to UI objects allocated from a separate thread).
*/
class PROGRESS_REPORTER
{
public:
@ -64,28 +70,26 @@ class PROGRESS_REPORTER
void AdvanceProgress();
/**
* Update the UI dialog.
* This function is compatible with OPENMP use.
* Update the UI dialog. *MUST* only be called from the main thread.
* Returns false if the user clicked Cancel.
*/
void KeepRefreshing( bool aWait = false );
bool KeepRefreshing( bool aWait = false );
protected:
int currentProgress() const;
virtual void updateUI() = 0;
virtual bool updateUI() = 0;
wxString m_rptMessage;
int m_phase;
int m_numPhases;
mutable std::mutex m_mutex;
std::atomic_int m_phase;
std::atomic_int m_numPhases;
std::atomic_int m_progress;
int m_maxProgress;
std::atomic_int m_maxProgress;
};
/**
* This class implements a wxProgressDialog that can be used in a OPENMP environment
* (i.e. the progress bar update can be called from different theads).
* It is mainly used in Zone fill calculations
*/
class WX_PROGRESS_REPORTER : public PROGRESS_REPORTER, public wxProgressDialog
{
public:
@ -104,7 +108,7 @@ public:
private:
virtual void updateUI() override;
virtual bool updateUI() override;
};
#endif

View File

@ -36,6 +36,7 @@
#include <make_unique.h>
#include <pgm_base.h>
#include <wildcards_and_files_ext.h>
#include <widgets/progress_reporter.h>
#include <thread>
@ -100,7 +101,7 @@ void FOOTPRINT_LIST_IMPL::loader_job()
{
wxString nickname;
while( m_queue_in.pop( nickname ) )
while( m_queue_in.pop( nickname ) && !m_cancelled )
{
CatchErrors( [this, &nickname]() {
m_lib_table->PrefetchLib( nickname );
@ -108,33 +109,68 @@ void FOOTPRINT_LIST_IMPL::loader_job()
} );
m_count_finished.fetch_add( 1 );
}
if( !m_first_to_finish.exchange( true ) )
{
// yay, we're first to finish!
if( m_loader->m_completion_cb )
{
m_loader->m_completion_cb();
}
if( m_progress_reporter )
m_progress_reporter->AdvanceProgress();
}
}
bool FOOTPRINT_LIST_IMPL::ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname )
bool FOOTPRINT_LIST_IMPL::ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname,
WX_PROGRESS_REPORTER* aProgressReporter )
{
if( aTable->GenLastModifiedChecksum( aNickname ) == m_libraries_last_mod_checksum )
return true;
m_progress_reporter = aProgressReporter;
m_cancelled = false;
FOOTPRINT_ASYNC_LOADER loader;
loader.SetList( this );
loader.Start( aTable, aNickname );
bool retval = loader.Join();
if( m_progress_reporter )
{
m_progress_reporter->SetMaxProgress( m_queue_in.size() );
m_progress_reporter->Report( _( "Fetching Footprint Libraries" ) );
}
while( !m_cancelled && loader.GetProgress() < 100 )
{
if( m_progress_reporter )
m_cancelled = !m_progress_reporter->KeepRefreshing();
else
wxMilliSleep( 20 );
}
if( m_progress_reporter )
m_progress_reporter->AdvancePhase();
if( !m_cancelled )
{
if( m_progress_reporter )
{
m_progress_reporter->SetMaxProgress( m_queue_out.size() );
m_progress_reporter->Report( _( "Loading Footprints" ) );
}
loader.Join();
}
if( m_progress_reporter )
m_progress_reporter->AdvancePhase();
if( m_cancelled )
{
m_errors.move_push( std::make_unique<IO_ERROR>
( _( "Loading incomplete; cancelled by user." ), nullptr, nullptr, 0 ) );
}
m_libraries_last_mod_checksum = aTable->GenLastModifiedChecksum( aNickname );
m_progress_reporter = nullptr;
return retval;
return m_errors.empty();
}
@ -145,7 +181,6 @@ void FOOTPRINT_LIST_IMPL::StartWorkers( FP_LIB_TABLE* aTable, wxString const* aN
m_lib_table = aTable;
// Clear data before reading files
m_first_to_finish.store( false );
m_count_finished.store( 0 );
m_errors.clear();
m_list.clear();
@ -176,6 +211,9 @@ bool FOOTPRINT_LIST_IMPL::JoinWorkers()
m_threads.clear();
m_queue_in.clear();
m_count_finished.store( 0 );
size_t total_count = m_queue_out.size();
LOCALE_IO toggle_locale;
@ -194,7 +232,7 @@ bool FOOTPRINT_LIST_IMPL::JoinWorkers()
threads.push_back( std::thread( [this, &queue_parsed]() {
wxString nickname;
while( this->m_queue_out.pop( nickname ) )
while( this->m_queue_out.pop( nickname ) && !m_cancelled )
{
wxArrayString fpnames;
@ -220,15 +258,29 @@ bool FOOTPRINT_LIST_IMPL::JoinWorkers()
}
}
for( auto const& fpname : fpnames )
for( int i = 0; i < fpnames.size() && !m_cancelled; i++ )
{
wxString fpname = fpnames[ i ];
FOOTPRINT_INFO* fpinfo = new FOOTPRINT_INFO_IMPL( this, nickname, fpname );
queue_parsed.move_push( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) );
}
if( m_progress_reporter )
m_progress_reporter->AdvanceProgress();
m_count_finished.fetch_add( 1 );
}
} ) );
}
while( !m_cancelled && m_count_finished.load() < total_count )
{
if( m_progress_reporter )
m_cancelled = !m_progress_reporter->KeepRefreshing();
else
wxMilliSleep( 20 );
}
for( auto& thr : threads )
thr.join();
@ -251,7 +303,10 @@ size_t FOOTPRINT_LIST_IMPL::CountFinished()
}
FOOTPRINT_LIST_IMPL::FOOTPRINT_LIST_IMPL() : m_loader( nullptr ), m_first_to_finish( false ), m_count_finished( 0 )
FOOTPRINT_LIST_IMPL::FOOTPRINT_LIST_IMPL() :
m_loader( nullptr ),
m_count_finished( 0 ),
m_progress_reporter( nullptr )
{
}

View File

@ -28,6 +28,7 @@
#include <footprint_info.h>
#include <sync_queue.h>
#include <widgets/progress_reporter.h>
class LOCALE_IO;
@ -60,9 +61,10 @@ class FOOTPRINT_LIST_IMPL : public FOOTPRINT_LIST
std::vector<std::thread> m_threads;
SYNC_QUEUE<wxString> m_queue_in;
SYNC_QUEUE<wxString> m_queue_out;
std::atomic_bool m_first_to_finish;
std::atomic_size_t m_count_finished;
long long m_libraries_last_mod_checksum;
WX_PROGRESS_REPORTER* m_progress_reporter;
std::atomic_bool m_cancelled;
/**
* Call aFunc, pushing any IO_ERRORs and std::exceptions it throws onto m_errors.
@ -87,8 +89,8 @@ public:
FOOTPRINT_LIST_IMPL();
virtual ~FOOTPRINT_LIST_IMPL();
virtual bool ReadFootprintFiles(
FP_LIB_TABLE* aTable, const wxString* aNickname = NULL ) override;
virtual bool ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname = nullptr,
WX_PROGRESS_REPORTER* aProgressReporter = nullptr ) override;
};
#endif // FOOTPRINT_INFO_IMPL_H

View File

@ -114,23 +114,9 @@ void ZONE_FILLER::Fill( std::vector<ZONE_CONTAINER*> aZones )
m_progressReporter->SetMaxProgress( toFill.size() );
}
#ifdef USE_OPENMP
// launch at least two threads, one to compute, second to update UI
#pragma omp parallel num_threads( std::max( omp_get_num_procs(), 2 ) )
#endif
m_count_done = 0;
std::thread fillWorker( [ this, toFill ]()
{
#ifdef USE_OPENMP
#pragma omp master
if( m_progressReporter )
{
m_progressReporter->KeepRefreshing( true );
}
#endif
#ifdef USE_OPENMP
#pragma omp for schedule(dynamic)
#endif
for( unsigned i = 0; i < toFill.size(); i++ )
{
SHAPE_POLY_SET rawPolys, finalPolys;
@ -142,17 +128,28 @@ void ZONE_FILLER::Fill( std::vector<ZONE_CONTAINER*> aZones )
toFill[i].m_zone->SetIsFilled( true );
if( m_progressReporter )
{
m_progressReporter->AdvanceProgress();
m_count_done.fetch_add( 1 );
}
} );
while( m_count_done.load() < toFill.size() )
{
if( m_progressReporter )
m_progressReporter->KeepRefreshing();
else
wxMilliSleep( 20 );
}
}
fillWorker.join();
// Now remove insulated copper islands
if( m_progressReporter )
{
m_progressReporter->AdvancePhase();
m_progressReporter->Report( _( "Removing insulated copper islands..." ) );
m_progressReporter->KeepRefreshing();
}
connectivity->SetProgressReporter( m_progressReporter );
@ -177,33 +174,31 @@ void ZONE_FILLER::Fill( std::vector<ZONE_CONTAINER*> aZones )
m_progressReporter->Report( _( "Caching polygon triangulations..." ) );
m_progressReporter->SetMaxProgress( toFill.size() );
}
#ifdef USE_OPENMP
// launch at least two threads, one to compute, second to update UI
#pragma omp parallel num_threads( std::max( omp_get_num_procs(), 2 ) )
#endif
{
#ifdef USE_OPENMP
#pragma omp master
if( m_progressReporter )
{
m_progressReporter->KeepRefreshing( true );
}
#endif
#ifdef USE_OPENMP
#pragma omp for schedule(dynamic)
#endif
m_count_done = 0;
std::thread triangulationWorker( [ this, toFill ]()
{
for( unsigned i = 0; i < toFill.size(); i++ )
{
if( m_progressReporter )
{
m_progressReporter->AdvanceProgress();
}
toFill[i].m_zone->CacheTriangulation();
m_count_done.fetch_add( 1 );
}
} );
while( m_count_done.load() < toFill.size() )
{
if( m_progressReporter )
m_progressReporter->KeepRefreshing();
else
wxMilliSleep( 10 );
}
triangulationWorker.join();
// If some zones must be filled by segments, create the filling segments
// (note, this is a outdated option, but it exists)
int zones_to_fill_count = 0;

View File

@ -119,6 +119,7 @@ private:
BOARD* m_board;
COMMIT* m_commit;
PROGRESS_REPORTER* m_progressReporter;
std::atomic_size_t m_count_done;
};
#endif

View File

@ -103,17 +103,11 @@ int PCB_EDIT_FRAME::Fill_All_Zones( wxWindow * aActiveWindow )
ZONE_FILLER filler( GetBoard() );
// progressReporter must be created *only* if needed
if( aActiveWindow )
{
std::unique_ptr<WX_PROGRESS_REPORTER> progressReporter(
new WX_PROGRESS_REPORTER( aActiveWindow, _( "Fill All Zones" ), 3 ) );
filler.SetProgressReporter( progressReporter.get() );
filler.Fill( toFill );
}
else // do not use a WX_PROGRESS_REPORTER in ZONE_FILLER instance
filler.Fill( toFill );
return 0;
}