I also see that just after setting m_abort, the parserThreads are immediately deleted by
cbThreadPool::AbortAllTasks()
std::for_each(m_tasksQueue.begin(), m_tasksQueue.end(), std::mem_fun_ref(&cbThreadedTaskElement::Delete));
so it doesn't even matter if each ParserThread is posted to abort or not.
Although they are in fact posted by cbThreadPool at
void cbThreadPool::cbWorkerThread::AbortTask()
{
wxMutexLocker lock(m_taskMutex);
if (m_pTask)
m_pTask->Abort();
}
I'm not sure, but looked at your description, I think you are not fully understand the logic here, let me explain.
The code:
void cbThreadPool::AbortAllTasks()
{
wxMutexLocker lock(m_Mutex);
std::for_each(m_threads.begin(), m_threads.end(), std::mem_fun(&cbWorkerThread::AbortTask));
std::for_each(m_tasksQueue.begin(), m_tasksQueue.end(), std::mem_fun_ref(&cbThreadedTaskElement::Delete));
m_tasksQueue.clear();
}
1, Tasks are running in the threads one by one. m_threads are all the running threads. m_tasksQueue are the queued tasks, and they are not running.
2, when you call cbWorkerThread::AbortTask(), it just mark the running task's flag to true:
inline void cbThreadedTask::Abort()
{
m_abort = true;
}
So that the running task has a chance to exit.
3, now, if you have a project, which contains 3 cpp files.
a.cpp -> x.h -> y.h -> z.h ->...
b.cpp -> x.h -> y.h -> z.h ->...
c.cpp -> x.h -> y.h -> z.h ->...
"->" stands for "#include" directive.
When the parser start to work, it create three tasks(that's one ParserThread object per a cpp file) and put them in the thread pool. Our thread pool currently only allow one running thread, because we can avoid multiply threads to access the symbol table(TokenTree).
Now, we are running the ParserThread associated with a.cpp, and if will create a new ParserThread x.h when it see a #include "x.h" directive. But please not that at this time, the ParserThread associated with x.h is not put into the thread pool, it just runs inside the top level(the a.cpp's) ParserThread's DoParse() function, it may recursively create another Parserthread object if it see a "#include y.h".
When you try to abort the parsing, you set the flag m_abort on the a.cpp's ParserThread, this is the running task, but the bad thing is, you are actually running the embedding y.h's ParserThread, so this task won't finished immediately, until all the header files are parsed, and the control flow goes back to the top level a.cpp's ParserThread, so it will take a lot of time!
The two ParserThread objects associated with b.cpp and c.cpp are the tasked put in the m_tasksQueue, so they will be deleted without any delay.
But traping on the ParserThreads call to cbThreadedTask::TestDestroy() always shows that m_abort is always false, never true. (except for the main Parser thread you mentioned above.
inline bool cbThreadedTask::TestDestroy() const
{
return m_abort;
}
So I'm guessing, then, that the 20 seconds (on my laptop) to close CodeBlocks.cbp (windows 7) is caused by the delete of each ParserThread. About 432 of them (give or take a few when I hit close project).
I'll do some more tracing to confirm that.
My objective is to find a way to post abort or delete those threads in a faster manner.
This may be folly on my part, but I have the nasty habit of opening, viewing, closing CodeBlocks.cbp before parsing finishes, then having to wait nearly 30 seconds for it to close. Half the time, CB shows the "... is not responding" message from Windows 7.
Thanks for the explanation.
For my example I exaplaned above, you 20 second are just waiting for the ParserThread associated with a.cpp to finish. Currently we need to find a way to exit the embedding(inner) Parserthread quickly.