1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) 2010 Sébastien Granjoux
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but 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.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
26 #include "command-queue.h"
27 #include <libanjuta/anjuta-debug.h>
28 #include <libanjuta/interfaces/ianjuta-project-backend.h>
34 2 Threads: GUI and Work
39 + Obvious, because it has the parent node
40 - Not possible, if the GUI has to get all child of the same node
42 2. Work add new node, GUI use tree data
44 3. Work create new node only but doesn't add it
46 4. GUI create a new node and call the save function later
49 The GUI cannot change links, else the Work cannot follow links when getting a
50 node. The Work can even need parent links.
51 => GUI cannot read links except in particular case
53 The GUI can read and write common part
54 => We need to copy the properties anyway when changing them
56 The GUI can only read common part
59 Have a proxy node for setting properties, this proxy will copy add common data
60 and keep a link with the original node and reference count. The GUI can still
61 read all data in the proxy without disturbing the Work which can change the
62 underlines node. The proxy address is returned immediatly when the
63 set_properties function is used. If another set_properties is done on the same
64 node the proxy is reused, incrementing reference count.
66 There is no need to proxy after add or remove functions, because the links are
67 already copied in the module.
69 After changing a property should we reload automatically ?
72 Reloading a node can change its property, so we need a proxy for the load too.
74 Proxy has to be created in GUI, because we need to update tree data.
76 Instead of using a Proxy, we can copy all data everytime, this will allow
81 Work has always a full access to link
82 GUI read a special GNode tree created by thread
84 Work can always read common data, and can write them before sending them to GUI
85 or in when modification are requested by the GUI (the GUI get a proxy)
86 GUI can only read common data
88 Work has always a full access to specific data.
89 GUI has no access to specific data
95 *---------------------------------------------------------------------------*/
97 struct _PmCommandQueue
100 GAsyncQueue
*work_queue
;
101 GAsyncQueue
*done_queue
;
110 *---------------------------------------------------------------------------*/
113 *---------------------------------------------------------------------------*/
116 pm_command_exit_work (PmJob
*job
)
118 PmCommandQueue
*queue
;
120 g_return_val_if_fail (job
!= NULL
, FALSE
);
122 queue
= (PmCommandQueue
*)job
->user_data
;
124 /* Push job in complete queue as g_thread_exit will stop the thread
126 g_async_queue_push (queue
->done_queue
, job
);
132 static PmCommandWork PmExitCommand
= {NULL
, pm_command_exit_work
, NULL
};
134 /* Forward declarations
135 *---------------------------------------------------------------------------*/
137 static gboolean
pm_command_queue_idle (PmCommandQueue
*queue
);
139 /* Worker thread functions
140 *---------------------------------------------------------------------------*/
142 /* Run work function in worker thread */
144 pm_command_queue_thread_main_loop (PmCommandQueue
*queue
)
152 job
= (PmJob
*)g_async_queue_pop (queue
->work_queue
);
154 /* Get work function and call it if possible */
155 func
= job
->work
->worker
;
161 /* Push completed job in queue */
162 g_async_queue_push (queue
->done_queue
, job
);
168 /* Main thread functions
169 *---------------------------------------------------------------------------*/
172 pm_command_queue_start_thread (PmCommandQueue
*queue
)
174 queue
->done_queue
= g_async_queue_new ();
175 queue
->work_queue
= g_async_queue_new ();
176 queue
->job_queue
= g_queue_new ();
178 queue
->worker
= g_thread_new ("am-project-worker",
179 (GThreadFunc
) pm_command_queue_thread_main_loop
, queue
);
181 if (queue
->worker
== NULL
) {
182 g_async_queue_unref (queue
->work_queue
);
183 queue
->work_queue
= NULL
;
184 g_async_queue_unref (queue
->done_queue
);
185 queue
->done_queue
= NULL
;
186 g_queue_free (queue
->job_queue
);
187 queue
->job_queue
= NULL
;
193 queue
->stopping
= FALSE
;
194 queue
->idle_func
= g_idle_add ((GSourceFunc
) pm_command_queue_idle
, queue
);
201 pm_command_queue_stop_thread (PmCommandQueue
*queue
)
203 if (queue
->job_queue
)
207 // Remove idle function
208 queue
->stopping
= TRUE
;
209 queue
->idle_func
= 0;
211 // Request to terminate thread
212 job
= pm_job_new (&PmExitCommand
, NULL
, NULL
, NULL
, 0, NULL
, NULL
, queue
);
213 g_async_queue_push (queue
->work_queue
, job
);
214 g_thread_join (queue
->worker
);
215 queue
->worker
= NULL
;
218 g_async_queue_unref (queue
->work_queue
);
219 queue
->work_queue
= NULL
;
220 g_queue_foreach (queue
->job_queue
, (GFunc
)pm_job_free
, NULL
);
221 g_queue_free (queue
->job_queue
);
222 queue
->job_queue
= NULL
;
225 job
= g_async_queue_try_pop (queue
->done_queue
);
226 if (job
== NULL
) break;
229 queue
->done_queue
= NULL
;
235 /* Run a command in job queue */
237 pm_command_queue_run_command (PmCommandQueue
*queue
)
239 gboolean running
= TRUE
;
241 if (queue
->busy
== 0)
243 /* Worker thread is waiting for new command, check job queue */
250 /* Get next command */
251 job
= g_queue_pop_head (queue
->job_queue
);
252 running
= job
!= NULL
;
255 /* Get setup function and call it if possible */
256 func
= job
->work
->setup
;
259 running
= func (job
);
264 /* Execute work function in the worker thread */
267 if (queue
->idle_func
== 0)
269 queue
->idle_func
= g_idle_add ((GSourceFunc
) pm_command_queue_idle
, queue
);
271 g_async_queue_push (queue
->work_queue
, job
);
275 /* Discard command */
285 pm_command_queue_idle (PmCommandQueue
*queue
)
294 /* Remove idle handler if queue is destroyed */
295 if (queue
->stopping
) return FALSE
;
297 /* Get completed command */
298 job
= (PmJob
*)g_async_queue_try_pop (queue
->done_queue
);
299 if (job
== NULL
) break;
301 /* Get complete function and call it if possible */
302 func
= job
->work
->complete
;
305 running
= func (job
);
311 running
= pm_command_queue_run_command (queue
);
312 if (!running
) queue
->idle_func
= 0;
318 *---------------------------------------------------------------------------*/
321 pm_job_new (PmCommandWork
* work
, AnjutaProjectNode
*node
, AnjutaProjectNode
*parent
, AnjutaProjectNode
*sibling
, AnjutaProjectNodeType type
, GFile
*file
, const gchar
*name
, gpointer user_data
)
325 job
= g_new0 (PmJob
, 1);
327 if (node
!= NULL
) job
->node
= g_object_ref (node
);
328 if (parent
!= NULL
) job
->parent
= g_object_ref (parent
);
329 if (sibling
!= NULL
) job
->sibling
= g_object_ref (sibling
);
331 if (file
!= NULL
) job
->file
= g_object_ref (file
);
332 if (name
!= NULL
) job
->name
= g_strdup (name
);
333 job
->user_data
= user_data
;
339 pm_job_free (PmJob
*job
)
341 if (job
->error
!= NULL
) g_error_free (job
->error
);
342 if (job
->map
!= NULL
) g_hash_table_destroy (job
->map
);
343 if (job
->file
!= NULL
) g_object_unref (job
->file
);
344 if (job
->name
!= NULL
) g_free (job
->name
);
345 if (job
->sibling
!= NULL
) g_object_unref (job
->sibling
);
346 if (job
->parent
!= NULL
) g_object_unref (job
->parent
);
347 if (job
->node
!= NULL
) g_object_unref (job
->node
);
351 pm_job_set_parent (PmJob
*job
, AnjutaProjectNode
*parent
)
353 if (job
->parent
!= parent
)
355 if (job
->parent
!= NULL
) g_object_unref (job
->parent
);
356 if (parent
!= NULL
) g_object_ref (parent
);
357 job
->parent
= parent
;
362 *---------------------------------------------------------------------------*/
365 pm_command_queue_push (PmCommandQueue
*queue
, PmJob
*job
)
367 g_queue_push_tail (queue
->job_queue
, job
);
369 pm_command_queue_run_command (queue
);
373 pm_command_queue_is_busy (PmCommandQueue
*queue
)
375 //g_message ("pm_command_queue_is_empty %d %d %d busy %d", g_queue_get_length (queue->job_queue), g_async_queue_length(queue->work_queue), g_async_queue_length(queue->done_queue), queue->busy);
380 pm_command_queue_new (void)
382 PmCommandQueue
*queue
;
384 queue
= g_new0 (PmCommandQueue
, 1);
386 queue
->job_queue
= NULL
;
387 queue
->work_queue
= NULL
;
388 queue
->done_queue
= NULL
;
389 queue
->worker
= NULL
;
390 queue
->idle_func
= 0;
391 queue
->stopping
= FALSE
;
394 pm_command_queue_start_thread (queue
);
400 pm_command_queue_delayed_free (gpointer data
)
408 pm_command_queue_free (PmCommandQueue
*queue
)
410 pm_command_queue_stop_thread (queue
);
412 g_idle_add (pm_command_queue_delayed_free
, queue
);