]> www.fi.muni.cz Git - evince.git/blob - shell/ev-job-queue.c
Include config.h. Bug #504721.
[evince.git] / shell / ev-job-queue.c
1 #include <config.h>
2 #include "ev-job-queue.h"
3
4 /* Like glib calling convention, all functions with _locked in their name assume
5  * that we've already locked the doc mutex and can freely and safely access
6  * data.
7  */
8 GCond *render_cond = NULL;
9 GMutex *ev_queue_mutex = NULL;
10
11 static GQueue *links_queue = NULL;
12 static GQueue *render_queue_high = NULL;
13 static GQueue *render_queue_low = NULL;
14 static GQueue *thumbnail_queue_high = NULL;
15 static GQueue *thumbnail_queue_low = NULL;
16 static GQueue *load_queue = NULL;
17 static GQueue *save_queue = NULL;
18 static GQueue *fonts_queue = NULL;
19 static GQueue *print_queue = NULL;
20
21 /* Queues used for backends supporting EvAsyncRender interface,
22    they are executed on the main thread */
23 static GQueue *async_render_queue_high = NULL;
24 static GQueue *async_render_queue_low = NULL;
25 static gboolean async_rendering = FALSE;
26
27 static void ev_job_queue_run_next (void);
28
29 static gboolean
30 remove_job_from_queue_locked (GQueue *queue, EvJob *job)
31 {
32         GList *list;
33
34         list = g_queue_find (queue, job);
35         if (list) {
36                 g_object_unref (G_OBJECT (job));
37                 g_queue_delete_link (queue, list);
38
39                 return TRUE;
40         }
41         return FALSE;
42 }
43
44 static gboolean
45 remove_job_from_async_queue (GQueue *queue, EvJob *job)
46 {
47         return remove_job_from_queue_locked (queue, job);
48 }
49
50 static void
51 add_job_to_async_queue (GQueue *queue, EvJob *job)
52 {
53         g_object_ref (job);
54         g_queue_push_tail (queue, job);
55 }
56
57 /**
58  * add_job_to_queue_locked:
59  * @queue: a #GQueue where the #EvJob will be added.
60  * @job: an #EvJob to be added to the specified #GQueue.
61  *
62  * Add the #EvJob to the specified #GQueue and woke up the ev_render_thread who
63  * is waiting for render_cond.
64  */
65 static void
66 add_job_to_queue_locked (GQueue *queue,
67                          EvJob  *job)
68 {
69         g_object_ref (job);
70         g_queue_push_tail (queue, job);
71         g_cond_broadcast (render_cond);
72 }
73
74 /**
75  * notify_finished:
76  * @job: the object that signal will be reseted.
77  *
78  * It does emit the job finished signal and returns %FALSE.
79  *
80  * Returns: %FALSE.
81  */
82 static gboolean
83 notify_finished (GObject *job)
84 {
85         ev_job_finished (EV_JOB (job));
86
87         return FALSE;
88 }
89
90 /**
91  * job_finished_cb:
92  * @job: the #EvJob that has been handled.
93  *
94  * It does finish the job last work and look if there is any more job to be
95  * handled.
96  */
97 static void
98 job_finished_cb (EvJob *job)
99 {
100         g_object_unref (job);
101         async_rendering = FALSE;
102         ev_job_queue_run_next ();
103 }
104
105 /**
106  * handle_job:
107  * @job: the #EvJob to be handled.
108  *
109  * First, it does check if the job is async and then it does attend it if
110  * possible giving a failed assertion otherwise. If the job isn't async it does
111  * attend it and notify that the job has been finished.
112  */
113 static void
114 handle_job (EvJob *job)
115 {
116         g_object_ref (G_OBJECT (job));
117
118         if (EV_JOB (job)->async) {
119                 async_rendering = TRUE;
120                 if (EV_IS_JOB_RENDER (job)) {
121                         g_signal_connect (job, "finished",
122                                           G_CALLBACK (job_finished_cb), NULL);
123                 } else {
124                         g_assert_not_reached ();
125                 }
126         }
127
128         if (EV_IS_JOB_THUMBNAIL (job))
129                 ev_job_thumbnail_run (EV_JOB_THUMBNAIL (job));
130         else if (EV_IS_JOB_LINKS (job))
131                 ev_job_links_run (EV_JOB_LINKS (job));
132         else if (EV_IS_JOB_LOAD (job))
133                 ev_job_load_run (EV_JOB_LOAD (job));
134         else if (EV_IS_JOB_SAVE (job))
135                 ev_job_save_run (EV_JOB_SAVE (job));
136         else if (EV_IS_JOB_RENDER (job))
137                 ev_job_render_run (EV_JOB_RENDER (job));
138         else if (EV_IS_JOB_FONTS (job))
139                 ev_job_fonts_run (EV_JOB_FONTS (job));
140         else if (EV_IS_JOB_PRINT (job))
141                 ev_job_print_run (EV_JOB_PRINT (job));
142
143         if (!EV_JOB (job)->async) {
144                 /* We let the idle own a ref, as we (the queue) are done with the job. */
145                 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
146                                  (GSourceFunc) notify_finished,
147                                  job,
148                                  g_object_unref);
149         }
150 }
151
152 /**
153  * search_for_jobs_unlocked:
154  *
155  * Check if there is any job at the synchronized queues and return any existing
156  * job in them taking in account the next priority:
157  *
158  *  render_queue_high       >
159  *  thumbnail_queue_high    >
160  *  render_queue_low        >
161  *  links_queue             >
162  *  load_queue              >
163  *  thumbnail_queue_low     >
164  *  fonts_queue             >
165  *  print_queue             >
166  *
167  * Returns: an available #EvJob in the queues taking in account stablished queue
168  *          priorities.
169  */
170 static EvJob *
171 search_for_jobs_unlocked (void)
172 {
173         EvJob *job;
174
175         job = (EvJob *) g_queue_pop_head (render_queue_high);
176         if (job)
177                 return job;
178
179         job = (EvJob *) g_queue_pop_head (thumbnail_queue_high);
180         if (job)
181                 return job;
182
183         job = (EvJob *) g_queue_pop_head (render_queue_low);
184         if (job)
185                 return job;
186
187         job = (EvJob *) g_queue_pop_head (links_queue);
188         if (job)
189                 return job;
190
191         job = (EvJob *) g_queue_pop_head (load_queue);
192         if (job)
193                 return job;
194
195         job = (EvJob *) g_queue_pop_head (save_queue);
196         if (job)
197                 return job;
198
199         job = (EvJob *) g_queue_pop_head (thumbnail_queue_low);
200         if (job)
201                 return job;
202
203         job = (EvJob *) g_queue_pop_head (fonts_queue);
204         if (job)
205                 return job;
206
207         job = (EvJob *) g_queue_pop_head (print_queue);
208         if (job)
209                 return job;
210
211         return NULL;
212 }
213
214 /**
215  * no_jobs_available_unlocked:
216  *
217  * Looks if there is any job at render, links, load, thumbnail. fonts and print
218  * queues.
219  *
220  * Returns: %TRUE if the render, links, load, thumbnail, fonts and print queues
221  *          are empty, %FALSE in other case.
222  */
223 static gboolean
224 no_jobs_available_unlocked (void)
225 {
226         return g_queue_is_empty (render_queue_high)
227                 && g_queue_is_empty (render_queue_low)
228                 && g_queue_is_empty (links_queue)
229                 && g_queue_is_empty (load_queue)
230                 && g_queue_is_empty (save_queue)
231                 && g_queue_is_empty (thumbnail_queue_high)
232                 && g_queue_is_empty (thumbnail_queue_low)
233                 && g_queue_is_empty (fonts_queue)
234                 && g_queue_is_empty (print_queue);
235 }
236
237 /* the thread mainloop function */
238 /**
239  * ev_render_thread:
240  * @data: data passed to the thread.
241  *
242  * The thread mainloop function. It does wait for any available job in synced
243  * queues to handle it.
244  *
245  * Returns: the return value of the thread, which will be returned by
246  *          g_thread_join().
247  */
248 static gpointer
249 ev_render_thread (gpointer data)
250 {
251         while (TRUE) {
252                 EvJob *job;
253
254                 g_mutex_lock (ev_queue_mutex);
255                 if (no_jobs_available_unlocked ()) {
256                         g_cond_wait (render_cond, ev_queue_mutex);
257                 }
258
259                 job = search_for_jobs_unlocked ();
260                 g_mutex_unlock (ev_queue_mutex);
261
262                 /* Now that we have our job, we handle it */
263                 if (job) {
264                         handle_job (job);
265                         g_object_unref (G_OBJECT (job));
266                 }
267         }
268         return NULL;
269
270 }
271
272 /**
273  * ev_job_queue_run_next:
274  *
275  * It does look for any job on the render high priority queue first and after
276  * in the render low priority one, and then it does handle it.
277  */
278 static void
279 ev_job_queue_run_next (void)
280 {
281         EvJob *job;
282
283         job = (EvJob *) g_queue_pop_head (async_render_queue_high);
284
285         if (job == NULL) {
286                 job = (EvJob *) g_queue_pop_head (async_render_queue_low);
287         }
288
289         /* Now that we have our job, we handle it */
290         if (job) {
291                 handle_job (job);
292                 g_object_unref (G_OBJECT (job));
293         }
294 }
295
296 /* Public Functions */
297 /**
298  * ev_job_queue_init:
299  *
300  * Creates a new cond, new mutex, a thread for evince job handling and inits
301  * every queue.
302  */
303 void
304 ev_job_queue_init (void)
305 {
306         if (!g_thread_supported ()) g_thread_init (NULL);
307
308         render_cond = g_cond_new ();
309         ev_queue_mutex = g_mutex_new ();
310
311         links_queue = g_queue_new ();
312         load_queue = g_queue_new ();
313         save_queue = g_queue_new ();
314         render_queue_high = g_queue_new ();
315         render_queue_low = g_queue_new ();
316         async_render_queue_high = g_queue_new ();
317         async_render_queue_low = g_queue_new ();
318         thumbnail_queue_high = g_queue_new ();
319         thumbnail_queue_low = g_queue_new ();
320         fonts_queue = g_queue_new ();
321         print_queue = g_queue_new ();
322
323         g_thread_create (ev_render_thread, NULL, FALSE, NULL);
324
325 }
326
327 static GQueue *
328 find_queue (EvJob         *job,
329             EvJobPriority  priority)
330 {
331         if (EV_JOB (job)->async) {
332                 if (EV_IS_JOB_RENDER (job)) {
333                         if (priority == EV_JOB_PRIORITY_HIGH)
334                                 return async_render_queue_high;
335                         else
336                                 return async_render_queue_low;
337                 }
338         } else {
339                 if (EV_IS_JOB_RENDER (job)) {
340                         if (priority == EV_JOB_PRIORITY_HIGH)
341                                 return render_queue_high;
342                         else
343                                 return render_queue_low;
344                 } else if (EV_IS_JOB_THUMBNAIL (job)) {
345                         if (priority == EV_JOB_PRIORITY_HIGH)
346                                 return thumbnail_queue_high;
347                         else
348                                 return thumbnail_queue_low;
349                 } else if (EV_IS_JOB_LOAD (job)) {
350                         /* the priority doesn't effect load */
351                         return load_queue;
352                 } else if (EV_IS_JOB_SAVE (job)) {
353                         /* the priority doesn't effect save */
354                         return save_queue;
355                 } else if (EV_IS_JOB_LINKS (job)) {
356                         /* the priority doesn't effect links */
357                         return links_queue;
358                 } else if (EV_IS_JOB_FONTS (job)) {
359                         /* the priority doesn't effect fonts */
360                         return fonts_queue;
361                 } else if (EV_IS_JOB_PRINT (job)) {
362                         /* the priority doesn't effect print */
363                         return print_queue;
364                 }
365         }
366
367         g_assert_not_reached ();
368         return NULL;
369 }
370
371 void
372 ev_job_queue_add_job (EvJob         *job,
373                       EvJobPriority  priority)
374 {
375         GQueue *queue;
376
377         g_return_if_fail (EV_IS_JOB (job));
378
379         queue = find_queue (job, priority);
380
381         if (!EV_JOB (job)->async) {
382                 g_mutex_lock (ev_queue_mutex);
383                 add_job_to_queue_locked (queue, job);
384                 g_mutex_unlock (ev_queue_mutex);
385         } else {
386                 add_job_to_async_queue (queue, job);
387                 if (!async_rendering) {
388                         ev_job_queue_run_next ();
389                 }
390         }
391 }
392
393 static gboolean
394 move_job_async (EvJob *job, GQueue *old_queue, GQueue *new_queue)
395 {
396         gboolean retval = FALSE;
397
398         g_object_ref (job);
399
400         if (remove_job_from_queue_locked (old_queue, job)) {
401                 add_job_to_async_queue (new_queue, job);
402                 retval = TRUE;
403         }
404
405         g_object_unref (job);
406
407         return retval;
408 }
409
410 static gboolean
411 move_job (EvJob *job, GQueue *old_queue, GQueue *new_queue)
412 {
413         gboolean retval = FALSE;
414
415         g_mutex_lock (ev_queue_mutex);
416         g_object_ref (job);
417
418         if (remove_job_from_queue_locked (old_queue, job)) {
419                 add_job_to_queue_locked (new_queue, job);
420                 retval = TRUE;
421         }
422
423         g_object_unref (job);
424         g_mutex_unlock (ev_queue_mutex);
425
426         return retval;
427 }
428
429 gboolean
430 ev_job_queue_update_job (EvJob         *job,
431                          EvJobPriority  new_priority)
432 {
433         gboolean retval = FALSE;
434         
435         g_return_val_if_fail (EV_IS_JOB (job), FALSE);
436
437         if (EV_JOB (job)->async) {
438                 if (EV_IS_JOB_RENDER (job)) {
439                         if (new_priority == EV_JOB_PRIORITY_LOW) {
440                                 retval = move_job_async (job, async_render_queue_high,
441                                                          async_render_queue_low);
442                         } else if (new_priority == EV_JOB_PRIORITY_HIGH) {
443                                 retval = move_job_async (job, async_render_queue_low,
444                                                          async_render_queue_high);
445                         }
446                 } else {
447                         g_assert_not_reached ();
448                 }
449         } else {
450                 if (EV_IS_JOB_THUMBNAIL (job)) {
451                         if (new_priority == EV_JOB_PRIORITY_LOW) {
452                                 retval = move_job (job, thumbnail_queue_high,
453                                                    thumbnail_queue_low);
454                         } else if (new_priority == EV_JOB_PRIORITY_HIGH) {
455                                 retval = move_job (job, thumbnail_queue_low,
456                                                    thumbnail_queue_high);
457                         }
458                 } else if (EV_IS_JOB_RENDER (job)) {
459                         if (new_priority == EV_JOB_PRIORITY_LOW) {
460                                 retval = move_job (job, render_queue_high,
461                                                    render_queue_low);
462                         } else if (new_priority == EV_JOB_PRIORITY_HIGH) {
463                                 retval = move_job (job, render_queue_low,
464                                                    render_queue_high);
465                         }
466                 } else {
467                         g_assert_not_reached ();
468                 }
469         }       
470
471         return retval;
472 }
473
474 gboolean
475 ev_job_queue_remove_job (EvJob *job)
476 {
477         gboolean retval = FALSE;
478
479         g_return_val_if_fail (EV_IS_JOB (job), FALSE);
480
481         if (EV_JOB (job)->async) {
482                 if (EV_IS_JOB_RENDER (job)) {
483                         retval = remove_job_from_async_queue (async_render_queue_high, job);
484                         retval = retval || remove_job_from_async_queue (async_render_queue_low, job);
485                 } else {
486                         g_assert_not_reached ();
487                 }
488         } else {
489                 g_mutex_lock (ev_queue_mutex);
490
491                 if (EV_IS_JOB_THUMBNAIL (job)) {
492                         retval = remove_job_from_queue_locked (thumbnail_queue_high, job);
493                         retval = retval || remove_job_from_queue_locked (thumbnail_queue_low, job);
494                 } else if (EV_IS_JOB_RENDER (job)) {
495                         retval = remove_job_from_queue_locked (render_queue_high, job);
496                         retval = retval || remove_job_from_queue_locked (render_queue_low, job);
497                 } else if (EV_IS_JOB_LINKS (job)) {
498                         retval = remove_job_from_queue_locked (links_queue, job);
499                 } else if (EV_IS_JOB_LOAD (job)) {
500                         retval = remove_job_from_queue_locked (load_queue, job);
501                 } else if (EV_IS_JOB_SAVE (job)) {
502                         retval = remove_job_from_queue_locked (save_queue, job);
503                 } else if (EV_IS_JOB_FONTS (job)) {
504                         retval = remove_job_from_queue_locked (fonts_queue, job);
505                 } else if (EV_IS_JOB_PRINT (job)) {
506                         retval = remove_job_from_queue_locked (print_queue, job);
507                 } else {
508                         g_assert_not_reached ();
509                 }
510
511                 g_mutex_unlock (ev_queue_mutex);
512         }
513         
514         return retval;
515 }
516
517