]> www.fi.muni.cz Git - evince.git/blob - shell/ev-job-queue.c
a860f377f39b2d30a572c5bbc1c967ca0e526756
[evince.git] / shell / ev-job-queue.c
1 #include "ev-job-queue.h"
2
3 /* Like glib calling convention, all functions with _locked in their name assume
4  * that we've already locked the doc mutex and can freely and safely access
5  * data.
6  */
7 GCond *render_cond = NULL;
8 GMutex *ev_queue_mutex = NULL;
9
10 static GQueue *links_queue = NULL;
11 static GQueue *render_queue_high = NULL;
12 static GQueue *render_queue_low = NULL;
13 static GQueue *thumbnail_queue_high = NULL;
14 static GQueue *thumbnail_queue_low = NULL;
15 static GQueue *load_queue = NULL;
16 static GQueue *xfer_queue = NULL;
17 static GQueue *fonts_queue = NULL;
18
19 /* Queues used for backends supporting EvAsyncRender interface,
20    they are executed on the main thread */
21 static GQueue *async_render_queue_high = NULL;
22 static GQueue *async_render_queue_low = NULL;
23 static gboolean async_rendering = FALSE;
24
25 static void ev_job_queue_run_next (void);
26
27 static gboolean
28 remove_job_from_queue_locked (GQueue *queue, EvJob *job)
29 {
30         GList *list;
31
32         list = g_queue_find (queue, job);
33         if (list) {
34                 g_object_unref (G_OBJECT (job));
35                 g_queue_delete_link (queue, list);
36
37                 return TRUE;
38         }
39         return FALSE;
40 }
41
42 static gboolean
43 remove_job_from_async_queue (GQueue *queue, EvJob *job)
44 {
45         return remove_job_from_queue_locked (queue, job);
46 }
47
48 static void
49 add_job_to_async_queue (GQueue *queue, EvJob *job)
50 {
51         g_object_ref (job);
52         g_queue_push_tail (queue, job);
53 }
54
55 static void
56 add_job_to_queue_locked (GQueue *queue,
57                          EvJob  *job)
58 {
59         g_object_ref (job);
60         g_queue_push_tail (queue, job);
61         g_cond_broadcast (render_cond);
62 }
63
64 static gboolean
65 notify_finished (GObject *job)
66 {
67         ev_job_finished (EV_JOB (job));
68
69         return FALSE;
70 }
71
72 static void
73 job_finished_cb (EvJob *job)
74 {
75         g_object_unref (job);
76         async_rendering = FALSE;
77         ev_job_queue_run_next ();
78 }
79
80 static void
81 handle_job (EvJob *job)
82 {
83         g_object_ref (G_OBJECT (job));
84
85         if (EV_JOB (job)->async) {
86                 async_rendering = TRUE;
87                 if (EV_IS_JOB_RENDER (job)) {
88                         g_signal_connect (job, "finished",
89                                           G_CALLBACK (job_finished_cb), NULL);
90                 } else {
91                         g_assert_not_reached ();
92                 }
93         }
94
95         if (EV_IS_JOB_THUMBNAIL (job))
96                 ev_job_thumbnail_run (EV_JOB_THUMBNAIL (job));
97         else if (EV_IS_JOB_LINKS (job))
98                 ev_job_links_run (EV_JOB_LINKS (job));
99         else if (EV_IS_JOB_LOAD (job))
100                 ev_job_load_run (EV_JOB_LOAD (job));
101         else if (EV_IS_JOB_XFER (job))
102                 ev_job_xfer_run (EV_JOB_XFER (job));
103         else if (EV_IS_JOB_RENDER (job))
104                 ev_job_render_run (EV_JOB_RENDER (job));
105         else if (EV_IS_JOB_FONTS (job))
106                 ev_job_fonts_run (EV_JOB_FONTS (job));
107
108         if (!EV_JOB (job)->async) {
109                 /* We let the idle own a ref, as we (the queue) are done with the job. */
110                 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
111                                  (GSourceFunc) notify_finished,
112                                  job,
113                                  g_object_unref);
114         }
115 }
116
117 static EvJob *
118 search_for_jobs_unlocked (void)
119 {
120         EvJob *job;
121
122         job = (EvJob *) g_queue_pop_head (render_queue_high);
123         if (job)
124                 return job;
125
126         job = (EvJob *) g_queue_pop_head (thumbnail_queue_high);
127         if (job)
128                 return job;
129
130         job = (EvJob *) g_queue_pop_head (render_queue_low);
131         if (job)
132                 return job;
133
134         job = (EvJob *) g_queue_pop_head (links_queue);
135         if (job)
136                 return job;
137
138         job = (EvJob *) g_queue_pop_head (load_queue);
139         if (job)
140                 return job;
141
142         job = (EvJob *) g_queue_pop_head (xfer_queue);
143         if (job)
144                 return job;
145
146         job = (EvJob *) g_queue_pop_head (thumbnail_queue_low);
147         if (job)
148                 return job;
149
150         job = (EvJob *) g_queue_pop_head (fonts_queue);
151         if (job)
152                 return job;
153
154         return NULL;
155 }
156
157 static gboolean
158 no_jobs_available_unlocked (void)
159 {
160         return g_queue_is_empty (render_queue_high)
161                 && g_queue_is_empty (render_queue_low)
162                 && g_queue_is_empty (links_queue)
163                 && g_queue_is_empty (load_queue)
164                 && g_queue_is_empty (xfer_queue)
165                 && g_queue_is_empty (thumbnail_queue_high)
166                 && g_queue_is_empty (thumbnail_queue_low)
167                 && g_queue_is_empty (fonts_queue);
168 }
169
170 /* the thread mainloop function */
171 static gpointer
172 ev_render_thread (gpointer data)
173 {
174         while (TRUE) {
175                 EvJob *job;
176
177                 g_mutex_lock (ev_queue_mutex);
178                 if (no_jobs_available_unlocked ()) {
179                         g_cond_wait (render_cond, ev_queue_mutex);
180                 }
181
182                 job = search_for_jobs_unlocked ();
183                 g_mutex_unlock (ev_queue_mutex);
184
185                 /* Now that we have our job, we handle it */
186                 if (job) {
187                         handle_job (job);
188                         g_object_unref (G_OBJECT (job));
189                 }
190         }
191         return NULL;
192
193 }
194
195 static void
196 ev_job_queue_run_next (void)
197 {
198         EvJob *job;
199
200         job = (EvJob *) g_queue_pop_head (async_render_queue_high);
201
202         if (job == NULL) {
203                 job = (EvJob *) g_queue_pop_head (async_render_queue_low);
204         }
205
206         /* Now that we have our job, we handle it */
207         if (job) {
208                 handle_job (job);
209                 g_object_unref (G_OBJECT (job));
210         }
211 }
212
213 /* Public Functions */
214 void
215 ev_job_queue_init (void)
216 {
217         if (!g_thread_supported ()) g_thread_init (NULL);
218
219         render_cond = g_cond_new ();
220         ev_queue_mutex = g_mutex_new ();
221
222         links_queue = g_queue_new ();
223         load_queue = g_queue_new ();
224         xfer_queue = g_queue_new ();
225         render_queue_high = g_queue_new ();
226         render_queue_low = g_queue_new ();
227         async_render_queue_high = g_queue_new ();
228         async_render_queue_low = g_queue_new ();
229         thumbnail_queue_high = g_queue_new ();
230         thumbnail_queue_low = g_queue_new ();
231         fonts_queue = g_queue_new ();
232
233         g_thread_create (ev_render_thread, NULL, FALSE, NULL);
234
235 }
236
237 static GQueue *
238 find_queue (EvJob         *job,
239             EvJobPriority  priority)
240 {
241         if (EV_JOB (job)->async) {
242                 if (EV_IS_JOB_RENDER (job)) {
243                         if (priority == EV_JOB_PRIORITY_HIGH)
244                                 return async_render_queue_high;
245                         else
246                                 return async_render_queue_low;
247                 }
248         } else {
249                 if (EV_IS_JOB_RENDER (job)) {
250                         if (priority == EV_JOB_PRIORITY_HIGH)
251                                 return render_queue_high;
252                         else
253                                 return render_queue_low;
254                 } else if (EV_IS_JOB_THUMBNAIL (job)) {
255                         if (priority == EV_JOB_PRIORITY_HIGH)
256                                 return thumbnail_queue_high;
257                         else
258                                 return thumbnail_queue_low;
259                 } else if (EV_IS_JOB_LOAD (job)) {
260                         /* the priority doesn't effect load */
261                         return load_queue;
262                 } else if (EV_IS_JOB_XFER (job)) {
263                         /* the priority doesn't effect xfer */
264                         return xfer_queue;
265                 } else if (EV_IS_JOB_LINKS (job)) {
266                         /* the priority doesn't effect links */
267                         return links_queue;
268                 } else if (EV_IS_JOB_FONTS (job)) {
269                         /* the priority doesn't effect fonts */
270                         return fonts_queue;
271                 }
272         }
273
274         g_assert_not_reached ();
275         return NULL;
276 }
277
278 void
279 ev_job_queue_add_job (EvJob         *job,
280                       EvJobPriority  priority)
281 {
282         GQueue *queue;
283
284         g_return_if_fail (EV_IS_JOB (job));
285
286         queue = find_queue (job, priority);
287
288         if (!EV_JOB (job)->async) {
289                 g_mutex_lock (ev_queue_mutex);
290                 add_job_to_queue_locked (queue, job);
291                 g_mutex_unlock (ev_queue_mutex);
292         } else {
293                 add_job_to_async_queue (queue, job);
294                 if (!async_rendering) {
295                         ev_job_queue_run_next ();
296                 }
297         }
298 }
299
300 static gboolean
301 move_job_async (EvJob *job, GQueue *old_queue, GQueue *new_queue)
302 {
303         gboolean retval = FALSE;
304
305         g_object_ref (job);
306
307         if (remove_job_from_queue_locked (old_queue, job)) {
308                 add_job_to_async_queue (new_queue, job);
309                 retval = TRUE;
310         }
311
312         g_object_unref (job);
313
314         return retval;
315 }
316
317 static gboolean
318 move_job (EvJob *job, GQueue *old_queue, GQueue *new_queue)
319 {
320         gboolean retval = FALSE;
321
322         g_mutex_lock (ev_queue_mutex);
323         g_object_ref (job);
324
325         if (remove_job_from_queue_locked (old_queue, job)) {
326                 add_job_to_queue_locked (new_queue, job);
327                 retval = TRUE;
328         }
329
330         g_object_unref (job);
331         g_mutex_unlock (ev_queue_mutex);
332
333         return retval;
334 }
335
336 gboolean
337 ev_job_queue_update_job (EvJob         *job,
338                          EvJobPriority  new_priority)
339 {
340         gboolean retval = FALSE;
341         
342         g_return_val_if_fail (EV_IS_JOB (job), FALSE);
343
344         if (EV_JOB (job)->async) {
345                 if (EV_IS_JOB_RENDER (job)) {
346                         if (new_priority == EV_JOB_PRIORITY_LOW) {
347                                 retval = move_job_async (job, async_render_queue_high,
348                                                          async_render_queue_low);
349                         } else if (new_priority == EV_JOB_PRIORITY_HIGH) {
350                                 retval = move_job_async (job, async_render_queue_low,
351                                                          async_render_queue_high);
352                         }
353                 } else {
354                         g_assert_not_reached ();
355                 }
356         } else {
357                 if (EV_IS_JOB_THUMBNAIL (job)) {
358                         if (new_priority == EV_JOB_PRIORITY_LOW) {
359                                 retval = move_job (job, thumbnail_queue_high,
360                                                    thumbnail_queue_low);
361                         } else if (new_priority == EV_JOB_PRIORITY_HIGH) {
362                                 retval = move_job (job, thumbnail_queue_low,
363                                                    thumbnail_queue_high);
364                         }
365                 } else if (EV_IS_JOB_RENDER (job)) {
366                         if (new_priority == EV_JOB_PRIORITY_LOW) {
367                                 retval = move_job (job, render_queue_high,
368                                                    render_queue_low);
369                         } else if (new_priority == EV_JOB_PRIORITY_HIGH) {
370                                 retval = move_job (job, render_queue_low,
371                                                    render_queue_high);
372                         }
373                 } else {
374                         g_assert_not_reached ();
375                 }
376         }       
377
378         return retval;
379 }
380
381 gboolean
382 ev_job_queue_remove_job (EvJob *job)
383 {
384         gboolean retval = FALSE;
385
386         g_return_val_if_fail (EV_IS_JOB (job), FALSE);
387
388         if (EV_JOB (job)->async) {
389                 if (EV_IS_JOB_RENDER (job)) {
390                         retval = remove_job_from_async_queue (async_render_queue_high, job);
391                         retval = retval || remove_job_from_async_queue (async_render_queue_low, job);
392                 } else {
393                         g_assert_not_reached ();
394                 }
395         } else {
396                 g_mutex_lock (ev_queue_mutex);
397
398                 if (EV_IS_JOB_THUMBNAIL (job)) {
399                         retval = remove_job_from_queue_locked (thumbnail_queue_high, job);
400                         retval = retval || remove_job_from_queue_locked (thumbnail_queue_low, job);
401                 } else if (EV_IS_JOB_RENDER (job)) {
402                         retval = remove_job_from_queue_locked (render_queue_high, job);
403                         retval = retval || remove_job_from_queue_locked (render_queue_low, job);
404                 } else if (EV_IS_JOB_LINKS (job)) {
405                         retval = remove_job_from_queue_locked (links_queue, job);
406                 } else if (EV_IS_JOB_LOAD (job)) {
407                         retval = remove_job_from_queue_locked (load_queue, job);
408                 } else if (EV_IS_JOB_XFER (job)) {
409                         retval = remove_job_from_queue_locked (xfer_queue, job);
410                 } else if (EV_IS_JOB_FONTS (job)) {
411                         retval = remove_job_from_queue_locked (fonts_queue, job);
412                 } else {
413                         g_assert_not_reached ();
414                 }
415
416                 g_mutex_unlock (ev_queue_mutex);
417         }
418         
419         return retval;
420 }
421
422