Code::Blocks  SVN r11506
systemheadersthread.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
3  * http://www.gnu.org/licenses/gpl-3.0.html
4  *
5  * $Revision: 11505 $
6  * $Id: systemheadersthread.cpp 11505 2018-10-20 14:29:48Z ollydbg $
7  * $HeadURL: https://svn.code.sf.net/p/codeblocks/code/trunk/src/plugins/codecompletion/systemheadersthread.cpp $
8  */
9 
10 #include <sdk.h>
11 
12 #ifndef CB_PRECOMP
13  #include <wx/app.h> // wxPostEvent
14  #include <wx/dir.h> // wxDirTraverser
15  #include <wx/event.h>
16  #include <wx/filename.h>
17 #endif
18 
19 #include "systemheadersthread.h"
20 
21 #define CC_SYSTEMHEADERSTHREAD_DEBUG_OUTPUT 0
22 
23 #if defined(CC_GLOBAL_DEBUG_OUTPUT)
24  #if CC_GLOBAL_DEBUG_OUTPUT == 1
25  #undef CC_SYSTEMHEADERSTHREAD_DEBUG_OUTPUT
26  #define CC_SYSTEMHEADERSTHREAD_DEBUG_OUTPUT 1
27  #elif CC_GLOBAL_DEBUG_OUTPUT == 2
28  #undef CC_SYSTEMHEADERSTHREAD_DEBUG_OUTPUT
29  #define CC_SYSTEMHEADERSTHREAD_DEBUG_OUTPUT 2
30  #endif
31 #endif
32 
33 #if CC_SYSTEMHEADERSTHREAD_DEBUG_OUTPUT == 1
34  #define TRACE(format, args...) \
35  CCLogger::Get()->DebugLog(F(format, ##args))
36  #define TRACE2(format, args...)
37 #elif CC_SYSTEMHEADERSTHREAD_DEBUG_OUTPUT == 2
38  #define TRACE(format, args...) \
39  do \
40  { \
41  if (g_EnableDebugTrace) \
42  CCLogger::Get()->DebugLog(F(format, ##args)); \
43  } \
44  while (false)
45  #define TRACE2(format, args...) \
46  CCLogger::Get()->DebugLog(F(format, ##args))
47 #else
48  #define TRACE(format, args...)
49  #define TRACE2(format, args...)
50 #endif
51 
52 // sent message when finish traversing all the folders, before the thread dies
54 
55 // Message used logging.
57 
58 // internal class declaration of HeaderDirTraverser (implementation below)
60 {
61 public:
63  SystemHeadersMap& headersMap, const wxString& searchDir);
64  ~HeaderDirTraverser() override;
65 
67  wxDirTraverseResult OnFile(const wxString& filename) override;
68 
70  wxDirTraverseResult OnDir(const wxString& dirname) override;
71 
72 private:
78  void AddLock(bool is_file);
79 
81 private:
82  /* the thread call Traverse() on this instance*/
84 
85  /* critical section to protect accessing m_SystemHeadersMap */
87 
88  /* dir to files map, for example, you have two dirs c:/a and c:/b
89  * so the map looks like: (usually the relative file path is stored
90  * c:/a ---> {c:/a/a1.h, c:/a/a2.h} ---> {a1.h, a2.h}
91  * c:/b ---> {c:/b/b1.h, c:/b/b2.h} ---> {b1.h, b2.h}
92  */
94 
95 #ifndef __WXMSW__
96  struct FileID
97  {
98  dev_t st_dev;
99  ino_t st_ino;
100 
101  bool operator< (const FileID &f) const
102  {
103  if (st_dev == f.st_dev)
104  return st_ino < f.st_ino;
105  else
106  return st_dev<f.st_dev;
107  }
108  };
110  std::set<FileID> m_VisitedDirsByID;
111 #endif // __WXMSW__
112 
113  /* which top level dir we are traversing header files in this Traverser object, the folder is in absolute
114  * format, like c:/a
115  */
117 
118  /* string set for header files, this is actually a file set reference in the m_SystemHeadersMap
119  * it store all the header files in relative format, such as: a1.h, a2.h
120  */
122 
124  bool m_Locked;
125 
126  /* numbers of dirs in the traversing */
127  size_t m_Dirs;
128 
129  /* numbers of files in the traversing */
130  size_t m_Files;
131 };
132 
133 // class SystemHeadersThread
134 
136  wxCriticalSection* critSect,
137  SystemHeadersMap& headersMap,
138  const wxArrayString& incDirs) :
140  m_Parent(parent),
141  m_SystemHeadersThreadCS(critSect),
142  m_SystemHeadersMap(headersMap),
143  m_IncludeDirs(incDirs)
144 {
145  Create();
146  SetPriority(60u);
147 }
148 
150 {
151  TRACE(_T("SystemHeadersThread: Terminated."));
152 }
153 
155 {
156  wxArrayString dirs;
157  {
159  // check to see m_SystemHeadersMap already contains the element of m_IncludeDirs, if not,
160  // just add one entry in the map.
161  for (size_t i=0; i<m_IncludeDirs.GetCount(); ++i)
162  {
164  {
165  dirs.Add(m_IncludeDirs[i]);
167  }
168  }
169  }
170 
171  // collect header files in each dir, this is done by HeaderDirTraverser
172  for (size_t i=0; i<dirs.GetCount(); ++i)
173  {
174  wxStopWatch timer;
175  timer.Start();
176  if ( TestDestroy() )
177  break;
178 
179  // Detect if this directory is for the file system root and skip it.
180  // The user probably doesn't want to wait for the whole disk to be scanned!
181  wxFileName dirName(dirs[i]);
182  if (dirName.IsAbsolute() && dirName.GetDirCount() == 0)
183  continue;
184  // check the dir is ready for traversing
185  wxDir dir(dirs[i]);
186  if (!dir.IsOpened())
187  {
188  CodeBlocksThreadEvent evt(wxEVT_COMMAND_MENU_SELECTED, idSystemHeadersThreadMessage);
189  evt.SetClientData(this);
190  evt.SetString(wxString::Format(_T("SystemHeadersThread: Unable to open: %s"),
191  dirs[i].wx_str()));
192  wxPostEvent(m_Parent, evt);
193  continue;
194  }
195 
196  {
197  CodeBlocksThreadEvent evt(wxEVT_COMMAND_MENU_SELECTED, idSystemHeadersThreadMessage);
198  evt.SetClientData(this);
199  evt.SetString(wxString::Format(_T("SystemHeadersThread: Start traversing: %s"),
200  dirs[i].wx_str()));
201  wxPostEvent(m_Parent, evt);
202  }
203 
205  dir.Traverse(traverser, wxEmptyString, wxDIR_FILES | wxDIR_DIRS);
206 
207  if ( TestDestroy() )
208  break;
209 
210  CodeBlocksThreadEvent evt(wxEVT_COMMAND_MENU_SELECTED, idSystemHeadersThreadMessage);
211  evt.SetClientData(this);
212  evt.SetString(wxString::Format(_T("SystemHeadersThread: Traversing %s finished, found %lu headers; time: %.3lf sec"),
213  dirs[i].wx_str(),
214  static_cast<unsigned long>(m_SystemHeadersMap[dirs[i]].size()),
215  timer.Time()*0.001));
216  wxPostEvent(m_Parent, evt);
217  }
218 
219  // send the idSystemHeadersThreadFinish event to notify its parent before the thread dies.
220  if ( !TestDestroy() )
221  {
222  CodeBlocksThreadEvent evt(wxEVT_COMMAND_MENU_SELECTED, idSystemHeadersThreadFinish);
223  evt.SetClientData(this);
224  if (!dirs.IsEmpty())
225  {
226  evt.SetString(wxString::Format(_T("SystemHeadersThread: Total number of paths: %lu"),
227  static_cast<unsigned long>(dirs.GetCount())));
228  }
229  wxPostEvent(m_Parent, evt);
230  }
231 
232  TRACE(_T("SystemHeadersThread: Done."));
233 
234  return NULL;
235 }
236 
237 // class HeaderDirTraverser
238 
240  wxCriticalSection* critSect,
241  SystemHeadersMap& headersMap,
242  const wxString& searchDir) :
243  m_Thread(thread),
244  m_SystemHeadersThreadCS(critSect),
245  m_SystemHeadersMap(headersMap),
246  m_SearchDir(searchDir),
247  m_Headers(headersMap[searchDir]),
248  m_Locked(false),
249  m_Dirs(0),
250  m_Files(0)
251 {
252 }
253 
255 {
256  if (m_Locked)
258 }
259 
261 {
262  // HeaderDirTraverser is used in a worker thread, so call TestDestroy() as often as it can to
263  // quickly terminate the thread
264  if (m_Thread->TestDestroy())
265  return wxDIR_STOP;
266 
267  AddLock(true); // true means we are adding a file
268 
269  wxFileName fn(filename);
270  if (!fn.HasExt() || fn.GetExt().GetChar(0) == _T('h'))
271  {
273  wxString header(fn.GetFullPath());
274  header.Replace(_T("\\"), _T("/"), true); // Unix style
275  m_Headers.insert(header);
276  }
277 
278  return wxDIR_CONTINUE;
279 }
280 
282 {
283  // HeaderDirTraverser is used in a worker thread, so call TestDestroy() as often as it can to
284  // quickly terminate the thread
285  if (m_Thread->TestDestroy())
286  return wxDIR_STOP;
287 
288  AddLock(false); // false means we are adding a dir
289 
290 #ifndef __WXMSW__
291  // Use stat to identify unique files. This is needed to prevent loops caused by sym-linking.
292  // The st_dev and st_ino are enough to uniquely identify a file on Unix like systems.
293  // On windows we don't detect loops because they are lest frequent and harder to make, if at all
294  // possible.
295  struct stat s;
296  if (stat(dirname.utf8_str().data(), &s)==0)
297  {
298  FileID f;
299  f.st_dev = s.st_dev;
300  f.st_ino = s.st_ino;
301 
302  if (m_VisitedDirsByID.find(f) != m_VisitedDirsByID.end())
303  return wxDIR_IGNORE;
304  m_VisitedDirsByID.insert(f);
305  }
306  else
307  return wxDIR_STOP;
308 #endif // __WXMSW__
309 
311  if (path.empty())
312  return wxDIR_IGNORE;
313  if (path.Last() != wxFILE_SEP_PATH)
314  path.Append(wxFILE_SEP_PATH);
315 
316  return GetStatus(path);
317 }
318 
319 void HeaderDirTraverser::AddLock(bool is_file)
320 {
321  if (is_file)
322  m_Files++;
323  else
324  m_Dirs++;
325 
326  if ((m_Files+m_Dirs) % 100 == 1)
327  {
328  TRACE(_T("HeaderDirTraverser: %lu directories and %lu files traversed. Unlocking temporarily."), static_cast<unsigned long>(m_Dirs), static_cast<unsigned long>(m_Files));
329 
330  if (m_Locked)
331  {
333  m_Locked = false;
334  }
335 
337  m_Locked = true;
338  }
339 }
340 
342 {
343  if (m_SystemHeadersMap.find(path) != m_SystemHeadersMap.end())
344  return wxDIR_IGNORE;
345  return wxDIR_CONTINUE;
346 }
int wxNewId()
virtual void * Entry()
bool IsOpened() const
const SystemHeadersMap & m_SystemHeadersMap
wxDirTraverseResult GetStatus(const wxString &path)
void AddLock(bool is_file)
this function will be called every time we meet a file or a dir, and we count the file and dir...
long Time() const
#define _T(string)
const wxScopedCharBuffer utf8_str() const
void SetPriority(unsigned int priority)
wxThreadError Create(unsigned int stackSize=0)
bool IsAbsolute(wxPathFormat format=wxPATH_NATIVE) const
bool m_Locked
indicates whether the critical section is entered or not, used in AddLock() function ...
const wxString & m_SearchDir
bool empty() const
virtual bool TestDestroy()
size_t Traverse(wxDirTraverser &sink, const wxString &filespec=wxEmptyString, int flags=wxDIR_DEFAULT) const
bool MakeRelativeTo(const wxString &pathBase=wxEmptyString, wxPathFormat format=wxPATH_NATIVE)
void wxPostEvent(wxEvtHandler *dest, const wxEvent &event)
wxString GetExt() const
wxCriticalSection * m_SystemHeadersThreadCS
this is the target the thread will sent any event to
#define TRACE(format, args...)
wxDirTraverseResult OnFile(const wxString &filename) override
call back function when we meet a file
wxArrayString m_IncludeDirs
this takes the result data
long idSystemHeadersThreadFinish
event ids used to notify parent objects
size_t Replace(const wxString &strOld, const wxString &strNew, bool replaceAll=true)
DLLIMPORT wxString cbResolveSymLinkedDirPathRecursive(wxString dirpath)
Call cbResolveSymLinkedPath until the path is not a symlink.
Definition: globals.cpp:1054
long idSystemHeadersThreadMessage
bool IsEmpty() const
wxString wxEmptyString
wxDirTraverseResult
wxString & Append(const char *psz)
SystemHeadersMap & m_SystemHeadersMap
protect multiply access to its data
void Start(long milliseconds=0)
bool HasExt() const
size_t Add(const wxString &str, size_t copies=1)
SystemHeadersThread(wxEvtHandler *parent, wxCriticalSection *critSect, SystemHeadersMap &headersMap, const wxArrayString &incDirs)
HeaderDirTraverser(wxThread *thread, wxCriticalSection *critSect, SystemHeadersMap &headersMap, const wxString &searchDir)
size_t GetCount() const
wxUniChar GetChar(size_t n) const
wxUniChar Last() const
wxString GetFullPath(wxPathFormat format=wxPATH_NATIVE) const
#define NULL
Definition: prefix.cpp:59
static wxString Format(const wxString &format,...)
std::map< wxString, StringSet > SystemHeadersMap
dir to files map, for example, you have two dirs c:/a and c:/b so the map looks like: c:/a —> {c:/a/...
wxCriticalSection * m_SystemHeadersThreadCS
size_t GetDirCount() const
std::set< wxString > StringSet
Definition: parser_base.h:19
wxDirTraverseResult OnDir(const wxString &dirname) override
call back function when we meet a dir