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 Hermet Park
parent 02baa6b462
commit 56d5396b7a
5 changed files with 14 additions and 42 deletions

View file

@ -318,9 +318,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);
} }
@ -331,13 +329,9 @@ void LottieImage::prepare()
auto picture = Picture::gen(); auto picture = Picture::gen();
//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); if (data.size > 0) picture->load((const char*)data.b64Data, data.size, data.mimeType);
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);
picture->ref(); picture->ref();
@ -348,13 +342,11 @@ void LottieImage::prepare()
void LottieImage::update() void LottieImage::update()
{ {
//Update the picture data //Update the picture data
TaskScheduler::async(false);
ARRAY_FOREACH(p, pooler) { ARRAY_FOREACH(p, pooler) {
if (data.size > 0) (*p)->load((const char*)data.b64Data, data.size, data.mimeType); if (data.size > 0) (*p)->load((const char*)data.b64Data, data.size, data.mimeType);
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

@ -629,8 +629,6 @@ static Paint* _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const
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;
@ -642,14 +640,12 @@ static Paint* _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const
auto size = b64Decode(href, strlen(href), &decoded); auto size = b64Decode(href, strlen(href), &decoded);
if (picture->load(decoded, size, mimetype) != Result::Success) { if (picture->load(decoded, size, mimetype) != Result::Success) {
tvg::free(decoded); tvg::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) != Result::Success) { if (picture->load(decoded, size, mimetype) != Result::Success) {
tvg::free(decoded); tvg::free(decoded);
TaskScheduler::async(true);
return nullptr; return nullptr;
} }
} }
@ -661,7 +657,6 @@ static Paint* _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const
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;
@ -670,13 +665,10 @@ static Paint* _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const
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.c_str()) != Result::Success) { if (picture->load(imagePath.c_str()) != Result::Success) {
TaskScheduler::async(true);
return nullptr; return nullptr;
} }
} }
TaskScheduler::async(true);
float w, h; float w, h;
Matrix m; Matrix m;
if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) { if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) {
@ -977,9 +969,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

@ -63,7 +63,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;
@ -202,8 +202,7 @@ static LoadModule* _findByType(const char* mimeType)
static LoadModule* _findFromCache(const char* filename) static LoadModule* _findFromCache(const char* filename)
{ {
ScopedLock lock(key); ScopedLock lock(_key);
INLIST_FOREACH(_activeLoaders, loader) { INLIST_FOREACH(_activeLoaders, loader) {
if (loader->cached && loader->hashpath && !strcmp(loader->hashpath, filename)) { if (loader->cached && loader->hashpath && !strcmp(loader->hashpath, filename)) {
++loader->sharing; ++loader->sharing;
@ -219,9 +218,10 @@ static LoadModule* _findFromCache(const char* data, uint32_t size, const char* m
auto type = _convert(mimeType); auto type = _convert(mimeType);
if (type == FileType::Unknown) return nullptr; if (type == FileType::Unknown) return nullptr;
ScopedLock lock(key);
auto key = HASH_KEY(data); auto key = HASH_KEY(data);
ScopedLock lock(_key);
INLIST_FOREACH(_activeLoaders, loader) { INLIST_FOREACH(_activeLoaders, loader) {
if (loader->type == type && loader->hashkey == key) { if (loader->type == type && loader->hashkey == key) {
++loader->sharing; ++loader->sharing;
@ -259,9 +259,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);
@ -289,7 +289,7 @@ LoadModule* LoaderMgr::loader(const char* filename, bool* invalid)
if (allowCache) { if (allowCache) {
loader->cache(duplicate(filename)); loader->cache(duplicate(filename));
{ {
ScopedLock lock(key); ScopedLock lock(_key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
} }
@ -304,7 +304,7 @@ LoadModule* LoaderMgr::loader(const char* filename, bool* invalid)
if (allowCache) { if (allowCache) {
loader->cache(duplicate(filename)); loader->cache(duplicate(filename));
{ {
ScopedLock lock(key); ScopedLock lock(_key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
} }
@ -347,7 +347,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const char* mimeT
if (loader->open(data, size, rpath, copy)) { if (loader->open(data, size, rpath, copy)) {
if (allowCache) { if (allowCache) {
loader->cache(HASH_KEY(data)); loader->cache(HASH_KEY(data));
ScopedLock lock(key); ScopedLock lock(_key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
return loader; return loader;
@ -364,7 +364,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const char* mimeT
if (loader->open(data, size, rpath, copy)) { if (loader->open(data, size, rpath, copy)) {
if (allowCache) { if (allowCache) {
loader->cache(HASH_KEY(data)); loader->cache(HASH_KEY(data));
ScopedLock lock(key); ScopedLock lock(_key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
return loader; return loader;
@ -390,7 +390,7 @@ LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, Colo
if (loader->open(data, w, h, cs, copy)) { if (loader->open(data, w, h, cs, copy)) {
if (!copy) { if (!copy) {
loader->cache(HASH_KEY((const char*)data)); loader->cache(HASH_KEY((const char*)data));
ScopedLock lock(key); ScopedLock lock(_key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
return loader; return loader;
@ -412,7 +412,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->name = duplicate(name); loader->name = duplicate(name);
loader->cached = true; //force it. loader->cached = true; //force it.
ScopedLock lock(key); ScopedLock lock(_key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
return loader; return loader;
} }
@ -426,6 +426,7 @@ LoadModule* LoaderMgr::loader(const char* name, const char* data, uint32_t size,
LoadModule* LoaderMgr::font(const char* name) LoadModule* LoaderMgr::font(const char* name)
{ {
ScopedLock lock(_key);
INLIST_FOREACH(_activeLoaders, loader) { INLIST_FOREACH(_activeLoaders, loader) {
if (loader->type != FileType::Ttf) continue; if (loader->type != FileType::Ttf) continue;
if (loader->cached && tvg::equal(name, static_cast<FontLoader*>(loader)->name)) { if (loader->cached && tvg::equal(name, static_cast<FontLoader*>(loader)->name)) {
@ -439,6 +440,7 @@ LoadModule* LoaderMgr::font(const char* name)
LoadModule* LoaderMgr::anyfont() LoadModule* LoaderMgr::anyfont()
{ {
ScopedLock lock(_key);
INLIST_FOREACH(_activeLoaders, loader) { INLIST_FOREACH(_activeLoaders, loader) {
if (loader->cached && loader->type == FileType::Ttf) { if (loader->cached && loader->type == FileType::Ttf) {
++loader->sharing; ++loader->sharing;

View file

@ -33,8 +33,6 @@ namespace tvg {
#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;
@ -150,7 +148,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) {
@ -171,8 +169,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,13 +215,6 @@ uint32_t TaskScheduler::threads()
} }
void TaskScheduler::async(bool on)
{
//toggle async tasking for each thread on/off
_async = on;
}
bool TaskScheduler::onthread() bool TaskScheduler::onthread()
{ {
return _tid != tid(); return _tid != tid();

View file

@ -112,7 +112,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);
static bool onthread(); //figure out whether on worker thread or not static bool onthread(); //figure out whether on worker thread or not
static ThreadID tid(); static ThreadID tid();
}; };