mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 21:53:41 +00:00
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:
parent
81599a14a0
commit
8aa12ca468
4 changed files with 36 additions and 20 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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,9 +597,14 @@ 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};
|
||||||
if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) {
|
if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
task->prepare();
|
||||||
if (tid == this->tid) {
|
auto i = idx++;
|
||||||
task->prepare();
|
for (unsigned n = 0; n < threadCnt; ++n) {
|
||||||
auto i = idx++;
|
if (taskQueues[(i + n) % threadCnt].tryPush(task)) return;
|
||||||
for (unsigned n = 0; n < threadCnt; ++n) {
|
|
||||||
if (taskQueues[(i + n) % threadCnt].tryPush(task)) return;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
taskQueues[i % threadCnt].push(task);
|
||||||
//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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue