Added byline
[anjuta.git] / plugins / am-project / command-queue.c
blob01eb5b89d4c2c6bce568b91bab181579a4422968
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /* command-queue.c
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.
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include "command-queue.h"
27 #include <libanjuta/anjuta-debug.h>
28 #include <libanjuta/interfaces/ianjuta-project-backend.h>
30 #include <string.h>
34 2 Threads: GUI and Work
36 Add new source.
38 1. Work add new node
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
57 =>
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
77 automatic reload.
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
94 /* Types
95 *---------------------------------------------------------------------------*/
97 struct _PmCommandQueue
99 GQueue *job_queue;
100 GAsyncQueue *work_queue;
101 GAsyncQueue *done_queue;
102 GThread *worker;
103 guint idle_func;
104 gboolean stopping;
105 guint busy;
109 /* Signal
110 *---------------------------------------------------------------------------*/
112 /* Command functions
113 *---------------------------------------------------------------------------*/
115 static gboolean
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
125 * immediatly */
126 g_async_queue_push (queue->done_queue, job);
127 g_thread_exit (0);
129 return TRUE;
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 */
143 static gpointer
144 pm_command_queue_thread_main_loop (PmCommandQueue *queue)
146 for (;;)
148 PmJob *job;
149 PmCommandFunc func;
151 /* Get new job */
152 job = (PmJob *)g_async_queue_pop (queue->work_queue);
154 /* Get work function and call it if possible */
155 func = job->work->worker;
156 if (func != NULL)
158 func (job);
161 /* Push completed job in queue */
162 g_async_queue_push (queue->done_queue, job);
165 return NULL;
168 /* Main thread functions
169 *---------------------------------------------------------------------------*/
171 static gboolean
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;
189 return FALSE;
191 else
193 queue->stopping = FALSE;
194 queue->idle_func = g_idle_add ((GSourceFunc) pm_command_queue_idle, queue);
196 return TRUE;
200 static gboolean
201 pm_command_queue_stop_thread (PmCommandQueue *queue)
203 if (queue->job_queue)
205 PmJob *job;
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;
217 // Free queue
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;
223 for (;;)
225 job = g_async_queue_try_pop (queue->done_queue);
226 if (job == NULL) break;
227 pm_job_free (job);
229 queue->done_queue = NULL;
232 return TRUE;
235 /* Run a command in job queue */
236 static gboolean
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 */
244 PmJob *job;
248 PmCommandFunc func;
250 /* Get next command */
251 job = g_queue_pop_head (queue->job_queue);
252 running = job != NULL;
253 if (!running) break;
255 /* Get setup function and call it if possible */
256 func = job->work->setup;
257 if (func != NULL)
259 running = func (job);
262 if (running)
264 /* Execute work function in the worker thread */
265 queue->busy = 1;
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);
273 else
275 /* Discard command */
276 pm_job_free (job);
278 } while (!running);
281 return running;
284 static gboolean
285 pm_command_queue_idle (PmCommandQueue *queue)
287 gboolean running;
289 for (;;)
291 PmCommandFunc func;
292 PmJob *job;
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;
303 if (func != NULL)
305 running = func (job);
307 pm_job_free (job);
308 queue->busy--;
311 running = pm_command_queue_run_command (queue);
312 if (!running) queue->idle_func = 0;
314 return running;
317 /* Job functions
318 *---------------------------------------------------------------------------*/
320 PmJob *
321 pm_job_new (PmCommandWork* work, AnjutaProjectNode *node, AnjutaProjectNode *parent, AnjutaProjectNode *sibling, AnjutaProjectNodeType type, GFile *file, const gchar *name, gpointer user_data)
323 PmJob *job;
325 job = g_new0 (PmJob, 1);
326 job->work = work;
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);
330 job->type = type;
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;
335 return job;
338 void
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);
350 void
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;
361 /* Public functions
362 *---------------------------------------------------------------------------*/
364 void
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);
372 gboolean
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);
376 return queue->busy;
379 PmCommandQueue*
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;
392 queue->busy = 0;
394 pm_command_queue_start_thread (queue);
396 return queue;
399 static gboolean
400 pm_command_queue_delayed_free (gpointer data)
402 g_free (data);
404 return FALSE;
407 void
408 pm_command_queue_free (PmCommandQueue *queue)
410 pm_command_queue_stop_thread (queue);
412 g_idle_add (pm_command_queue_delayed_free, queue);