diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index 5e95be956d..59b21e365b 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -38,6 +38,10 @@ #include "../menu/menu_entries.h" #endif +#ifdef HAVE_THREADS +#include +#endif + #include "badges.h" #include "cheevos.h" #include "var.h" @@ -219,6 +223,11 @@ typedef struct typedef struct { + retro_task_t* task; +#ifdef HAVE_THREADS + slock_t* task_lock; +#endif + cheevos_console_t console_id; bool core_supports; bool addrs_patched; @@ -247,16 +256,24 @@ typedef struct static cheevos_locals_t cheevos_locals = { + /* task */ NULL, +#ifdef HAVE_THREADS + /* task_lock */ NULL, +#endif + /* console_id */ CHEEVOS_CONSOLE_NONE, /* core_supports */ true, /* addrs_patched */ false, /* add_buffer */ 0, /* add_hits */ 0, + /* core */ {NULL, 0}, /* unofficial */ {NULL, 0}, /* leaderboards */ NULL, /* lboard_count */ 0, + /* token */ {0}, + { /* meminfo[0] */ {NULL, 0, 0}, /* meminfo[1] */ {NULL, 0, 0}, @@ -265,9 +282,17 @@ static cheevos_locals_t cheevos_locals = } }; -bool cheevos_loaded = false; -int cheats_are_enabled = 0; -int cheats_were_enabled = 0; +bool cheevos_loaded = false; +int cheats_are_enabled = 0; +int cheats_were_enabled = 0; + +#ifdef HAVE_THREADS +#define CHEEVOS_LOCK(l) do { slock_lock(l); } while (0) +#define CHEEVOS_UNLOCK(l) do { slock_unlock(l); } while (0) +#else +#define CHEEVOS_LOCK(l) +#define CHEEVOS_UNLOCK(l) +#endif /***************************************************************************** Supporting functions. @@ -2462,18 +2487,28 @@ bool cheevos_apply_cheats(bool *data_bool) bool cheevos_unload(void) { - if (!cheevos_loaded) - return false; + CHEEVOS_LOCK(cheevos_locals.task_lock); - cheevos_free_cheevo_set(&cheevos_locals.core); - cheevos_free_cheevo_set(&cheevos_locals.unofficial); + if (cheevos_locals.task) + { + task_queue_cancel_task(cheevos_locals.task); + while (cheevos_locals.task) /* nothing */; + } + + if (cheevos_loaded) + { + cheevos_free_cheevo_set(&cheevos_locals.core); + cheevos_free_cheevo_set(&cheevos_locals.unofficial); + } + + CHEEVOS_UNLOCK(cheevos_locals.task_lock); cheevos_locals.core.cheevos = NULL; cheevos_locals.unofficial.cheevos = NULL; cheevos_locals.core.count = 0; cheevos_locals.unofficial.count = 0; - cheevos_loaded = 0; + cheevos_loaded = false; return true; } @@ -2695,7 +2730,6 @@ void cheevos_test(void) bool cheevos_set_cheats(void) { cheats_were_enabled = cheats_are_enabled; - return true; } @@ -2716,6 +2750,21 @@ cheevos_console_t cheevos_get_console(void) #include "coro.h" +/* Uncomment the following two lines to debug cheevos_iterate, this will + * disable the coroutine yielding. + * + * The code is very easy to understand. It's meant to be like BASIC: + * CORO_GOTO will jump execution to another label, CORO_GOSUB will + * call another label, and CORO_RET will return from a CORO_GOSUB. + * + * This coroutine code is inspired in a very old pure C implementation + * that runs everywhere: + * + * https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html + */ +/*#undef CORO_YIELD +#define CORO_YIELD()*/ + enum { /* Negative values because CORO_SUB generates positive values */ @@ -3719,13 +3768,20 @@ static void cheevos_task_handler(retro_task_t *task) if (!coro) return; - if (!cheevos_iterate(coro)) + if (!cheevos_iterate(coro) || task_get_cancelled(task)) { task_set_finished(task, true); + + CHEEVOS_LOCK(cheevos_locals.task_lock); + cheevos_locals.task = NULL; + CHEEVOS_UNLOCK(cheevos_locals.task_lock); + if (coro->data) free(coro->data); + if ((void*)coro->path) free((void*)coro->path); + free((void*)coro); } } @@ -3736,7 +3792,7 @@ bool cheevos_load(const void *data) const struct retro_game_info *info = NULL; coro_t *coro = NULL; - cheevos_loaded = 0; + cheevos_loaded = false; if (!cheevos_locals.core_supports || !data) return false; @@ -3795,6 +3851,18 @@ bool cheevos_load(const void *data) task->progress = 0; task->title = NULL; +#ifdef HAVE_THREADS + if (cheevos_locals.task_lock == NULL) + { + cheevos_locals.task_lock = slock_new(); + } +#endif + + CHEEVOS_LOCK(cheevos_locals.task_lock); + cheevos_locals.task = task; + CHEEVOS_UNLOCK(cheevos_locals.task_lock); + task_queue_push(task); + return true; }