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 
128  if( m_progress_reporter )
129  {
131  m_progress_reporter->Report( _( "Fetching Footprint Libraries" ) );
132  }
133 
134  m_cancelled = false;
135 
136  FOOTPRINT_ASYNC_LOADER loader;
137 
138  loader.SetList( this );
139  loader.Start( aTable, aNickname );
140 
141 
142  while( !m_cancelled && (int)m_count_finished.load() < m_loader->m_total_libs )
143  {
145  m_cancelled = true;
146 
147  wxMilliSleep( 20 );
148  }
149 
150  if( m_cancelled )
151  {
152  loader.Abort();
153  }
154  else
155  {
156  if( m_progress_reporter )
157  {
160  m_progress_reporter->Report( _( "Loading Footprints" ) );
161  }
162 
163  loader.Join();
164 
165  if( m_progress_reporter )
167  }
168 
169  if( m_cancelled )
170  m_list_timestamp = 0; // God knows what we got before we were cancelled
171  else
172  m_list_timestamp = generatedTimestamp;
173 
174  return m_errors.empty();
175 }
176 
177 
178 void FOOTPRINT_LIST_IMPL::StartWorkers( FP_LIB_TABLE* aTable, wxString const* aNickname,
179  FOOTPRINT_ASYNC_LOADER* aLoader, unsigned aNThreads )
180 {
181  m_loader = aLoader;
182  m_lib_table = aTable;
183 
184  // Clear data before reading files
185  m_count_finished.store( 0 );
186  m_errors.clear();
187  m_list.clear();
188  m_threads.clear();
189  m_queue_in.clear();
190  m_queue_out.clear();
191 
192  if( aNickname )
193  m_queue_in.push( *aNickname );
194  else
195  {
196  for( auto const& nickname : aTable->GetLogicalLibs() )
197  m_queue_in.push( nickname );
198  }
199 
201 
202  for( unsigned i = 0; i < aNThreads; ++i )
203  {
204  m_threads.emplace_back( &FOOTPRINT_LIST_IMPL::loader_job, this );
205  }
206 }
207 
209 {
210  std::lock_guard<std::mutex> lock1( m_join );
211 
212  // To safely stop our workers, we set the cancellation flag (they will each
213  // exit on their next safe loop location when this is set). Then we need to wait
214  // for all threads to finish as closing the implementation will free the queues
215  // that the threads write to.
216  for( auto& i : m_threads )
217  i.join();
218 
219  m_threads.clear();
220  m_queue_in.clear();
221  m_count_finished.store( 0 );
222 
223  // If we have cancelled in the middle of a load, clear our timestamp to re-load next time
224  if( m_cancelled )
225  m_list_timestamp = 0;
226 }
227 
229 {
230  {
231  std::lock_guard<std::mutex> lock1( m_join );
232 
233  for( auto& i : m_threads )
234  i.join();
235 
236  m_threads.clear();
237  m_queue_in.clear();
238  m_count_finished.store( 0 );
239  }
240 
241  size_t total_count = m_queue_out.size();
242 
243  LOCALE_IO toggle_locale;
244 
245  // Parse the footprints in parallel. WARNING! This requires changing the locale, which is
246  // GLOBAL. It is only threadsafe to construct the LOCALE_IO before the threads are created,
247  // destroy it after they finish, and block the main (GUI) thread while they work. Any deviation
248  // from this will cause nasal demons.
249  //
250  // TODO: blast LOCALE_IO into the sun
251 
253  std::vector<std::thread> threads;
254 
255  for( size_t ii = 0; ii < std::thread::hardware_concurrency() + 1; ++ii )
256  {
257  threads.emplace_back( [this, &queue_parsed]() {
258  wxString nickname;
259 
260  while( this->m_queue_out.pop( nickname ) && !m_cancelled )
261  {
262  wxArrayString fpnames;
263 
264  try
265  {
266  m_lib_table->FootprintEnumerate( fpnames, nickname, false );
267  }
268  catch( const IO_ERROR& ioe )
269  {
270  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
271  }
272  catch( const std::exception& se )
273  {
274  // This is a round about way to do this, but who knows what THROW_IO_ERROR()
275  // may be tricked out to do someday, keep it in the game.
276  try
277  {
278  THROW_IO_ERROR( se.what() );
279  }
280  catch( const IO_ERROR& ioe )
281  {
282  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
283  }
284  }
285 
286  for( unsigned jj = 0; jj < fpnames.size() && !m_cancelled; ++jj )
287  {
288  wxString fpname = fpnames[jj];
289  FOOTPRINT_INFO* fpinfo = new FOOTPRINT_INFO_IMPL( this, nickname, fpname );
290  queue_parsed.move_push( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) );
291  }
292 
293  if( m_progress_reporter )
295 
296  m_count_finished.fetch_add( 1 );
297  }
298  } );
299  }
300 
301  while( !m_cancelled && (size_t)m_count_finished.load() < total_count )
302  {
304  m_cancelled = true;
305 
306  wxMilliSleep( 30 );
307  }
308 
309  for( auto& thr : threads )
310  thr.join();
311 
312  std::unique_ptr<FOOTPRINT_INFO> fpi;
313 
314  while( queue_parsed.pop( fpi ) )
315  m_list.push_back( std::move( fpi ) );
316 
317  std::sort( m_list.begin(), m_list.end(), []( std::unique_ptr<FOOTPRINT_INFO> const& lhs,
318  std::unique_ptr<FOOTPRINT_INFO> const& rhs ) -> bool
319  {
320  return *lhs < *rhs;
321  } );
322 
323  return m_errors.empty();
324 }
325 
326 
328  m_loader( nullptr ),
329  m_count_finished( 0 ),
330  m_list_timestamp( 0 ),
331  m_progress_reporter( nullptr ),
332  m_cancelled( false )
333 {
334 }
335 
336 
338 {
339  StopWorkers();
340 }
341 
342 
343 void FOOTPRINT_LIST_IMPL::WriteCacheToFile( wxTextFile* aCacheFile )
344 {
345  if( aCacheFile->Exists() )
346  {
347  if( !aCacheFile->Open() )
348  return;
349 
350  aCacheFile->Clear();
351  }
352  else
353  {
354  if( !aCacheFile->Create() )
355  return;
356  }
357 
358  aCacheFile->AddLine( wxString::Format( "%lld", m_list_timestamp ) );
359 
360  for( auto& fpinfo : m_list )
361  {
362  aCacheFile->AddLine( fpinfo->GetLibNickname() );
363  aCacheFile->AddLine( fpinfo->GetName() );
364  aCacheFile->AddLine( EscapeString( fpinfo->GetDescription(), CTX_LINE ) );
365  aCacheFile->AddLine( EscapeString( fpinfo->GetKeywords(), CTX_LINE ) );
366  aCacheFile->AddLine( wxString::Format( "%d", fpinfo->GetOrderNum() ) );
367  aCacheFile->AddLine( wxString::Format( "%u", fpinfo->GetPadCount() ) );
368  aCacheFile->AddLine( wxString::Format( "%u", fpinfo->GetUniquePadCount() ) );
369  }
370 
371  aCacheFile->Write();
372  aCacheFile->Close();
373 }
374 
375 
376 void FOOTPRINT_LIST_IMPL::ReadCacheFromFile( wxTextFile* aCacheFile )
377 {
378  m_list_timestamp = 0;
379  m_list.clear();
380 
381  try
382  {
383  if( aCacheFile->Exists() && aCacheFile->Open() )
384  {
385  aCacheFile->GetFirstLine().ToLongLong( &m_list_timestamp );
386 
387  while( aCacheFile->GetCurrentLine() + 6 < aCacheFile->GetLineCount() )
388  {
389  wxString libNickname = aCacheFile->GetNextLine();
390  wxString name = aCacheFile->GetNextLine();
391  wxString description = UnescapeString( aCacheFile->GetNextLine() );
392  wxString keywords = UnescapeString( aCacheFile->GetNextLine() );
393  int orderNum = wxAtoi( aCacheFile->GetNextLine() );
394  unsigned int padCount = (unsigned) wxAtoi( aCacheFile->GetNextLine() );
395  unsigned int uniquePadCount = (unsigned) wxAtoi( aCacheFile->GetNextLine() );
396 
397  auto* fpinfo = new FOOTPRINT_INFO_IMPL( libNickname, name, description, keywords,
398  orderNum, padCount, uniquePadCount );
399  m_list.emplace_back( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) );
400  }
401  }
402  }
403  catch( ... )
404  {
405  // whatever went wrong, invalidate the cache
406  m_list_timestamp = 0;
407  }
408 
409  // Sanity check: an empty list is very unlikely to be correct.
410  if( m_list.size() == 0 )
411  m_list_timestamp = 0;
412 
413  if( aCacheFile->IsOpened() )
414  aCacheFile->Close();
415 }
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.
virtual 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:216
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
virtual 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.
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aNickname, bool aBestEfforts)
Return a list of footprint names contained within the library given by aNickname.
PROGRESS_REPORTER * m_progress_reporter
#define NULL
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.
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
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.
const wxString & GetKeywords() const
Definition: class_module.h:234
wxString m_keywords
Footprint keywords.
FOOTPRINT_ASYNC_LOADER * m_loader
const wxString & GetDescription() const
Definition: class_module.h:231
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:60
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:201
#define _(s)
Definition: 3d_actions.cpp:33
void StopWorkers() override
Stop worker threads.
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:152
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:77
void ReadCacheFromFile(wxTextFile *aFile) override
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
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 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
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
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.