~ubuntu-branches/ubuntu/precise/evince/precise-updates

« back to all changes in this revision

Viewing changes to libview/ev-job-scheduler.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2009-01-19 10:39:43 UTC
  • mto: (1.6.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 70.
  • Revision ID: james.westby@ubuntu.com-20090119103943-dhe56g9rmuq7khys
Tags: upstream-2.25.5
ImportĀ upstreamĀ versionĀ 2.25.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, 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 gpointer ev_job_thread_proxy               (gpointer        data);
 
34
static void     ev_scheduler_thread_job_cancelled (EvSchedulerJob *job,
 
35
                                                   GCancellable   *cancellable);
 
36
 
 
37
/* EvJobQueue */
 
38
static GQueue queue_urgent = G_QUEUE_INIT;
 
39
static GQueue queue_high = G_QUEUE_INIT;
 
40
static GQueue queue_low = G_QUEUE_INIT;
 
41
static GQueue queue_none = G_QUEUE_INIT;
 
42
 
 
43
static GCond *job_queue_cond = NULL;
 
44
static GMutex *job_queue_mutex = NULL;
 
45
static GQueue *job_queue[EV_JOB_N_PRIORITIES] = {
 
46
        &queue_urgent,
 
47
        &queue_high,
 
48
        &queue_low,
 
49
        &queue_none
 
50
};
 
51
 
 
52
static void
 
53
ev_job_queue_push (EvSchedulerJob *job,
 
54
                   EvJobPriority   priority)
 
55
{
 
56
        ev_debug_message (DEBUG_JOBS, "%s priority %d", EV_GET_TYPE_NAME (job->job), priority);
 
57
        
 
58
        g_mutex_lock (job_queue_mutex);
 
59
 
 
60
        g_queue_push_tail (job_queue[priority], job);
 
61
        g_cond_broadcast (job_queue_cond);
 
62
        
 
63
        g_mutex_unlock (job_queue_mutex);
 
64
}
 
65
 
 
66
static EvSchedulerJob *
 
67
ev_job_queue_get_next_unlocked (void)
 
68
{
 
69
        gint i;
 
70
        EvSchedulerJob *job = NULL;
 
71
        
 
72
        for (i = EV_JOB_PRIORITY_URGENT; i < EV_JOB_N_PRIORITIES; i++) {
 
73
                job = (EvSchedulerJob *) g_queue_pop_head (job_queue[i]);
 
74
                if (job)
 
75
                        break;
 
76
        }
 
77
 
 
78
        ev_debug_message (DEBUG_JOBS, "%s", job ? EV_GET_TYPE_NAME (job->job) : "No jobs in queue");
 
79
 
 
80
        return job;
 
81
}
 
82
 
 
83
static gpointer
 
84
ev_job_scheduler_init (gpointer data)
 
85
{
 
86
        job_queue_cond = g_cond_new ();
 
87
        job_queue_mutex = g_mutex_new ();
 
88
        g_thread_create (ev_job_thread_proxy, NULL, FALSE, NULL);
 
89
        
 
90
        return NULL;
 
91
}
 
92
 
 
93
static void
 
94
ev_scheduler_job_list_add (EvSchedulerJob *job)
 
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
 
107
ev_scheduler_job_list_remove (EvSchedulerJob *job)
 
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
 
119
ev_scheduler_job_free (EvSchedulerJob *job)
 
120
{
 
121
        if (!job)
 
122
                return;
 
123
 
 
124
        g_object_unref (job->job);
 
125
        g_free (job);
 
126
}
 
127
 
 
128
static void
 
129
ev_scheduler_job_destroy (EvSchedulerJob *job)
 
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,
 
139
                                                      G_CALLBACK (ev_scheduler_thread_job_cancelled),
 
140
                                                      job);
 
141
        }
 
142
        
 
143
        ev_scheduler_job_list_remove (job);
 
144
        ev_scheduler_job_free (job);
 
145
}
 
146
 
 
147
static void
 
148
ev_scheduler_thread_job_cancelled (EvSchedulerJob *job,
 
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);
 
166
                ev_scheduler_job_destroy (job);
 
167
        } else {
 
168
                g_mutex_unlock (job_queue_mutex);
 
169
        }
 
170
}
 
171
 
 
172
static void
 
173
ev_job_thread (EvJob *job)
 
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
                        result = ev_job_run (job);
 
184
        } while (result);
 
185
}
 
186
 
 
187
static gboolean
 
188
ev_job_idle (EvJob *job)
 
189
{
 
190
        ev_debug_message (DEBUG_JOBS, "%s", EV_GET_TYPE_NAME (job));
 
191
 
 
192
        if (g_cancellable_is_cancelled (job->cancellable))
 
193
                return FALSE;
 
194
 
 
195
        return ev_job_run (job);
 
196
}
 
197
 
 
198
static gpointer
 
199
ev_job_thread_proxy (gpointer data)
 
200
{
 
201
        while (TRUE) {
 
202
                EvSchedulerJob *job;
 
203
 
 
204
                g_mutex_lock (job_queue_mutex);
 
205
                job = ev_job_queue_get_next_unlocked ();
 
206
                if (!job) {
 
207
                        g_cond_wait (job_queue_cond, job_queue_mutex);
 
208
                        g_mutex_unlock (job_queue_mutex);
 
209
                        continue;
 
210
                }
 
211
                g_mutex_unlock (job_queue_mutex);
 
212
                
 
213
                ev_job_thread (job->job);
 
214
                ev_scheduler_job_destroy (job);
 
215
        }
 
216
 
 
217
        return NULL;
 
218
}
 
219
 
 
220
void
 
221
ev_job_scheduler_push_job (EvJob         *job,
 
222
                           EvJobPriority  priority)
 
223
{
 
224
        static GOnce once_init = G_ONCE_INIT;
 
225
        EvSchedulerJob *s_job;
 
226
 
 
227
        g_once (&once_init, ev_job_scheduler_init, NULL);
 
228
 
 
229
        ev_debug_message (DEBUG_JOBS, "%s pirority %d", EV_GET_TYPE_NAME (job), priority);
 
230
 
 
231
        s_job = g_new0 (EvSchedulerJob, 1);
 
232
        s_job->job = g_object_ref (job);
 
233
        s_job->priority = priority;
 
234
 
 
235
        ev_scheduler_job_list_add (s_job);
 
236
        
 
237
        switch (ev_job_get_run_mode (job)) {
 
238
        case EV_JOB_RUN_THREAD:
 
239
                g_signal_connect_swapped (job->cancellable, "cancelled",
 
240
                                          G_CALLBACK (ev_scheduler_thread_job_cancelled),
 
241
                                          s_job);
 
242
                ev_job_queue_push (s_job, priority);
 
243
                break;
 
244
        case EV_JOB_RUN_MAIN_LOOP:
 
245
                g_signal_connect_swapped (job, "finished",
 
246
                                          G_CALLBACK (ev_scheduler_job_destroy),
 
247
                                          s_job);
 
248
                g_signal_connect_swapped (job, "cancelled",
 
249
                                          G_CALLBACK (ev_scheduler_job_destroy),
 
250
                                          s_job);
 
251
                g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
 
252
                                 (GSourceFunc)ev_job_idle,
 
253
                                 g_object_ref (job),
 
254
                                 (GDestroyNotify)g_object_unref);
 
255
                break;
 
256
        default:
 
257
                g_assert_not_reached ();
 
258
        }
 
259
}
 
260
 
 
261
void
 
262
ev_job_scheduler_update_job (EvJob         *job,
 
263
                             EvJobPriority  priority)
 
264
{
 
265
        GSList         *l;
 
266
        EvSchedulerJob *s_job = NULL;
 
267
        gboolean        need_resort = FALSE;
 
268
 
 
269
        /* Main loop jobs are scheduled inmediately */
 
270
        if (ev_job_get_run_mode (job) == EV_JOB_RUN_MAIN_LOOP)
 
271
                return;
 
272
 
 
273
        ev_debug_message (DEBUG_JOBS, "%s pirority %d", EV_GET_TYPE_NAME (job), priority);
 
274
        
 
275
        G_LOCK (job_list);
 
276
 
 
277
        for (l = job_list; l; l = l->next) {
 
278
                s_job = (EvSchedulerJob *)l->data;
 
279
 
 
280
                if (s_job->job == job) {
 
281
                        need_resort = (s_job->priority != priority);
 
282
                        break;
 
283
                }
 
284
        }
 
285
        
 
286
        G_UNLOCK (job_list);
 
287
 
 
288
        if (need_resort) {
 
289
                GList *list;
 
290
        
 
291
                g_mutex_lock (job_queue_mutex);
 
292
                
 
293
                list = g_queue_find (job_queue[s_job->priority], s_job);
 
294
                if (list) {
 
295
                        ev_debug_message (DEBUG_JOBS, "Moving job %s from pirority %d to %d",
 
296
                                          EV_GET_TYPE_NAME (job), s_job->priority, priority);
 
297
                        g_queue_delete_link (job_queue[s_job->priority], list);
 
298
                        g_queue_push_tail (job_queue[priority], s_job);
 
299
                        g_cond_broadcast (job_queue_cond);
 
300
                }
 
301
                
 
302
                g_mutex_unlock (job_queue_mutex);
 
303
        }
 
304
}
 
305