diff --git a/libretro-common/queues/task_queue.c b/libretro-common/queues/task_queue.c index adf1f7fa5f..5eab7be447 100644 --- a/libretro-common/queues/task_queue.c +++ b/libretro-common/queues/task_queue.c @@ -28,10 +28,18 @@ #include +#if defined(HAVE_GCD) && !defined(HAVE_THREADS) +#error "gcd uses threads, what are you doing" +#endif + #ifdef HAVE_THREADS #include #endif +#ifdef HAVE_GCD +#include +#endif + typedef struct { retro_task_t *front; @@ -72,6 +80,10 @@ static bool worker_continue = true; /* use running_lock when touching it */ #endif +#ifdef HAVE_GCD +static unsigned gcd_queue_count = 0; +#endif + static void task_queue_msg_push(retro_task_t *task, unsigned prio, unsigned duration, bool flush, const char *fmt, ...) @@ -481,11 +493,14 @@ static void threaded_worker(void *userdata) retro_task_t *task = NULL; bool finished = false; - if (!worker_continue) - break; /* should we keep running until all tasks finished? */ - slock_lock(running_lock); + if (!worker_continue) + { + slock_unlock(running_lock); + break; /* should we keep running until all tasks finished? */ + } + /* Get first task to run */ if (!(task = tasks_running.front)) { @@ -602,6 +617,152 @@ static struct retro_task_impl impl_threaded = { }; #endif +#ifdef HAVE_GCD + +static void gcd_worker(retro_task_t *task) +{ + bool finished = false; + slock_lock(running_lock); + + if (!worker_continue) + { + gcd_queue_count--; + if (!gcd_queue_count) + scond_signal(worker_cond); + slock_unlock(running_lock); + return; + } + + if (task->when) + { + retro_time_t now = cpu_features_get_time_usec(); + retro_time_t delay = task->when - now - 500; + if (delay > 0) + { + dispatch_time_t after = dispatch_time(DISPATCH_TIME_NOW, delay); + dispatch_after(after, dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), + ^{ gcd_worker(task); }); + slock_unlock(running_lock); + return; + } + } + + slock_unlock(running_lock); + + task->handler(task); + + slock_lock(property_lock); + finished = ((task->flags & RETRO_TASK_FLG_FINISHED) > 0) ? true : false; + slock_unlock(property_lock); + + if (!finished) + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), + ^{ gcd_worker(task); }); + else + { + /* Remove task from running queue */ + slock_lock(running_lock); + slock_lock(queue_lock); + gcd_queue_count--; + if (!gcd_queue_count) + scond_signal(worker_cond); + task_queue_remove(&tasks_running, task); + slock_unlock(queue_lock); + slock_unlock(running_lock); + + /* Add task to finished queue */ + slock_lock(finished_lock); + task_queue_put(&tasks_finished, task); + slock_unlock(finished_lock); + } +} + +static void retro_task_gcd_push_running(retro_task_t *task) +{ + slock_lock(running_lock); + slock_lock(queue_lock); + task_queue_put(&tasks_running, task); + gcd_queue_count++; + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), + ^{ gcd_worker(task); }); + slock_unlock(queue_lock); + slock_unlock(running_lock); +} + +static void retro_task_gcd_wait(retro_task_condition_fn_t cond, void* data) +{ + bool wait = false; + + do + { + retro_task_t *task = NULL; + retro_task_threaded_gather(); + + slock_lock(running_lock); + wait = false; + /* can't just look at the first task like threaded, they're not sorted by when */ + for (task = tasks_running.front; !wait && task; task = task->next) + wait |= !task->when; + slock_unlock(running_lock); + + if (!wait) + { + slock_lock(finished_lock); + for (task = tasks_finished.front; !wait && task; task = task->next) + wait |= !task->when; + slock_unlock(finished_lock); + } + } while (wait && (!cond || cond(data))); +} + +static void retro_task_gcd_init(void) +{ + running_lock = slock_new(); + finished_lock = slock_new(); + property_lock = slock_new(); + queue_lock = slock_new(); + worker_cond = scond_new(); + + slock_lock(running_lock); + worker_continue = true; + slock_unlock(running_lock); +} + +static void retro_task_gcd_deinit(void) +{ + slock_lock(running_lock); + worker_continue = false; + if (gcd_queue_count) + scond_wait(worker_cond, running_lock); + slock_unlock(running_lock); + + scond_free(worker_cond); + slock_free(running_lock); + slock_free(finished_lock); + slock_free(property_lock); + slock_free(queue_lock); + + worker_cond = NULL; + running_lock = NULL; + finished_lock = NULL; + property_lock = NULL; + queue_lock = NULL; +} + +static struct retro_task_impl impl_gcd = { + NULL, + retro_task_gcd_push_running, + retro_task_threaded_cancel, + retro_task_threaded_reset, + retro_task_gcd_wait, + retro_task_threaded_gather, + retro_task_threaded_find, + retro_task_threaded_retrieve, + retro_task_gcd_init, + retro_task_gcd_deinit +}; +#endif + /* Deinitializes the task system. * This deinitializes the task system. * The tasks that are running at @@ -621,7 +782,11 @@ void task_queue_init(bool threaded, retro_task_queue_msg_t msg_push) if (threaded) { task_threaded_enable = true; +#ifdef HAVE_GCD + impl_current = &impl_gcd; +#else impl_current = &impl_threaded; +#endif } #endif @@ -659,7 +824,7 @@ void task_queue_retrieve(task_retriever_data_t *data) void task_queue_check(void) { #ifdef HAVE_THREADS - bool current_threaded = (impl_current == &impl_threaded); + bool current_threaded = (impl_current != &impl_regular); bool want_threaded = task_threaded_enable; if (want_threaded != current_threaded) diff --git a/pkg/apple/BaseConfig.xcconfig b/pkg/apple/BaseConfig.xcconfig index 742cb395e1..3516a1fddc 100644 --- a/pkg/apple/BaseConfig.xcconfig +++ b/pkg/apple/BaseConfig.xcconfig @@ -28,6 +28,7 @@ OTHER_CFLAGS = $(inherited) -DHAVE_DYNAMIC OTHER_CFLAGS = $(inherited) -DHAVE_EASTEREGG OTHER_CFLAGS = $(inherited) -DHAVE_FILTERS_BUILTIN OTHER_CFLAGS = $(inherited) -DHAVE_FLAC +OTHER_CFLAGS = $(inherited) -DHAVE_GCD OTHER_CFLAGS = $(inherited) -DHAVE_GFX_WIDGETS OTHER_CFLAGS = $(inherited) -DHAVE_GIT_VERSION OTHER_CFLAGS = $(inherited) -DHAVE_GLSL