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/filter.h>
#include <core/history.h>
#include <core/idle.h>
#include <core/playlist.h>
#include <core/tags/tags.h>
#include <core/tempq.h>
@ -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();
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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 */

View File

@ -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); /* <DIR>/Hyrule Symphony/ */
test_equal(idle_run_task(), (bool)false); /* <DIR>/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);

View File

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

View File

@ -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);
}

View File

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