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 
43 
45 {
46  FP_LIB_TABLE* fptable = m_owner->GetTable();
47 
48  wxASSERT( fptable );
49 
50  std::unique_ptr<MODULE> footprint( fptable->LoadEnumeratedFootprint( m_nickname, m_fpname ) );
51  if( footprint.get() == NULL ) // Should happen only with malformed/broken libraries
52  {
53  m_pad_count = 0;
55  }
56  else
57  {
58  m_pad_count = footprint->GetPadCount( DO_NOT_INCLUDE_NPTH );
59  m_unique_pad_count = footprint->GetUniquePadCount( DO_NOT_INCLUDE_NPTH );
60  m_keywords = footprint->GetKeywords();
61  m_doc = footprint->GetDescription();
62 
63  // tell ensure_loaded() I'm loaded.
64  m_loaded = true;
65  }
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  if( m_list_timestamp == aTable->GenerateTimestamp( aNickname ) )
122  return true;
123 
124  m_progress_reporter = aProgressReporter;
125  m_cancelled = false;
126 
127  FOOTPRINT_ASYNC_LOADER loader;
128 
129  loader.SetList( this );
130  loader.Start( aTable, aNickname );
131 
132  if( m_progress_reporter )
133  {
135  m_progress_reporter->Report( _( "Fetching Footprint Libraries" ) );
136  }
137 
138  while( !m_cancelled && (int)m_count_finished.load() < m_loader->m_total_libs )
139  {
140  if( m_progress_reporter )
142  else
143  wxMilliSleep( 20 );
144  }
145 
146  if( m_progress_reporter )
148 
149  if( !m_cancelled )
150  {
151  if( m_progress_reporter )
152  {
154  m_progress_reporter->Report( _( "Loading Footprints" ) );
155  }
156 
157  loader.Join();
158  }
159 
160  if( m_progress_reporter )
162 
163  if( m_cancelled )
164  {
165  m_errors.move_push( std::make_unique<IO_ERROR>
166  ( _( "Loading incomplete; cancelled by user." ), nullptr, nullptr, 0 ) );
167  }
168 
169  m_progress_reporter = nullptr;
170 
171  return m_errors.empty();
172 }
173 
174 
175 void FOOTPRINT_LIST_IMPL::StartWorkers( FP_LIB_TABLE* aTable, wxString const* aNickname,
176  FOOTPRINT_ASYNC_LOADER* aLoader, unsigned aNThreads )
177 {
178  m_loader = aLoader;
179  m_lib_table = aTable;
180  m_library = aNickname;
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  for( auto& i : m_threads )
209  i.join();
210 
211  m_threads.clear();
212  m_queue_in.clear();
213  m_count_finished.store( 0 );
214 
215  size_t total_count = m_queue_out.size();
216 
217  LOCALE_IO toggle_locale;
218 
219  // Parse the footprints in parallel. WARNING! This requires changing the locale, which is
220  // GLOBAL. It is only threadsafe to construct the LOCALE_IO before the threads are created,
221  // destroy it after they finish, and block the main (GUI) thread while they work. Any deviation
222  // from this will cause nasal demons.
223  //
224  // TODO: blast LOCALE_IO into the sun
225 
227  std::vector<std::thread> threads;
228 
229  for( size_t ii = 0; ii < std::thread::hardware_concurrency() + 1; ++ii )
230  {
231  threads.push_back( std::thread( [this, &queue_parsed]() {
232  wxString nickname;
233 
234  while( this->m_queue_out.pop( nickname ) && !m_cancelled )
235  {
236  wxArrayString fpnames;
237 
238  try
239  {
240  m_lib_table->FootprintEnumerate( fpnames, nickname );
241  }
242  catch( const IO_ERROR& ioe )
243  {
244  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
245  }
246  catch( const std::exception& se )
247  {
248  // This is a round about way to do this, but who knows what THROW_IO_ERROR()
249  // may be tricked out to do someday, keep it in the game.
250  try
251  {
252  THROW_IO_ERROR( se.what() );
253  }
254  catch( const IO_ERROR& ioe )
255  {
256  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
257  }
258  }
259 
260  for( unsigned jj = 0; jj < fpnames.size() && !m_cancelled; ++jj )
261  {
262  wxString fpname = fpnames[jj];
263  FOOTPRINT_INFO* fpinfo = new FOOTPRINT_INFO_IMPL( this, nickname, fpname );
264  queue_parsed.move_push( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) );
265  }
266 
267  if( m_progress_reporter )
269 
270  m_count_finished.fetch_add( 1 );
271  }
272  } ) );
273  }
274 
275  while( !m_cancelled && (size_t)m_count_finished.load() < total_count )
276  {
277  if( m_progress_reporter )
279  else
280  wxMilliSleep( 20 );
281  }
282 
283  for( auto& thr : threads )
284  thr.join();
285 
286  std::unique_ptr<FOOTPRINT_INFO> fpi;
287 
288  while( queue_parsed.pop( fpi ) )
289  m_list.push_back( std::move( fpi ) );
290 
291  std::sort( m_list.begin(), m_list.end(),
292  []( std::unique_ptr<FOOTPRINT_INFO> const& lhs,
293  std::unique_ptr<FOOTPRINT_INFO> const& rhs ) -> bool { return *lhs < *rhs; } );
294 
295  if( m_cancelled )
296  m_list_timestamp = 0; // God knows what we got before we were cancelled
297  else
299 
300  return m_errors.empty();
301 }
302 
303 
305  m_loader( nullptr ),
306  m_count_finished( 0 ),
307  m_list_timestamp( 0 ),
308  m_progress_reporter( nullptr ),
309  m_cancelled( false )
310 {
311 }
312 
313 
315 {
316  for( auto& i : m_threads )
317  i.join();
318 }
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
MODULE * LoadEnumeratedFootprint(const wxString &aNickname, const wxString &aFootprintName)
Function LoadEnumeratedFootprint.
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
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.
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.
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.
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
const wxString * m_library
YYCODETYPE lhs
int m_pad_count
Number of pads.
see class PGM_BASE
Implementation of std::make_unique for pre C++14 compilation environments.
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.
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