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