1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2007 Imendio AB
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
24 #include "giggle-error.h"
25 #include "giggle-sysdeps.h"
26 #include "giggle-dispatcher.h"
30 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GIGGLE_TYPE_DISPATCHER, GiggleDispatcherPriv))
32 typedef struct GiggleDispatcherPriv GiggleDispatcherPriv
;
37 GiggleExecuteCallback callback
;
45 struct GiggleDispatcherPriv
{
48 DispatcherJob
*current_job
;
49 guint current_job_wait_id
;
51 guint current_job_read_id
;
57 static void giggle_dispatcher_finalize (GObject
*object
);
60 static void dispatcher_queue_job (GiggleDispatcher
*dispatcher
,
62 static void dispatcher_unqueue_job (GiggleDispatcher
*dispatcher
,
64 static gboolean
dispatcher_start_job (GiggleDispatcher
*dispatcher
,
66 static void dispatcher_stop_current_job (GiggleDispatcher
*dispatcher
);
67 static void dispatcher_start_next_job (GiggleDispatcher
*dispatcher
);
68 static void dispatcher_signal_job_failed (GiggleDispatcher
*dispatcher
,
71 static void dispatcher_job_free (DispatcherJob
*job
);
72 static gboolean
dispatcher_is_free_to_start (GiggleDispatcher
*dispatcher
);
73 static gboolean
dispatcher_is_current_job (GiggleDispatcher
*dispatcher
,
76 static void dispatcher_job_finished_cb (GPid pid
,
78 GiggleDispatcher
*dispatcher
);
79 static gboolean
dispatcher_job_read_cb (GIOChannel
*source
,
80 GIOCondition condition
,
81 GiggleDispatcher
*dispatcher
);
84 G_DEFINE_TYPE (GiggleDispatcher
, giggle_dispatcher
, G_TYPE_OBJECT
)
87 giggle_dispatcher_class_init (GiggleDispatcherClass
*class)
89 GObjectClass
*object_class
= G_OBJECT_CLASS (class);
91 object_class
->finalize
= giggle_dispatcher_finalize
;
93 g_type_class_add_private (object_class
, sizeof (GiggleDispatcherPriv
));
97 giggle_dispatcher_init (GiggleDispatcher
*dispatcher
)
99 GiggleDispatcherPriv
*priv
;
101 priv
= GET_PRIV (dispatcher
);
103 priv
->queue
= g_queue_new ();
104 priv
->current_job
= NULL
;
105 priv
->current_job_wait_id
= 0;
109 giggle_dispatcher_finalize (GObject
*object
)
111 GiggleDispatcher
*dispatcher
= GIGGLE_DISPATCHER (object
);
112 GiggleDispatcherPriv
*priv
= GET_PRIV (object
);
115 if (priv
->current_job_wait_id
) {
116 dispatcher_stop_current_job (dispatcher
);
119 while ((job
= g_queue_pop_head (priv
->queue
))) {
120 dispatcher_job_free (job
);
122 g_queue_free (priv
->queue
);
124 G_OBJECT_CLASS (giggle_dispatcher_parent_class
)->finalize (object
);
128 dispatcher_queue_job (GiggleDispatcher
*dispatcher
, DispatcherJob
*job
)
130 GiggleDispatcherPriv
*priv
;
132 priv
= GET_PRIV (dispatcher
);
134 d(g_print ("GiggleDispatcher::queue_job\n"));
136 g_queue_push_tail (priv
->queue
, job
);
140 dispatcher_unqueue_job (GiggleDispatcher
*dispatcher
, guint id
)
142 GiggleDispatcherPriv
*priv
;
145 priv
= GET_PRIV (dispatcher
);
147 d(g_print ("GiggleDispatcher::unqueue_job\n"));
149 for (l
= priv
->queue
->head
; l
; l
= l
->next
) {
150 DispatcherJob
*job
= (DispatcherJob
*) l
->data
;
153 g_queue_delete_link (priv
->queue
, l
);
154 dispatcher_job_free (job
);
161 dispatcher_start_job (GiggleDispatcher
*dispatcher
, DispatcherJob
*job
)
163 GiggleDispatcherPriv
*priv
;
166 GError
*error
= NULL
;
168 priv
= GET_PRIV (dispatcher
);
170 g_assert (priv
->current_job
== NULL
);
172 if (!g_shell_parse_argv (job
->command
, &argc
, &argv
, &error
)) {
176 if (!g_spawn_async_with_pipes (job
->wd
, argv
,
178 G_SPAWN_SEARCH_PATH
| G_SPAWN_DO_NOT_REAP_CHILD
,
181 NULL
, &job
->std_out
, &job
->std_err
,
186 d(g_print ("GiggleDispatcher::run_job(job-started)\n"));
188 priv
->channel
= g_io_channel_unix_new (job
->std_out
);
189 g_io_channel_set_encoding (priv
->channel
, NULL
, NULL
);
190 priv
->output
= g_string_new ("");
193 priv
->current_job
= job
;
194 priv
->current_job_read_id
= g_io_add_watch_full (priv
->channel
,
195 G_PRIORITY_HIGH_IDLE
,
197 (GIOFunc
) dispatcher_job_read_cb
,
199 priv
->current_job_wait_id
= g_child_watch_add (job
->pid
,
200 (GChildWatchFunc
) dispatcher_job_finished_cb
,
207 dispatcher_signal_job_failed (dispatcher
, job
, error
);
208 dispatcher_job_free (job
);
210 g_error_free (error
);
211 priv
->current_job
= NULL
;
212 priv
->current_job_wait_id
= 0;
218 dispatcher_stop_current_job (GiggleDispatcher
*dispatcher
)
220 GiggleDispatcherPriv
*priv
;
222 priv
= GET_PRIV (dispatcher
);
224 g_assert (priv
->current_job_wait_id
!= 0);
225 g_source_remove (priv
->current_job_wait_id
);
226 priv
->current_job_wait_id
= 0;
228 g_source_remove (priv
->current_job_read_id
);
229 priv
->current_job_read_id
= 0;
231 g_io_channel_unref (priv
->channel
);
232 priv
->channel
= NULL
;
234 g_string_free (priv
->output
, TRUE
);
237 g_assert (priv
->current_job
!= NULL
);
238 giggle_sysdeps_kill_pid (priv
->current_job
->pid
);
240 dispatcher_job_free (priv
->current_job
);
241 priv
->current_job
= NULL
;
245 dispatcher_start_next_job (GiggleDispatcher
*dispatcher
)
247 GiggleDispatcherPriv
*priv
;
250 priv
= GET_PRIV (dispatcher
);
252 while ((job
= (DispatcherJob
*) g_queue_pop_head (priv
->queue
))) {
253 if (dispatcher_start_job (dispatcher
, job
)) {
260 dispatcher_signal_job_failed (GiggleDispatcher
*dispatcher
,
264 job
->callback (dispatcher
, job
->id
, error
, NULL
, 0, job
->user_data
);
268 dispatcher_job_free (DispatcherJob
*job
)
270 g_free (job
->command
);
274 g_spawn_close_pid (job
->pid
);
278 close (job
->std_out
);
282 close (job
->std_err
);
285 g_slice_free (DispatcherJob
, job
);
289 dispatcher_is_free_to_start (GiggleDispatcher
*dispatcher
)
291 GiggleDispatcherPriv
*priv
;
293 priv
= GET_PRIV (dispatcher
);
295 if (priv
->current_job
) {
303 dispatcher_is_current_job (GiggleDispatcher
*dispatcher
, guint id
)
305 GiggleDispatcherPriv
*priv
;
307 priv
= GET_PRIV (dispatcher
);
309 if (priv
->current_job
&& priv
->current_job
->id
== id
) {
317 dispatcher_job_finished_cb (GPid pid
,
319 GiggleDispatcher
*dispatcher
)
321 GiggleDispatcherPriv
*priv
;
326 priv
= GET_PRIV (dispatcher
);
327 job
= priv
->current_job
;
329 d(g_print ("GiggleDispatcher::job_finished_cb\n"));
331 g_source_remove (priv
->current_job_read_id
);
332 priv
->current_job_read_id
= 0;
334 g_io_channel_read_to_end (priv
->channel
, &str
, &len
, NULL
);
337 g_string_append_len (priv
->output
, str
, len
);
341 job
->callback (dispatcher
, job
->id
, NULL
,
342 priv
->output
->str
, priv
->length
,
345 dispatcher_job_free (job
);
346 g_io_channel_unref (priv
->channel
);
347 g_string_free (priv
->output
, TRUE
);
348 priv
->current_job
= NULL
;
349 priv
->current_job_wait_id
= 0;
350 dispatcher_start_next_job (dispatcher
);
354 dispatcher_job_read_cb (GIOChannel
*source
,
355 GIOCondition condition
,
356 GiggleDispatcher
*dispatcher
)
358 GiggleDispatcherPriv
*priv
;
363 GError
*error
= NULL
;
365 priv
= GET_PRIV (dispatcher
);
366 status
= G_IO_STATUS_NORMAL
;
368 while (count
< 10 && status
== G_IO_STATUS_NORMAL
) {
369 status
= g_io_channel_read_line (source
, &str
, &length
, NULL
, &error
);
373 g_string_append_len (priv
->output
, str
, length
);
374 priv
->length
+= length
;
379 if (status
== G_IO_STATUS_ERROR
) {
380 dispatcher_signal_job_failed (dispatcher
, priv
->current_job
, error
);
381 dispatcher_stop_current_job (dispatcher
);
382 dispatcher_start_next_job (dispatcher
);
391 giggle_dispatcher_new (void)
393 return g_object_new (GIGGLE_TYPE_DISPATCHER
, NULL
);
397 giggle_dispatcher_execute (GiggleDispatcher
*dispatcher
,
399 const gchar
*command
,
400 GiggleExecuteCallback callback
,
406 g_return_val_if_fail (GIGGLE_IS_DISPATCHER (dispatcher
), 0);
407 g_return_val_if_fail (command
!= NULL
, 0);
408 g_return_val_if_fail (callback
!= NULL
, 0);
410 job
= g_slice_new0 (DispatcherJob
);
412 job
->command
= g_strdup (command
);
413 job
->callback
= callback
;
414 job
->user_data
= user_data
;
422 job
->wd
= g_strdup (wd
);
427 if (dispatcher_is_free_to_start (dispatcher
)) {
428 dispatcher_start_job (dispatcher
, job
);
430 dispatcher_queue_job (dispatcher
, job
);
437 giggle_dispatcher_cancel (GiggleDispatcher
*dispatcher
, guint id
)
439 g_return_if_fail (GIGGLE_IS_DISPATCHER (dispatcher
));
440 g_return_if_fail (id
> 0);
442 if (dispatcher_is_current_job (dispatcher
, id
)) {
443 dispatcher_stop_current_job (dispatcher
);
444 dispatcher_start_next_job (dispatcher
);
446 dispatcher_unqueue_job (dispatcher
, id
);