taskschduler: fix a regression deadlock issue

This fix introduces a workaround to enforce synchronous tasking on worker threads.
Sometimes, out of threads get stuck in a deadlock condition.

@Issue: https://github.com/thorvg/thorvg/issues/1636
This commit is contained in:
Hermet Park 2023-09-18 11:38:34 +09:00 committed by Hermet Park
parent 81599a14a0
commit 8aa12ca468
4 changed files with 36 additions and 20 deletions

View file

@ -25,6 +25,7 @@
#include "tvgShape.h" #include "tvgShape.h"
#include "tvgLottieModel.h" #include "tvgLottieModel.h"
#include "tvgLottieBuilder.h" #include "tvgLottieBuilder.h"
#include "tvgTaskScheduler.h"
/************************************************************************/ /************************************************************************/
@ -577,11 +578,17 @@ static void _updateImage(LottieGroup* parent, LottieImage* image, int32_t frameN
if (!picture) { if (!picture) {
picture = Picture::gen().release(); picture = Picture::gen().release();
//force to load a picture on the same thread
TaskScheduler::async(false);
if (image->size > 0) { if (image->size > 0) {
if (picture->load((const char*)image->b64Data, image->size, image->mimeType, false) != Result::Success) return; if (picture->load((const char*)image->b64Data, image->size, image->mimeType, false) != Result::Success) return;
} else { } else {
if (picture->load(image->path) != Result::Success) return; if (picture->load(image->path) != Result::Success) return;
} }
TaskScheduler::async(true);
} }
if (ctx.propagator) { if (ctx.propagator) {

View file

@ -550,12 +550,15 @@ static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mim
return false; return false;
} }
#include "tvgTaskScheduler.h"
static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath) static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath)
{ {
if (!node->node.image.href) return nullptr; if (!node->node.image.href) return nullptr;
auto picture = Picture::gen(); auto picture = Picture::gen();
TaskScheduler::async(false); //force to load a picture on the same thread
const char* href = node->node.image.href; const char* href = node->node.image.href;
if (!strncmp(href, "data:", sizeof("data:") - 1)) { if (!strncmp(href, "data:", sizeof("data:") - 1)) {
href += sizeof("data:") - 1; href += sizeof("data:") - 1;
@ -567,12 +570,14 @@ static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode*
auto size = b64Decode(href, strlen(href), &decoded); auto size = b64Decode(href, strlen(href), &decoded);
if (picture->load(decoded, size, mimetype, false) != Result::Success) { if (picture->load(decoded, size, mimetype, false) != Result::Success) {
free(decoded); free(decoded);
TaskScheduler::async(true);
return nullptr; return nullptr;
} }
} else { } else {
auto size = svgUtilURLDecode(href, &decoded); auto size = svgUtilURLDecode(href, &decoded);
if (picture->load(decoded, size, mimetype, false) != Result::Success) { if (picture->load(decoded, size, mimetype, false) != Result::Success) {
free(decoded); free(decoded);
TaskScheduler::async(true);
return nullptr; return nullptr;
} }
} }
@ -584,6 +589,7 @@ static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode*
const char *dot = strrchr(href, '.'); const char *dot = strrchr(href, '.');
if (dot && !strcmp(dot, ".svg")) { if (dot && !strcmp(dot, ".svg")) {
TVGLOG("SVG", "Embedded svg file is disabled."); TVGLOG("SVG", "Embedded svg file is disabled.");
TaskScheduler::async(true);
return nullptr; return nullptr;
} }
string imagePath = href; string imagePath = href;
@ -591,8 +597,13 @@ static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode*
auto last = svgPath.find_last_of("/"); auto last = svgPath.find_last_of("/");
imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1)) + imagePath; imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1)) + imagePath;
} }
if (picture->load(imagePath) != Result::Success) return nullptr; if (picture->load(imagePath) != Result::Success) {
TaskScheduler::async(true);
return nullptr;
} }
}
TaskScheduler::async(true);
float w, h; float w, h;
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1}; Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
@ -605,6 +616,7 @@ static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode*
picture->transform(m); picture->transform(m);
_applyComposition(loaderData, picture.get(), node, vBox, svgPath); _applyComposition(loaderData, picture.get(), node, vBox, svgPath);
return picture; return picture;
} }

View file

@ -100,17 +100,18 @@ struct TaskQueue {
}; };
static thread_local bool _async = true; //toggle async tasking for each thread on/off
struct TaskSchedulerImpl struct TaskSchedulerImpl
{ {
uint32_t threadCnt; uint32_t threadCnt;
vector<thread> threads; vector<thread> threads;
vector<TaskQueue> taskQueues; vector<TaskQueue> taskQueues;
atomic<uint32_t> idx{0}; atomic<uint32_t> idx{0};
thread::id tid;
TaskSchedulerImpl(unsigned threadCnt) : threadCnt(threadCnt), taskQueues(threadCnt) TaskSchedulerImpl(unsigned threadCnt) : threadCnt(threadCnt), taskQueues(threadCnt)
{ {
tid = this_thread::get_id();
threads.reserve(threadCnt); threads.reserve(threadCnt);
for (unsigned i = 0; i < threadCnt; ++i) { for (unsigned i = 0; i < threadCnt; ++i) {
@ -146,24 +147,13 @@ struct TaskSchedulerImpl
void request(Task* task) void request(Task* task)
{ {
//Async //Async
if (threadCnt > 0) { if (threadCnt > 0 && _async) {
auto tid = this_thread::get_id();
if (tid == this->tid) {
task->prepare(); task->prepare();
auto i = idx++; auto i = idx++;
for (unsigned n = 0; n < threadCnt; ++n) { for (unsigned n = 0; n < threadCnt; ++n) {
if (taskQueues[(i + n) % threadCnt].tryPush(task)) return; if (taskQueues[(i + n) % threadCnt].tryPush(task)) return;
} }
taskQueues[i % threadCnt].push(task); taskQueues[i % threadCnt].push(task);
//Not thread-safety now, it's requested from a worker-thread
} else {
for (unsigned i = 0; i < threadCnt; ++i) {
if (tid == threads[i].get_id()) {
task->prepare();
(*task)(i + 1);
}
}
}
//Sync //Sync
} else { } else {
task->run(0); task->run(0);
@ -205,3 +195,9 @@ unsigned TaskScheduler::threads()
if (inst) return inst->threadCnt; if (inst) return inst->threadCnt;
return 0; return 0;
} }
void TaskScheduler::async(bool on)
{
_async = on;
}

View file

@ -38,6 +38,7 @@ struct TaskScheduler
static void init(unsigned threads); static void init(unsigned threads);
static void term(); static void term();
static void request(Task* task); static void request(Task* task);
static void async(bool on);
}; };
struct Task struct Task