[anjuta/newproject] am: Use threads again in autotools project plugin



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 (&amp_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 (&amp_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 (&amp_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 (&amp_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 (&amp_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 (&amp_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 (&amp_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 (&amp_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 (&amp_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 (&amp_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 (&amp_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 (&amp_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 (&amp_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 (&amp_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 (&amp_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]