2828
2929#include <features/features_cpu.h>
3030
31+ #if defined(HAVE_GCD ) && !defined(HAVE_THREADS )
32+ #error "gcd uses threads, what are you doing"
33+ #endif
34+
3135#ifdef HAVE_THREADS
3236#include <rthreads/rthreads.h>
3337#endif
3438
39+ #ifdef HAVE_GCD
40+ #include <dispatch/dispatch.h>
41+ #endif
42+
3543typedef struct
3644{
3745 retro_task_t * front ;
@@ -72,6 +80,10 @@ static bool worker_continue = true;
7280/* use running_lock when touching it */
7381#endif
7482
83+ #ifdef HAVE_GCD
84+ static unsigned gcd_queue_count = 0 ;
85+ #endif
86+
7587static void task_queue_msg_push (retro_task_t * task ,
7688 unsigned prio , unsigned duration ,
7789 bool flush , const char * fmt , ...)
@@ -481,10 +493,13 @@ static void threaded_worker(void *userdata)
481493 retro_task_t * task = NULL ;
482494 bool finished = false;
483495
496+ slock_lock (running_lock );
497+
484498 if (!worker_continue )
499+ {
500+ slock_unlock (running_lock );
485501 break ; /* should we keep running until all tasks finished? */
486-
487- slock_lock (running_lock );
502+ }
488503
489504 /* Get first task to run */
490505 if (!(task = tasks_running .front ))
@@ -602,6 +617,160 @@ static struct retro_task_impl impl_threaded = {
602617};
603618#endif
604619
620+ #ifdef HAVE_GCD
621+
622+ static void gcd_worker (retro_task_t * task )
623+ {
624+ bool finished = false;
625+ slock_lock (running_lock );
626+
627+ if (!worker_continue )
628+ {
629+ gcd_queue_count -- ;
630+ if (!gcd_queue_count )
631+ scond_signal (worker_cond );
632+ slock_unlock (running_lock );
633+ return ;
634+ }
635+
636+ if (task -> when )
637+ {
638+ retro_time_t now = cpu_features_get_time_usec ();
639+ retro_time_t delay = task -> when - now - 500 ;
640+ if (delay > 0 )
641+ {
642+ dispatch_time_t after = dispatch_time (DISPATCH_TIME_NOW , delay );
643+ dispatch_after (after , dispatch_get_global_queue (QOS_CLASS_USER_INITIATED , 0 ),
644+ ^{ gcd_worker (task ); });
645+ slock_unlock (running_lock );
646+ return ;
647+ }
648+ }
649+
650+ slock_unlock (running_lock );
651+
652+ task -> handler (task );
653+
654+ slock_lock (property_lock );
655+ finished = ((task -> flags & RETRO_TASK_FLG_FINISHED ) > 0 ) ? true : false;
656+ slock_unlock (property_lock );
657+
658+ if (!finished )
659+ dispatch_async (dispatch_get_global_queue (QOS_CLASS_USER_INITIATED , 0 ),
660+ ^{ gcd_worker (task ); });
661+ else
662+ {
663+ /* Remove task from running queue */
664+ slock_lock (running_lock );
665+ slock_lock (queue_lock );
666+ gcd_queue_count -- ;
667+ if (!gcd_queue_count )
668+ scond_signal (worker_cond );
669+ task_queue_remove (& tasks_running , task );
670+ slock_unlock (queue_lock );
671+ slock_unlock (running_lock );
672+
673+ /* Add task to finished queue */
674+ slock_lock (finished_lock );
675+ task_queue_put (& tasks_finished , task );
676+ slock_unlock (finished_lock );
677+ }
678+ }
679+
680+ static void retro_task_gcd_push_running (retro_task_t * task )
681+ {
682+ slock_lock (running_lock );
683+ slock_lock (queue_lock );
684+ task_queue_put (& tasks_running , task );
685+ gcd_queue_count ++ ;
686+ dispatch_async (dispatch_get_global_queue (QOS_CLASS_USER_INITIATED , 0 ),
687+ ^{ gcd_worker (task ); });
688+ slock_unlock (queue_lock );
689+ slock_unlock (running_lock );
690+ }
691+
692+ static void retro_task_gcd_wait (retro_task_condition_fn_t cond , void * data )
693+ {
694+ bool wait = false;
695+
696+ do
697+ {
698+ retro_task_t * task = NULL ;
699+ retro_task_threaded_gather ();
700+
701+ slock_lock (running_lock );
702+ wait = false;
703+ /* can't just look at the first task like threaded, they're not sorted by when */
704+ for (task = tasks_running .front ; !wait && task ; task = task -> next )
705+ wait |= !task -> when ;
706+ slock_unlock (running_lock );
707+
708+ if (!wait )
709+ {
710+ slock_lock (finished_lock );
711+ for (task = tasks_finished .front ; !wait && task ; task = task -> next )
712+ wait |= !task -> when ;
713+ slock_unlock (finished_lock );
714+ }
715+ } while (wait && (!cond || cond (data )));
716+ }
717+
718+ static void retro_task_gcd_init (void )
719+ {
720+ retro_task_t * task = NULL ;
721+
722+ running_lock = slock_new ();
723+ finished_lock = slock_new ();
724+ property_lock = slock_new ();
725+ queue_lock = slock_new ();
726+ worker_cond = scond_new ();
727+
728+ slock_lock (running_lock );
729+ worker_continue = true;
730+ for (task = tasks_running .front ; task ; task = task -> next )
731+ {
732+ gcd_queue_count ++ ;
733+ dispatch_async (dispatch_get_global_queue (QOS_CLASS_USER_INITIATED , 0 ),
734+ ^{ gcd_worker (task ); });
735+ };
736+ slock_unlock (running_lock );
737+ }
738+
739+ static void retro_task_gcd_deinit (void )
740+ {
741+ slock_lock (running_lock );
742+ worker_continue = false;
743+ if (gcd_queue_count )
744+ scond_wait (worker_cond , running_lock );
745+ slock_unlock (running_lock );
746+
747+ scond_free (worker_cond );
748+ slock_free (running_lock );
749+ slock_free (finished_lock );
750+ slock_free (property_lock );
751+ slock_free (queue_lock );
752+
753+ worker_cond = NULL ;
754+ running_lock = NULL ;
755+ finished_lock = NULL ;
756+ property_lock = NULL ;
757+ queue_lock = NULL ;
758+ }
759+
760+ static struct retro_task_impl impl_gcd = {
761+ NULL ,
762+ retro_task_gcd_push_running ,
763+ retro_task_threaded_cancel ,
764+ retro_task_threaded_reset ,
765+ retro_task_gcd_wait ,
766+ retro_task_threaded_gather ,
767+ retro_task_threaded_find ,
768+ retro_task_threaded_retrieve ,
769+ retro_task_gcd_init ,
770+ retro_task_gcd_deinit
771+ };
772+ #endif
773+
605774/* Deinitializes the task system.
606775 * This deinitializes the task system.
607776 * The tasks that are running at
@@ -621,7 +790,11 @@ void task_queue_init(bool threaded, retro_task_queue_msg_t msg_push)
621790 if (threaded )
622791 {
623792 task_threaded_enable = true;
793+ #ifdef HAVE_GCD
794+ impl_current = & impl_gcd ;
795+ #else
624796 impl_current = & impl_threaded ;
797+ #endif
625798 }
626799#endif
627800
@@ -659,7 +832,7 @@ void task_queue_retrieve(task_retriever_data_t *data)
659832void task_queue_check (void )
660833{
661834#ifdef HAVE_THREADS
662- bool current_threaded = (impl_current == & impl_threaded );
835+ bool current_threaded = (impl_current != & impl_regular );
663836 bool want_threaded = task_threaded_enable ;
664837
665838 if (want_threaded != current_threaded )
0 commit comments