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