]> www.fi.muni.cz Git - evince.git/blob - libview/ev-job-scheduler.c
900714eec77c5f7920a5e6b0bdcc6b9ca0b75485
[evince.git] / libview / ev-job-scheduler.c
1 /* ev-job-scheduler.c
2  *  this file is part of evince, a gnome document viewer
3  *
4  * Copyright (C) 2008 Carlos Garcia Campos <carlosgc@gnome.org>
5  *
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.
10  *
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.
15  *
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include "ev-debug.h"
22 #include "ev-job-scheduler.h"
23
24 typedef struct _EvSchedulerJob {
25         EvJob         *job;
26         EvJobPriority  priority;
27         GSList        *job_link;
28 } EvSchedulerJob;
29
30 G_LOCK_DEFINE_STATIC(job_list);
31 static GSList *job_list = NULL;
32
33 static gpointer ev_job_thread_proxy               (gpointer        data);
34 static void     ev_scheduler_thread_job_cancelled (EvSchedulerJob *job,
35                                                    GCancellable   *cancellable);
36
37 /* EvJobQueue */
38 static GQueue queue_urgent = G_QUEUE_INIT;
39 static GQueue queue_high = G_QUEUE_INIT;
40 static GQueue queue_low = G_QUEUE_INIT;
41 static GQueue queue_none = G_QUEUE_INIT;
42
43 static GCond *job_queue_cond = NULL;
44 static GMutex *job_queue_mutex = NULL;
45 static GQueue *job_queue[EV_JOB_N_PRIORITIES] = {
46         &queue_urgent,
47         &queue_high,
48         &queue_low,
49         &queue_none
50 };
51
52 static void
53 ev_job_queue_push (EvSchedulerJob *job,
54                    EvJobPriority   priority)
55 {
56         ev_debug_message (DEBUG_JOBS, "%s priority %d", EV_GET_TYPE_NAME (job->job), priority);
57         
58         g_mutex_lock (job_queue_mutex);
59
60         g_queue_push_tail (job_queue[priority], job);
61         g_cond_broadcast (job_queue_cond);
62         
63         g_mutex_unlock (job_queue_mutex);
64 }
65
66 static EvSchedulerJob *
67 ev_job_queue_get_next_unlocked (void)
68 {
69         gint i;
70         EvSchedulerJob *job = NULL;
71         
72         for (i = EV_JOB_PRIORITY_URGENT; i < EV_JOB_N_PRIORITIES; i++) {
73                 job = (EvSchedulerJob *) g_queue_pop_head (job_queue[i]);
74                 if (job)
75                         break;
76         }
77
78         ev_debug_message (DEBUG_JOBS, "%s", job ? EV_GET_TYPE_NAME (job->job) : "No jobs in queue");
79
80         return job;
81 }
82
83 static gpointer
84 ev_job_scheduler_init (gpointer data)
85 {
86         job_queue_cond = g_cond_new ();
87         job_queue_mutex = g_mutex_new ();
88         g_thread_create (ev_job_thread_proxy, NULL, FALSE, NULL);
89         
90         return NULL;
91 }
92
93 static void
94 ev_scheduler_job_list_add (EvSchedulerJob *job)
95 {
96         ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
97         
98         G_LOCK (job_list);
99
100         job_list = g_slist_prepend (job_list, job);
101         job->job_link = job_list;
102         
103         G_UNLOCK (job_list);
104 }
105
106 static void
107 ev_scheduler_job_list_remove (EvSchedulerJob *job)
108 {
109         ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
110         
111         G_LOCK (job_list);
112
113         job_list = g_slist_delete_link (job_list, job->job_link);
114         
115         G_UNLOCK (job_list);
116 }
117
118 static void
119 ev_scheduler_job_free (EvSchedulerJob *job)
120 {
121         if (!job)
122                 return;
123
124         g_object_unref (job->job);
125         g_free (job);
126 }
127
128 static void
129 ev_scheduler_job_destroy (EvSchedulerJob *job)
130 {
131         ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
132
133         if (job->job->run_mode == EV_JOB_RUN_MAIN_LOOP) {
134                 g_signal_handlers_disconnect_by_func (job->job, 
135                                                       G_CALLBACK (ev_scheduler_job_destroy),
136                                                       job);
137         } else {
138                 g_signal_handlers_disconnect_by_func (job->job->cancellable,
139                                                       G_CALLBACK (ev_scheduler_thread_job_cancelled),
140                                                       job);
141         }
142         
143         ev_scheduler_job_list_remove (job);
144         ev_scheduler_job_free (job);
145 }
146
147 static void
148 ev_scheduler_thread_job_cancelled (EvSchedulerJob *job,
149                                    GCancellable   *cancellable)
150 {
151         GList   *list;
152         
153         ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
154
155         g_mutex_lock (job_queue_mutex);
156
157         /* If the job is not still running,
158          * remove it from the job queue and job list.
159          * If the job is currently running, it will be
160          * destroyed as soon as it finishes. 
161          */
162         list = g_queue_find (job_queue[job->priority], job);
163         if (list) {
164                 g_queue_delete_link (job_queue[job->priority], list);
165                 g_mutex_unlock (job_queue_mutex);
166                 ev_scheduler_job_destroy (job);
167         } else {
168                 g_mutex_unlock (job_queue_mutex);
169         }
170 }
171
172 static void
173 ev_job_thread (EvJob *job)
174 {
175         gboolean result;
176
177         ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job));
178
179         do {
180                 if (g_cancellable_is_cancelled (job->cancellable))
181                         result = FALSE;
182                 else
183                         result = ev_job_run (job);
184         } while (result);
185 }
186
187 static gboolean
188 ev_job_idle (EvJob *job)
189 {
190         ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job));
191
192         if (g_cancellable_is_cancelled (job->cancellable))
193                 return FALSE;
194
195         return ev_job_run (job);
196 }
197
198 static gpointer
199 ev_job_thread_proxy (gpointer data)
200 {
201         while (TRUE) {
202                 EvSchedulerJob *job;
203
204                 g_mutex_lock (job_queue_mutex);
205                 job = ev_job_queue_get_next_unlocked ();
206                 if (!job) {
207                         g_cond_wait (job_queue_cond, job_queue_mutex);
208                         g_mutex_unlock (job_queue_mutex);
209                         continue;
210                 }
211                 g_mutex_unlock (job_queue_mutex);
212                 
213                 ev_job_thread (job->job);
214                 ev_scheduler_job_destroy (job);
215         }
216
217         return NULL;
218 }
219
220 void
221 ev_job_scheduler_push_job (EvJob         *job,
222                            EvJobPriority  priority)
223 {
224         static GOnce once_init = G_ONCE_INIT;
225         EvSchedulerJob *s_job;
226
227         g_once (&once_init, ev_job_scheduler_init, NULL);
228
229         ev_debug_message (DEBUG_JOBS, "%s pirority %d", EV_GET_TYPE_NAME (job), priority);
230
231         s_job = g_new0 (EvSchedulerJob, 1);
232         s_job->job = g_object_ref (job);
233         s_job->priority = priority;
234
235         ev_scheduler_job_list_add (s_job);
236         
237         switch (ev_job_get_run_mode (job)) {
238         case EV_JOB_RUN_THREAD:
239                 g_signal_connect_swapped (job->cancellable, "cancelled",
240                                           G_CALLBACK (ev_scheduler_thread_job_cancelled),
241                                           s_job);
242                 ev_job_queue_push (s_job, priority);
243                 break;
244         case EV_JOB_RUN_MAIN_LOOP:
245                 g_signal_connect_swapped (job, "finished",
246                                           G_CALLBACK (ev_scheduler_job_destroy),
247                                           s_job);
248                 g_signal_connect_swapped (job, "cancelled",
249                                           G_CALLBACK (ev_scheduler_job_destroy),
250                                           s_job);
251                 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
252                                  (GSourceFunc)ev_job_idle,
253                                  g_object_ref (job),
254                                  (GDestroyNotify)g_object_unref);
255                 break;
256         default:
257                 g_assert_not_reached ();
258         }
259 }
260
261 void
262 ev_job_scheduler_update_job (EvJob         *job,
263                              EvJobPriority  priority)
264 {
265         GSList         *l;
266         EvSchedulerJob *s_job = NULL;
267         gboolean        need_resort = FALSE;
268
269         /* Main loop jobs are scheduled inmediately */
270         if (ev_job_get_run_mode (job) == EV_JOB_RUN_MAIN_LOOP)
271                 return;
272
273         ev_debug_message (DEBUG_JOBS, "%s pirority %d", EV_GET_TYPE_NAME (job), priority);
274         
275         G_LOCK (job_list);
276
277         for (l = job_list; l; l = l->next) {
278                 s_job = (EvSchedulerJob *)l->data;
279
280                 if (s_job->job == job) {
281                         need_resort = (s_job->priority != priority);
282                         break;
283                 }
284         }
285         
286         G_UNLOCK (job_list);
287
288         if (need_resort) {
289                 GList *list;
290         
291                 g_mutex_lock (job_queue_mutex);
292                 
293                 list = g_queue_find (job_queue[s_job->priority], s_job);
294                 if (list) {
295                         ev_debug_message (DEBUG_JOBS, "Moving job %s from pirority %d to %d",
296                                           EV_GET_TYPE_NAME (job), s_job->priority, priority);
297                         g_queue_delete_link (job_queue[s_job->priority], list);
298                         g_queue_push_tail (job_queue[priority], s_job);
299                         g_cond_broadcast (job_queue_cond);
300                 }
301                 
302                 g_mutex_unlock (job_queue_mutex);
303         }
304 }
305