Commit | Line | Data |
---|---|---|
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 | ||
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 | } |