cdrom: adjust pause behavior
[pcsx_rearmed.git] / deps / libretro-common / queues / task_queue.c
1 /* Copyright  (C) 2010-2020 The RetroArch team
2  *
3  * ---------------------------------------------------------------------------------------
4  * The following license statement only applies to this file (task_queue.c).
5  * ---------------------------------------------------------------------------------------
6  *
7  * Permission is hereby granted, free of charge,
8  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26
27 #include <queues/task_queue.h>
28
29 #include <features/features_cpu.h>
30
31 #ifdef HAVE_THREADS
32 #include <rthreads/rthreads.h>
33 #endif
34
35 typedef struct
36 {
37    retro_task_t *front;
38    retro_task_t *back;
39 } task_queue_t;
40
41 struct retro_task_impl
42 {
43    retro_task_queue_msg_t msg_push;
44    void (*push_running)(retro_task_t *);
45    void (*cancel)(void *);
46    void (*reset)(void);
47    void (*wait)(retro_task_condition_fn_t, void *);
48    void (*gather)(void);
49    bool (*find)(retro_task_finder_t, void*);
50    void (*retrieve)(task_retriever_data_t *data);
51    void (*init)(void);
52    void (*deinit)(void);
53 };
54
55 /* TODO/FIXME - static globals */
56 static retro_task_queue_msg_t msg_push_bak  = NULL;
57 static task_queue_t tasks_running           = {NULL, NULL};
58 static task_queue_t tasks_finished          = {NULL, NULL};
59
60 static struct retro_task_impl *impl_current = NULL;
61 static bool task_threaded_enable            = false;
62
63 #ifdef HAVE_THREADS
64 static uintptr_t main_thread_id             = 0;
65 static slock_t *running_lock                = NULL;
66 static slock_t *finished_lock               = NULL;
67 static slock_t *property_lock               = NULL;
68 static slock_t *queue_lock                  = NULL;
69 static scond_t *worker_cond                 = NULL;
70 static sthread_t *worker_thread             = NULL;
71 static bool worker_continue                 = true; 
72 /* use running_lock when touching it */
73 #endif
74
75 static void task_queue_msg_push(retro_task_t *task,
76       unsigned prio, unsigned duration,
77       bool flush, const char *fmt, ...)
78 {
79    char buf[1024];
80    va_list ap;
81
82    buf[0] = '\0';
83
84    va_start(ap, fmt);
85    vsnprintf(buf, sizeof(buf), fmt, ap);
86    va_end(ap);
87
88    if (impl_current->msg_push)
89       impl_current->msg_push(task, buf, prio, duration, flush);
90 }
91
92 static void task_queue_push_progress(retro_task_t *task)
93 {
94 #ifdef HAVE_THREADS
95    /* msg_push callback interacts directly with the task properties (particularly title).
96     * make sure another thread doesn't modify them while rendering
97     */
98    slock_lock(property_lock);
99 #endif
100
101    if (task->title && !task->mute)
102    {
103       if (task->finished)
104       {
105          if (task->error)
106             task_queue_msg_push(task, 1, 60, true, "%s: %s",
107                "Task failed", task->title);
108          else
109             task_queue_msg_push(task, 1, 60, false, "100%%: %s", task->title);
110       }
111       else
112       {
113          if (task->progress >= 0 && task->progress <= 100)
114             task_queue_msg_push(task, 1, 60, true, "%i%%: %s",
115                   task->progress, task->title);
116          else
117             task_queue_msg_push(task, 1, 60, false, "%s...", task->title);
118       }
119
120       if (task->progress_cb)
121          task->progress_cb(task);
122    }
123
124 #ifdef HAVE_THREADS
125    slock_unlock(property_lock);
126 #endif
127 }
128
129 static void task_queue_put(task_queue_t *queue, retro_task_t *task)
130 {
131    task->next                   = NULL;
132
133    if (queue->front)
134    {
135       /* Make sure to insert in order - the queue is 
136        * sorted by 'when' so items that aren't scheduled
137        * to run immediately are at the back of the queue. 
138        * Items with the same 'when' are inserted after
139        * all the other items with the same 'when'. 
140        * This primarily affects items with a 'when' of 0.
141        */
142       if (queue->back)
143       {
144          if (queue->back->when > task->when)
145          {
146             retro_task_t** prev = &queue->front;
147             while (*prev && (*prev)->when <= task->when)
148                prev             = &((*prev)->next);
149
150             task->next          = *prev;
151             *prev               = task;
152             return;
153          }
154
155          queue->back->next      = task;
156       }
157    }
158    else
159       queue->front              = task;
160
161    queue->back                  = task;
162 }
163
164 static retro_task_t *task_queue_get(task_queue_t *queue)
165 {
166    retro_task_t *task = queue->front;
167
168    if (task)
169    {
170       queue->front = task->next;
171       task->next   = NULL;
172    }
173
174    return task;
175 }
176
177 static void retro_task_internal_gather(void)
178 {
179    retro_task_t *task = NULL;
180    while ((task = task_queue_get(&tasks_finished)))
181    {
182       task_queue_push_progress(task);
183
184       if (task->callback)
185          task->callback(task, task->task_data, task->user_data, task->error);
186
187       if (task->cleanup)
188           task->cleanup(task);
189
190       if (task->error)
191          free(task->error);
192
193       if (task->title)
194          free(task->title);
195
196       free(task);
197    }
198 }
199
200 static void retro_task_regular_push_running(retro_task_t *task)
201 {
202    task_queue_put(&tasks_running, task);
203 }
204
205 static void retro_task_regular_cancel(void *task)
206 {
207    retro_task_t *t = (retro_task_t*)task;
208    t->cancelled    = true;
209 }
210
211 static void retro_task_regular_gather(void)
212 {
213    retro_task_t *task  = NULL;
214    retro_task_t *queue = NULL;
215    retro_task_t *next  = NULL;
216
217    while ((task = task_queue_get(&tasks_running)))
218    {
219       task->next = queue;
220       queue = task;
221    }
222
223    for (task = queue; task; task = next)
224    {
225       next = task->next;
226
227       if (!task->when || task->when < cpu_features_get_time_usec())
228       {
229          task->handler(task);
230
231          task_queue_push_progress(task);
232       }
233
234       if (task->finished)
235          task_queue_put(&tasks_finished, task);
236       else
237          retro_task_regular_push_running(task);
238    }
239
240    retro_task_internal_gather();
241 }
242
243 static void retro_task_regular_wait(retro_task_condition_fn_t cond, void* data)
244 {
245    while ((tasks_running.front && !tasks_running.front->when) && (!cond || cond(data)))
246       retro_task_regular_gather();
247 }
248
249 static void retro_task_regular_reset(void)
250 {
251    retro_task_t *task = tasks_running.front;
252
253    for (; task; task = task->next)
254       task->cancelled = true;
255 }
256
257 static void retro_task_regular_init(void) { }
258 static void retro_task_regular_deinit(void) { }
259
260 static bool retro_task_regular_find(retro_task_finder_t func, void *user_data)
261 {
262    retro_task_t *task = tasks_running.front;
263
264    for (; task; task = task->next)
265    {
266       if (func(task, user_data))
267          return true;
268    }
269
270    return false;
271 }
272
273 static void retro_task_regular_retrieve(task_retriever_data_t *data)
274 {
275    retro_task_t *task          = NULL;
276    task_retriever_info_t *tail = NULL;
277
278    /* Parse all running tasks and handle matching handlers */
279    for (task = tasks_running.front; task != NULL; task = task->next)
280    {
281       task_retriever_info_t *info = NULL;
282       if (task->handler != data->handler)
283          continue;
284
285       /* Create new link */
286       info       = (task_retriever_info_t*)
287          malloc(sizeof(task_retriever_info_t));
288       info->data = malloc(data->element_size);
289       info->next = NULL;
290
291       /* Call retriever function and fill info-specific data */
292       if (!data->func(task, info->data))
293       {
294          free(info->data);
295          free(info);
296          continue;
297       }
298
299       /* Add link to list */
300       if (data->list)
301       {
302          if (tail)
303          {
304             tail->next = info;
305             tail       = tail->next;
306          }
307          else
308             tail       = info;
309       }
310       else
311       {
312          data->list    = info;
313          tail          = data->list;
314       }
315    }
316 }
317
318 static struct retro_task_impl impl_regular = {
319    NULL,
320    retro_task_regular_push_running,
321    retro_task_regular_cancel,
322    retro_task_regular_reset,
323    retro_task_regular_wait,
324    retro_task_regular_gather,
325    retro_task_regular_find,
326    retro_task_regular_retrieve,
327    retro_task_regular_init,
328    retro_task_regular_deinit
329 };
330
331 #ifdef HAVE_THREADS
332
333 /* 'queue_lock' must be held for the duration of this function */
334 static void task_queue_remove(task_queue_t *queue, retro_task_t *task)
335 {
336    retro_task_t     *t = NULL;
337    retro_task_t *front = queue->front;
338
339    /* Remove first element if needed */
340    if (task == front)
341    {
342       queue->front     = task->next;
343       if (queue->back == task) /* if only element, also update back */
344          queue->back   = NULL;
345       task->next       = NULL;
346       return;
347    }
348
349    /* Parse queue */
350    t = front;
351
352    while (t && t->next)
353    {
354       /* Remove task and update queue */
355       if (t->next == task)
356       {
357          t->next    = task->next;
358          task->next = NULL;
359
360          /* When removing the tail of the queue, update the tail pointer */
361          if (queue->back == task)
362          {
363             if (queue->back == task)
364                queue->back = t;
365          }
366          break;
367       }
368
369       /* Update iterator */
370       t = t->next;
371    }
372 }
373
374 static void retro_task_threaded_push_running(retro_task_t *task)
375 {
376    slock_lock(running_lock);
377    slock_lock(queue_lock);
378    task_queue_put(&tasks_running, task);
379    scond_signal(worker_cond);
380    slock_unlock(queue_lock);
381    slock_unlock(running_lock);
382 }
383
384 static void retro_task_threaded_cancel(void *task)
385 {
386    retro_task_t *t;
387
388    slock_lock(running_lock);
389
390    for (t = tasks_running.front; t; t = t->next)
391    {
392       if (t == task)
393       {
394         t->cancelled = true;
395         break;
396       }
397    }
398
399    slock_unlock(running_lock);
400 }
401
402 static void retro_task_threaded_gather(void)
403 {
404    retro_task_t *task = NULL;
405
406    slock_lock(running_lock);
407    for (task = tasks_running.front; task; task = task->next)
408       task_queue_push_progress(task);
409    slock_unlock(running_lock);
410
411    slock_lock(finished_lock);
412    retro_task_internal_gather();
413    slock_unlock(finished_lock);
414 }
415
416 static void retro_task_threaded_wait(retro_task_condition_fn_t cond, void* data)
417 {
418    bool wait = false;
419
420    do
421    {
422       retro_task_threaded_gather();
423
424       slock_lock(running_lock);
425       wait = (tasks_running.front && !tasks_running.front->when);
426       slock_unlock(running_lock);
427
428       if (!wait)
429       {
430          slock_lock(finished_lock);
431          wait = (tasks_finished.front && !tasks_finished.front->when);
432          slock_unlock(finished_lock);
433       }
434    } while (wait && (!cond || cond(data)));
435 }
436
437 static void retro_task_threaded_reset(void)
438 {
439    retro_task_t *task = NULL;
440
441    slock_lock(running_lock);
442    for (task = tasks_running.front; task; task = task->next)
443       task->cancelled = true;
444    slock_unlock(running_lock);
445 }
446
447 static bool retro_task_threaded_find(
448       retro_task_finder_t func, void *user_data)
449 {
450    retro_task_t *task = NULL;
451    bool        result = false;
452
453    slock_lock(running_lock);
454    for (task = tasks_running.front; task; task = task->next)
455    {
456       if (func(task, user_data))
457       {
458          result = true;
459          break;
460       }
461    }
462    slock_unlock(running_lock);
463
464    return result;
465 }
466
467 static void retro_task_threaded_retrieve(task_retriever_data_t *data)
468 {
469    /* Protect access to running tasks */
470    slock_lock(running_lock);
471
472    /* Call regular retrieve function */
473    retro_task_regular_retrieve(data);
474
475    /* Release access to running tasks */
476    slock_unlock(running_lock);
477 }
478
479 static void threaded_worker(void *userdata)
480 {
481    (void)userdata;
482
483    for (;;)
484    {
485       retro_task_t *task  = NULL;
486       bool       finished = false;
487
488       if (!worker_continue)
489          break; /* should we keep running until all tasks finished? */
490
491       slock_lock(running_lock);
492
493       /* Get first task to run */
494       if (!(task = tasks_running.front))
495       {
496          scond_wait(worker_cond, running_lock);
497          slock_unlock(running_lock);
498          continue;
499       }
500
501       if (task->when)
502       {
503          retro_time_t now   = cpu_features_get_time_usec();
504          retro_time_t delay = task->when - now - 500; /* allow half a millisecond for context switching */
505          if (delay > 0)
506          {
507             scond_wait_timeout(worker_cond, running_lock, delay);
508             slock_unlock(running_lock);
509             continue;
510          }
511       }
512
513       slock_unlock(running_lock);
514
515       task->handler(task);
516
517       slock_lock(property_lock);
518       finished = task->finished;
519       slock_unlock(property_lock);
520
521       /* Update queue */
522       if (!finished)
523       {
524          /* Move the task to the back of the queue */
525          /* mimics retro_task_threaded_push_running, 
526           * but also includes a task_queue_remove */
527          slock_lock(running_lock);
528          slock_lock(queue_lock);
529
530          /* do nothing if only item in queue */
531          if (task->next) 
532          {
533             task_queue_remove(&tasks_running, task);
534             task_queue_put(&tasks_running, task);
535             scond_signal(worker_cond);
536          }
537          slock_unlock(queue_lock);
538          slock_unlock(running_lock);
539       }
540       else
541       {
542          /* Remove task from running queue */
543          slock_lock(running_lock);
544          slock_lock(queue_lock);
545          task_queue_remove(&tasks_running, task);
546          slock_unlock(queue_lock);
547          slock_unlock(running_lock);
548
549          /* Add task to finished queue */
550          slock_lock(finished_lock);
551          task_queue_put(&tasks_finished, task);
552          slock_unlock(finished_lock);
553       }
554    }
555 }
556
557 static void retro_task_threaded_init(void)
558 {
559    running_lock    = slock_new();
560    finished_lock   = slock_new();
561    property_lock   = slock_new();
562    queue_lock      = slock_new();
563    worker_cond     = scond_new();
564
565    slock_lock(running_lock);
566    worker_continue = true;
567    slock_unlock(running_lock);
568
569    worker_thread   = sthread_create(threaded_worker, NULL);
570 }
571
572 static void retro_task_threaded_deinit(void)
573 {
574    slock_lock(running_lock);
575    worker_continue = false;
576    scond_signal(worker_cond);
577    slock_unlock(running_lock);
578
579    sthread_join(worker_thread);
580
581    scond_free(worker_cond);
582    slock_free(running_lock);
583    slock_free(finished_lock);
584    slock_free(property_lock);
585    slock_free(queue_lock);
586
587    worker_thread   = NULL;
588    worker_cond     = NULL;
589    running_lock    = NULL;
590    finished_lock   = NULL;
591    property_lock   = NULL;
592    queue_lock      = NULL;
593 }
594
595 static struct retro_task_impl impl_threaded = {
596    NULL,
597    retro_task_threaded_push_running,
598    retro_task_threaded_cancel,
599    retro_task_threaded_reset,
600    retro_task_threaded_wait,
601    retro_task_threaded_gather,
602    retro_task_threaded_find,
603    retro_task_threaded_retrieve,
604    retro_task_threaded_init,
605    retro_task_threaded_deinit
606 };
607 #endif
608
609 /* Deinitializes the task system.
610  * This deinitializes the task system.
611  * The tasks that are running at
612  * the moment will stay on hold */
613 void task_queue_deinit(void)
614 {
615    if (impl_current)
616       impl_current->deinit();
617    impl_current = NULL;
618 }
619
620 void task_queue_init(bool threaded, retro_task_queue_msg_t msg_push)
621 {
622    impl_current   = &impl_regular;
623 #ifdef HAVE_THREADS
624    main_thread_id = sthread_get_current_thread_id();
625    if (threaded)
626    {
627       task_threaded_enable = true;
628       impl_current         = &impl_threaded;
629    }
630 #endif
631
632    msg_push_bak            = msg_push;
633
634    impl_current->msg_push  = msg_push;
635    impl_current->init();
636 }
637
638 void task_queue_set_threaded(void)
639 {
640    task_threaded_enable = true;
641 }
642
643 void task_queue_unset_threaded(void)
644 {
645    task_threaded_enable = false;
646 }
647
648 bool task_queue_is_threaded(void)
649 {
650    return task_threaded_enable;
651 }
652
653 bool task_queue_find(task_finder_data_t *find_data)
654 {
655    return impl_current->find(find_data->func, find_data->userdata);
656 }
657
658 void task_queue_retrieve(task_retriever_data_t *data)
659 {
660    impl_current->retrieve(data);
661 }
662
663 void task_queue_check(void)
664 {
665 #ifdef HAVE_THREADS
666    bool current_threaded = (impl_current == &impl_threaded);
667    bool want_threaded    = task_threaded_enable;
668
669    if (want_threaded != current_threaded)
670       task_queue_deinit();
671
672    if (!impl_current)
673       task_queue_init(want_threaded, msg_push_bak);
674 #endif
675
676    impl_current->gather();
677 }
678
679 bool task_queue_push(retro_task_t *task)
680 {
681    /* Ignore this task if a related one is already running */
682    if (task->type == TASK_TYPE_BLOCKING)
683    {
684       retro_task_t *running = NULL;
685       bool            found = false;
686
687 #ifdef HAVE_THREADS
688       slock_lock(queue_lock);
689 #endif
690       running = tasks_running.front;
691
692       for (; running; running = running->next)
693       {
694          if (running->type == TASK_TYPE_BLOCKING)
695          {
696             found = true;
697             break;
698          }
699       }
700
701 #ifdef HAVE_THREADS
702       slock_unlock(queue_lock);
703 #endif
704
705       /* skip this task, user must try again later */
706       if (found)
707          return false;
708    }
709
710    /* The lack of NULL checks in the following functions
711     * is proposital to ensure correct control flow by the users. */
712    impl_current->push_running(task);
713
714    return true;
715 }
716
717 void task_queue_wait(retro_task_condition_fn_t cond, void* data)
718 {
719    impl_current->wait(cond, data);
720 }
721
722 void task_queue_reset(void)
723 {
724    impl_current->reset();
725 }
726
727 /**
728  * Signals a task to end without waiting for
729  * it to complete. */
730 void task_queue_cancel_task(void *task)
731 {
732    impl_current->cancel(task);
733 }
734
735 void *task_queue_retriever_info_next(task_retriever_info_t **link)
736 {
737    void *data = NULL;
738
739    /* Grab data and move to next link */
740    if (*link)
741    {
742       data  = (*link)->data;
743       *link = (*link)->next;
744    }
745
746    return data;
747 }
748
749 void task_queue_retriever_info_free(task_retriever_info_t *list)
750 {
751    task_retriever_info_t *info;
752
753    /* Free links including retriever-specific data */
754    while (list)
755    {
756       info = list->next;
757       free(list->data);
758       free(list);
759       list = info;
760    }
761 }
762
763 bool task_is_on_main_thread(void)
764 {
765 #ifdef HAVE_THREADS
766    return sthread_get_current_thread_id() == main_thread_id;
767 #else
768    return true;
769 #endif
770 }
771
772 void task_set_finished(retro_task_t *task, bool finished)
773 {
774 #ifdef HAVE_THREADS
775    slock_lock(property_lock);
776 #endif
777    task->finished = finished;
778 #ifdef HAVE_THREADS
779    slock_unlock(property_lock);
780 #endif
781 }
782
783 void task_set_mute(retro_task_t *task, bool mute)
784 {
785 #ifdef HAVE_THREADS
786    slock_lock(property_lock);
787 #endif
788    task->mute = mute;
789 #ifdef HAVE_THREADS
790    slock_unlock(property_lock);
791 #endif
792 }
793
794 void task_set_error(retro_task_t *task, char *error)
795 {
796 #ifdef HAVE_THREADS
797    slock_lock(property_lock);
798 #endif
799    task->error = error;
800 #ifdef HAVE_THREADS
801    slock_unlock(property_lock);
802 #endif
803 }
804
805 void task_set_progress(retro_task_t *task, int8_t progress)
806 {
807 #ifdef HAVE_THREADS
808    slock_lock(property_lock);
809 #endif
810    task->progress = progress;
811 #ifdef HAVE_THREADS
812    slock_unlock(property_lock);
813 #endif
814 }
815
816 void task_set_title(retro_task_t *task, char *title)
817 {
818 #ifdef HAVE_THREADS
819    slock_lock(property_lock);
820 #endif
821    task->title = title;
822 #ifdef HAVE_THREADS
823    slock_unlock(property_lock);
824 #endif
825 }
826
827 void task_set_data(retro_task_t *task, void *data)
828 {
829 #ifdef HAVE_THREADS
830    slock_lock(running_lock);
831 #endif
832    task->task_data = data;
833 #ifdef HAVE_THREADS
834    slock_unlock(running_lock);
835 #endif
836 }
837
838 void task_set_cancelled(retro_task_t *task, bool cancelled)
839 {
840 #ifdef HAVE_THREADS
841    slock_lock(running_lock);
842 #endif
843    task->cancelled = cancelled;
844 #ifdef HAVE_THREADS
845    slock_unlock(running_lock);
846 #endif
847 }
848
849 void task_free_title(retro_task_t *task)
850 {
851 #ifdef HAVE_THREADS
852    slock_lock(property_lock);
853 #endif
854    if (task->title)
855       free(task->title);
856    task->title = NULL;
857 #ifdef HAVE_THREADS
858    slock_unlock(property_lock);
859 #endif
860 }
861
862 void* task_get_data(retro_task_t *task)
863 {
864    void *data = NULL;
865
866 #ifdef HAVE_THREADS
867    slock_lock(running_lock);
868 #endif
869    data = task->task_data;
870 #ifdef HAVE_THREADS
871    slock_unlock(running_lock);
872 #endif
873
874    return data;
875 }
876
877 bool task_get_cancelled(retro_task_t *task)
878 {
879    bool cancelled = false;
880
881 #ifdef HAVE_THREADS
882    slock_lock(running_lock);
883 #endif
884    cancelled = task->cancelled;
885 #ifdef HAVE_THREADS
886    slock_unlock(running_lock);
887 #endif
888
889    return cancelled;
890 }
891
892 bool task_get_finished(retro_task_t *task)
893 {
894    bool finished = false;
895
896 #ifdef HAVE_THREADS
897    slock_lock(property_lock);
898 #endif
899    finished = task->finished;
900 #ifdef HAVE_THREADS
901    slock_unlock(property_lock);
902 #endif
903
904    return finished;
905 }
906
907 bool task_get_mute(retro_task_t *task)
908 {
909    bool mute = false;
910
911 #ifdef HAVE_THREADS
912    slock_lock(property_lock);
913 #endif
914    mute = task->mute;
915 #ifdef HAVE_THREADS
916    slock_unlock(property_lock);
917 #endif
918
919    return mute;
920 }
921
922 char* task_get_error(retro_task_t *task)
923 {
924    char *error = NULL;
925
926 #ifdef HAVE_THREADS
927    slock_lock(property_lock);
928 #endif
929    error = task->error;
930 #ifdef HAVE_THREADS
931    slock_unlock(property_lock);
932 #endif
933
934    return error;
935 }
936
937 int8_t task_get_progress(retro_task_t *task)
938 {
939    int8_t progress = 0;
940
941 #ifdef HAVE_THREADS
942    slock_lock(property_lock);
943 #endif
944    progress = task->progress;
945 #ifdef HAVE_THREADS
946    slock_unlock(property_lock);
947 #endif
948
949    return progress;
950 }
951
952 char* task_get_title(retro_task_t *task)
953 {
954    char *title = NULL;
955
956 #ifdef HAVE_THREADS
957    slock_lock(property_lock);
958 #endif
959    title = task->title;
960 #ifdef HAVE_THREADS
961    slock_unlock(property_lock);
962 #endif
963
964    return title;
965 }
966
967 retro_task_t *task_init(void)
968 {
969    /* TODO/FIXME - static local global */
970    static uint32_t task_count = 0;
971    retro_task_t *task         = (retro_task_t*)malloc(sizeof(*task));
972
973    if (!task)
974       return NULL;
975
976    task->handler           = NULL;
977    task->callback          = NULL;
978    task->cleanup           = NULL;
979    task->finished          = false;
980    task->cancelled         = false;
981    task->mute              = false;
982    task->task_data         = NULL;
983    task->user_data         = NULL;
984    task->state             = NULL;
985    task->error             = NULL;
986    task->progress          = 0;
987    task->progress_cb       = NULL;
988    task->title             = NULL;
989    task->type              = TASK_TYPE_NONE;
990    task->ident             = task_count++;
991    task->frontend_userdata = NULL;
992    task->alternative_look  = false;
993    task->next              = NULL;
994    task->when              = 0;
995
996    return task;
997 }