Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ev-job-scheduler.c
Go to the documentation of this file.
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 {
27  GSList *job_link;
29 
31 static GSList *job_list = NULL;
32 
33 static volatile EvJob *running_job = NULL;
34 
35 static gpointer ev_job_thread_proxy (gpointer data);
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 static GCond job_queue_cond;
45 static GMutex job_queue_mutex;
46 
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
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 *
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  g_thread_new ("EvJobScheduler", ev_job_thread_proxy, NULL);
89 
90  return NULL;
91 }
92 
93 static void
95 {
96  ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
97 
98  G_LOCK (job_list);
99 
100  job_list = g_slist_prepend (job_list, job);
101  job->job_link = job_list;
102 
103  G_UNLOCK (job_list);
104 }
105 
106 static void
108 {
109  ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
110 
111  G_LOCK (job_list);
112 
113  job_list = g_slist_delete_link (job_list, job->job_link);
114 
115  G_UNLOCK (job_list);
116 }
117 
118 static void
120 {
121  if (!job)
122  return;
123 
124  g_object_unref (job->job);
125  g_free (job);
126 }
127 
128 static void
130 {
131  ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
132 
133  if (job->job->run_mode == EV_JOB_RUN_MAIN_LOOP) {
134  g_signal_handlers_disconnect_by_func (job->job,
135  G_CALLBACK (ev_scheduler_job_destroy),
136  job);
137  } else {
138  g_signal_handlers_disconnect_by_func (job->job->cancellable,
140  job);
141  }
142 
144  ev_scheduler_job_free (job);
145 }
146 
147 static void
149  GCancellable *cancellable)
150 {
151  GList *list;
152 
153  ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job->job));
154 
155  g_mutex_lock (&job_queue_mutex);
156 
157  /* If the job is not still running,
158  * remove it from the job queue and job list.
159  * If the job is currently running, it will be
160  * destroyed as soon as it finishes.
161  */
162  list = g_queue_find (job_queue[job->priority], job);
163  if (list) {
164  g_queue_delete_link (job_queue[job->priority], list);
165  g_mutex_unlock (&job_queue_mutex);
167  } else {
168  g_mutex_unlock (&job_queue_mutex);
169  }
170 }
171 
172 static void
174 {
175  gboolean result;
176 
177  ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job));
178 
179  do {
180  if (g_cancellable_is_cancelled (job->cancellable))
181  result = FALSE;
182  else {
183  g_atomic_pointer_set (&running_job, job);
184  result = ev_job_run (job);
185  }
186  } while (result);
187 
188  g_atomic_pointer_set (&running_job, NULL);
189 }
190 
191 static gboolean
193 {
194  ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job));
195 
196  if (g_cancellable_is_cancelled (job->cancellable))
197  return FALSE;
198 
199  return ev_job_run (job);
200 }
201 
202 static gpointer
203 ev_job_thread_proxy (gpointer data)
204 {
205  while (TRUE) {
206  EvSchedulerJob *job;
207 
208  g_mutex_lock (&job_queue_mutex);
210  if (!job) {
211  g_cond_wait (&job_queue_cond, &job_queue_mutex);
212  g_mutex_unlock (&job_queue_mutex);
213  continue;
214  }
215  g_mutex_unlock (&job_queue_mutex);
216 
217  ev_job_thread (job->job);
219  }
220 
221  return NULL;
222 }
223 
224 void
226  EvJobPriority priority)
227 {
228  static GOnce once_init = G_ONCE_INIT;
229  EvSchedulerJob *s_job;
230 
231  g_once (&once_init, ev_job_scheduler_init, NULL);
232 
233  ev_debug_message (DEBUG_JOBS, "%s pirority %d", EV_GET_TYPE_NAME (job), priority);
234 
235  s_job = g_new0 (EvSchedulerJob, 1);
236  s_job->job = g_object_ref (job);
237  s_job->priority = priority;
238 
240 
241  switch (ev_job_get_run_mode (job)) {
242  case EV_JOB_RUN_THREAD:
243  g_signal_connect_swapped (job->cancellable, "cancelled",
245  s_job);
246  ev_job_queue_push (s_job, priority);
247  break;
249  g_signal_connect_swapped (job, "finished",
250  G_CALLBACK (ev_scheduler_job_destroy),
251  s_job);
252  g_signal_connect_swapped (job, "cancelled",
253  G_CALLBACK (ev_scheduler_job_destroy),
254  s_job);
255  g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
256  (GSourceFunc)ev_job_idle,
257  g_object_ref (job),
258  (GDestroyNotify)g_object_unref);
259  break;
260  default:
261  g_assert_not_reached ();
262  }
263 }
264 
265 void
267  EvJobPriority priority)
268 {
269  GSList *l;
270  EvSchedulerJob *s_job = NULL;
271  gboolean need_resort = FALSE;
272 
273  /* Main loop jobs are scheduled inmediately */
275  return;
276 
277  ev_debug_message (DEBUG_JOBS, "%s pirority %d", EV_GET_TYPE_NAME (job), priority);
278 
279  G_LOCK (job_list);
280 
281  for (l = job_list; l; l = l->next) {
282  s_job = (EvSchedulerJob *)l->data;
283 
284  if (s_job->job == job) {
285  need_resort = (s_job->priority != priority);
286  break;
287  }
288  }
289 
290  G_UNLOCK (job_list);
291 
292  if (need_resort) {
293  GList *list;
294 
295  g_mutex_lock (&job_queue_mutex);
296 
297  list = g_queue_find (job_queue[s_job->priority], s_job);
298  if (list) {
299  ev_debug_message (DEBUG_JOBS, "Moving job %s from pirority %d to %d",
300  EV_GET_TYPE_NAME (job), s_job->priority, priority);
301  g_queue_delete_link (job_queue[s_job->priority], list);
302  g_queue_push_tail (job_queue[priority], s_job);
303  g_cond_broadcast (&job_queue_cond);
304  }
305 
306  g_mutex_unlock (&job_queue_mutex);
307  }
308 }
309 
315 EvJob *
317 {
318  return g_atomic_pointer_get (&running_job);
319 }