2015-06-10 16:15:31 +00:00
|
|
|
#include "workqueue.hpp"
|
|
|
|
|
2018-08-14 15:42:41 +00:00
|
|
|
#include <components/debug/debuglog.hpp>
|
2016-02-06 21:29:06 +00:00
|
|
|
|
2020-06-24 17:28:54 +00:00
|
|
|
#include <numeric>
|
|
|
|
|
2015-06-10 16:15:31 +00:00
|
|
|
namespace SceneUtil
|
|
|
|
{
|
|
|
|
|
2016-02-06 21:29:06 +00:00
|
|
|
void WorkItem::waitTillDone()
|
2015-06-10 16:15:31 +00:00
|
|
|
{
|
2020-06-24 17:28:54 +00:00
|
|
|
if (mDone)
|
2015-06-10 16:15:31 +00:00
|
|
|
return;
|
|
|
|
|
2020-06-24 17:28:54 +00:00
|
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
|
|
|
while (!mDone)
|
2015-06-10 16:15:31 +00:00
|
|
|
{
|
2020-06-24 17:28:54 +00:00
|
|
|
mCondition.wait(lock);
|
2015-06-10 16:15:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-06 21:29:06 +00:00
|
|
|
void WorkItem::signalDone()
|
2015-06-10 16:15:31 +00:00
|
|
|
{
|
2015-06-14 19:04:59 +00:00
|
|
|
{
|
2020-06-24 17:28:54 +00:00
|
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
|
|
|
mDone = true;
|
2015-06-14 19:04:59 +00:00
|
|
|
}
|
2020-06-24 17:28:54 +00:00
|
|
|
mCondition.notify_all();
|
2015-06-10 16:15:31 +00:00
|
|
|
}
|
|
|
|
|
2016-02-06 21:29:06 +00:00
|
|
|
bool WorkItem::isDone() const
|
2015-06-10 16:15:31 +00:00
|
|
|
{
|
2020-06-24 17:28:54 +00:00
|
|
|
return mDone;
|
2015-06-10 16:15:31 +00:00
|
|
|
}
|
|
|
|
|
2021-05-25 11:44:40 +00:00
|
|
|
WorkQueue::WorkQueue(std::size_t workerThreads)
|
2015-06-10 16:15:31 +00:00
|
|
|
: mIsReleased(false)
|
|
|
|
{
|
2021-05-25 11:44:40 +00:00
|
|
|
start(workerThreads);
|
2015-06-10 16:15:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
WorkQueue::~WorkQueue()
|
2021-05-25 11:44:40 +00:00
|
|
|
{
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WorkQueue::start(std::size_t workerThreads)
|
|
|
|
{
|
Reset mIsReleased before starting threads
To fix TSAN warning:
WARNING: ThreadSanitizer: data race (pid=68597)
Write of size 1 at 0x7b3800079234 by main thread:
#0 SceneUtil::WorkQueue::start(unsigned long) /home/elsid/dev/openmw/components/sceneutil/workqueue.cpp:51 (openmw+0x10daa10)
#1 SceneUtil::WorkQueue::WorkQueue(unsigned long) /home/elsid/dev/openmw/components/sceneutil/workqueue.cpp:39 (openmw+0x10dad97)
#2 OMW::Engine::prepareEngine(Settings::Manager&) /home/elsid/dev/openmw/apps/openmw/engine.cpp:700 (openmw+0xf7cb5a)
#3 OMW::Engine::go() /home/elsid/dev/openmw/apps/openmw/engine.cpp:949 (openmw+0xf82688)
#4 runApplication(int, char**) /home/elsid/dev/openmw/apps/openmw/main.cpp:316 (openmw+0xf62611)
#5 wrapApplication(int (*)(int, char**), int, char**, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/elsid/dev/openmw/components/debug/debugging.cpp:205 (openmw+0x125df1c)
#6 main /home/elsid/dev/openmw/apps/openmw/main.cpp:328 (openmw+0x596323)
Previous read of size 1 at 0x7b3800079234 by thread T10 (mutexes: write M19275778865205896):
#0 SceneUtil::WorkQueue::removeWorkItem() /home/elsid/dev/openmw/components/sceneutil/workqueue.cpp:86 (openmw+0x10d9e51)
#1 SceneUtil::WorkThread::run() /home/elsid/dev/openmw/components/sceneutil/workqueue.cpp:127 (openmw+0x10da52a)
#2 operator() /home/elsid/dev/openmw/components/sceneutil/workqueue.cpp:114 (openmw+0x10da664)
#3 __invoke_impl<void, SceneUtil::WorkThread::WorkThread(SceneUtil::WorkQueue&)::<lambda()> > /usr/include/c++/11.1.0/bits/invoke.h:61 (openmw+0x10da664)
#4 __invoke<SceneUtil::WorkThread::WorkThread(SceneUtil::WorkQueue&)::<lambda()> > /usr/include/c++/11.1.0/bits/invoke.h:96 (openmw+0x10da664)
#5 _M_invoke<0> /usr/include/c++/11.1.0/bits/std_thread.h:253 (openmw+0x10da664)
#6 operator() /usr/include/c++/11.1.0/bits/std_thread.h:260 (openmw+0x10da664)
#7 _M_run /usr/include/c++/11.1.0/bits/std_thread.h:211 (openmw+0x10da664)
#8 execute_native_thread_routine /build/gcc/src/gcc/libstdc++-v3/src/c++11/thread.cc:82 (libstdc++.so.6+0xd33c3)
Location is heap block of size 216 at 0x7b3800079220 allocated by main thread:
#0 operator new(unsigned long) /build/gcc/src/gcc/libsanitizer/tsan/tsan_new_delete.cpp:64 (libtsan.so.0+0x91824)
#1 OMW::Engine::prepareEngine(Settings::Manager&) /home/elsid/dev/openmw/apps/openmw/engine.cpp:700 (openmw+0xf7cb4c)
#2 OMW::Engine::go() /home/elsid/dev/openmw/apps/openmw/engine.cpp:949 (openmw+0xf82688)
#3 runApplication(int, char**) /home/elsid/dev/openmw/apps/openmw/main.cpp:316 (openmw+0xf62611)
#4 wrapApplication(int (*)(int, char**), int, char**, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/elsid/dev/openmw/components/debug/debugging.cpp:205 (openmw+0x125df1c)
#5 main /home/elsid/dev/openmw/apps/openmw/main.cpp:328 (openmw+0x596323)
Mutex M19275778865205896 is already destroyed.
Thread T10 (tid=68609, running) created by main thread at:
#0 pthread_create /build/gcc/src/gcc/libsanitizer/tsan/tsan_interceptors_posix.cpp:969 (libtsan.so.0+0x61c3a)
#1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) /build/gcc/src/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/gthr-default.h:663 (libstdc++.so.6+0xd36aa)
#2 std::_MakeUniq<SceneUtil::WorkThread>::__single_object std::make_unique<SceneUtil::WorkThread, SceneUtil::WorkQueue&>(SceneUtil::WorkQueue&) /usr/include/c++/11.1.0/bits/unique_ptr.h:962 (openmw+0x10da987)
#3 SceneUtil::WorkQueue::start(unsigned long) /home/elsid/dev/openmw/components/sceneutil/workqueue.cpp:50 (openmw+0x10da987)
#4 SceneUtil::WorkQueue::WorkQueue(unsigned long) /home/elsid/dev/openmw/components/sceneutil/workqueue.cpp:39 (openmw+0x10dad97)
#5 OMW::Engine::prepareEngine(Settings::Manager&) /home/elsid/dev/openmw/apps/openmw/engine.cpp:700 (openmw+0xf7cb5a)
#6 OMW::Engine::go() /home/elsid/dev/openmw/apps/openmw/engine.cpp:949 (openmw+0xf82688)
#7 runApplication(int, char**) /home/elsid/dev/openmw/apps/openmw/main.cpp:316 (openmw+0xf62611)
#8 wrapApplication(int (*)(int, char**), int, char**, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/elsid/dev/openmw/components/debug/debugging.cpp:205 (openmw+0x125df1c)
#9 main /home/elsid/dev/openmw/apps/openmw/main.cpp:328 (openmw+0x596323)
2021-08-07 17:29:08 +00:00
|
|
|
{
|
|
|
|
const std::lock_guard lock(mMutex);
|
|
|
|
mIsReleased = false;
|
|
|
|
}
|
2021-05-25 11:44:40 +00:00
|
|
|
while (mThreads.size() < workerThreads)
|
|
|
|
mThreads.emplace_back(std::make_unique<WorkThread>(*this));
|
|
|
|
}
|
|
|
|
|
|
|
|
void WorkQueue::stop()
|
2015-06-10 16:15:31 +00:00
|
|
|
{
|
|
|
|
{
|
2020-06-24 17:28:54 +00:00
|
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
2016-01-03 17:20:34 +00:00
|
|
|
while (!mQueue.empty())
|
2016-03-28 22:25:51 +00:00
|
|
|
mQueue.pop_back();
|
2015-06-10 16:15:31 +00:00
|
|
|
mIsReleased = true;
|
2020-06-24 17:28:54 +00:00
|
|
|
mCondition.notify_all();
|
2015-06-10 16:15:31 +00:00
|
|
|
}
|
|
|
|
|
2020-06-24 17:28:54 +00:00
|
|
|
mThreads.clear();
|
2015-06-10 16:15:31 +00:00
|
|
|
}
|
|
|
|
|
2016-03-28 22:25:51 +00:00
|
|
|
void WorkQueue::addWorkItem(osg::ref_ptr<WorkItem> item, bool front)
|
2015-06-10 16:15:31 +00:00
|
|
|
{
|
2016-02-06 21:29:06 +00:00
|
|
|
if (item->isDone())
|
|
|
|
{
|
2018-08-14 15:42:41 +00:00
|
|
|
Log(Debug::Error) << "Error: trying to add a work item that is already completed";
|
2016-02-06 21:29:06 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-24 17:28:54 +00:00
|
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
2016-03-28 22:25:51 +00:00
|
|
|
if (front)
|
2021-07-10 10:11:48 +00:00
|
|
|
mQueue.push_front(std::move(item));
|
2016-03-28 22:25:51 +00:00
|
|
|
else
|
2021-07-10 10:11:48 +00:00
|
|
|
mQueue.push_back(std::move(item));
|
2020-06-24 17:28:54 +00:00
|
|
|
mCondition.notify_one();
|
2015-06-10 16:15:31 +00:00
|
|
|
}
|
|
|
|
|
2016-02-06 21:29:06 +00:00
|
|
|
osg::ref_ptr<WorkItem> WorkQueue::removeWorkItem()
|
2015-06-10 16:15:31 +00:00
|
|
|
{
|
2020-06-24 17:28:54 +00:00
|
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
2016-01-03 17:20:34 +00:00
|
|
|
while (mQueue.empty() && !mIsReleased)
|
2015-06-10 16:15:31 +00:00
|
|
|
{
|
2020-06-24 17:28:54 +00:00
|
|
|
mCondition.wait(lock);
|
2015-06-10 16:15:31 +00:00
|
|
|
}
|
2016-02-22 18:06:12 +00:00
|
|
|
if (!mQueue.empty())
|
2015-06-10 16:15:31 +00:00
|
|
|
{
|
2021-07-10 10:11:48 +00:00
|
|
|
osg::ref_ptr<WorkItem> item = std::move(mQueue.front());
|
2016-03-28 22:25:51 +00:00
|
|
|
mQueue.pop_front();
|
2015-06-10 16:15:31 +00:00
|
|
|
return item;
|
|
|
|
}
|
2021-07-10 10:19:59 +00:00
|
|
|
return nullptr;
|
2015-06-10 16:15:31 +00:00
|
|
|
}
|
|
|
|
|
2017-02-22 01:18:18 +00:00
|
|
|
unsigned int WorkQueue::getNumItems() const
|
|
|
|
{
|
2020-06-24 17:28:54 +00:00
|
|
|
std::unique_lock<std::mutex> lock(mMutex);
|
2017-02-22 01:18:18 +00:00
|
|
|
return mQueue.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int WorkQueue::getNumActiveThreads() const
|
|
|
|
{
|
2020-06-24 17:28:54 +00:00
|
|
|
return std::accumulate(mThreads.begin(), mThreads.end(), 0u,
|
|
|
|
[] (auto r, const auto& t) { return r + t->isActive(); });
|
2017-02-22 01:18:18 +00:00
|
|
|
}
|
|
|
|
|
2020-06-24 17:28:54 +00:00
|
|
|
WorkThread::WorkThread(WorkQueue& workQueue)
|
|
|
|
: mWorkQueue(&workQueue)
|
2017-10-15 15:03:11 +00:00
|
|
|
, mActive(false)
|
2020-06-24 17:28:54 +00:00
|
|
|
, mThread([this] { run(); })
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
WorkThread::~WorkThread()
|
2015-06-10 16:15:31 +00:00
|
|
|
{
|
2020-06-24 17:28:54 +00:00
|
|
|
mThread.join();
|
2015-06-10 16:15:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void WorkThread::run()
|
|
|
|
{
|
|
|
|
while (true)
|
|
|
|
{
|
2016-02-06 21:29:06 +00:00
|
|
|
osg::ref_ptr<WorkItem> item = mWorkQueue->removeWorkItem();
|
2015-06-10 16:15:31 +00:00
|
|
|
if (!item)
|
|
|
|
return;
|
2017-02-22 01:18:18 +00:00
|
|
|
mActive = true;
|
2015-06-10 16:15:31 +00:00
|
|
|
item->doWork();
|
2016-02-06 21:29:06 +00:00
|
|
|
item->signalDone();
|
2017-02-22 01:18:18 +00:00
|
|
|
mActive = false;
|
2015-06-10 16:15:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-22 01:18:18 +00:00
|
|
|
bool WorkThread::isActive() const
|
|
|
|
{
|
|
|
|
return mActive;
|
|
|
|
}
|
|
|
|
|
2015-06-10 16:15:31 +00:00
|
|
|
}
|