git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / queues / task_queue.c
CommitLineData
3719602c
PC
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
35typedef struct
36{
37 retro_task_t *front;
38 retro_task_t *back;
39} task_queue_t;
40
41struct 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 */
56static retro_task_queue_msg_t msg_push_bak = NULL;
57static task_queue_t tasks_running = {NULL, NULL};
58static task_queue_t tasks_finished = {NULL, NULL};
59
60static struct retro_task_impl *impl_current = NULL;
61static bool task_threaded_enable = false;
62
63#ifdef HAVE_THREADS
64static uintptr_t main_thread_id = 0;
65static slock_t *running_lock = NULL;
66static slock_t *finished_lock = NULL;
67static slock_t *property_lock = NULL;
68static slock_t *queue_lock = NULL;
69static scond_t *worker_cond = NULL;
70static sthread_t *worker_thread = NULL;
71static bool worker_continue = true;
72/* use running_lock when touching it */
73#endif
74
75static 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
92static 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
129static 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
164static 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
177static 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
200static void retro_task_regular_push_running(retro_task_t *task)
201{
202 task_queue_put(&tasks_running, task);
203}
204
205static void retro_task_regular_cancel(void *task)
206{
207 retro_task_t *t = (retro_task_t*)task;
208 t->cancelled = true;
209}
210
211static 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
243static 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
249static 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
257static void retro_task_regular_init(void) { }
258static void retro_task_regular_deinit(void) { }
259
260static 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
273static 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
318static 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 */
334static 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
374static 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
384static 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
402static 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
416static 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
437static 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
447static 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
467static 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
479static 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
557static 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
572static 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
595static 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 */
613void task_queue_deinit(void)
614{
615 if (impl_current)
616 impl_current->deinit();
617 impl_current = NULL;
618}
619
620void 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
638void task_queue_set_threaded(void)
639{
640 task_threaded_enable = true;
641}
642
643void task_queue_unset_threaded(void)
644{
645 task_threaded_enable = false;
646}
647
648bool task_queue_is_threaded(void)
649{
650 return task_threaded_enable;
651}
652
653bool task_queue_find(task_finder_data_t *find_data)
654{
655 return impl_current->find(find_data->func, find_data->userdata);
656}
657
658void task_queue_retrieve(task_retriever_data_t *data)
659{
660 impl_current->retrieve(data);
661}
662
663void 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
679bool 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
717void task_queue_wait(retro_task_condition_fn_t cond, void* data)
718{
719 impl_current->wait(cond, data);
720}
721
722void 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. */
730void task_queue_cancel_task(void *task)
731{
732 impl_current->cancel(task);
733}
734
735void *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
749void 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
763bool 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
772void 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
783void 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
794void 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
805void 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
816void 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
827void 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
838void 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
849void 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
862void* 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
877bool 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
892bool 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
907bool 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
922char* 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
937int8_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
952char* 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
967retro_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}