diff --git a/input/input_overlay.c b/input/input_overlay.c index 3d05c9635a..4a85ad6a71 100644 --- a/input/input_overlay.c +++ b/input/input_overlay.c @@ -180,7 +180,7 @@ void input_overlay_set_scale_factor(float scale) input_overlay_set_vertex_geom(); } -static void input_overlay_free_overlay(struct overlay *overlay) +void input_overlay_free_overlay(struct overlay *overlay) { size_t i; @@ -551,6 +551,9 @@ static void input_overlay_loaded(void *task_data, void *user_data, const char *e input_overlay_t *ol; driver_t *driver = driver_get_ptr(); + if (err) + return; + /* We can't display when the menu is up */ if (settings->input.overlay_hide_in_menu && menu_driver_alive()) { diff --git a/input/input_overlay.h b/input/input_overlay.h index 438e4f9bbf..81f81f0011 100644 --- a/input/input_overlay.h +++ b/input/input_overlay.h @@ -198,6 +198,7 @@ typedef struct **/ void input_overlay_free(void); +void input_overlay_free_overlay(struct overlay *overlay); /** * input_overlay_init diff --git a/tasks/task_database.c b/tasks/task_database.c index 75f46ceee7..d54d56bd4b 100644 --- a/tasks/task_database.c +++ b/tasks/task_database.c @@ -493,7 +493,7 @@ static void rarch_dbscan_task_handler(rarch_task_t *task) database_state_handle_t *dbstate = &db->state; const char *name = dbinfo ? dbinfo->list->elems[dbinfo->list_ptr].data : NULL; - if (!dbinfo) + if (!dbinfo || task->cancelled) goto task_finished; switch (dbinfo->status) diff --git a/tasks/task_file_transfer.c b/tasks/task_file_transfer.c index 727353505b..2666b48e37 100644 --- a/tasks/task_file_transfer.c +++ b/tasks/task_file_transfer.c @@ -442,7 +442,7 @@ static void rarch_task_file_load_handler(rarch_task_t *task) break; } - if (nbio->is_finished && nbio->image.is_finished) + if (nbio->is_finished && nbio->image.is_finished && !task->cancelled) { task->task_data = malloc(sizeof(nbio->image.ti)); memcpy(task->task_data, &nbio->image.ti, sizeof(nbio->image.ti)); @@ -453,6 +453,13 @@ static void rarch_task_file_load_handler(rarch_task_t *task) if (nbio->is_finished) goto task_finished; + + if (task->cancelled) + { + task->error = strdup("Task canceled."); + goto task_finished; + } + return; task_finished: diff --git a/tasks/task_http.c b/tasks/task_http.c index bc07608c7b..c894a3dbb1 100644 --- a/tasks/task_http.c +++ b/tasks/task_http.c @@ -162,6 +162,9 @@ static void rarch_task_http_transfer_handler(rarch_task_t *task) break; } + if (task->cancelled) + goto task_finished; + return; task_finished: task->finished = true; @@ -174,14 +177,17 @@ task_finished: if (tmp && http->cb) http->cb(tmp, len); - if (net_http_error(http->handle)) + if (net_http_error(http->handle) || task->cancelled) { tmp = (char*)net_http_data(http->handle, &len, true); if (tmp) free(tmp); - task->error = strdup("Download failed."); + if (task->cancelled) + task->error = strdup("Task cancelled."); + else + task->error = strdup("Download failed."); } else { diff --git a/tasks/task_overlay.c b/tasks/task_overlay.c index 33e5258bf9..f07be6f52c 100644 --- a/tasks/task_overlay.c +++ b/tasks/task_overlay.c @@ -629,17 +629,38 @@ static void rarch_task_overlay_handler(rarch_task_t *task) goto task_finished; } + if (task->cancelled) + goto task_finished; + return; task_finished: task->finished = true; - data = (overlay_task_data_t*)calloc(1, sizeof(*data)); + if (task->cancelled) + { + struct overlay *o; + unsigned i; - data->overlays = loader->overlays; - data->size = loader->size; - data->active = loader->active; + if (task->error) + free(task->error); - task->task_data = data; + task->error = strdup("Task cancelled."); + + for (i = 0; i < loader->size; i++) + input_overlay_free_overlay(&loader->overlays[i]); + + free(loader->overlays); + } + else + { + data = (overlay_task_data_t*)calloc(1, sizeof(*data)); + + data->overlays = loader->overlays; + data->size = loader->size; + data->active = loader->active; + + task->task_data = data; + } if (loader->overlay_path) free(loader->overlay_path); diff --git a/tasks/tasks.c b/tasks/tasks.c index 429434b980..c5f1197fea 100644 --- a/tasks/tasks.c +++ b/tasks/tasks.c @@ -9,56 +9,54 @@ #include "rthreads/rthreads.h" #endif -static rarch_task_t *running_queue = NULL; -static rarch_task_t *finished_queue = NULL; +typedef struct { + rarch_task_t *front; + rarch_task_t *back; +} task_queue_t; struct rarch_task_impl { void (*push_running)(rarch_task_t *); - void (*push_finished)(rarch_task_t *); + void (*cancel)(void); + void (*wait)(void); void (*gather)(void); void (*init)(void); void (*deinit)(void); }; +static task_queue_t tasks_running = {NULL, NULL}; +static task_queue_t tasks_finished = {NULL, NULL}; static struct rarch_task_impl *impl_current = NULL; -static void _push_task(rarch_task_t **q, rarch_task_t *task) +static void task_queue_put(task_queue_t *queue, rarch_task_t *task) { - if (*q == NULL) - *q = task; - else - { - rarch_task_t *last = *q; - while (last->next) - last = last->next; - - last->next = task; - } - task->next = NULL; + + if (queue->front == NULL) + queue->front = task; + else + queue->back->next = task; + + queue->back = task; } -static rarch_task_t *_pop_task(rarch_task_t **q) +static rarch_task_t *task_queue_get(task_queue_t *queue) { - rarch_task_t *task = *q; + rarch_task_t *task = queue->front; - if (*q == NULL) - return NULL; - - *q = task->next; - task->next = NULL; + if (task) + { + queue->front = task->next; + task->next = NULL; + } return task; } static void rarch_task_internal_gather(void) { - rarch_task_t *next = NULL; - while ((next = _pop_task(&finished_queue)) != NULL) + rarch_task_t *task; + while ((task = task_queue_get(&tasks_finished)) != NULL) { - rarch_task_t *task = next; - next = next->next; - if (task->callback) task->callback(task->task_data, task->user_data, task->error); @@ -69,14 +67,9 @@ static void rarch_task_internal_gather(void) } } -static void regular_push_finished(rarch_task_t *task) -{ - _push_task(&finished_queue, task); -} - static void regular_push_running(rarch_task_t *task) { - _push_task(&running_queue, task); + task_queue_put(&tasks_running, task); } static void regular_gather(void) @@ -87,7 +80,7 @@ static void regular_gather(void) /* mimics threaded_gather() for compatibility, a faster implementation * can be written for systems without HAVE_THREADS if necessary. */ - while ((task = _pop_task(&running_queue)) != NULL) + while ((task = task_queue_get(&tasks_running)) != NULL) { task->next = queue; queue = task; @@ -99,7 +92,7 @@ static void regular_gather(void) task->handler(task); if (task->finished) - regular_push_finished(task); + task_queue_put(&tasks_finished, task); else regular_push_running(task); } @@ -107,6 +100,20 @@ static void regular_gather(void) rarch_task_internal_gather(); } +static void regular_wait(void) +{ + while (tasks_running.front) + regular_gather(); +} + +static void regular_cancel() +{ + rarch_task_t *task; + + for (task = tasks_running.front; task; task = task->next) + task->cancelled = true; +} + static void regular_init(void) { } @@ -117,7 +124,8 @@ static void regular_deinit(void) static struct rarch_task_impl impl_regular = { regular_push_running, - regular_push_finished, + regular_cancel, + regular_wait, regular_gather, regular_init, regular_deinit @@ -130,17 +138,10 @@ static scond_t *worker_cond = NULL; static sthread_t *worker_thread = NULL; static bool worker_continue = true; /* use running_lock when touching it */ -static void threaded_push_finished(rarch_task_t *task) -{ - slock_lock(finished_lock); - _push_task(&finished_queue, task); - slock_unlock(finished_lock); -} - static void threaded_push_running(rarch_task_t *task) { slock_lock(running_lock); - _push_task(&running_queue, task); + task_queue_put(&tasks_running, task); scond_signal(worker_cond); slock_unlock(running_lock); } @@ -152,6 +153,29 @@ static void threaded_gather(void) slock_unlock(finished_lock); } +static void threaded_wait(void) +{ + bool wait; + do + { + threaded_gather(); + + slock_lock(running_lock); + wait = (tasks_running.front != NULL); + slock_unlock(running_lock); + } while (wait); +} + +static void threaded_cancel(void) +{ + rarch_task_t *task; + + slock_lock(running_lock); + for (task = tasks_running.front; task; task = task->next) + task->cancelled = true; + slock_unlock(running_lock); +} + static void threaded_worker(void *userdata) { (void)userdata; @@ -171,7 +195,7 @@ static void threaded_worker(void *userdata) if (!worker_continue) break; /* should we keep running until all tasks finished? */ - while ((task = _pop_task(&running_queue)) != NULL) + while ((task = task_queue_get(&tasks_running)) != NULL) { task->next = queue; queue = task; @@ -192,7 +216,11 @@ static void threaded_worker(void *userdata) task->handler(task); if (task->finished) - threaded_push_finished(task); + { + slock_lock(finished_lock); + task_queue_put(&tasks_finished, task); + slock_unlock(finished_lock); + } else threaded_push_running(task); } @@ -235,7 +263,8 @@ static void threaded_deinit(void) static struct rarch_task_impl impl_threaded = { threaded_push_running, - threaded_push_finished, + threaded_cancel, + threaded_wait, threaded_gather, threaded_init, threaded_deinit @@ -245,7 +274,8 @@ static struct rarch_task_impl impl_threaded = { void rarch_task_init(void) { #ifdef HAVE_THREADS - if (config_get_ptr()->threaded_data_runloop_enable) + settings_t *settings = config_get_ptr(); + if (settings->threaded_data_runloop_enable) impl_current = &impl_threaded; else #endif @@ -281,7 +311,19 @@ void rarch_task_check(void) impl_current->gather(); } +/* The lack of NULL checks in the following functions is proposital + * to ensure correct control flow by the users. */ void rarch_task_push(rarch_task_t *task) { impl_current->push_running(task); } + +void rarch_task_wait(void) +{ + impl_current->wait(); +} + +void rarch_task_reset(void) +{ + impl_current->cancel(); +} diff --git a/tasks/tasks.h b/tasks/tasks.h index f571e7a385..c95df314e1 100644 --- a/tasks/tasks.h +++ b/tasks/tasks.h @@ -37,9 +37,12 @@ struct rarch_task { rarch_task_handler_t handler; rarch_task_callback_t callback; /* always called from the main loop */ - /* set by the handler */ + /* set to true by the handler to signal the task has finished executing. */ bool finished; + /* set to true by the task system to signal the task *must* end. */ + bool cancelled; + /* created by the handler, destroyed by the user */ void *task_data; @@ -56,12 +59,57 @@ struct rarch_task { rarch_task_t *next; }; -/* MAIN THREAD ONLY */ +/** + * @brief Initializes the task system + * + * This function initializes the task system and chooses an appropriate + * implementation according to the settings. + * + * This function must only be called from the main thread. + */ void rarch_task_init(void); + +/** + * @brief Deinitializes the task system + * + * This function deinitializes the task system. The tasks that are running at + * the moment will stay on hold until rarch_task_init() is called again. + */ void rarch_task_deinit(void); + +/** + * @brief Checks for finished tasks + * + * Takes the finished tasks, if any, and runs their callbacks. + * + * This function must only be called from the main thread. + */ void rarch_task_check(void); -/* MAIN AND TASK THREADS */ +/** + * @brief Sends a signal to terminate all the tasks. + * + * This function won't terminate the tasks immediately. They will finish as + * soon as possible. + * + * This function must only be called from the main thread. + */ +void rarch_task_reset(void); + +/** + * @brief Blocks until all tasks have finished. + * + * This function must only be called from the main thread. + */ +void rarch_task_wait(void); + +/** + * @brief Pushes a task + * + * The task will start as soon as possible. + * + * @param task + */ void rarch_task_push(rarch_task_t *task); #ifdef HAVE_NETWORKING