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-2018 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 #include <mutex>
43 
44 
46 {
47  FP_LIB_TABLE* fptable = m_owner->GetTable();
48 
49  wxASSERT( fptable );
50 
51  const MODULE* footprint = fptable->GetEnumeratedFootprint( m_nickname, m_fpname );
52 
53  if( footprint == NULL ) // Should happen only with malformed/broken libraries
54  {
55  m_pad_count = 0;
57  }
58  else
59  {
62  m_keywords = footprint->GetKeywords();
63  m_doc = footprint->GetDescription();
64  }
65 
66  m_loaded = true;
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  PROGRESS_REPORTER* aProgressReporter )
121 {
122  long long int generatedTimestamp = aTable->GenerateTimestamp( aNickname );
123 
124  if( generatedTimestamp == m_list_timestamp )
125  return true;
126 
127  m_progress_reporter = aProgressReporter;
128  m_cancelled = false;
129 
130  FOOTPRINT_ASYNC_LOADER loader;
131 
132  loader.SetList( this );
133  loader.Start( aTable, aNickname );
134 
135  if( m_progress_reporter )
136  {
138  m_progress_reporter->Report( _( "Fetching Footprint Libraries" ) );
139  }
140 
141  while( !m_cancelled && (int)m_count_finished.load() < m_loader->m_total_libs )
142  {
144  m_cancelled = true;
145 
146  wxMilliSleep( 20 );
147  }
148 
149  if( m_cancelled )
150  {
151  loader.Abort();
152  }
153  else
154  {
155  if( m_progress_reporter )
156  {
159  m_progress_reporter->Report( _( "Loading Footprints" ) );
160  }
161 
162  loader.Join();
163 
164  if( m_progress_reporter )
166  }
167 
168  if( m_cancelled )
169  m_list_timestamp = 0; // God knows what we got before we were cancelled
170  else
171  m_list_timestamp = generatedTimestamp;
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  std::lock_guard<std::mutex> lock1( m_join );
210 
211  // To safely stop our workers, we set the cancellation flag (they will each
212  // exit on their next safe loop location when this is set). Then we need to wait
213  // for all threads to finish as closing the implementation will free the queues
214  // that the threads write to.
215  for( auto& i : m_threads )
216  i.join();
217 
218  m_threads.clear();
219  m_queue_in.clear();
220  m_count_finished.store( 0 );
221 
222  // If we have cancelled in the middle of a load, clear our timestamp to re-load next time
223  if( m_cancelled )
224  m_list_timestamp = 0;
225 }
226 
228 {
229  {
230  std::lock_guard<std::mutex> lock1( m_join );
231 
232  for( auto& i : m_threads )
233  i.join();
234 
235  m_threads.clear();
236  m_queue_in.clear();
237  m_count_finished.store( 0 );
238  }
239 
240  size_t total_count = m_queue_out.size();
241 
242  LOCALE_IO toggle_locale;
243 
244  // Parse the footprints in parallel. WARNING! This requires changing the locale, which is
245  // GLOBAL. It is only threadsafe to construct the LOCALE_IO before the threads are created,
246  // destroy it after they finish, and block the main (GUI) thread while they work. Any deviation
247  // from this will cause nasal demons.
248  //
249  // TODO: blast LOCALE_IO into the sun
250 
252  std::vector<std::thread> threads;
253 
254  for( size_t ii = 0; ii < std::thread::hardware_concurrency() + 1; ++ii )
255  {
256  threads.push_back( std::thread( [this, &queue_parsed]() {
257  wxString nickname;
258 
259  while( this->m_queue_out.pop( nickname ) && !m_cancelled )
260  {
261  wxArrayString fpnames;
262 
263  try
264  {
265  m_lib_table->FootprintEnumerate( fpnames, nickname );
266  }
267  catch( const IO_ERROR& ioe )
268  {
269  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
270  }
271  catch( const std::exception& se )
272  {
273  // This is a round about way to do this, but who knows what THROW_IO_ERROR()
274  // may be tricked out to do someday, keep it in the game.
275  try
276  {
277  THROW_IO_ERROR( se.what() );
278  }
279  catch( const IO_ERROR& ioe )
280  {
281  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
282  }
283  }
284 
285  for( unsigned jj = 0; jj < fpnames.size() && !m_cancelled; ++jj )
286  {
287  wxString fpname = fpnames[jj];
288  FOOTPRINT_INFO* fpinfo = new FOOTPRINT_INFO_IMPL( this, nickname, fpname );
289  queue_parsed.move_push( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) );
290  }
291 
292  if( m_progress_reporter )
294 
295  m_count_finished.fetch_add( 1 );
296  }
297  } ) );
298  }
299 
300  while( !m_cancelled && (size_t)m_count_finished.load() < total_count )
301  {
303  m_cancelled = true;
304 
305  wxMilliSleep( 30 );
306  }
307 
308  for( auto& thr : threads )
309  thr.join();
310 
311  std::unique_ptr<FOOTPRINT_INFO> fpi;
312 
313  while( queue_parsed.pop( fpi ) )
314  m_list.push_back( std::move( fpi ) );
315 
316  std::sort( m_list.begin(), m_list.end(), []( std::unique_ptr<FOOTPRINT_INFO> const& lhs,
317  std::unique_ptr<FOOTPRINT_INFO> const& rhs ) -> bool
318  {
319  return *lhs < *rhs;
320  } );
321 
322  return m_errors.empty();
323 }
324 
325 
327  m_loader( nullptr ),
328  m_count_finished( 0 ),
329  m_list_timestamp( 0 ),
330  m_progress_reporter( nullptr ),
331  m_cancelled( false )
332 {
333 }
334 
335 
337 {
338  StopWorkers();
339 }
340 
341 
342 void FOOTPRINT_LIST_IMPL::WriteCacheToFile( wxTextFile* aCacheFile )
343 {
344  if( aCacheFile->Exists() )
345  {
346  aCacheFile->Open();
347  aCacheFile->Clear();
348  }
349  else
350  {
351  aCacheFile->Create();
352  }
353 
354  aCacheFile->AddLine( wxString::Format( "%lld", m_list_timestamp ) );
355 
356  for( auto& fpinfo : m_list )
357  {
358  aCacheFile->AddLine( fpinfo->GetLibNickname() );
359  aCacheFile->AddLine( fpinfo->GetName() );
360  aCacheFile->AddLine( fpinfo->GetDescription() );
361  aCacheFile->AddLine( fpinfo->GetKeywords() );
362  aCacheFile->AddLine( wxString::Format( "%d", fpinfo->GetOrderNum() ) );
363  aCacheFile->AddLine( wxString::Format( "%u", fpinfo->GetPadCount() ) );
364  aCacheFile->AddLine( wxString::Format( "%u", fpinfo->GetUniquePadCount() ) );
365  }
366 
367  aCacheFile->Write();
368  aCacheFile->Close();
369 }
370 
371 
372 void FOOTPRINT_LIST_IMPL::ReadCacheFromFile( wxTextFile* aCacheFile )
373 {
374  try
375  {
376  m_list.clear();
377 
378  if( aCacheFile->Exists() )
379  aCacheFile->Open();
380  else
381  return;
382 
383  aCacheFile->GetFirstLine().ToLongLong( &m_list_timestamp );
384 
385  while( aCacheFile->GetCurrentLine() + 6 < aCacheFile->GetLineCount() )
386  {
387  wxString libNickname = aCacheFile->GetNextLine();
388  wxString name = aCacheFile->GetNextLine();
389  wxString description = aCacheFile->GetNextLine();
390  wxString keywords = aCacheFile->GetNextLine();
391  int orderNum = wxAtoi( aCacheFile->GetNextLine() );
392  unsigned int padCount = (unsigned) wxAtoi( aCacheFile->GetNextLine() );
393  unsigned int uniquePadCount = (unsigned) wxAtoi( aCacheFile->GetNextLine() );
394 
395  auto* fpinfo = new FOOTPRINT_INFO_IMPL( libNickname, name, description, keywords,
396  orderNum, padCount, uniquePadCount );
397  m_list.emplace_back( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) );
398  }
399  }
400  catch( ... )
401  {
402  // whatever went wrong, invalidate the cache
403  m_list_timestamp = 0;
404  }
405 
406  if( aCacheFile->IsOpened() )
407  aCacheFile->Close();
408 }
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.
void WriteCacheToFile(wxTextFile *aFile) override
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
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown...
Definition: common.h:179
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.
bool empty() const
Return true iff the queue is empty.
Definition: sync_queue.h:85
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.
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.
The common library.
void PrefetchLib(const wxString &aNickname)
Function PrefetchLib If possible, prefetches the specified library (e.g.
const wxString & GetKeywords() const
Definition: class_module.h:199
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.
wxString m_keywords
Footprint keywords.
FOOTPRINT_ASYNC_LOADER * m_loader
unsigned GetPadCount(INCLUDE_NPTH_T aIncludeNPTH=INCLUDE_NPTH_T(INCLUDE_NPTH)) const
GetPadCount returns the number of pads.
YYCODETYPE lhs
see class PGM_BASE
Implementation of std::make_unique for pre C++14 compilation environments.
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
const wxString & GetDescription() const
Definition: class_module.h:196
void StopWorkers() override
Stop worker threads.
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.
unsigned GetUniquePadCount(INCLUDE_NPTH_T aIncludeNPTH=INCLUDE_NPTH_T(INCLUDE_NPTH)) const
GetUniquePadCount returns the number of unique pads.
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...
FP_LIB_TABLE * GetTable() const
void Abort()
Safely stop the current process.