Kaydet (Commit) bdaa13a8 authored tarafından Michael Stahl's avatar Michael Stahl

comphelper:: fix MSVC hang in ThreadPool::shutdown(), try #2

This takes a different approach than commit
9899ffd2.

Change the ThreadPool to automatically shutdown and join all threads
whenever waitUntilDone() is called.  Then start the threads again
in pushTask().

Because the ThreadPool is meant to be used synchronously with
waitUntilDone() called after adding all required tasks, this should
obviate the need to call shutdown() before process exit, as
there won't be any threads running at that point.

Change-Id: I2b8e639004a94cf05ccb4522aa1f0d3dac88a936
Reviewed-on: https://gerrit.libreoffice.org/35510Reviewed-by: 's avatarMichael Meeks <michael.meeks@collabora.com>
Tested-by: 's avatarJenkins <ci@libreoffice.org>
üst 9f3b59a5
......@@ -75,21 +75,20 @@ public:
}
};
ThreadPool::ThreadPool( sal_Int32 nWorkers ) :
mbTerminate( false )
ThreadPool::ThreadPool(sal_Int32 nWorkers)
: mbTerminate(true)
, mnWorkers(nWorkers)
{
std::unique_lock< std::mutex > aGuard( maMutex );
for( sal_Int32 i = 0; i < nWorkers; i++ )
maWorkers.push_back( new ThreadWorker( this ) );
for(rtl::Reference<ThreadWorker> & rpWorker : maWorkers)
rpWorker->launch();
}
ThreadPool::~ThreadPool()
{
shutdown();
// note: calling shutdown from global variable dtor blocks forever on Win7
// note2: there isn't enough MSVCRT left on exit to call assert() properly
// so these asserts just print something to stderr but exit status is
// still 0, but hopefully they will be more helpful on non-WNT platforms
assert(mbTerminate);
assert(maTasks.empty());
}
struct ThreadPoolStatic : public rtl::StaticWithInit< std::shared_ptr< ThreadPool >,
......@@ -136,7 +135,11 @@ void ThreadPool::shutdown()
return;
std::unique_lock< std::mutex > aGuard( maMutex );
shutdownLocked(aGuard);
}
void ThreadPool::shutdownLocked(std::unique_lock<std::mutex>& aGuard)
{
if( maWorkers.empty() )
{ // no threads at all -> execute the work in-line
ThreadTask *pTask;
......@@ -173,6 +176,14 @@ void ThreadPool::pushTask( ThreadTask *pTask )
{
std::unique_lock< std::mutex > aGuard( maMutex );
mbTerminate = false;
if (maWorkers.size() < mnWorkers && maWorkers.size() <= maTasks.size())
{
maWorkers.push_back( new ThreadWorker( this ) );
maWorkers.back()->launch();
}
pTask->mpTag->onTaskPushed();
maTasks.insert( maTasks.begin(), pTask );
......@@ -217,6 +228,14 @@ void ThreadPool::waitUntilDone(const std::shared_ptr<ThreadTaskTag>& rTag)
}
rTag->waitUntilDone();
{
std::unique_lock< std::mutex > aGuard( maMutex );
if (maTasks.empty()) // check if there are still tasks from another tag
{
shutdownLocked(aGuard);
}
}
}
std::shared_ptr<ThreadTaskTag> ThreadPool::createThreadTaskTag()
......
......@@ -68,7 +68,7 @@ public:
void waitUntilDone(const std::shared_ptr<ThreadTaskTag>&);
/// return the number of live worker threads
sal_Int32 getWorkerCount() const { return maWorkers.size(); }
sal_Int32 getWorkerCount() const { return mnWorkers; }
/// wait until all work is completed, then join all threads
void shutdown();
......@@ -85,11 +85,13 @@ private:
@return a new task to perform, or NULL if list empty or terminated
*/
ThreadTask *popWorkLocked( std::unique_lock< std::mutex > & rGuard, bool bWait );
void shutdownLocked(std::unique_lock<std::mutex>&);
/// signalled when all in-progress tasks are complete
std::mutex maMutex;
std::condition_variable maTasksChanged;
bool mbTerminate;
std::size_t mnWorkers;
std::vector< ThreadTask * > maTasks;
std::vector< rtl::Reference< ThreadWorker > > maWorkers;
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment