core/idle: Add support for running idle tasks in a new thread

Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
This commit is contained in:
Anna Schumaker 2016-04-30 10:11:48 -04:00
parent 6a44f9e1a1
commit 6b52775e58
8 changed files with 79 additions and 35 deletions

View File

@ -6,6 +6,7 @@
#include <core/collection.h> #include <core/collection.h>
#include <core/filter.h> #include <core/filter.h>
#include <core/history.h> #include <core/history.h>
#include <core/idle.h>
#include <core/playlist.h> #include <core/playlist.h>
#include <core/tags/tags.h> #include <core/tags/tags.h>
#include <core/tempq.h> #include <core/tempq.h>
@ -13,6 +14,7 @@
void core_init(int *argc, char ***argv, struct core_init_data *init) void core_init(int *argc, char ***argv, struct core_init_data *init)
{ {
idle_init();
filter_init(); filter_init();
tags_init(); tags_init();
playlist_init(init->playlist_ops); playlist_init(init->playlist_ops);
@ -31,4 +33,5 @@ void core_deinit()
playlist_deinit(); playlist_deinit();
tags_deinit(); tags_deinit();
filter_deinit(); filter_deinit();
idle_deinit();
} }

View File

@ -8,21 +8,58 @@
struct idle_task { struct idle_task {
void (*idle_func)(void *); void (*idle_func)(void *);
void *idle_data; void *idle_data;
enum idle_sync_t idle_sync;
}; };
static GQueue idle_queue = G_QUEUE_INIT; static GThreadPool *idle_pool = NULL;
static float queued = 0.0; static GQueue idle_queue = G_QUEUE_INIT;
static float serviced = 0.0; 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) void idle_schedule(enum idle_sync_t sync, void (*func)(void *), void *data)
{ {
struct idle_task *task = g_malloc(sizeof(struct idle_task)); struct idle_task *task = g_malloc(sizeof(struct idle_task));
task->idle_func = func; task->idle_func = func;
task->idle_data = data; task->idle_data = data;
task->idle_sync = sync;
g_queue_push_tail(&idle_queue, task); g_queue_push_tail(&idle_queue, task);
queued++; g_atomic_int_inc(&queued);
} }
bool idle_run_task() bool idle_run_task()
@ -31,31 +68,24 @@ bool idle_run_task()
if (!g_queue_is_empty(&idle_queue)) { if (!g_queue_is_empty(&idle_queue)) {
task = g_queue_pop_head(&idle_queue); task = g_queue_pop_head(&idle_queue);
task->idle_func(task->idle_data); if (task->idle_sync == IDLE_ASYNC)
g_free(task); g_thread_pool_push(idle_pool, task, NULL);
serviced++; else
__idle_run_task(task);
} }
if (g_queue_is_empty(&idle_queue)) { if (g_atomic_int_get(&queued) != g_atomic_int_get(&serviced))
queued = 0.0; return true;
serviced = 0.0;
} queued = 0;
return !g_queue_is_empty(&idle_queue); serviced = 0;
return false;
} }
float idle_progress() 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 1.0;
return serviced / queued; return (float)g_atomic_int_get(&serviced) / (float)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);
}
} }

View File

@ -13,7 +13,7 @@ static gboolean __on_idle(gpointer data)
if (idle_run_task()) { if (idle_run_task()) {
gtk_progress_bar_set_fraction(progress, idle_progress()); gtk_progress_bar_set_fraction(progress, idle_progress());
return gtk_widget_is_visible(gui_builder_widget("o_window")); return G_SOURCE_CONTINUE;
} else { } else {
gtk_widget_hide(GTK_WIDGET(progress)); gtk_widget_hide(GTK_WIDGET(progress));
idle_id = 0; idle_id = 0;
@ -33,5 +33,4 @@ void gui_idle_disable()
{ {
if (idle_id) if (idle_id)
g_source_remove(idle_id); g_source_remove(idle_id);
idle_cancel();
} }

View File

@ -17,10 +17,18 @@
enum idle_sync_t { enum idle_sync_t {
IDLE_SYNC, /* Run task in the main thread. */ IDLE_SYNC, /* Run task in the main thread. */
IDLE_ASYNC, /* Run task in a separate thread. */
}; };
#define IDLE_FUNC(x) ((void (*)(void *))x) #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. */ /* Called to schedule a function to run later. */
void idle_schedule(enum idle_sync_t, void (*)(void *), void *); 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. */ /* Called to find the percentage of idle tasks that have been run. */
float idle_progress(); float idle_progress();
/* Called to cancel all idle tasks on the queue. */
void idle_cancel();
#endif /* OCARINA_CORE_IDLE_H */ #endif /* OCARINA_CORE_IDLE_H */

View File

@ -57,11 +57,7 @@ static void test_add()
test_equal(lib->li_path, "tests/Music"); test_equal(lib->li_path, "tests/Music");
test_equal(queue_size(q), 0); test_equal(queue_size(q), 0);
test_equal(idle_run_task(), (bool)true); /* collection validation */ while (idle_run_task()) {};
test_equal(idle_run_task(), (bool)true); /* tests/Music/ */
test_equal(idle_run_task(), (bool)true); /* <DIR>/Hyrule Symphony/ */
test_equal(idle_run_task(), (bool)false); /* <DIR>/Ocarina of Time/ */
test_equal(track_db_get()->db_size, 48); test_equal(track_db_get()->db_size, 48);
test_equal(lib->li_size, 48); test_equal(lib->li_size, 48);
test_equal(queue_size(q), 48); test_equal(queue_size(q), 48);

View File

@ -14,6 +14,7 @@ static void test_init()
{ {
struct queue *q = history_get_queue(); struct queue *q = history_get_queue();
idle_init();
filter_init(); filter_init();
tags_init(); tags_init();
playlist_init(NULL); playlist_init(NULL);

View File

@ -21,6 +21,8 @@ static void test_idle_queue(unsigned int n)
{ {
cur = -1; cur = -1;
idle_init();
test_equal(idle_progress(), (float)1.0); test_equal(idle_progress(), (float)1.0);
test_equal(idle_run_task(), (bool)false); 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_run_task(), (bool)false);
test_equal(idle_progress(), (float)1.0); 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++) for (unsigned int i = 0; i < n; i++)
idle_schedule(IDLE_SYNC, inc_cur, GINT_TO_POINTER(i)); idle_schedule(IDLE_SYNC, inc_cur, GINT_TO_POINTER(i));
test_equal(idle_progress(), (float)0.0); test_equal(idle_progress(), (float)0.0);
idle_cancel(); idle_deinit();
test_equal(idle_progress(), (float)1.0); test_equal(idle_progress(), (float)1.0);
} }

View File

@ -13,6 +13,7 @@
static void test_init() static void test_init()
{ {
idle_init();
filter_init(); filter_init();
tags_init(); tags_init();
playlist_init(NULL); playlist_init(NULL);