summaryrefslogtreecommitdiff
path: root/libview/ev-job-scheduler.c
diff options
context:
space:
mode:
Diffstat (limited to 'libview/ev-job-scheduler.c')
-rw-r--r--libview/ev-job-scheduler.c305
1 files changed, 305 insertions, 0 deletions
diff --git a/libview/ev-job-scheduler.c b/libview/ev-job-scheduler.c
new file mode 100644
index 00000000..f874832f
--- /dev/null
+++ b/libview/ev-job-scheduler.c
@@ -0,0 +1,305 @@
+/* ev-job-scheduler.c
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2008 Carlos Garcia Campos <[email protected]>
+ *
+ * Evince is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Evince is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "ev-debug.h"
+#include "ev-job-scheduler.h"
+
+typedef struct _EvSchedulerJob {
+ EvJob *job;
+ EvJobPriority priority;
+ GSList *job_link;
+} EvSchedulerJob;
+
+G_LOCK_DEFINE_STATIC(job_list);
+static GSList *job_list = NULL;
+
+static gpointer ev_job_thread_proxy (gpointer data);
+static void ev_scheduler_thread_job_cancelled (EvSchedulerJob *job,
+ GCancellable *cancellable);
+
+/* EvJobQueue */
+static GQueue queue_urgent = G_QUEUE_INIT;
+static GQueue queue_high = G_QUEUE_INIT;
+static GQueue queue_low = G_QUEUE_INIT;
+static GQueue queue_none = G_QUEUE_INIT;
+
+static GCond *job_queue_cond = NULL;
+static GMutex *job_queue_mutex = NULL;
+static GQueue *job_queue[EV_JOB_N_PRIORITIES] = {
+ &queue_urgent,
+ &queue_high,
+ &queue_low,
+ &queue_none
+};
+
+static void
+ev_job_queue_push (EvSchedulerJob *job,
+ EvJobPriority priority)
+{
+ ev_debug_message (DEBUG_JOBS, "%s priority %d", EV_GET_TYPE_NAME (job->job), priority);
+
+ g_mutex_lock (job_queue_mutex);
+
+ g_queue_push_tail (job_queue[priority], job);
+ g_cond_broadcast (job_queue_cond);
+
+ g_mutex_unlock (job_queue_mutex);
+}
+
+static EvSchedulerJob *
+ev_job_queue_get_next_unlocked (void)
+{
+ gint i;
+ EvSchedulerJob *job = NULL;
+
+ for (i = EV_JOB_PRIORITY_URGENT; i < EV_JOB_N_PRIORITIES; i++) {
+ job = (EvSchedulerJob *) g_queue_pop_head (job_queue[i]);
+ if (job)
+ break;
+ }
+
+ ev_debug_message (DEBUG_JOBS, "%s", job ? EV_GET_TYPE_NAME (job->job) : "No jobs in queue");
+
+ return job;
+}
+
+static gpointer
+ev_job_scheduler_init (gpointer data)
+{
+ job_queue_cond = g_cond_new ();
+ job_queue_mutex = g_mutex_new ();
+ g_thread_create (ev_job_thread_proxy, NULL, FALSE, NULL);
+
+ return NULL;
+}
+
+static void
+ev_scheduler_job_list_add (EvSchedulerJob *job)
+{
+ ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
+
+ G_LOCK (job_list);
+
+ job_list = g_slist_prepend (job_list, job);
+ job->job_link = job_list;
+
+ G_UNLOCK (job_list);
+}
+
+static void
+ev_scheduler_job_list_remove (EvSchedulerJob *job)
+{
+ ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
+
+ G_LOCK (job_list);
+
+ job_list = g_slist_delete_link (job_list, job->job_link);
+
+ G_UNLOCK (job_list);
+}
+
+static void
+ev_scheduler_job_free (EvSchedulerJob *job)
+{
+ if (!job)
+ return;
+
+ g_object_unref (job->job);
+ g_free (job);
+}
+
+static void
+ev_scheduler_job_destroy (EvSchedulerJob *job)
+{
+ ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
+
+ if (job->job->run_mode == EV_JOB_RUN_MAIN_LOOP) {
+ g_signal_handlers_disconnect_by_func (job->job,
+ G_CALLBACK (ev_scheduler_job_destroy),
+ job);
+ } else {
+ g_signal_handlers_disconnect_by_func (job->job->cancellable,
+ G_CALLBACK (ev_scheduler_thread_job_cancelled),
+ job);
+ }
+
+ ev_scheduler_job_list_remove (job);
+ ev_scheduler_job_free (job);
+}
+
+static void
+ev_scheduler_thread_job_cancelled (EvSchedulerJob *job,
+ GCancellable *cancellable)
+{
+ GList *list;
+
+ ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
+
+ g_mutex_lock (job_queue_mutex);
+
+ /* If the job is not still running,
+ * remove it from the job queue and job list.
+ * If the job is currently running, it will be
+ * destroyed as soon as it finishes.
+ */
+ list = g_queue_find (job_queue[job->priority], job);
+ if (list) {
+ g_queue_delete_link (job_queue[job->priority], list);
+ g_mutex_unlock (job_queue_mutex);
+ ev_scheduler_job_destroy (job);
+ } else {
+ g_mutex_unlock (job_queue_mutex);
+ }
+}
+
+static void
+ev_job_thread (EvJob *job)
+{
+ gboolean result;
+
+ ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job));
+
+ do {
+ if (g_cancellable_is_cancelled (job->cancellable))
+ result = FALSE;
+ else
+ result = ev_job_run (job);
+ } while (result);
+}
+
+static gboolean
+ev_job_idle (EvJob *job)
+{
+ ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job));
+
+ if (g_cancellable_is_cancelled (job->cancellable))
+ return FALSE;
+
+ return ev_job_run (job);
+}
+
+static gpointer
+ev_job_thread_proxy (gpointer data)
+{
+ while (TRUE) {
+ EvSchedulerJob *job;
+
+ g_mutex_lock (job_queue_mutex);
+ job = ev_job_queue_get_next_unlocked ();
+ if (!job) {
+ g_cond_wait (job_queue_cond, job_queue_mutex);
+ g_mutex_unlock (job_queue_mutex);
+ continue;
+ }
+ g_mutex_unlock (job_queue_mutex);
+
+ ev_job_thread (job->job);
+ ev_scheduler_job_destroy (job);
+ }
+
+ return NULL;
+}
+
+void
+ev_job_scheduler_push_job (EvJob *job,
+ EvJobPriority priority)
+{
+ static GOnce once_init = G_ONCE_INIT;
+ EvSchedulerJob *s_job;
+
+ g_once (&once_init, ev_job_scheduler_init, NULL);
+
+ ev_debug_message (DEBUG_JOBS, "%s pirority %d", EV_GET_TYPE_NAME (job), priority);
+
+ s_job = g_new0 (EvSchedulerJob, 1);
+ s_job->job = g_object_ref (job);
+ s_job->priority = priority;
+
+ ev_scheduler_job_list_add (s_job);
+
+ switch (ev_job_get_run_mode (job)) {
+ case EV_JOB_RUN_THREAD:
+ g_signal_connect_swapped (job->cancellable, "cancelled",
+ G_CALLBACK (ev_scheduler_thread_job_cancelled),
+ s_job);
+ ev_job_queue_push (s_job, priority);
+ break;
+ case EV_JOB_RUN_MAIN_LOOP:
+ g_signal_connect_swapped (job, "finished",
+ G_CALLBACK (ev_scheduler_job_destroy),
+ s_job);
+ g_signal_connect_swapped (job, "cancelled",
+ G_CALLBACK (ev_scheduler_job_destroy),
+ s_job);
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+ (GSourceFunc)ev_job_idle,
+ g_object_ref (job),
+ (GDestroyNotify)g_object_unref);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+void
+ev_job_scheduler_update_job (EvJob *job,
+ EvJobPriority priority)
+{
+ GSList *l;
+ EvSchedulerJob *s_job = NULL;
+ gboolean need_resort = FALSE;
+
+ /* Main loop jobs are scheduled inmediately */
+ if (ev_job_get_run_mode (job) == EV_JOB_RUN_MAIN_LOOP)
+ return;
+
+ ev_debug_message (DEBUG_JOBS, "%s pirority %d", EV_GET_TYPE_NAME (job), priority);
+
+ G_LOCK (job_list);
+
+ for (l = job_list; l; l = l->next) {
+ s_job = (EvSchedulerJob *)l->data;
+
+ if (s_job->job == job) {
+ need_resort = (s_job->priority != priority);
+ break;
+ }
+ }
+
+ G_UNLOCK (job_list);
+
+ if (need_resort) {
+ GList *list;
+
+ g_mutex_lock (job_queue_mutex);
+
+ list = g_queue_find (job_queue[s_job->priority], s_job);
+ if (list) {
+ ev_debug_message (DEBUG_JOBS, "Moving job %s from pirority %d to %d",
+ EV_GET_TYPE_NAME (job), s_job->priority, priority);
+ g_queue_delete_link (job_queue[s_job->priority], list);
+ g_queue_push_tail (job_queue[priority], s_job);
+ g_cond_broadcast (job_queue_cond);
+ }
+
+ g_mutex_unlock (job_queue_mutex);
+ }
+}
+