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-2019 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 <pgm_base.h>
39 
40 #include <thread>
41 #include <mutex>
42 
43 
45 {
46  FP_LIB_TABLE* fptable = m_owner->GetTable();
47 
48  wxASSERT( fptable );
49 
50  const MODULE* footprint = fptable->GetEnumeratedFootprint( m_nickname, m_fpname );
51 
52  if( footprint == NULL ) // Should happen only with malformed/broken libraries
53  {
54  m_pad_count = 0;
56  }
57  else
58  {
61  m_keywords = footprint->GetKeywords();
62  m_doc = footprint->GetDescription();
63  }
64 
65  m_loaded = true;
66 }
67 
68 
69 bool FOOTPRINT_LIST_IMPL::CatchErrors( const std::function<void()>& aFunc )
70 {
71  try
72  {
73  aFunc();
74  }
75  catch( const IO_ERROR& ioe )
76  {
77  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
78  return false;
79  }
80  catch( const std::exception& se )
81  {
82  // This is a round about way to do this, but who knows what THROW_IO_ERROR()
83  // may be tricked out to do someday, keep it in the game.
84  try
85  {
86  THROW_IO_ERROR( se.what() );
87  }
88  catch( const IO_ERROR& ioe )
89  {
90  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
91  }
92  return false;
93  }
94 
95  return true;
96 }
97 
98 
100 {
101  wxString nickname;
102 
103  while( m_queue_in.pop( nickname ) && !m_cancelled )
104  {
105  CatchErrors( [this, &nickname]() {
106  m_lib_table->PrefetchLib( nickname );
107  m_queue_out.push( nickname );
108  } );
109 
110  m_count_finished.fetch_add( 1 );
111 
112  if( m_progress_reporter )
114  }
115 }
116 
117 
118 bool FOOTPRINT_LIST_IMPL::ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname,
119  PROGRESS_REPORTER* aProgressReporter )
120 {
121  long long int generatedTimestamp = aTable->GenerateTimestamp( aNickname );
122 
123  if( generatedTimestamp == m_list_timestamp )
124  return true;
125 
126  m_progress_reporter = aProgressReporter;
127  m_cancelled = false;
128 
129  FOOTPRINT_ASYNC_LOADER loader;
130 
131  loader.SetList( this );
132  loader.Start( aTable, aNickname );
133 
134  if( m_progress_reporter )
135  {
137  m_progress_reporter->Report( _( "Fetching Footprint Libraries" ) );
138  }
139 
140  while( !m_cancelled && (int)m_count_finished.load() < m_loader->m_total_libs )
141  {
143  m_cancelled = true;
144 
145  wxMilliSleep( 20 );
146  }
147 
148  if( m_cancelled )
149  {
150  loader.Abort();
151  }
152  else
153  {
154  if( m_progress_reporter )
155  {
158  m_progress_reporter->Report( _( "Loading Footprints" ) );
159  }
160 
161  loader.Join();
162 
163  if( m_progress_reporter )
165  }
166 
167  if( m_cancelled )
168  m_list_timestamp = 0; // God knows what we got before we were cancelled
169  else
170  m_list_timestamp = generatedTimestamp;
171 
172  return m_errors.empty();
173 }
174 
175 
176 void FOOTPRINT_LIST_IMPL::StartWorkers( FP_LIB_TABLE* aTable, wxString const* aNickname,
177  FOOTPRINT_ASYNC_LOADER* aLoader, unsigned aNThreads )
178 {
179  m_loader = aLoader;
180  m_lib_table = aTable;
181 
182  // Clear data before reading files
183  m_count_finished.store( 0 );
184  m_errors.clear();
185  m_list.clear();
186  m_threads.clear();
187  m_queue_in.clear();
188  m_queue_out.clear();
189 
190  if( aNickname )
191  m_queue_in.push( *aNickname );
192  else
193  {
194  for( auto const& nickname : aTable->GetLogicalLibs() )
195  m_queue_in.push( nickname );
196  }
197 
199 
200  for( unsigned i = 0; i < aNThreads; ++i )
201  {
202  m_threads.push_back( std::thread( &FOOTPRINT_LIST_IMPL::loader_job, this ) );
203  }
204 }
205 
207 {
208  std::lock_guard<std::mutex> lock1( m_join );
209 
210  // To safely stop our workers, we set the cancellation flag (they will each
211  // exit on their next safe loop location when this is set). Then we need to wait
212  // for all threads to finish as closing the implementation will free the queues
213  // that the threads write to.
214  for( auto& i : m_threads )
215  i.join();
216 
217  m_threads.clear();
218  m_queue_in.clear();
219  m_count_finished.store( 0 );
220 
221  // If we have cancelled in the middle of a load, clear our timestamp to re-load next time
222  if( m_cancelled )
223  m_list_timestamp = 0;
224 }
225 
227 {
228  {
229  std::lock_guard<std::mutex> lock1( m_join );
230 
231  for( auto& i : m_threads )
232  i.join();
233 
234  m_threads.clear();
235  m_queue_in.clear();
236  m_count_finished.store( 0 );
237  }
238 
239  size_t total_count = m_queue_out.size();
240 
241  LOCALE_IO toggle_locale;
242 
243  // Parse the footprints in parallel. WARNING! This requires changing the locale, which is
244  // GLOBAL. It is only threadsafe to construct the LOCALE_IO before the threads are created,
245  // destroy it after they finish, and block the main (GUI) thread while they work. Any deviation
246  // from this will cause nasal demons.
247  //
248  // TODO: blast LOCALE_IO into the sun
249 
251  std::vector<std::thread> threads;
252 
253  for( size_t ii = 0; ii < std::thread::hardware_concurrency() + 1; ++ii )
254  {
255  threads.push_back( std::thread( [this, &queue_parsed]() {
256  wxString nickname;
257 
258  while( this->m_queue_out.pop( nickname ) && !m_cancelled )
259  {
260  wxArrayString fpnames;
261 
262  try
263  {
264  m_lib_table->FootprintEnumerate( fpnames, nickname );
265  }
266  catch( const IO_ERROR& ioe )
267  {
268  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
269  }
270  catch( const std::exception& se )
271  {
272  // This is a round about way to do this, but who knows what THROW_IO_ERROR()
273  // may be tricked out to do someday, keep it in the game.
274  try
275  {
276  THROW_IO_ERROR( se.what() );
277  }
278  catch( const IO_ERROR& ioe )
279  {
280  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
281  }
282  }
283 
284  for( unsigned jj = 0; jj < fpnames.size() && !m_cancelled; ++jj )
285  {
286  wxString fpname = fpnames[jj];
287  FOOTPRINT_INFO* fpinfo = new FOOTPRINT_INFO_IMPL( this, nickname, fpname );
288  queue_parsed.move_push( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) );
289  }
290 
291  if( m_progress_reporter )
293 
294  m_count_finished.fetch_add( 1 );
295  }
296  } ) );
297  }
298 
299  while( !m_cancelled && (size_t)m_count_finished.load() < total_count )
300  {
302  m_cancelled = true;
303 
304  wxMilliSleep( 30 );
305  }
306 
307  for( auto& thr : threads )
308  thr.join();
309 
310  std::unique_ptr<FOOTPRINT_INFO> fpi;
311 
312  while( queue_parsed.pop( fpi ) )
313  m_list.push_back( std::move( fpi ) );
314 
315  std::sort( m_list.begin(), m_list.end(), []( std::unique_ptr<FOOTPRINT_INFO> const& lhs,
316  std::unique_ptr<FOOTPRINT_INFO> const& rhs ) -> bool
317  {
318  return *lhs < *rhs;
319  } );
320 
321  return m_errors.empty();
322 }
323 
324 
326  m_loader( nullptr ),
327  m_count_finished( 0 ),
328  m_list_timestamp( 0 ),
329  m_progress_reporter( nullptr ),
330  m_cancelled( false )
331 {
332 }
333 
334 
336 {
337  StopWorkers();
338 }
339 
340 
341 void FOOTPRINT_LIST_IMPL::WriteCacheToFile( wxTextFile* aCacheFile )
342 {
343  if( aCacheFile->Exists() )
344  {
345  aCacheFile->Open();
346  aCacheFile->Clear();
347  }
348  else
349  {
350  aCacheFile->Create();
351  }
352 
353  aCacheFile->AddLine( wxString::Format( "%lld", m_list_timestamp ) );
354 
355  for( auto& fpinfo : m_list )
356  {
357  aCacheFile->AddLine( fpinfo->GetLibNickname() );
358  aCacheFile->AddLine( fpinfo->GetName() );
359  aCacheFile->AddLine( EscapeString( fpinfo->GetDescription(), CTX_DELIMITED_STR ) );
360  aCacheFile->AddLine( EscapeString( fpinfo->GetKeywords(), CTX_DELIMITED_STR ) );
361  aCacheFile->AddLine( wxString::Format( "%d", fpinfo->GetOrderNum() ) );
362  aCacheFile->AddLine( wxString::Format( "%u", fpinfo->GetPadCount() ) );
363  aCacheFile->AddLine( wxString::Format( "%u", fpinfo->GetUniquePadCount() ) );
364  }
365 
366  aCacheFile->Write();
367  aCacheFile->Close();
368 }
369 
370 
371 void FOOTPRINT_LIST_IMPL::ReadCacheFromFile( wxTextFile* aCacheFile )
372 {
373  m_list_timestamp = 0;
374  m_list.clear();
375 
376  try
377  {
378  if( aCacheFile->Exists() )
379  {
380  aCacheFile->Open();
381 
382  aCacheFile->GetFirstLine().ToLongLong( &m_list_timestamp );
383 
384  while( aCacheFile->GetCurrentLine() + 6 < aCacheFile->GetLineCount() )
385  {
386  wxString libNickname = aCacheFile->GetNextLine();
387  wxString name = aCacheFile->GetNextLine();
388  wxString description = UnescapeString( aCacheFile->GetNextLine() );
389  wxString keywords = UnescapeString( aCacheFile->GetNextLine() );
390  int orderNum = wxAtoi( aCacheFile->GetNextLine() );
391  unsigned int padCount = (unsigned) wxAtoi( aCacheFile->GetNextLine() );
392  unsigned int uniquePadCount = (unsigned) wxAtoi( aCacheFile->GetNextLine() );
393 
394  auto* fpinfo = new FOOTPRINT_INFO_IMPL( libNickname, name, description, keywords,
395  orderNum, padCount, uniquePadCount );
396  m_list.emplace_back( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) );
397  }
398  }
399  }
400  catch( ... )
401  {
402  // whatever went wrong, invalidate the cache
403  m_list_timestamp = 0;
404  }
405 
406  // Sanity check: an empty list is very unlikely to be correct.
407  if( m_list.size() == 0 )
408  m_list_timestamp = 0;
409 
410  if( aCacheFile->IsOpened() )
411  aCacheFile->Close();
412 }
void push(T const &aValue)
Push a value onto the queue.
Definition: sync_queue.h:45
unsigned GetPadCount(INCLUDE_NPTH_T aIncludeNPTH=INCLUDE_NPTH_T(INCLUDE_NPTH)) const
GetPadCount returns the number of pads.
void AdvancePhase()
Uses the next vailable virtual zone of the dialog progress bar.
void WriteCacheToFile(wxTextFile *aFile) override
SYNC_QUEUE< wxString > m_queue_out
unsigned GetUniquePadCount(INCLUDE_NPTH_T aIncludeNPTH=INCLUDE_NPTH_T(INCLUDE_NPTH)) const
GetUniquePadCount returns the number of unique pads.
bool pop(T &aReceiver)
Pop a value off the queue into the provided variable.
Definition: sync_queue.h:66
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: common.h:175
void StartWorkers(FP_LIB_TABLE *aTable, wxString const *aNickname, FOOTPRINT_ASYNC_LOADER *aLoader, unsigned aNThreads) override
Launch worker threads to load footprints.
A progress reporter for use in multi-threaded environments.
std::atomic_bool m_cancelled
ERRLIST m_errors
some can be PARSE_ERRORs also
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.
unsigned m_pad_count
Number of 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.
long long GenerateTimestamp(const wxString *aNickname)
Generate a hashed timestamp representing the last-mod-times of the library indicated by aNickname,...
wxString m_doc
Footprint description.
PROGRESS_REPORTER * m_progress_reporter
void Start(FP_LIB_TABLE *aTable, wxString const *aNickname=nullptr, unsigned aNThreads=DEFAULT_THREADS)
Launch the worker threads.
bool JoinWorkers() override
Join worker threads.
bool empty() const
Return true iff the queue is empty.
Definition: sync_queue.h:85
const MODULE * GetEnumeratedFootprint(const wxString &aNickname, const wxString &aFootprintName)
Function GetEnumeratedFootprint.
wxString m_fpname
Module name.
Subclass of DIALOG_DISPLAY_HTML_TEXT_BASE, which is generated by wxFormBuilder.
Definition of file extensions used in Kicad.
void PrefetchLib(const wxString &aNickname)
Function PrefetchLib If possible, prefetches the specified library (e.g.
std::atomic_size_t m_count_finished
#define THROW_IO_ERROR(msg)
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.
#define _(s)
const wxString & GetKeywords() const
Definition: class_module.h:203
wxString m_keywords
Footprint keywords.
FOOTPRINT_ASYNC_LOADER * m_loader
YYCODETYPE lhs
const wxString & GetDescription() const
Definition: class_module.h:200
size_t size() const
Return the size of the queue.
Definition: sync_queue.h:94
see class PGM_BASE
const char * name
Definition: DXF_plotter.cpp:61
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
void StopWorkers() override
Stop worker threads.
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:123
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
These Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which ar...
Definition: string.cpp:50
void ReadCacheFromFile(wxTextFile *aFile) override
size_t i
Definition: json11.cpp:597
bool KeepRefreshing(bool aWait=false)
Update the UI dialog.
The common library.
bool ReadFootprintFiles(FP_LIB_TABLE *aTable, const wxString *aNickname=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr) override
Read all the footprints provided by the combination of aTable and aNickname.
FP_LIB_TABLE * GetTable() const
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:76
void clear()
Clear the queue.
Definition: sync_queue.h:103
unsigned m_unique_pad_count
Number of unique pads.
std::vector< wxString > GetLogicalLibs()
Return the logical library names, all of them that are pertinent to a look up done on this LIB_TABLE.
void Abort()
Safely stop the current process.