KiCad PCB EDA Suite
footprint_info_impl.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2011 Jean-Pierre Charras, <jp.charras@wanadoo.fr>
5  * Copyright (C) 2013-2016 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6  * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software: you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation, either version 3 of the License, or (at your
11  * option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 
23 #include <footprint_info_impl.h>
24 
25 #include <class_module.h>
26 #include <common.h>
27 #include <fctsys.h>
28 #include <footprint_info.h>
29 #include <fp_lib_table.h>
30 #include <html_messagebox.h>
31 #include <io_mgr.h>
32 #include <kiface_ids.h>
33 #include <kiway.h>
34 #include <lib_id.h>
35 #include <macros.h>
36 #include <make_unique.h>
37 #include <pgm_base.h>
40 
41 #include <thread>
42 
43 
45 {
46  FP_LIB_TABLE* fptable = m_owner->GetTable();
47 
48  wxASSERT( fptable );
49 
50  std::unique_ptr<MODULE> footprint( fptable->FootprintLoad( m_nickname, m_fpname ) );
51 
52  if( footprint.get() == NULL ) // Should happen only with malformed/broken libraries
53  {
54  m_pad_count = 0;
56  }
57  else
58  {
59  m_pad_count = footprint->GetPadCount( DO_NOT_INCLUDE_NPTH );
60  m_unique_pad_count = footprint->GetUniquePadCount( DO_NOT_INCLUDE_NPTH );
61  m_keywords = footprint->GetKeywords();
62  m_doc = footprint->GetDescription();
63 
64  // tell ensure_loaded() I'm loaded.
65  m_loaded = true;
66  }
67 }
68 
69 
70 bool FOOTPRINT_LIST_IMPL::CatchErrors( const std::function<void()>& aFunc )
71 {
72  try
73  {
74  aFunc();
75  }
76  catch( const IO_ERROR& ioe )
77  {
78  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
79  return false;
80  }
81  catch( const std::exception& se )
82  {
83  // This is a round about way to do this, but who knows what THROW_IO_ERROR()
84  // may be tricked out to do someday, keep it in the game.
85  try
86  {
87  THROW_IO_ERROR( se.what() );
88  }
89  catch( const IO_ERROR& ioe )
90  {
91  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
92  }
93  return false;
94  }
95 
96  return true;
97 }
98 
99 
101 {
102  wxString nickname;
103 
104  while( m_queue_in.pop( nickname ) && !m_cancelled )
105  {
106  CatchErrors( [this, &nickname]() {
107  m_lib_table->PrefetchLib( nickname );
108  m_queue_out.push( nickname );
109  } );
110 
111  m_count_finished.fetch_add( 1 );
112 
113  if( m_progress_reporter )
115  }
116 }
117 
118 
119 bool FOOTPRINT_LIST_IMPL::ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname,
120  WX_PROGRESS_REPORTER* aProgressReporter )
121 {
122  if( aTable->GenLastModifiedChecksum( aNickname ) == m_libraries_last_mod_checksum )
123  return true;
124 
125  m_progress_reporter = aProgressReporter;
126  m_cancelled = false;
127 
128  FOOTPRINT_ASYNC_LOADER loader;
129 
130  loader.SetList( this );
131  loader.Start( aTable, aNickname );
132 
133  if( m_progress_reporter )
134  {
136  m_progress_reporter->Report( _( "Fetching Footprint Libraries" ) );
137  }
138 
139  while( !m_cancelled && loader.GetProgress() < 100 )
140  {
141  if( m_progress_reporter )
143  else
144  wxMilliSleep( 20 );
145  }
146 
147  if( m_progress_reporter )
149 
150  if( !m_cancelled )
151  {
152  if( m_progress_reporter )
153  {
155  m_progress_reporter->Report( _( "Loading Footprints" ) );
156  }
157 
158  loader.Join();
159  }
160 
161  if( m_progress_reporter )
163 
164  if( m_cancelled )
165  {
166  m_errors.move_push( std::make_unique<IO_ERROR>
167  ( _( "Loading incomplete; cancelled by user." ), nullptr, nullptr, 0 ) );
168  }
169 
171  m_progress_reporter = nullptr;
172 
173  return m_errors.empty();
174 }
175 
176 
177 void FOOTPRINT_LIST_IMPL::StartWorkers( FP_LIB_TABLE* aTable, wxString const* aNickname,
178  FOOTPRINT_ASYNC_LOADER* aLoader, unsigned aNThreads )
179 {
180  m_loader = aLoader;
181  m_lib_table = aTable;
182 
183  // Clear data before reading files
184  m_count_finished.store( 0 );
185  m_errors.clear();
186  m_list.clear();
187  m_threads.clear();
188  m_queue_in.clear();
189  m_queue_out.clear();
190 
191  if( aNickname )
192  m_queue_in.push( *aNickname );
193  else
194  {
195  for( auto const& nickname : aTable->GetLogicalLibs() )
196  m_queue_in.push( nickname );
197  }
198 
200 
201  for( unsigned i = 0; i < aNThreads; ++i )
202  {
203  m_threads.push_back( std::thread( &FOOTPRINT_LIST_IMPL::loader_job, this ) );
204  }
205 }
206 
208 {
209  for( auto& i : m_threads )
210  i.join();
211 
212  m_threads.clear();
213  m_queue_in.clear();
214  m_count_finished.store( 0 );
215 
216  size_t total_count = m_queue_out.size();
217 
218  LOCALE_IO toggle_locale;
219 
220  // Parse the footprints in parallel. WARNING! This requires changing the locale, which is
221  // GLOBAL. It is only threadsafe to construct the LOCALE_IO before the threads are created,
222  // destroy it after they finish, and block the main (GUI) thread while they work. Any deviation
223  // from this will cause nasal demons.
224  //
225  // TODO: blast LOCALE_IO into the sun
226 
228  std::vector<std::thread> threads;
229 
230  for( size_t ii = 0; ii < std::thread::hardware_concurrency() + 1; ++ii )
231  {
232  threads.push_back( std::thread( [this, &queue_parsed]() {
233  wxString nickname;
234 
235  while( this->m_queue_out.pop( nickname ) && !m_cancelled )
236  {
237  wxArrayString fpnames;
238 
239  try
240  {
241  this->m_lib_table->FootprintEnumerate( fpnames, nickname );
242  }
243  catch( const IO_ERROR& ioe )
244  {
245  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
246  }
247  catch( const std::exception& se )
248  {
249  // This is a round about way to do this, but who knows what THROW_IO_ERROR()
250  // may be tricked out to do someday, keep it in the game.
251  try
252  {
253  THROW_IO_ERROR( se.what() );
254  }
255  catch( const IO_ERROR& ioe )
256  {
257  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
258  }
259  }
260 
261  for( unsigned jj = 0; jj < fpnames.size() && !m_cancelled; ++jj )
262  {
263  wxString fpname = fpnames[jj];
264  FOOTPRINT_INFO* fpinfo = new FOOTPRINT_INFO_IMPL( this, nickname, fpname );
265  queue_parsed.move_push( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) );
266  }
267 
268  if( m_progress_reporter )
270 
271  m_count_finished.fetch_add( 1 );
272  }
273  } ) );
274  }
275 
276  while( !m_cancelled && m_count_finished.load() < total_count )
277  {
278  if( m_progress_reporter )
280  else
281  wxMilliSleep( 20 );
282  }
283 
284  for( auto& thr : threads )
285  thr.join();
286 
287  std::unique_ptr<FOOTPRINT_INFO> fpi;
288 
289  while( queue_parsed.pop( fpi ) )
290  m_list.push_back( std::move( fpi ) );
291 
292  std::sort( m_list.begin(), m_list.end(),
293  []( std::unique_ptr<FOOTPRINT_INFO> const& lhs,
294  std::unique_ptr<FOOTPRINT_INFO> const& rhs ) -> bool { return *lhs < *rhs; } );
295 
296  return m_errors.empty();
297 }
298 
299 
301 {
302  return m_count_finished.load();
303 }
304 
305 
307  m_loader( nullptr ),
308  m_count_finished( 0 ),
310  m_progress_reporter( nullptr ),
311  m_cancelled( false )
312 {
313 }
314 
315 
317 {
318  for( auto& i : m_threads )
319  i.join();
320 }
void push(T const &aValue)
Push a value onto the queue.
Definition: sync_queue.h:45
void AdvancePhase()
Uses the next vailable virtual zone of the dialog progress bar.
FOOTPRINT_INFO_IMPL(FOOTPRINT_LIST *aOwner, const wxString &aNickname, const wxString &aFootprintName)
SYNC_QUEUE< wxString > m_queue_out
size_t size() const
Return the size of the queue.
Definition: sync_queue.h:94
bool pop(T &aReceiver)
Pop a value off the queue into the provided variable.
Definition: sync_queue.h:66
Class LOCALE_IO is a class that can be instantiated within a scope in which you are expecting excepti...
Definition: common.h:166
long long m_libraries_last_mod_checksum
virtual void StartWorkers(FP_LIB_TABLE *aTable, wxString const *aNickname, FOOTPRINT_ASYNC_LOADER *aLoader, unsigned aNThreads) override
Launch worker threads to load footprints.
std::atomic_bool m_cancelled
ERRLIST m_errors
some can be PARSE_ERRORs also
MODULE * FootprintLoad(const wxString &aNickname, const wxString &aFootprintName)
Function FootprintLoad.
virtual size_t CountFinished() override
Return the number of libraries finished (successfully or otherwise).
void loader_job()
Function loader_job loads footprints from m_queue_in.
FP_LIB_TABLE * m_lib_table
no ownership
Synchronized, locking queue.
Definition: sync_queue.h:30
void Report(const wxString &aMessage)
Display aMessage in the progress bar dialog.
This class can be used to populate a FOOTPRINT_LIST asynchronously.
bool empty() const
Return true iff the queue is empty.
Definition: sync_queue.h:85
int m_unique_pad_count
Number of unique pads.
std::vector< std::thread > m_threads
FOOTPRINT_LIST * m_owner
provides access to FP_LIB_TABLE
void move_push(T &&aValue)
Move a value onto the queue.
Definition: sync_queue.h:54
SYNC_QUEUE< wxString > m_queue_in
This file contains miscellaneous commonly used macros and functions.
virtual bool ReadFootprintFiles(FP_LIB_TABLE *aTable, const wxString *aNickname=nullptr, WX_PROGRESS_REPORTER *aProgressReporter=nullptr) override
Read all the footprints provided by the combination of aTable and aNickname.
wxString m_doc
Footprint description.
long long GenLastModifiedChecksum(const wxString *aNickname)
Generate a checksum of the last-mod-date of aNickname&#39;s directory, or a checksum of all the libraries...
void Start(FP_LIB_TABLE *aTable, wxString const *aNickname=nullptr, unsigned aNThreads=DEFAULT_THREADS)
Launch the worker threads.
virtual bool JoinWorkers() override
Join worker threads.
wxString m_fpname
Module name.
WX_PROGRESS_REPORTER * m_progress_reporter
Subclass of DIALOG_DISPLAY_HTML_TEXT_BASE, which is generated by wxFormBuilder.
The common library.
void PrefetchLib(const wxString &aNickname)
Function PrefetchLib If possible, prefetches the specified library (e.g.
std::atomic_size_t m_count_finished
bool Join()
Wait until the worker threads are finished, and then perform any required single-threaded finishing o...
bool CatchErrors(const std::function< void()> &aFunc)
Call aFunc, pushing any IO_ERRORs and std::exceptions it throws onto m_errors.
wxString m_keywords
Footprint keywords.
FOOTPRINT_ASYNC_LOADER * m_loader
YYCODETYPE lhs
int m_pad_count
Number of pads.
see class PGM_BASE
Implementation of std::make_unique for pre C++14 compilation environments.
int GetProgress() const
Get the current completion percentage.
bool KeepRefreshing(bool aWait=false)
Update the UI dialog.
The common library.
Module description (excepted pads)
wxString m_nickname
library as known in FP_LIB_TABLE
virtual void load() override
lazily load stuff not filled in by constructor. This may throw IO_ERRORS.
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aNickname)
Return a list of footprint names contained within the library given by aNickname. ...
void SetMaxProgress(int aMaxProgress)
Fix the value thar gives the 100 precent progress bar length (inside the current virtual zone) ...
void AdvanceProgress()
Increment the progress bar length (inside the current virtual zone)
void SetList(FOOTPRINT_LIST *aList)
Assign a FOOTPRINT_LIST to the loader.
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:47
void clear()
Clear the queue.
Definition: sync_queue.h:103
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
std::vector< wxString > GetLogicalLibs()
Return the logical library names, all of them that are pertinent to a look up done on this LIB_TABLE...
FP_LIB_TABLE * GetTable() const