2 * this file is part of evince, a gnome document viewer
4 * Copyright (C) 2008 Carlos Garcia Campos <carlosgc@gnome.org>
6 * Evince is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Evince is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #include "ev-job-scheduler.h"
24 typedef struct _EvSchedulerJob {
26 EvJobPriority priority;
30 G_LOCK_DEFINE_STATIC(job_list);
31 static GSList *job_list = NULL;
33 static volatile EvJob *running_job = NULL;
35 static gpointer ev_job_thread_proxy (gpointer data);
36 static void ev_scheduler_thread_job_cancelled (EvSchedulerJob *job,
37 GCancellable *cancellable);
40 static GQueue queue_urgent = G_QUEUE_INIT;
41 static GQueue queue_high = G_QUEUE_INIT;
42 static GQueue queue_low = G_QUEUE_INIT;
43 static GQueue queue_none = G_QUEUE_INIT;
45 static GCond *job_queue_cond = NULL;
46 static GMutex *job_queue_mutex = NULL;
47 static GQueue *job_queue[EV_JOB_N_PRIORITIES] = {
55 ev_job_queue_push (EvSchedulerJob *job,
56 EvJobPriority priority)
58 ev_debug_message (DEBUG_JOBS, "%s priority %d", EV_GET_TYPE_NAME (job->job), priority);
60 g_mutex_lock (job_queue_mutex);
62 g_queue_push_tail (job_queue[priority], job);
63 g_cond_broadcast (job_queue_cond);
65 g_mutex_unlock (job_queue_mutex);
68 static EvSchedulerJob *
69 ev_job_queue_get_next_unlocked (void)
72 EvSchedulerJob *job = NULL;
74 for (i = EV_JOB_PRIORITY_URGENT; i < EV_JOB_N_PRIORITIES; i++) {
75 job = (EvSchedulerJob *) g_queue_pop_head (job_queue[i]);
80 ev_debug_message (DEBUG_JOBS, "%s", job ? EV_GET_TYPE_NAME (job->job) : "No jobs in queue");
86 ev_job_scheduler_init (gpointer data)
88 job_queue_cond = g_cond_new ();
89 job_queue_mutex = g_mutex_new ();
90 g_thread_create (ev_job_thread_proxy, NULL, FALSE, NULL);
96 ev_scheduler_job_list_add (EvSchedulerJob *job)
98 ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
102 job_list = g_slist_prepend (job_list, job);
103 job->job_link = job_list;
109 ev_scheduler_job_list_remove (EvSchedulerJob *job)
111 ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
115 job_list = g_slist_delete_link (job_list, job->job_link);
121 ev_scheduler_job_free (EvSchedulerJob *job)
126 g_object_unref (job->job);
131 ev_scheduler_job_destroy (EvSchedulerJob *job)
133 ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
135 if (job->job->run_mode == EV_JOB_RUN_MAIN_LOOP) {
136 g_signal_handlers_disconnect_by_func (job->job,
137 G_CALLBACK (ev_scheduler_job_destroy),
140 g_signal_handlers_disconnect_by_func (job->job->cancellable,
141 G_CALLBACK (ev_scheduler_thread_job_cancelled),
145 ev_scheduler_job_list_remove (job);
146 ev_scheduler_job_free (job);
150 ev_scheduler_thread_job_cancelled (EvSchedulerJob *job,
151 GCancellable *cancellable)
155 ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
157 g_mutex_lock (job_queue_mutex);
159 /* If the job is not still running,
160 * remove it from the job queue and job list.
161 * If the job is currently running, it will be
162 * destroyed as soon as it finishes.
164 list = g_queue_find (job_queue[job->priority], job);
166 g_queue_delete_link (job_queue[job->priority], list);
167 g_mutex_unlock (job_queue_mutex);
168 ev_scheduler_job_destroy (job);
170 g_mutex_unlock (job_queue_mutex);
175 ev_job_thread (EvJob *job)
179 ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job));
182 if (g_cancellable_is_cancelled (job->cancellable))
185 g_atomic_pointer_set (&running_job, job);
186 result = ev_job_run (job);
190 g_atomic_pointer_set (&running_job, NULL);
194 ev_job_idle (EvJob *job)
196 ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job));
198 if (g_cancellable_is_cancelled (job->cancellable))
201 return ev_job_run (job);
205 ev_job_thread_proxy (gpointer data)
210 g_mutex_lock (job_queue_mutex);
211 job = ev_job_queue_get_next_unlocked ();
213 g_cond_wait (job_queue_cond, job_queue_mutex);
214 g_mutex_unlock (job_queue_mutex);
217 g_mutex_unlock (job_queue_mutex);
219 ev_job_thread (job->job);
220 ev_scheduler_job_destroy (job);
227 ev_job_scheduler_push_job (EvJob *job,
228 EvJobPriority priority)
230 static GOnce once_init = G_ONCE_INIT;
231 EvSchedulerJob *s_job;
233 g_once (&once_init, ev_job_scheduler_init, NULL);
235 ev_debug_message (DEBUG_JOBS, "%s pirority %d", EV_GET_TYPE_NAME (job), priority);
237 s_job = g_new0 (EvSchedulerJob, 1);
238 s_job->job = g_object_ref (job);
239 s_job->priority = priority;
241 ev_scheduler_job_list_add (s_job);
243 switch (ev_job_get_run_mode (job)) {
244 case EV_JOB_RUN_THREAD:
245 g_signal_connect_swapped (job->cancellable, "cancelled",
246 G_CALLBACK (ev_scheduler_thread_job_cancelled),
248 ev_job_queue_push (s_job, priority);
250 case EV_JOB_RUN_MAIN_LOOP:
251 g_signal_connect_swapped (job, "finished",
252 G_CALLBACK (ev_scheduler_job_destroy),
254 g_signal_connect_swapped (job, "cancelled",
255 G_CALLBACK (ev_scheduler_job_destroy),
257 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
258 (GSourceFunc)ev_job_idle,
260 (GDestroyNotify)g_object_unref);
263 g_assert_not_reached ();
268 ev_job_scheduler_update_job (EvJob *job,
269 EvJobPriority priority)
272 EvSchedulerJob *s_job = NULL;
273 gboolean need_resort = FALSE;
275 /* Main loop jobs are scheduled inmediately */
276 if (ev_job_get_run_mode (job) == EV_JOB_RUN_MAIN_LOOP)
279 ev_debug_message (DEBUG_JOBS, "%s pirority %d", EV_GET_TYPE_NAME (job), priority);
283 for (l = job_list; l; l = l->next) {
284 s_job = (EvSchedulerJob *)l->data;
286 if (s_job->job == job) {
287 need_resort = (s_job->priority != priority);
297 g_mutex_lock (job_queue_mutex);
299 list = g_queue_find (job_queue[s_job->priority], s_job);
301 ev_debug_message (DEBUG_JOBS, "Moving job %s from pirority %d to %d",
302 EV_GET_TYPE_NAME (job), s_job->priority, priority);
303 g_queue_delete_link (job_queue[s_job->priority], list);
304 g_queue_push_tail (job_queue[priority], s_job);
305 g_cond_broadcast (job_queue_cond);
308 g_mutex_unlock (job_queue_mutex);
313 ev_job_scheduler_get_running_thread_job (void)
315 return g_atomic_pointer_get (&running_job);