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