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:
parent
6a44f9e1a1
commit
6b52775e58
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
78
core/idle.c
78
core/idle.c
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue