]> www.fi.muni.cz Git - evince.git/blob - libview/ev-job-scheduler.c
[dualscreen] fix crash on ctrl+w and fix control window closing
[evince.git] / libview / ev-job-scheduler.c
1 /* ev-job-scheduler.c
2  *  this file is part of evince, a gnome document viewer
3  *
4  * Copyright (C) 2008 Carlos Garcia Campos <carlosgc@gnome.org>
5  *
6  * Evince is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Evince is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20
21 #include "ev-debug.h"
22 #include "ev-job-scheduler.h"
23
24 typedef struct _EvSchedulerJob {
25         EvJob         *job;
26         EvJobPriority  priority;
27         GSList        *job_link;
28 } EvSchedulerJob;
29
30 G_LOCK_DEFINE_STATIC(job_list);
31 static GSList *job_list = NULL;
32
33 static volatile EvJob *running_job = NULL;
34
35 static gpointer ev_job_thread_proxy               (gpointer        data);
36 static void     ev_scheduler_thread_job_cancelled (EvSchedulerJob *job,
37                                                    GCancellable   *cancellable);
38
39 /* EvJobQueue */
40 static GQueue queue_urgent = G_QUEUE_INIT;
41 static GQueue queue_high = G_QUEUE_INIT;
42 static GQueue queue_low = G_QUEUE_INIT;
43 static GQueue queue_none = G_QUEUE_INIT;
44
45 static GCond *job_queue_cond = NULL;
46 static GMutex *job_queue_mutex = NULL;
47 static GQueue *job_queue[EV_JOB_N_PRIORITIES] = {
48         &queue_urgent,
49         &queue_high,
50         &queue_low,
51         &queue_none
52 };
53
54 static void
55 ev_job_queue_push (EvSchedulerJob *job,
56                    EvJobPriority   priority)
57 {
58         ev_debug_message (DEBUG_JOBS, "%s priority %d", EV_GET_TYPE_NAME (job->job), priority);
59         
60         g_mutex_lock (job_queue_mutex);
61
62         g_queue_push_tail (job_queue[priority], job);
63         g_cond_broadcast (job_queue_cond);
64         
65         g_mutex_unlock (job_queue_mutex);
66 }
67
68 static EvSchedulerJob *
69 ev_job_queue_get_next_unlocked (void)
70 {
71         gint i;
72         EvSchedulerJob *job = NULL;
73         
74         for (i = EV_JOB_PRIORITY_URGENT; i < EV_JOB_N_PRIORITIES; i++) {
75                 job = (EvSchedulerJob *) g_queue_pop_head (job_queue[i]);
76                 if (job)
77                         break;
78         }
79
80         ev_debug_message (DEBUG_JOBS, "%s", job ? EV_GET_TYPE_NAME (job->job) : "No jobs in queue");
81
82         return job;
83 }
84
85 static gpointer
86 ev_job_scheduler_init (gpointer data)
87 {
88         job_queue_cond = g_cond_new ();
89         job_queue_mutex = g_mutex_new ();
90         g_thread_create (ev_job_thread_proxy, NULL, FALSE, NULL);
91         
92         return NULL;
93 }
94
95 static void
96 ev_scheduler_job_list_add (EvSchedulerJob *job)
97 {
98         ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
99         
100         G_LOCK (job_list);
101
102         job_list = g_slist_prepend (job_list, job);
103         job->job_link = job_list;
104         
105         G_UNLOCK (job_list);
106 }
107
108 static void
109 ev_scheduler_job_list_remove (EvSchedulerJob *job)
110 {
111         ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
112         
113         G_LOCK (job_list);
114
115         job_list = g_slist_delete_link (job_list, job->job_link);
116         
117         G_UNLOCK (job_list);
118 }
119
120 static void
121 ev_scheduler_job_free (EvSchedulerJob *job)
122 {
123         if (!job)
124                 return;
125
126         g_object_unref (job->job);
127         g_free (job);
128 }
129
130 static void
131 ev_scheduler_job_destroy (EvSchedulerJob *job)
132 {
133         ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
134
135         if (job->job->run_mode == EV_JOB_RUN_MAIN_LOOP) {
136                 g_signal_handlers_disconnect_by_func (job->job, 
137                                                       G_CALLBACK (ev_scheduler_job_destroy),
138                                                       job);
139         } else {
140                 g_signal_handlers_disconnect_by_func (job->job->cancellable,
141                                                       G_CALLBACK (ev_scheduler_thread_job_cancelled),
142                                                       job);
143         }
144         
145         ev_scheduler_job_list_remove (job);
146         ev_scheduler_job_free (job);
147 }
148
149 static void
150 ev_scheduler_thread_job_cancelled (EvSchedulerJob *job,
151                                    GCancellable   *cancellable)
152 {
153         GList   *list;
154         
155         ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
156
157         g_mutex_lock (job_queue_mutex);
158
159         /* If the job is not still running,
160          * remove it from the job queue and job list.
161          * If the job is currently running, it will be
162          * destroyed as soon as it finishes. 
163          */
164         list = g_queue_find (job_queue[job->priority], job);
165         if (list) {
166                 g_queue_delete_link (job_queue[job->priority], list);
167                 g_mutex_unlock (job_queue_mutex);
168                 ev_scheduler_job_destroy (job);
169         } else {
170                 g_mutex_unlock (job_queue_mutex);
171         }
172 }
173
174 static void
175 ev_job_thread (EvJob *job)
176 {
177         gboolean result;
178
179         ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job));
180
181         do {
182                 if (g_cancellable_is_cancelled (job->cancellable))
183                         result = FALSE;
184                 else {
185                         g_atomic_pointer_set (&running_job, job);
186                         result = ev_job_run (job);
187                 }
188         } while (result);
189
190         g_atomic_pointer_set (&running_job, NULL);
191 }
192
193 static gboolean
194 ev_job_idle (EvJob *job)
195 {
196         ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job));
197
198         if (g_cancellable_is_cancelled (job->cancellable))
199                 return FALSE;
200
201         return ev_job_run (job);
202 }
203
204 static gpointer
205 ev_job_thread_proxy (gpointer data)
206 {
207         while (TRUE) {
208                 EvSchedulerJob *job;
209
210                 g_mutex_lock (job_queue_mutex);
211                 job = ev_job_queue_get_next_unlocked ();
212                 if (!job) {
213                         g_cond_wait (job_queue_cond, job_queue_mutex);
214                         g_mutex_unlock (job_queue_mutex);
215                         continue;
216                 }
217                 g_mutex_unlock (job_queue_mutex);
218                 
219                 ev_job_thread (job->job);
220                 ev_scheduler_job_destroy (job);
221         }
222
223         return NULL;
224 }
225
226 void
227 ev_job_scheduler_push_job (EvJob         *job,
228                            EvJobPriority  priority)
229 {
230         static GOnce once_init = G_ONCE_INIT;
231         EvSchedulerJob *s_job;
232
233         g_once (&once_init, ev_job_scheduler_init, NULL);
234
235         ev_debug_message (DEBUG_JOBS, "%s pirority %d", EV_GET_TYPE_NAME (job), priority);
236
237         s_job = g_new0 (EvSchedulerJob, 1);
238         s_job->job = g_object_ref (job);
239         s_job->priority = priority;
240
241         ev_scheduler_job_list_add (s_job);
242         
243         switch (ev_job_get_run_mode (job)) {
244         case EV_JOB_RUN_THREAD:
245                 g_signal_connect_swapped (job->cancellable, "cancelled",
246                                           G_CALLBACK (ev_scheduler_thread_job_cancelled),
247                                           s_job);
248                 ev_job_queue_push (s_job, priority);
249                 break;
250         case EV_JOB_RUN_MAIN_LOOP:
251                 g_signal_connect_swapped (job, "finished",
252                                           G_CALLBACK (ev_scheduler_job_destroy),
253                                           s_job);
254                 g_signal_connect_swapped (job, "cancelled",
255                                           G_CALLBACK (ev_scheduler_job_destroy),
256                                           s_job);
257                 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
258                                  (GSourceFunc)ev_job_idle,
259                                  g_object_ref (job),
260                                  (GDestroyNotify)g_object_unref);
261                 break;
262         default:
263                 g_assert_not_reached ();
264         }
265 }
266
267 void
268 ev_job_scheduler_update_job (EvJob         *job,
269                              EvJobPriority  priority)
270 {
271         GSList         *l;
272         EvSchedulerJob *s_job = NULL;
273         gboolean        need_resort = FALSE;
274
275         /* Main loop jobs are scheduled inmediately */
276         if (ev_job_get_run_mode (job) == EV_JOB_RUN_MAIN_LOOP)
277                 return;
278
279         ev_debug_message (DEBUG_JOBS, "%s pirority %d", EV_GET_TYPE_NAME (job), priority);
280         
281         G_LOCK (job_list);
282
283         for (l = job_list; l; l = l->next) {
284                 s_job = (EvSchedulerJob *)l->data;
285
286                 if (s_job->job == job) {
287                         need_resort = (s_job->priority != priority);
288                         break;
289                 }
290         }
291         
292         G_UNLOCK (job_list);
293
294         if (need_resort) {
295                 GList *list;
296         
297                 g_mutex_lock (job_queue_mutex);
298                 
299                 list = g_queue_find (job_queue[s_job->priority], s_job);
300                 if (list) {
301                         ev_debug_message (DEBUG_JOBS, "Moving job %s from pirority %d to %d",
302                                           EV_GET_TYPE_NAME (job), s_job->priority, priority);
303                         g_queue_delete_link (job_queue[s_job->priority], list);
304                         g_queue_push_tail (job_queue[priority], s_job);
305                         g_cond_broadcast (job_queue_cond);
306                 }
307                 
308                 g_mutex_unlock (job_queue_mutex);
309         }
310 }
311
312 EvJob *
313 ev_job_scheduler_get_running_thread_job (void)
314 {
315         return g_atomic_pointer_get (&running_job);
316 }