diff --git a/core/core.c b/core/core.c index 283e446a..91eec0ea 100644 --- a/core/core.c +++ b/core/core.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ void core_init(int *argc, char ***argv, struct core_init_data *init) { + idle_init(); filter_init(); tags_init(); playlist_init(init->playlist_ops); @@ -31,4 +33,5 @@ void core_deinit() playlist_deinit(); tags_deinit(); filter_deinit(); + idle_deinit(); } diff --git a/core/idle.c b/core/idle.c index 7d881c62..3d1d9712 100644 --- a/core/idle.c +++ b/core/idle.c @@ -8,21 +8,58 @@ struct idle_task { void (*idle_func)(void *); void *idle_data; + enum idle_sync_t idle_sync; }; -static GQueue idle_queue = G_QUEUE_INIT; -static float queued = 0.0; -static float serviced = 0.0; +static GThreadPool *idle_pool = NULL; +static GQueue idle_queue = G_QUEUE_INIT; +static unsigned int queued = 0; +static unsigned int serviced = 0; +void __idle_run_task(struct idle_task *task) +{ + task->idle_func(task->idle_data); + g_free(task); + g_atomic_int_inc(&serviced); +} + +void __idle_thread(gpointer task, gpointer data) +{ + __idle_run_task(task); +} + + +void idle_init() +{ + idle_pool = g_thread_pool_new(__idle_thread, NULL, 1, true, NULL); +} + +void idle_deinit() +{ + struct idle_task *task; + + while (!g_queue_is_empty(&idle_queue)) { + task = g_queue_pop_head(&idle_queue); + g_free(task); + } + + if (idle_pool) + g_thread_pool_free(idle_pool, true, false); + + queued = 0; + serviced = 0; +} + void idle_schedule(enum idle_sync_t sync, void (*func)(void *), void *data) { struct idle_task *task = g_malloc(sizeof(struct idle_task)); task->idle_func = func; task->idle_data = data; + task->idle_sync = sync; g_queue_push_tail(&idle_queue, task); - queued++; + g_atomic_int_inc(&queued); } bool idle_run_task() @@ -31,31 +68,24 @@ bool idle_run_task() if (!g_queue_is_empty(&idle_queue)) { task = g_queue_pop_head(&idle_queue); - task->idle_func(task->idle_data); - g_free(task); - serviced++; + if (task->idle_sync == IDLE_ASYNC) + g_thread_pool_push(idle_pool, task, NULL); + else + __idle_run_task(task); } - if (g_queue_is_empty(&idle_queue)) { - queued = 0.0; - serviced = 0.0; - } - return !g_queue_is_empty(&idle_queue); + if (g_atomic_int_get(&queued) != g_atomic_int_get(&serviced)) + return true; + + queued = 0; + serviced = 0; + return false; } float idle_progress() { - if (g_queue_is_empty(&idle_queue)) + if (g_atomic_int_get(&serviced) == 0 && + g_atomic_int_get(&queued) == 0) return 1.0; - return serviced / queued; -} - -void idle_cancel() -{ - struct idle_task *task; - - while (!g_queue_is_empty(&idle_queue)) { - task = g_queue_pop_head(&idle_queue); - g_free(task); - } + return (float)g_atomic_int_get(&serviced) / (float)queued; } diff --git a/gui/idle.c b/gui/idle.c index 31e962b0..3b053024 100644 --- a/gui/idle.c +++ b/gui/idle.c @@ -13,7 +13,7 @@ static gboolean __on_idle(gpointer data) if (idle_run_task()) { gtk_progress_bar_set_fraction(progress, idle_progress()); - return gtk_widget_is_visible(gui_builder_widget("o_window")); + return G_SOURCE_CONTINUE; } else { gtk_widget_hide(GTK_WIDGET(progress)); idle_id = 0; @@ -33,5 +33,4 @@ void gui_idle_disable() { if (idle_id) g_source_remove(idle_id); - idle_cancel(); } diff --git a/include/core/idle.h b/include/core/idle.h index dff12386..071231c5 100644 --- a/include/core/idle.h +++ b/include/core/idle.h @@ -17,10 +17,18 @@ enum idle_sync_t { IDLE_SYNC, /* Run task in the main thread. */ + IDLE_ASYNC, /* Run task in a separate thread. */ }; #define IDLE_FUNC(x) ((void (*)(void *))x) + +/* Called to initialize the idle queue. */ +void idle_init(); + +/* Called to deinitialize the idle queue. */ +void idle_deinit(); + /* Called to schedule a function to run later. */ void idle_schedule(enum idle_sync_t, void (*)(void *), void *); @@ -33,7 +41,4 @@ bool idle_run_task(); /* Called to find the percentage of idle tasks that have been run. */ float idle_progress(); -/* Called to cancel all idle tasks on the queue. */ -void idle_cancel(); - #endif /* OCARINA_CORE_IDLE_H */ diff --git a/tests/core/collection.c b/tests/core/collection.c index 17c93c4f..6c29146a 100644 --- a/tests/core/collection.c +++ b/tests/core/collection.c @@ -57,11 +57,7 @@ static void test_add() test_equal(lib->li_path, "tests/Music"); test_equal(queue_size(q), 0); - test_equal(idle_run_task(), (bool)true); /* collection validation */ - test_equal(idle_run_task(), (bool)true); /* tests/Music/ */ - test_equal(idle_run_task(), (bool)true); /* /Hyrule Symphony/ */ - test_equal(idle_run_task(), (bool)false); /* /Ocarina of Time/ */ - + while (idle_run_task()) {}; test_equal(track_db_get()->db_size, 48); test_equal(lib->li_size, 48); test_equal(queue_size(q), 48); diff --git a/tests/core/history.c b/tests/core/history.c index bddc7f6b..0f7ae384 100644 --- a/tests/core/history.c +++ b/tests/core/history.c @@ -14,6 +14,7 @@ static void test_init() { struct queue *q = history_get_queue(); + idle_init(); filter_init(); tags_init(); playlist_init(NULL); diff --git a/tests/core/idle.c b/tests/core/idle.c index c54d5c33..0f025992 100644 --- a/tests/core/idle.c +++ b/tests/core/idle.c @@ -21,6 +21,8 @@ static void test_idle_queue(unsigned int n) { cur = -1; + idle_init(); + test_equal(idle_progress(), (float)1.0); test_equal(idle_run_task(), (bool)false); @@ -37,11 +39,18 @@ static void test_idle_queue(unsigned int n) test_equal(idle_run_task(), (bool)false); test_equal(idle_progress(), (float)1.0); + for (unsigned int i = n; i < (2 * n); i++) + idle_schedule(IDLE_ASYNC, inc_cur, GINT_TO_POINTER(i)); + while (idle_run_task()) {} + test_equal(idle_progress(), (float)1.0); + test_equal(func_passed, (bool)true); + test_equal(cur, (2 * n) - 1); + for (unsigned int i = 0; i < n; i++) idle_schedule(IDLE_SYNC, inc_cur, GINT_TO_POINTER(i)); test_equal(idle_progress(), (float)0.0); - idle_cancel(); + idle_deinit(); test_equal(idle_progress(), (float)1.0); } diff --git a/tests/core/tempq.c b/tests/core/tempq.c index 6026fba1..d7c2b113 100644 --- a/tests/core/tempq.c +++ b/tests/core/tempq.c @@ -13,6 +13,7 @@ static void test_init() { + idle_init(); filter_init(); tags_init(); playlist_init(NULL);