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>
39 
40 #include <thread>
41 
42 
44 {
45  FP_LIB_TABLE* fptable = m_owner->GetTable();
46 
47  wxASSERT( fptable );
48 
49  std::unique_ptr<MODULE> footprint( fptable->FootprintLoad( m_nickname, m_fpname ) );
50 
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( 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 ) )
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 
113  if( !m_first_to_finish.exchange( true ) )
114  {
115  // yay, we're first to finish!
117  {
119  }
120  }
121 }
122 
123 
124 bool FOOTPRINT_LIST_IMPL::ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname )
125 {
126  FOOTPRINT_ASYNC_LOADER loader;
127 
128  loader.SetList( this );
129  loader.Start( aTable, aNickname );
130  return loader.Join();
131 }
132 
133 
134 void FOOTPRINT_LIST_IMPL::StartWorkers( FP_LIB_TABLE* aTable, wxString const* aNickname,
135  FOOTPRINT_ASYNC_LOADER* aLoader, unsigned aNThreads )
136 {
137  m_loader = aLoader;
138  m_lib_table = aTable;
139 
140  // Clear data before reading files
141  m_first_to_finish.store( false );
142  m_count_finished.store( 0 );
143  m_errors.clear();
144  m_list.clear();
145  m_threads.clear();
146  m_queue_in.clear();
147  m_queue_out.clear();
148 
149  if( aNickname )
150  m_queue_in.push( *aNickname );
151  else
152  {
153  for( auto const& nickname : aTable->GetLogicalLibs() )
154  m_queue_in.push( nickname );
155  }
156 
158 
159  for( unsigned i = 0; i < aNThreads; ++i )
160  {
161  m_threads.push_back( std::thread( &FOOTPRINT_LIST_IMPL::loader_job, this ) );
162  }
163 }
164 
166 {
167  for( auto& i : m_threads )
168  i.join();
169 
170  m_threads.clear();
171  m_queue_in.clear();
172 
173  LOCALE_IO toggle_locale;
174 
175  // Parse the footprints in parallel. WARNING! This requires changing the locale, which is
176  // GLOBAL. It is only threadsafe to construct the LOCALE_IO before the threads are created,
177  // destroy it after they finish, and block the main (GUI) thread while they work. Any deviation
178  // from this will cause nasal demons.
179  //
180  // TODO: blast LOCALE_IO into the sun
181 
183  std::vector<std::thread> threads;
184 
185  for( size_t i = 0; i < std::thread::hardware_concurrency() + 1; ++i )
186  {
187  threads.push_back( std::thread( [this, &queue_parsed]() {
188  wxString nickname;
189 
190  while( this->m_queue_out.pop( nickname ) )
191  {
192  wxArrayString fpnames;
193 
194  try
195  {
196  this->m_lib_table->FootprintEnumerate( fpnames, nickname );
197  }
198  catch( const IO_ERROR& ioe )
199  {
200  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
201  }
202  catch( const std::exception& se )
203  {
204  // This is a round about way to do this, but who knows what THROW_IO_ERROR()
205  // may be tricked out to do someday, keep it in the game.
206  try
207  {
208  THROW_IO_ERROR( se.what() );
209  }
210  catch( const IO_ERROR& ioe )
211  {
212  m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
213  }
214  }
215 
216  for( auto const& fpname : fpnames )
217  {
218  FOOTPRINT_INFO* fpinfo = new FOOTPRINT_INFO_IMPL( this, nickname, fpname );
219  queue_parsed.move_push( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) );
220  }
221  }
222  } ) );
223  }
224 
225  for( auto& thr : threads )
226  thr.join();
227 
228  std::unique_ptr<FOOTPRINT_INFO> fpi;
229 
230  while( queue_parsed.pop( fpi ) )
231  m_list.push_back( std::move( fpi ) );
232 
233  std::sort( m_list.begin(), m_list.end(),
234  []( std::unique_ptr<FOOTPRINT_INFO> const& lhs,
235  std::unique_ptr<FOOTPRINT_INFO> const& rhs ) -> bool { return *lhs < *rhs; } );
236 
237  return m_errors.empty();
238 }
239 
240 
242 {
243  return m_count_finished.load();
244 }
245 
246 
248 {
249 }
250 
251 
253 {
254  for( auto& i : m_threads )
255  i.join();
256 }
void push(T const &aValue)
Push a value onto the queue.
Definition: sync_queue.h:45
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
virtual void StartWorkers(FP_LIB_TABLE *aTable, wxString const *aNickname, FOOTPRINT_ASYNC_LOADER *aLoader, unsigned aNThreads) override
Launch worker threads to load footprints.
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
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.
wxString m_doc
Footprint description.
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.
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...
wxString m_keywords
Footprint keywords.
FOOTPRINT_ASYNC_LOADER * m_loader
int m_pad_count
Number of pads.
see class PGM_BASE
std::atomic_bool m_first_to_finish
Implementation of std::make_unique for pre C++14 compilation environments.
std::function< void()> m_completion_cb
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 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
virtual bool ReadFootprintFiles(FP_LIB_TABLE *aTable, const wxString *aNickname=NULL) override
Read all the footprints provided by the combination of aTable and aNickname.
bool CatchErrors(std::function< void()> aFunc)
Call aFunc, pushing any IO_ERRORs and std::exceptions it throws onto m_errors.
std::vector< wxString > GetLogicalLibs()
Function GetLogicalLibs.
FP_LIB_TABLE * GetTable() const