[anjuta/newproject] am: Use threads again in autotools project plugin
- From: Sebastien Granjoux <sgranjoux src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [anjuta/newproject] am: Use threads again in autotools project plugin
- Date: Sat, 6 Nov 2010 13:52:33 +0000 (UTC)
commit b4917637c2479bcc6295b32d89b2467e1c9afea9
Author: Sébastien Granjoux <seb sfo free fr>
Date: Sat Nov 6 10:45:12 2010 +0100
am: Use threads again in autotools project plugin
plugins/am-project/Makefile.am | 4 +-
plugins/am-project/am-project-private.h | 4 +
plugins/am-project/am-project.c | 404 +++++++++++++++++++-------
plugins/am-project/command-queue.c | 493 +++++++++++++++++++++++++++++++
plugins/am-project/command-queue.h | 74 +++++
5 files changed, 877 insertions(+), 102 deletions(-)
---
diff --git a/plugins/am-project/Makefile.am b/plugins/am-project/Makefile.am
index 958016c..4254ce2 100644
--- a/plugins/am-project/Makefile.am
+++ b/plugins/am-project/Makefile.am
@@ -46,7 +46,9 @@ libam_project_la_SOURCES = \
am-properties.c \
am-properties.h \
am-node.c \
- am-node.h
+ am-node.h \
+ command-queue.c \
+ command-queue.h
libam_project_la_LDFLAGS = $(ANJUTA_PLUGIN_LDFLAGS)
diff --git a/plugins/am-project/am-project-private.h b/plugins/am-project/am-project-private.h
index 7c12614..260008c 100644
--- a/plugins/am-project/am-project-private.h
+++ b/plugins/am-project/am-project-private.h
@@ -23,6 +23,7 @@
#define _AM_PROJECT_PRIVATE_H_
#include "am-project.h"
+#include "command-queue.h"
G_BEGIN_DECLS
@@ -72,6 +73,9 @@ struct _AmpProject {
AnjutaTokenStyle *ac_space_list;
AnjutaTokenStyle *am_space_list;
AnjutaTokenStyle *arg_list;
+
+ /* Command queue */
+ PmCommandQueue *queue;
};
typedef struct _AmpNodeInfo AmpNodeInfo;
diff --git a/plugins/am-project/am-project.c b/plugins/am-project/am-project.c
index 3b1ba51..9de1709 100644
--- a/plugins/am-project/am-project.c
+++ b/plugins/am-project/am-project.c
@@ -28,6 +28,7 @@
#include "am-project-private.h"
#include "am-node.h"
+#include "command-queue.h"
#include <libanjuta/interfaces/ianjuta-project.h>
#include <libanjuta/anjuta-debug.h>
@@ -1147,7 +1148,6 @@ project_load_data (AmpProject *project, AnjutaToken *name, AnjutaToken *list, An
{
/* Create target */
target = amp_target_new (target_id, info->base.type, install, flags, NULL);
- amp_target_add_token (target, ANJUTA_TOKEN_ARGUMENT, arg);
anjuta_project_node_append (parent, target);
DEBUG_PRINT ("create target %p name %s", target, target_id);
}
@@ -2273,61 +2273,290 @@ amp_project_add_file (AmpProject *project, GFile *file, AnjutaTokenFile* token)
g_object_add_toggle_ref (G_OBJECT (token), remove_config_file, project);
}
-
-/* Implement IAnjutaProject
+/* Worker thread
*---------------------------------------------------------------------------*/
+static gboolean
+amp_load_setup (PmJob *job)
+{
+ job->parent = anjuta_project_node_parent (job->node);
+ job->proxy = amp_project_duplicate_node (job->node);
+ g_message ("type %x proxy %x parent %p node %p proxy %p", job->node->type, job->proxy->type, job->parent, job->node, job->proxy);
+
+ return TRUE;
+}
static gboolean
-iproject_load_node (IAnjutaProject *obj, AnjutaProjectNode *node, GError **error)
+amp_load_work (PmJob *job)
+{
+ job->proxy = amp_project_load_node (AMP_PROJECT (job->user_data), job->proxy, &job->error);
+
+ return TRUE;
+}
+
+static gboolean
+amp_load_complete (PmJob *job)
{
- AnjutaProjectNode *new_node;
- AnjutaProjectNode *proxy;
- AnjutaProjectNode *parent;
GHashTable *map;
- GError *err = NULL;
-
- if (node == NULL) node = AMP_PROJECT (obj)->root;
- parent = anjuta_project_node_parent (node);
- proxy = amp_project_duplicate_node (node);
-
- new_node = amp_project_load_node (AMP_PROJECT (obj), proxy, &err);
- map = amp_project_map_node (node, new_node);
+ map = amp_project_map_node (job->node, job->proxy);
+ g_message ("type %x proxy %x", job->node->type, job->proxy->type);
g_hash_table_foreach (map, (GHFunc)amp_project_replace_node, map);
- node->parent = parent;
- proxy->parent = NULL;
+ job->node->parent = job->parent;
+ job->proxy->parent = NULL;
g_hash_table_destroy (map);
- g_object_unref (proxy);
- g_signal_emit_by_name (obj, "node-loaded", node, err);
- g_error_free (err);
+ //g_object_unref (job->proxy);
+ job->proxy = NULL;
+ g_signal_emit_by_name (AMP_PROJECT (job->user_data), "node-loaded", job->node, job->error);
+ g_error_free (job->error);
- return new_node != NULL;
+ return TRUE;
}
+static PmCommandWork amp_load_job = {amp_load_setup, amp_load_work, amp_load_complete};
+
static gboolean
-iproject_save_node (IAnjutaProject *obj, AnjutaProjectNode *node, GError **error)
+amp_save_setup (PmJob *job)
{
- GError *err = NULL;
+ return TRUE;
+}
+
+static gboolean
+amp_save_work (PmJob *job)
+{
+ switch (anjuta_project_node_get_node_type (job->node))
+ {
+ case ANJUTA_PROJECT_ROOT:
+ amp_project_save (AMP_PROJECT (job->user_data), &job->error);
+ break;
+ default:
+ project_node_save (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+amp_save_complete (PmJob *job)
+{
+ g_signal_emit_by_name (AMP_PROJECT (job->user_data), "node-saved", job->node, job->error);
+ g_error_free (job->error);
+
+ return TRUE;
+}
+
+static PmCommandWork amp_save_job = {amp_save_setup, amp_save_work, amp_save_complete};
+
+static gboolean
+amp_add_before_setup (PmJob *job)
+{
+ anjuta_project_node_insert_before (job->parent, job->sibling, job->node);
- if (node == NULL) node = AMP_PROJECT (obj)->root;
+ return TRUE;
+}
+
+static gboolean
+amp_add_after_setup (PmJob *job)
+{
+ anjuta_project_node_insert_after (job->parent, job->sibling, job->node);
- switch (anjuta_project_node_get_node_type (node))
+ return TRUE;
+}
+
+static gboolean
+amp_add_work (PmJob *job)
+{
+ switch (anjuta_project_node_get_node_type (job->node))
{
- case ANJUTA_PROJECT_ROOT:
- if (!amp_project_save (AMP_PROJECT (obj), &err))
- {
- node = NULL;
- }
+ case ANJUTA_PROJECT_GROUP:
+ amp_group_create_token (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ case ANJUTA_PROJECT_TARGET:
+ amp_target_create_token (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ case ANJUTA_PROJECT_SOURCE:
+ amp_source_create_token (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ case ANJUTA_PROJECT_MODULE:
+ amp_module_create_token (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ case ANJUTA_PROJECT_PACKAGE:
+ amp_package_create_token (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+amp_add_complete (PmJob *job)
+{
+ g_signal_emit_by_name (AMP_PROJECT (job->user_data), "node-changed", job->node, job->error);
+
+ return TRUE;
+}
+
+static PmCommandWork amp_add_before_job = {amp_add_before_setup, amp_add_work, amp_add_complete};
+static PmCommandWork amp_add_after_job = {amp_add_after_setup, amp_add_work, amp_add_complete};
+
+
+static gboolean
+amp_remove_setup (PmJob *job)
+{
+ anjuta_project_node_set_state (job->node, ANJUTA_PROJECT_REMOVED);
+
+ return TRUE;
+}
+
+static gboolean
+amp_remove_work (PmJob *job)
+{
+ switch (anjuta_project_node_get_node_type (job->node))
+ {
+ case ANJUTA_PROJECT_GROUP:
+ amp_group_delete_token (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ case ANJUTA_PROJECT_TARGET:
+ amp_target_delete_token (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ case ANJUTA_PROJECT_SOURCE:
+ amp_source_delete_token (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ case ANJUTA_PROJECT_MODULE:
+ amp_module_delete_token (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ case ANJUTA_PROJECT_PACKAGE:
+ amp_package_delete_token (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+amp_remove_complete (PmJob *job)
+{
+ g_signal_emit_by_name (AMP_PROJECT (job->user_data), "node-changed", job->node, job->error);
+
+ return TRUE;
+}
+
+static PmCommandWork amp_remove_job = {amp_remove_setup, amp_remove_work, amp_remove_complete};
+
+static gboolean
+amp_set_property_setup (PmJob *job)
+{
+ return TRUE;
+}
+
+static gboolean
+amp_set_property_work (PmJob *job)
+{
+ gint flags;
+
+ flags = ((AmpProperty *)job->property->native)->flags;
+
+ if (flags & AM_PROPERTY_IN_CONFIGURE)
+ {
+ amp_project_update_ac_property (AMP_PROJECT (job->user_data), job->property);
+ }
+ else if (flags & AM_PROPERTY_IN_MAKEFILE)
+ {
+ amp_project_update_am_property (AMP_PROJECT (job->user_data), job->node, job->property);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+amp_set_property_complete (PmJob *job)
+{
+ g_signal_emit_by_name (AMP_PROJECT (job->user_data), "node-changed", job->node, job->error);
+
+ return TRUE;
+}
+
+static PmCommandWork amp_set_property_job = {amp_set_property_setup, amp_set_property_work, amp_set_property_complete};
+
+static gboolean
+amp_remove_property_setup (PmJob *job)
+{
+ anjuta_project_node_set_state (job->node, ANJUTA_PROJECT_REMOVED);
+
+ return TRUE;
+}
+
+static gboolean
+amp_remove_property_work (PmJob *job)
+{
+ switch (anjuta_project_node_get_node_type (job->node))
+ {
+ case ANJUTA_PROJECT_GROUP:
+ amp_group_delete_token (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ case ANJUTA_PROJECT_TARGET:
+ amp_target_delete_token (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ case ANJUTA_PROJECT_SOURCE:
+ amp_source_delete_token (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ case ANJUTA_PROJECT_MODULE:
+ amp_module_delete_token (AMP_PROJECT (job->user_data), job->node, &job->error);
+ break;
+ case ANJUTA_PROJECT_PACKAGE:
+ amp_package_delete_token (AMP_PROJECT (job->user_data), job->node, &job->error);
break;
default:
- node = project_node_save (AMP_PROJECT (obj), node, error);
break;
}
- g_signal_emit_by_name (obj, "node-saved", node, err);
- g_error_free (err);
- return node != NULL;
+ return TRUE;
+}
+
+static gboolean
+amp_remove_property_complete (PmJob *job)
+{
+ g_signal_emit_by_name (AMP_PROJECT (job->user_data), "node-changed", job->node, job->error);
+
+ return TRUE;
+}
+
+static PmCommandWork amp_remove_property_job = {amp_remove_property_setup, amp_remove_property_work, amp_remove_property_complete};
+
+/* Implement IAnjutaProject
+ *---------------------------------------------------------------------------*/
+
+
+static gboolean
+iproject_load_node (IAnjutaProject *obj, AnjutaProjectNode *node, GError **error)
+{
+ PmJob *load_job;
+
+ if (node == NULL) node = AMP_PROJECT (obj)->root;
+
+ load_job = pm_job_new (&_load_job, node, NULL, NULL, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, load_job);
+
+ return TRUE;
+}
+
+static gboolean
+iproject_save_node (IAnjutaProject *obj, AnjutaProjectNode *node, GError **error)
+{
+ PmJob *save_job;
+
+ if (node == NULL) node = AMP_PROJECT (obj)->root;
+
+ save_job = pm_job_new (&_save_job, node, NULL, NULL, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, save_job);
+
+ return TRUE;
}
static AnjutaProjectNode *
@@ -2335,6 +2564,7 @@ iproject_add_node_before (IAnjutaProject *obj, AnjutaProjectNode *parent, Anjuta
{
AnjutaProjectNode *node;
GFile *directory = NULL;
+ PmJob *add_job;
switch (type & ANJUTA_PROJECT_TYPE_MASK)
{
@@ -2346,35 +2576,34 @@ iproject_add_node_before (IAnjutaProject *obj, AnjutaProjectNode *parent, Anjuta
}
node = project_node_new (AMP_PROJECT (obj), type, file, name, err);
if (directory != NULL) g_object_unref (directory);
- anjuta_project_node_insert_before (parent, sibling, node);
- amp_group_create_token (AMP_PROJECT (obj), node, NULL);
+ add_job = pm_job_new (&_add_before_job, node, parent, sibling, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, add_job);
break;
case ANJUTA_PROJECT_TARGET:
node = project_node_new (AMP_PROJECT (obj), type, file, name, err);
- anjuta_project_node_insert_before (parent, sibling, node);
- amp_target_create_token (AMP_PROJECT (obj), node, NULL);
+ add_job = pm_job_new (&_add_before_job, node, parent, sibling, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, add_job);
break;
case ANJUTA_PROJECT_SOURCE:
node = project_node_new (AMP_PROJECT (obj), type, file, name, err);
- anjuta_project_node_insert_before (parent, sibling, node);
- amp_source_create_token (AMP_PROJECT (obj), node, NULL);
+ add_job = pm_job_new (&_add_before_job, node, parent, sibling, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, add_job);
break;
case ANJUTA_PROJECT_MODULE:
node = project_node_new (AMP_PROJECT (obj), type, file, name, err);
- anjuta_project_node_insert_before (parent, sibling, node);
- amp_module_create_token (AMP_PROJECT (obj), node, NULL);
+ add_job = pm_job_new (&_add_before_job, node, parent, sibling, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, add_job);
break;
case ANJUTA_PROJECT_PACKAGE:
node = project_node_new (AMP_PROJECT (obj), type, file, name, err);
- anjuta_project_node_insert_before (parent, sibling, node);
- amp_package_create_token (AMP_PROJECT (obj), node, NULL);
+ add_job = pm_job_new (&_add_before_job, node, parent, sibling, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, add_job);
break;
default:
node = project_node_new (AMP_PROJECT (obj), type, file, name, err);
anjuta_project_node_insert_before (parent, sibling, node);
break;
}
- g_signal_emit_by_name (obj, "node-changed", node, NULL);
return node;
}
@@ -2384,7 +2613,8 @@ iproject_add_node_after (IAnjutaProject *obj, AnjutaProjectNode *parent, AnjutaP
{
AnjutaProjectNode *node;
GFile *directory = NULL;
-
+ PmJob *add_job;
+
switch (type & ANJUTA_PROJECT_TYPE_MASK)
{
case ANJUTA_PROJECT_GROUP:
@@ -2395,35 +2625,34 @@ iproject_add_node_after (IAnjutaProject *obj, AnjutaProjectNode *parent, AnjutaP
}
node = project_node_new (AMP_PROJECT (obj), type, file, name, err);
if (directory != NULL) g_object_unref (directory);
- anjuta_project_node_insert_after (parent, sibling, node);
- amp_group_create_token (AMP_PROJECT (obj), node, NULL);
+ add_job = pm_job_new (&_add_after_job, node, parent, sibling, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, add_job);
break;
case ANJUTA_PROJECT_TARGET:
node = project_node_new (AMP_PROJECT (obj), type, file, name, err);
- anjuta_project_node_insert_after (parent, sibling, node);
- amp_target_create_token (AMP_PROJECT (obj), node, NULL);
+ add_job = pm_job_new (&_add_after_job, node, parent, sibling, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, add_job);
break;
case ANJUTA_PROJECT_SOURCE:
node = project_node_new (AMP_PROJECT (obj), type, file, name, err);
- anjuta_project_node_insert_after (parent, sibling, node);
- amp_source_create_token (AMP_PROJECT (obj), node, NULL);
+ add_job = pm_job_new (&_add_after_job, node, parent, sibling, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, add_job);
break;
case ANJUTA_PROJECT_MODULE:
node = project_node_new (AMP_PROJECT (obj), type, file, name, err);
- anjuta_project_node_insert_after (parent, sibling, node);
- amp_module_create_token (AMP_PROJECT (obj), node, NULL);
+ add_job = pm_job_new (&_add_after_job, node, parent, sibling, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, add_job);
break;
case ANJUTA_PROJECT_PACKAGE:
node = project_node_new (AMP_PROJECT (obj), type, file, name, err);
- anjuta_project_node_insert_before (parent, sibling, node);
- amp_package_create_token (AMP_PROJECT (obj), node, NULL);
+ add_job = pm_job_new (&_add_after_job, node, parent, sibling, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, add_job);
break;
default:
node = project_node_new (AMP_PROJECT (obj), type, file, name, err);
- anjuta_project_node_insert_after (parent, sibling, node);
+ anjuta_project_node_insert_before (parent, sibling, node);
break;
}
- g_signal_emit_by_name (obj, "node-changed", node, NULL);
return node;
}
@@ -2431,31 +2660,10 @@ iproject_add_node_after (IAnjutaProject *obj, AnjutaProjectNode *parent, AnjutaP
static gboolean
iproject_remove_node (IAnjutaProject *obj, AnjutaProjectNode *node, GError **err)
{
- AnjutaProjectNodeType type = anjuta_project_node_get_node_type (node);
-
- anjuta_project_node_set_state (node, ANJUTA_PROJECT_REMOVED);
-
- switch (type & ANJUTA_PROJECT_TYPE_MASK)
- {
- case ANJUTA_PROJECT_GROUP:
- amp_group_delete_token (AMP_PROJECT (obj), node, NULL);
- break;
- case ANJUTA_PROJECT_TARGET:
- amp_target_delete_token (AMP_PROJECT (obj), node, NULL);
- break;
- case ANJUTA_PROJECT_SOURCE:
- amp_source_delete_token (AMP_PROJECT (obj), node, NULL);
- break;
- case ANJUTA_PROJECT_MODULE:
- amp_module_delete_token (AMP_PROJECT (obj), node, NULL);
- break;
- case ANJUTA_PROJECT_PACKAGE:
- amp_package_delete_token (AMP_PROJECT (obj), node, NULL);
- break;
- default:
- break;
- }
- g_signal_emit_by_name (obj, "node-changed", node, NULL);
+ PmJob *remove_job;
+
+ remove_job = pm_job_new (&_remove_job, node, NULL, NULL, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, remove_job);
return TRUE;
}
@@ -2464,21 +2672,12 @@ static AnjutaProjectProperty *
iproject_set_property (IAnjutaProject *obj, AnjutaProjectNode *node, AnjutaProjectProperty *property, const gchar *value, GError **error)
{
AnjutaProjectProperty *new_prop;
- gint flags;
-
- new_prop = amp_node_property_set (node, property, value);
+ PmJob *set_property_job;
- flags = ((AmpProperty *)new_prop->native)->flags;
-
- if (flags & AM_PROPERTY_IN_CONFIGURE)
- {
- amp_project_update_ac_property (AMP_PROJECT (obj), new_prop);
- }
- else if (flags & AM_PROPERTY_IN_MAKEFILE)
- {
- amp_project_update_am_property (AMP_PROJECT (obj), node, new_prop);
- }
- g_signal_emit_by_name (obj, "node-changed", node, NULL);
+ new_prop = amp_node_property_set (node, property, value);
+ set_property_job = pm_job_new (&_set_property_job, node, NULL, NULL, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+ set_property_job->property = new_prop;
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, set_property_job);
return new_prop;
}
@@ -2486,11 +2685,11 @@ iproject_set_property (IAnjutaProject *obj, AnjutaProjectNode *node, AnjutaProje
static gboolean
iproject_remove_property (IAnjutaProject *obj, AnjutaProjectNode *node, AnjutaProjectProperty *property, GError **error)
{
- AnjutaProjectProperty *old_prop;
+ PmJob *remove_property_job;
- old_prop = anjuta_project_node_remove_property (node, property);
- amp_property_free (old_prop);
- g_signal_emit_by_name (obj, "node-changed", node, NULL);
+ remove_property_job = pm_job_new (&_remove_property_job, node, NULL, NULL, ANJUTA_PROJECT_UNKNOWN, NULL, NULL, obj);
+ remove_property_job->property = property;
+ pm_command_queue_push (AMP_PROJECT (obj)->queue, remove_property_job);
return TRUE;
}
@@ -2630,6 +2829,7 @@ amp_project_dispose (GObject *object)
project = AMP_PROJECT (object);
amp_project_unload (project);
if (project->root) amp_root_free (project->root);
+ pm_command_queue_free (project->queue);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@@ -2649,6 +2849,8 @@ amp_project_instance_init (AmpProject *project)
project->am_space_list = NULL;
project->ac_space_list = NULL;
project->arg_list = NULL;
+
+ project->queue = pm_command_queue_new ();
}
static void
diff --git a/plugins/am-project/command-queue.c b/plugins/am-project/command-queue.c
new file mode 100644
index 0000000..ad1e2ee
--- /dev/null
+++ b/plugins/am-project/command-queue.c
@@ -0,0 +1,493 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/* command-queue.c
+ *
+ * Copyright (C) 2010 Sébastien Granjoux
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "command-queue.h"
+#include <libanjuta/anjuta-debug.h>
+#include <libanjuta/interfaces/ianjuta-project-backend.h>
+
+#include <string.h>
+
+/*
+
+2 Threads: GUI and Work
+
+Add new source.
+
+1. Work add new node
+ + Obvious, because it has the parent node
+ - Not possible, if the GUI has to get all child of the same node
+
+2. Work add new node, GUI use tree data
+
+3. Work create new node only but doesn't add it
+
+4. GUI create a new node and call the save function later
+
+
+The GUI cannot change links, else the Work cannot follow links when getting a
+node. The Work can even need parent links.
+=> GUI cannot read links except in particular case
+
+The GUI can read and write common part
+=> We need to copy the properties anyway when changing them
+
+The GUI can only read common part
+=>
+
+Have a proxy node for setting properties, this proxy will copy add common data
+and keep a link with the original node and reference count. The GUI can still
+read all data in the proxy without disturbing the Work which can change the
+underlines node. The proxy address is returned immediatly when the
+set_properties function is used. If another set_properties is done on the same
+node the proxy is reused, incrementing reference count.
+
+There is no need to proxy after add or remove functions, because the links are
+already copied in the module.
+
+After changing a property should we reload automatically ?
+
+
+Reloading a node can change its property, so we need a proxy for the load too.
+
+Proxy has to be created in GUI, because we need to update tree data.
+
+Instead of using a Proxy, we can copy all data everytime, this will allow
+automatic reload.
+
+
+
+ Work has always a full access to link
+ GUI read a special GNode tree created by thread
+
+ Work can always read common data, and can write them before sending them to GUI
+ or in when modification are requested by the GUI (the GUI get a proxy)
+ GUI can only read common data
+
+ Work has always a full access to specific data.
+ GUI has no access to specific data
+
+
+*/
+
+/* Types
+ *---------------------------------------------------------------------------*/
+
+struct _PmCommandQueue
+{
+ GQueue *job_queue;
+ GAsyncQueue *work_queue;
+ GAsyncQueue *done_queue;
+ GThread *worker;
+ guint idle_func;
+ gboolean stopping;
+ guint busy;
+};
+
+
+/* Signal
+ *---------------------------------------------------------------------------*/
+
+/* Command functions
+ *---------------------------------------------------------------------------*/
+
+static gboolean
+pm_command_exit_work (PmJob *job)
+{
+ PmCommandQueue *queue;
+
+ g_return_val_if_fail (job != NULL, FALSE);
+
+ queue = (PmCommandQueue *)job->user_data;
+
+ /* Push job in complete queue as g_thread_exit will stop the thread
+ * immediatly */
+ g_async_queue_push (queue->done_queue, job);
+ g_thread_exit (0);
+
+ return TRUE;
+}
+
+static PmCommandWork PmExitCommand = {NULL, pm_command_exit_work, NULL};
+
+/* Forward declarations
+ *---------------------------------------------------------------------------*/
+
+static gboolean pm_command_queue_idle (PmCommandQueue *queue);
+
+/* Worker thread functions
+ *---------------------------------------------------------------------------*/
+
+static gint
+pm_project_compare_node (AnjutaProjectNode *old_node, AnjutaProjectNode *new_node)
+{
+ gchar *name1;
+ gchar *name2;
+ GFile *file1;
+ GFile *file2;
+
+ name1 = anjuta_project_node_get_name (old_node);
+ name2 = anjuta_project_node_get_name (new_node);
+ file1 = anjuta_project_node_get_file (old_node);
+ file2 = anjuta_project_node_get_file (new_node);
+
+ return (anjuta_project_node_get_node_type (old_node) == anjuta_project_node_get_node_type (new_node))
+ && ((name1 == NULL) || (name2 == NULL) || (strcmp (name1, name2) == 0))
+ && ((file1 == NULL) || (file2 == NULL) || g_file_equal (file1, file2)) ? 0 : 1;
+}
+
+static void
+pm_project_map_children (PmJob *job, AnjutaProjectNode *old_node, AnjutaProjectNode *new_node)
+{
+ GList *children = NULL;
+
+ if (new_node != NULL)
+ {
+ for (new_node = anjuta_project_node_first_child (new_node); new_node != NULL; new_node = anjuta_project_node_next_sibling (new_node))
+ {
+ children = g_list_prepend (children, new_node);
+ }
+ children = g_list_reverse (children);
+ }
+
+ for (old_node = anjuta_project_node_first_child (old_node); old_node != NULL; old_node = anjuta_project_node_next_sibling (old_node))
+ {
+ GList *same;
+
+ same = g_list_find_custom (children, old_node, (GCompareFunc)pm_project_compare_node);
+
+ if (same != NULL)
+ {
+ g_hash_table_insert (job->map, old_node, (AnjutaProjectNode *)same->data);
+
+ pm_project_map_children ((PmJob *)job, old_node, (AnjutaProjectNode *)same->data);
+ children = g_list_delete_link (children, same);
+ }
+ else
+ {
+ /* Mark deleted node */
+ g_hash_table_insert (job->map, old_node, NULL);
+ pm_project_map_children ((PmJob *)job, old_node, NULL);
+ }
+ }
+
+ g_list_free (children);
+}
+
+static void
+pm_project_map_node (PmJob *job)
+{
+ if ((job->proxy != NULL) && (job->node != NULL))
+ {
+ AnjutaProjectNode *old_node;
+ AnjutaProjectNode *new_node;
+
+ job->map = g_hash_table_new (g_direct_hash, NULL);
+ old_node = job->proxy;
+ new_node = job->node;
+
+ g_hash_table_insert (job->map, old_node, new_node);
+
+ pm_project_map_children (job, old_node, new_node);
+ }
+}
+
+
+#if 0
+static gboolean
+pm_command_load_work (AnjutaPmProject *project, PmJob *job)
+{
+ AnjutaProjectNode *node;
+
+
+ if (ianjuta_project_load_node (project->project, job->node, &(job->error)))
+ {
+ pm_project_map_node (job);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+pm_command_save_work (AnjutaPmProject *project, PmJob *job)
+{
+ return ianjuta_project_save_node (project->project, job->node, &(job->error));
+}
+
+static gboolean
+pm_command_exit_work (AnjutaPmProject *project, PmJob *job)
+{
+ g_return_val_if_fail (job != NULL, FALSE);
+
+ /* Push job in complete queue as g_thread_exit will stop the thread
+ * immediatly */
+ g_async_queue_push (project->done_queue, job);
+ g_thread_exit (0);
+
+ return TRUE;
+}
+#endif
+
+/* Run work function in worker thread */
+static gpointer
+pm_command_queue_thread_main_loop (PmCommandQueue *queue)
+{
+ for (;;)
+ {
+ PmJob *job;
+ PmCommandFunc func;
+
+ /* Get new job */
+ job = (PmJob *)g_async_queue_pop (queue->work_queue);
+
+ /* Get work function and call it if possible */
+ func = job->work->worker;
+ if (func != NULL)
+ {
+ func (job);
+ }
+
+ /* Push completed job in queue */
+ g_async_queue_push (queue->done_queue, job);
+ }
+
+ return NULL;
+}
+
+/* Main thread functions
+ *---------------------------------------------------------------------------*/
+
+static gboolean
+pm_command_queue_start_thread (PmCommandQueue *queue, GError **error)
+{
+ queue->done_queue = g_async_queue_new ();
+ queue->work_queue = g_async_queue_new ();
+ queue->job_queue = g_queue_new ();
+
+ queue->worker = g_thread_create ((GThreadFunc) pm_command_queue_thread_main_loop, queue, TRUE, error);
+
+ if (queue->worker == NULL) {
+ g_async_queue_unref (queue->work_queue);
+ queue->work_queue = NULL;
+ g_async_queue_unref (queue->done_queue);
+ queue->done_queue = NULL;
+ g_queue_free (queue->job_queue);
+ queue->job_queue = NULL;
+
+ return FALSE;
+ }
+ else
+ {
+ queue->idle_func = g_idle_add ((GSourceFunc) pm_command_queue_idle, queue);
+ queue->stopping = FALSE;
+
+ return TRUE;
+ }
+}
+
+static gboolean
+pm_command_queue_stop_thread (PmCommandQueue *queue)
+{
+ if (queue->job_queue)
+ {
+ PmJob *job;
+
+ queue->stopping = TRUE;
+
+ // Remove idle function
+ g_idle_remove_by_data (queue);
+ queue->idle_func = 0;
+
+ // Request to terminate thread
+ job = pm_job_new (&PmExitCommand, NULL, NULL, NULL, 0, NULL, NULL, queue);
+ g_async_queue_push (queue->work_queue, job);
+ g_thread_join (queue->worker);
+ queue->worker = NULL;
+
+ // Free queue
+ g_async_queue_unref (queue->work_queue);
+ queue->work_queue = NULL;
+ g_queue_foreach (queue->job_queue, (GFunc)pm_job_free, NULL);
+ g_queue_free (queue->job_queue);
+ queue->job_queue = NULL;
+ for (;;)
+ {
+ job = g_async_queue_try_pop (queue->done_queue);
+ if (job == NULL) break;
+ pm_job_free (job);
+ }
+ queue->done_queue = NULL;
+ }
+
+ return TRUE;
+}
+
+/* Run a command in job queue */
+static gboolean
+pm_command_queue_run_command (PmCommandQueue *queue)
+{
+ gboolean running = TRUE;
+
+ if (queue->busy == 0)
+ {
+ /* Worker thread is waiting for new command, check job queue */
+ PmJob *job;
+
+ do
+ {
+ PmCommandFunc func;
+
+ /* Get next command */
+ job = g_queue_pop_head (queue->job_queue);
+ running = job != NULL;
+ if (!running) break;
+
+ /* Get setup function and call it if possible */
+ func = job->work->setup;
+ if (func != NULL)
+ {
+ running = func (job);
+ }
+
+ if (running)
+ {
+ /* Execute work function in the worker thread */
+ queue->busy = 1;
+
+ if (queue->idle_func == 0)
+ {
+ queue->idle_func = g_idle_add ((GSourceFunc) pm_command_queue_idle, queue);
+ }
+ g_async_queue_push (queue->work_queue, job);
+ }
+ else
+ {
+ /* Discard command */
+ pm_job_free (job);
+ }
+ } while (!running);
+ }
+
+ return running;
+}
+
+static gboolean
+pm_command_queue_idle (PmCommandQueue *queue)
+{
+ gboolean running;
+
+ for (;;)
+ {
+ PmCommandFunc func;
+ PmJob *job;
+
+ /* Get completed command */
+ job = (PmJob *)g_async_queue_try_pop (queue->done_queue);
+ if (job == NULL) break;
+
+ /* Get complete function and call it if possible */
+ func = job->work->complete;
+ if (func != NULL)
+ {
+ running = func (job);
+ }
+ pm_job_free (job);
+ queue->busy--;
+ }
+
+ running = pm_command_queue_run_command (queue);
+ if (!running) queue->idle_func = 0;
+
+ return running;
+}
+
+/* Job functions
+ *---------------------------------------------------------------------------*/
+
+PmJob *
+pm_job_new (PmCommandWork* work, AnjutaProjectNode *node, AnjutaProjectNode *parent, AnjutaProjectNode *sibling, AnjutaProjectNodeType type, GFile *file, const gchar *name, gpointer user_data)
+{
+ PmJob *job;
+
+ job = g_new0 (PmJob, 1);
+ job->work = work;
+ job->node = node;
+ job->parent = parent;
+ job->sibling = sibling;
+ job->type = type;
+ if (file != NULL) job->file = g_object_ref (file);
+ if (name != NULL) job->name = g_strdup (name);
+ job->user_data = user_data;
+
+ return job;
+}
+
+void
+pm_job_free (PmJob *job)
+{
+ if (job->error != NULL) g_error_free (job->error);
+ if (job->map != NULL) g_hash_table_destroy (job->map);
+ if (job->file != NULL) g_object_unref (job->file);
+ if (job->name != NULL) g_free (job->name);
+}
+
+/* Public functions
+ *---------------------------------------------------------------------------*/
+
+void
+pm_command_queue_push (PmCommandQueue *queue, PmJob *job)
+{
+ g_queue_push_tail (queue->job_queue, job);
+
+ pm_command_queue_run_command (queue);
+}
+
+PmCommandQueue*
+pm_command_queue_new (void)
+{
+ PmCommandQueue *queue;
+
+ queue = g_new0 (PmCommandQueue, 1);
+
+ queue->job_queue = NULL;
+ queue->work_queue = NULL;
+ queue->done_queue = NULL;
+ queue->worker = NULL;
+ queue->idle_func = 0;
+ queue->stopping = FALSE;
+ queue->busy = 0;
+
+ pm_command_queue_start_thread (queue, NULL);
+
+ return queue;
+}
+
+void
+pm_command_queue_free (PmCommandQueue *queue)
+{
+ pm_command_queue_stop_thread (queue);
+
+ g_free (queue);
+}
+
diff --git a/plugins/am-project/command-queue.h b/plugins/am-project/command-queue.h
new file mode 100644
index 0000000..863919f
--- /dev/null
+++ b/plugins/am-project/command-queue.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/* command-queue.h
+ *
+ * Copyright (C) 2010 Sébastien Granjoux
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _COMMAND_QUEUE_H_
+#define _COMMAND_QUEUE_H_
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <glib-object.h>
+
+#include <libanjuta/anjuta-project.h>
+
+G_BEGIN_DECLS
+
+typedef struct _PmJob PmJob;
+
+typedef struct _PmCommandQueue PmCommandQueue;
+
+typedef gboolean (*PmCommandFunc) (PmJob *job);
+
+typedef struct _PmCommandWork
+{
+ PmCommandFunc setup;
+ PmCommandFunc worker;
+ PmCommandFunc complete;
+} PmCommandWork;
+
+struct _PmJob
+{
+ PmCommandWork *work;
+ AnjutaProjectNodeType type;
+ GFile *file;
+ gchar *name;
+ AnjutaProjectNode *node;
+ AnjutaProjectNode *sibling;
+ AnjutaProjectNode *parent;
+ GError *error;
+ AnjutaProjectNode *proxy;
+ AnjutaProjectProperty *property;
+ GHashTable *map;
+ gpointer user_data;
+};
+
+PmJob * pm_job_new (PmCommandWork *work, AnjutaProjectNode *node, AnjutaProjectNode *parent, AnjutaProjectNode *sibling, AnjutaProjectNodeType type, GFile *file, const gchar *name, gpointer user_data);
+void pm_job_free (PmJob *job);
+
+PmCommandQueue* pm_command_queue_new (void);
+void pm_command_queue_free (PmCommandQueue *queue);
+
+void pm_command_queue_push (PmCommandQueue *queue, PmJob *job);
+
+
+
+G_END_DECLS
+
+#endif /* _COMMAND_QUEUE_H_ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]