common: ++loader thread safety

fortitfy the thread safety
in multi-threaded resource loading.

issue: https://github.com/thorvg/thorvg/issues/3306
This commit is contained in:
Hermet Park 2025-03-24 18:02:09 +09:00 committed by Mira Grudzinska
parent 72a1b0339b
commit 6708fae17b
5 changed files with 12 additions and 43 deletions

View file

@ -201,9 +201,7 @@ void LottieFont::prepare()
{ {
if (!data.b64src || !name) return; if (!data.b64src || !name) return;
TaskScheduler::async(false);
Text::load(name, data.b64src, data.size, "ttf", false); Text::load(name, data.b64src, data.size, "ttf", false);
TaskScheduler::async(true);
} }
@ -214,13 +212,9 @@ void LottieImage::prepare()
auto picture = Picture::gen().release(); auto picture = Picture::gen().release();
//force to load a picture on the same thread //force to load a picture on the same thread
TaskScheduler::async(false);
if (data.size > 0) picture->load((const char*)data.b64Data, data.size, data.mimeType, false); if (data.size > 0) picture->load((const char*)data.b64Data, data.size, data.mimeType, false);
else picture->load(data.path); else picture->load(data.path);
TaskScheduler::async(true);
picture->size(data.width, data.height); picture->size(data.width, data.height);
PP(picture)->ref(); PP(picture)->ref();
@ -231,13 +225,11 @@ void LottieImage::prepare()
void LottieImage::update() void LottieImage::update()
{ {
//Update the picture data //Update the picture data
TaskScheduler::async(false);
for (auto p = pooler.begin(); p < pooler.end(); ++p) { for (auto p = pooler.begin(); p < pooler.end(); ++p) {
if (data.size > 0) (*p)->load((const char*)data.b64Data, data.size, data.mimeType, false); if (data.size > 0) (*p)->load((const char*)data.b64Data, data.size, data.mimeType, false);
else (*p)->load(data.path); else (*p)->load(data.path);
(*p)->size(data.width, data.height); (*p)->size(data.width, data.height);
} }
TaskScheduler::async(true);
} }

View file

@ -573,8 +573,6 @@ static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode*
if (!node->node.image.href || !strlen(node->node.image.href)) return nullptr; if (!node->node.image.href || !strlen(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;
@ -586,14 +584,12 @@ 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;
} }
} }
@ -605,7 +601,6 @@ 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;
@ -614,13 +609,10 @@ static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode*
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) { if (picture->load(imagePath) != Result::Success) {
TaskScheduler::async(true);
return nullptr; 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) {
@ -931,9 +923,7 @@ static void _loadFonts(Array<FontFace>& fonts)
auto size = b64Decode(p->src + shift, p->srcLen - shift, &p->decoded); auto size = b64Decode(p->src + shift, p->srcLen - shift, &p->decoded);
TaskScheduler::async(false);
if (Text::load(p->name, p->decoded, size) != Result::Success) TVGERR("SVG", "Error while loading the ttf font named \"%s\".", p->name); if (Text::load(p->name, p->decoded, size) != Result::Success) TVGERR("SVG", "Error while loading the ttf font named \"%s\".", p->name);
TaskScheduler::async(true);
} }
} }

View file

@ -68,7 +68,7 @@ uintptr_t HASH_KEY(const char* data)
ColorSpace ImageLoader::cs = ColorSpace::ARGB8888; ColorSpace ImageLoader::cs = ColorSpace::ARGB8888;
static Key key; static Key _key;
static Inlist<LoadModule> _activeLoaders; static Inlist<LoadModule> _activeLoaders;
@ -215,7 +215,7 @@ static LoadModule* _findByType(const string& mimeType)
static LoadModule* _findFromCache(const string& path) static LoadModule* _findFromCache(const string& path)
{ {
ScopedLock lock(key); ScopedLock lock(_key);
auto loader = _activeLoaders.head; auto loader = _activeLoaders.head;
@ -235,11 +235,10 @@ static LoadModule* _findFromCache(const char* data, uint32_t size, const string&
auto type = _convert(mimeType); auto type = _convert(mimeType);
if (type == FileType::Unknown) return nullptr; if (type == FileType::Unknown) return nullptr;
ScopedLock lock(key);
auto loader = _activeLoaders.head;
auto key = HASH_KEY(data); auto key = HASH_KEY(data);
ScopedLock lock(_key);
auto loader = _activeLoaders.head;
while (loader) { while (loader) {
if (loader->type == type && loader->hashkey == key) { if (loader->type == type && loader->hashkey == key) {
++loader->sharing; ++loader->sharing;
@ -285,9 +284,9 @@ bool LoaderMgr::term()
bool LoaderMgr::retrieve(LoadModule* loader) bool LoaderMgr::retrieve(LoadModule* loader)
{ {
if (!loader) return false; if (!loader) return false;
if (loader->close()) { if (loader->close()) {
if (loader->cached()) { if (loader->cached()) {
ScopedLock lock(key);
_activeLoaders.remove(loader); _activeLoaders.remove(loader);
} }
delete(loader); delete(loader);
@ -316,7 +315,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
loader->hashpath = strdup(path.c_str()); loader->hashpath = strdup(path.c_str());
loader->pathcache = true; loader->pathcache = true;
{ {
ScopedLock lock(key); ScopedLock lock(_key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
} }
@ -332,7 +331,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
loader->hashpath = strdup(path.c_str()); loader->hashpath = strdup(path.c_str());
loader->pathcache = true; loader->pathcache = true;
{ {
ScopedLock lock(key); ScopedLock lock(_key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
} }
@ -390,7 +389,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
if (loader->open(data, size, copy)) { if (loader->open(data, size, copy)) {
if (allowCache) { if (allowCache) {
loader->hashkey = HASH_KEY(data); loader->hashkey = HASH_KEY(data);
ScopedLock lock(key); ScopedLock lock(_key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
return loader; return loader;
@ -407,7 +406,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
if (loader->open(data, size, copy)) { if (loader->open(data, size, copy)) {
if (allowCache) { if (allowCache) {
loader->hashkey = HASH_KEY(data); loader->hashkey = HASH_KEY(data);
ScopedLock lock(key); ScopedLock lock(_key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
return loader; return loader;
@ -433,7 +432,7 @@ LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool
if (loader->open(data, w, h, copy)) { if (loader->open(data, w, h, copy)) {
if (!copy) { if (!copy) {
loader->hashkey = HASH_KEY((const char*)data); loader->hashkey = HASH_KEY((const char*)data);
ScopedLock lock(key); ScopedLock lock(_key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
return loader; return loader;
@ -455,7 +454,7 @@ LoadModule* LoaderMgr::loader(const char* name, const char* data, uint32_t size,
if (loader->open(data, size, copy)) { if (loader->open(data, size, copy)) {
loader->hashpath = strdup(name); loader->hashpath = strdup(name);
loader->pathcache = true; loader->pathcache = true;
ScopedLock lock(key); ScopedLock lock(_key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
return loader; return loader;
} }

View file

@ -40,8 +40,6 @@ static TaskSchedulerImpl* inst = nullptr;
#ifdef THORVG_THREAD_SUPPORT #ifdef THORVG_THREAD_SUPPORT
static thread_local bool _async = true;
struct TaskQueue { struct TaskQueue {
Inlist<Task> taskDeque; Inlist<Task> taskDeque;
mutex mtx; mutex mtx;
@ -157,7 +155,7 @@ struct TaskSchedulerImpl
void request(Task* task) void request(Task* task)
{ {
//Async //Async
if (threads.count > 0 && _async) { if (threads.count > 0) {
task->prepare(); task->prepare();
auto i = idx++; auto i = idx++;
for (uint32_t n = 0; n < threads.count; ++n) { for (uint32_t n = 0; n < threads.count; ++n) {
@ -178,8 +176,6 @@ struct TaskSchedulerImpl
#else //THORVG_THREAD_SUPPORT #else //THORVG_THREAD_SUPPORT
static bool _async = true;
struct TaskSchedulerImpl struct TaskSchedulerImpl
{ {
TaskSchedulerImpl(TVG_UNUSED uint32_t threadCnt) {} TaskSchedulerImpl(TVG_UNUSED uint32_t threadCnt) {}
@ -219,11 +215,4 @@ uint32_t TaskScheduler::threads()
{ {
if (inst) return inst->threadCnt(); if (inst) return inst->threadCnt();
return 0; return 0;
}
void TaskScheduler::async(bool on)
{
//toggle async tasking for each thread on/off
_async = on;
} }

View file

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