From 0727204e36e919f06e80181482981c3f19669d76 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Wed, 11 Nov 2015 21:00:05 +0000
Subject: Prepared the next generation of parallel processings.

git-svn-id: svn://svn.gna.org/svn/chrysalide/trunk@606 abbe820e-26c8-41b2-8c08-b7b2b41f8b0a
---
 ChangeLog                          |  20 ++
 src/analysis/disass/disassembler.c |   8 +-
 src/analysis/project.c             |   8 +-
 src/glibext/configuration.c        |   2 +-
 src/glibext/delayed-int.h          |   4 +-
 src/glibext/delayed.c              | 499 ++++++++++++++++++++++++++++++-------
 src/glibext/delayed.h              |  18 +-
 src/glibext/gcodebuffer.c          |   8 +-
 src/gtkext/gtkextstatusbar.c       |   2 +-
 9 files changed, 473 insertions(+), 96 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d9fe98b..8ff7f0b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+15-11-11  Cyrille Bagard <nocbos@gmail.com>
+
+	* src/analysis/disass/disassembler.c:
+	* src/analysis/project.c:
+	Update code.
+
+	* src/glibext/configuration.c:
+	Typo.
+
+	* src/glibext/delayed.c:
+	* src/glibext/delayed.h:
+	* src/glibext/delayed-int.h:
+	Prepare the next generation of parallel processings.
+
+	* src/glibext/gcodebuffer.c:
+	Update code.
+
+	* src/gtkext/gtkextstatusbar.c:
+	Typo.
+
 15-11-09  Cyrille Bagard <nocbos@gmail.com>
 
 	* plugins/pychrysa/pychrysa.c:
diff --git a/src/analysis/disass/disassembler.c b/src/analysis/disass/disassembler.c
index 990ab0f..2f9d293 100644
--- a/src/analysis/disass/disassembler.c
+++ b/src/analysis/disass/disassembler.c
@@ -116,6 +116,11 @@ G_DEFINE_TYPE(GDelayedDisassembly, g_delayed_disassembly, G_TYPE_DELAYED_WORK);
 
 static void g_delayed_disassembly_class_init(GDelayedDisassemblyClass *klass)
 {
+    GDelayedWorkClass *work;                /* Version en classe parente   */
+
+    work = G_DELAYED_WORK_CLASS(klass);
+
+    work->run = (run_task_fc)g_delayed_disassembly_process;
 
 }
 
@@ -134,7 +139,6 @@ static void g_delayed_disassembly_class_init(GDelayedDisassemblyClass *klass)
 
 static void g_delayed_disassembly_init(GDelayedDisassembly *disass)
 {
-    G_DELAYED_WORK(disass)->run = (run_task_fc)g_delayed_disassembly_process;
 
 }
 
@@ -711,6 +715,6 @@ void disassemble_binary(GLoadedBinary *binary, GArchInstruction **instrs, GCodeB
     g_signal_connect(disass, "work-completed", G_CALLBACK(ack), binary);
 
     queue = get_work_queue();
-    g_work_queue_schedule_work(queue, G_DELAYED_WORK(disass));
+    g_work_queue_schedule_work(queue, G_DELAYED_WORK(disass), DEFAULT_WORK_GROUP);
 
 }
diff --git a/src/analysis/project.c b/src/analysis/project.c
index 8fef602..ed60929 100644
--- a/src/analysis/project.c
+++ b/src/analysis/project.c
@@ -971,12 +971,16 @@ G_DEFINE_TYPE(GDelayedStudy, g_delayed_study, G_TYPE_DELAYED_WORK);
 static void g_delayed_study_class_init(GDelayedStudyClass *klass)
 {
     GObjectClass *object;                   /* Autre version de la classe  */
+    GDelayedWorkClass *work;                /* Version en classe parente   */
 
     object = G_OBJECT_CLASS(klass);
+    work = G_DELAYED_WORK_CLASS(klass);
 
     object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_study_dispose;
     object->finalize = (GObjectFinalizeFunc)g_delayed_study_finalize;
 
+    work->run = (run_task_fc)g_delayed_study_process;
+
 }
 
 
@@ -994,8 +998,6 @@ static void g_delayed_study_class_init(GDelayedStudyClass *klass)
 
 static void g_delayed_study_init(GDelayedStudy *dstudy)
 {
-    G_DELAYED_WORK(dstudy)->run = (run_task_fc)g_delayed_study_process;
-
     dstudy->only_preload = false;
 
 }
@@ -1210,6 +1212,6 @@ void study_new_content(GDelayedStudy *dstudy)
     GWorkQueue *queue;                      /* Gestionnaire de différés    */
 
     queue = get_work_queue();
-    g_work_queue_schedule_work(queue, G_DELAYED_WORK(dstudy));
+    g_work_queue_schedule_work(queue, G_DELAYED_WORK(dstudy), DEFAULT_WORK_GROUP);
 
 }
diff --git a/src/glibext/configuration.c b/src/glibext/configuration.c
index 3becaaa..8717fb4 100644
--- a/src/glibext/configuration.c
+++ b/src/glibext/configuration.c
@@ -80,7 +80,7 @@ struct _GCfgParamClass
 
     /* Signaux */
 
-    void (*modified) (GCfgParam *);
+    void (* modified) (GCfgParam *);
 
 };
 
diff --git a/src/glibext/delayed-int.h b/src/glibext/delayed-int.h
index acef7d7..ecd9b4e 100644
--- a/src/glibext/delayed-int.h
+++ b/src/glibext/delayed-int.h
@@ -43,8 +43,6 @@ struct _GDelayedWork
 
     DL_LIST_ITEM(link);                     /* Lien vers les maillons      */
 
-    run_task_fc run;                        /* Traitement externalisé      */
-
     bool completed;                         /* Fin de la tâche ?           */
     GMutex mutex;                           /* Accès à la variable         */
     GCond cond;                             /* Attente de changement       */
@@ -56,6 +54,8 @@ struct _GDelayedWorkClass
 {
     GObjectClass parent;                    /* A laisser en premier        */
 
+    run_task_fc run;                        /* Traitement externalisé      */
+
     /* Signaux */
 
     void (* work_completed) (GDelayedWork *);
diff --git a/src/glibext/delayed.c b/src/glibext/delayed.c
index 32a3d16..967865b 100644
--- a/src/glibext/delayed.c
+++ b/src/glibext/delayed.c
@@ -24,7 +24,10 @@
 #include "delayed.h"
 
 
+#include <assert.h>
 #include <malloc.h>
+#include <stdio.h>
+#include <string.h>
 
 
 #include "delayed-int.h"
@@ -54,62 +57,71 @@ static void g_delayed_work_process(GDelayedWork *, GtkExtStatusBar *);
 /* -------------------------- THREAD DE TRAITEMENTS DEDIES -------------------------- */
 
 
-#define G_TYPE_TYPED_QUEUE               g_typed_queue_get_type()
-#define G_TYPED_QUEUE(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj), g_typed_queue_get_type(), GTypedQueue))
-#define G_IS_TYPED_QUEUE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_typed_queue_get_type()))
-#define G_TYPED_QUEUE_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_TYPED_QUEUE, GTypedQueueClass))
-#define G_IS_TYPED_QUEUE_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_TYPED_QUEUE))
-#define G_TYPED_QUEUE_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_TYPED_QUEUE, GTypedQueueClass))
+#define G_TYPE_WORK_GROUP               g_work_group_get_type()
+#define G_WORK_GROUP(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_group_get_type(), GWorkGroup))
+#define G_IS_WORK_GROUP(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_group_get_type()))
+#define G_WORK_GROUP_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_GROUP, GWorkGroupClass))
+#define G_IS_WORK_GROUP_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_GROUP))
+#define G_WORK_GROUP_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_GROUP, GWorkGroupClass))
 
 
 /* File de traitement pour un type donné (instance) */
-typedef struct _GTypedQueue
+typedef struct _GWorkGroup
 {
     GObject parent;                         /* A laisser en premier        */
 
-    GType type;                             /* Type de travaux menés       */
+    wgroup_id_t id;                         /* Identifiant de travaux menés*/
 
     GtkExtStatusBar *statusbar;             /* Barre de statut principale  */
 
     GDelayedWork *works;                    /* Tâches à mener à bien       */
     GMutex mutex;                           /* Verrou pour l'accès         */
     GCond cond;                             /* Réveil pour un traitement   */
+    GCond wait_cond;                        /* Réveil d'attente de fin     */
 
-    GThread *thread;                        /* Procédure de traitement     */
+    GThread **threads;                      /* Procédure de traitement     */
+    guint threads_count;                    /* Nombre de procédures        */
+    bool force_exit;                        /* Procédure d'arrêt           */
 
-} GTypedQueue;
+} GWorkGroup;
 
 /* File de traitement pour un type donné (classe) */
-typedef struct _GTypedQueueClass
+typedef struct _GWorkGroupClass
 {
     GObjectClass parent;                    /* A laisser en premier        */
 
-} GTypedQueueClass;
+} GWorkGroupClass;
 
 
-/* Indique le type défini pour les travaux typés. */
-static GType g_typed_queue_get_type(void);
+/* Indique le type défini pour les groupes de travail. */
+static GType g_work_group_get_type(void);
 
-/* Initialise la classe des travaux typés. */
-static void g_typed_queue_class_init(GTypedQueueClass *);
+/* Initialise la classe des groupes de travail. */
+static void g_work_group_class_init(GWorkGroupClass *);
 
-/* Initialise une instance de gestionnaire de travaux typés. */
-static void g_typed_queue_init(GTypedQueue *);
+/* Initialise une instance de groupe de travail. */
+static void g_work_group_init(GWorkGroup *);
 
 /* Supprime toutes les références externes. */
-static void g_typed_queue_dispose(GTypedQueue *);
+static void g_work_group_dispose(GWorkGroup *);
 
 /* Procède à la libération totale de la mémoire. */
-static void g_typed_queue_finalize(GTypedQueue *);
+static void g_work_group_finalize(GWorkGroup *);
 
 /* Crée un nouveau thread dédié à un type de travaux donné. */
-static GTypedQueue *g_typed_queue_new(GType, GtkExtStatusBar *);
+static GWorkGroup *g_work_group_new(wgroup_id_t, GtkExtStatusBar *);
+
+/* Fournit l'identifiant associé à un groupe de travail. */
+static wgroup_id_t g_work_group_get_id(const GWorkGroup *);
 
 /* Place une nouvelle tâche en attente dans une file dédiée. */
-static void g_typed_queue_schedule(GTypedQueue *, GDelayedWork *);
+static void g_work_group_schedule(GWorkGroup *, GDelayedWork *);
 
 /* Assure le traitement en différé. */
-static void *g_typed_queue_process(GTypedQueue *);
+static void *g_work_group_process(GWorkGroup *);
+
+/* Attend que toutes les tâches d'un groupe soient traitées. */
+static void g_work_group_wait_for_completion(GWorkGroup *);
 
 
 
@@ -123,8 +135,11 @@ struct _GWorkQueue
 
     GtkExtStatusBar *statusbar;             /* Barre de statut principale  */
 
-    GTypedQueue **threads;                  /* Files de traitement         */
-    size_t threads_count;                   /* Nombre de files internes    */
+    wgroup_id_t generator;                  /* Générateur d'identifiants   */
+
+    GWorkGroup **groups;                   /* Files de traitement         */
+    size_t groups_count;                    /* Nombre de files internes    */
+    GMutex mutex;                           /* Verrou pour l'accès         */
 
 };
 
@@ -142,6 +157,15 @@ static void g_work_queue_class_init(GWorkQueueClass *);
 /* Initialise une instance de gestionnaire de travaux différés. */
 static void g_work_queue_init(GWorkQueue *);
 
+/* Supprime toutes les références externes. */
+static void g_work_queue_dispose(GWorkQueue *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_work_queue_finalize(GWorkQueue *);
+
+/* Donne l'assurance de l'existence d'un groupe de travail. */
+static GWorkGroup *g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t);
+
 
 
 /* ---------------------------------------------------------------------------------- */
@@ -262,7 +286,7 @@ static void g_delayed_work_finalize(GDelayedWork *work)
 
 static void g_delayed_work_process(GDelayedWork *work, GtkExtStatusBar *statusbar)
 {
-    work->run(work, statusbar);
+    G_DELAYED_WORK_GET_CLASS(work)->run(work, statusbar);
 
     g_mutex_lock(&work->mutex);
 
@@ -302,19 +326,19 @@ void g_delayed_work_wait_for_completion(GDelayedWork *work)
 
 
 /* ---------------------------------------------------------------------------------- */
-/*                            THREAD DE TRAITEMENTS DEDIES                            */
+/*                           THREADS DES TRAITEMENTS DEDIES                           */
 /* ---------------------------------------------------------------------------------- */
 
 
-/* Indique le type défini pour les travaux typés. */
-G_DEFINE_TYPE(GTypedQueue, g_typed_queue, G_TYPE_OBJECT);
+/* Indique le type défini pour les groupes de travail. */
+G_DEFINE_TYPE(GWorkGroup, g_work_group, G_TYPE_OBJECT);
 
 
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : klass = classe à initialiser.                                *
 *                                                                             *
-*  Description : Initialise la classe des travaux typés.                      *
+*  Description : Initialise la classe des groupes de travail.                 *
 *                                                                             *
 *  Retour      : -                                                            *
 *                                                                             *
@@ -322,23 +346,23 @@ G_DEFINE_TYPE(GTypedQueue, g_typed_queue, G_TYPE_OBJECT);
 *                                                                             *
 ******************************************************************************/
 
-static void g_typed_queue_class_init(GTypedQueueClass *klass)
+static void g_work_group_class_init(GWorkGroupClass *klass)
 {
     GObjectClass *object;                   /* Autre version de la classe  */
 
     object = G_OBJECT_CLASS(klass);
 
-    object->dispose = (GObjectFinalizeFunc/* ! */)g_typed_queue_dispose;
-    object->finalize = (GObjectFinalizeFunc)g_typed_queue_finalize;
+    object->dispose = (GObjectFinalizeFunc/* ! */)g_work_group_dispose;
+    object->finalize = (GObjectFinalizeFunc)g_work_group_finalize;
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : queue = instance à initialiser.                              *
+*  Paramètres  : group = instance à initialiser.                              *
 *                                                                             *
-*  Description : Initialise une instance de gestionnaire de travaux typés.    *
+*  Description : Initialise une instance de groupe de travail.                *
 *                                                                             *
 *  Retour      : -                                                            *
 *                                                                             *
@@ -346,18 +370,36 @@ static void g_typed_queue_class_init(GTypedQueueClass *klass)
 *                                                                             *
 ******************************************************************************/
 
-static void g_typed_queue_init(GTypedQueue *queue)
+static void g_work_group_init(GWorkGroup *group)
 {
-    g_mutex_init(&queue->mutex);
-    g_cond_init(&queue->cond);
+    guint i;                                /* Boucle de parcours          */
+    char name[16];                          /* Désignation humaine         */
+
+    g_mutex_init(&group->mutex);
+    g_cond_init(&group->cond);
+    g_cond_init(&group->wait_cond);
+
+    group->threads_count = g_get_num_processors();
+
+    group->threads = (GThread **)calloc(group->threads_count, sizeof(GThread *));
 
-    queue->thread = g_thread_new("chrysalide_queue", (GThreadFunc)g_typed_queue_process, queue);
-    if (!queue->thread)
-        goto gtqi_error;
+    for (i = 0; i < group->threads_count; i++)
+    {
+        snprintf(name, sizeof(name), "work_group_%u", i);
+
+        group->threads[i] = g_thread_new(name, (GThreadFunc)g_work_group_process, group);
+        if (!group->threads[i])
+            goto gwgi_error;
+
+    }
 
- gtqi_error:
+ gwgi_error:
 
-    /* TODO */ return;
+    group->threads_count = i;
+
+    assert(i > 0);
+
+    group->force_exit = false;
 
 }
 
@@ -374,19 +416,41 @@ static void g_typed_queue_init(GTypedQueue *queue)
 *                                                                             *
 ******************************************************************************/
 
-static void g_typed_queue_dispose(GTypedQueue *queue)
+static void g_work_group_dispose(GWorkGroup *group)
 {
-    g_mutex_clear(&queue->mutex);
-    g_cond_clear(&queue->cond);
+    guint i;                                /* Boucle de parcours          */
+    GDelayedWork *work;                     /* Travail à oublier           */
+
+    group->force_exit = true;
+
+    g_cond_broadcast(&group->cond);
+
+    for (i = 0; i < group->threads_count; i++)
+        g_thread_join(group->threads[i]);
+
+    g_object_unref(group->statusbar);
+
+    while (!dl_list_empty(group->works))
+    {
+        work = group->works;
+        delayed_work_list_del(work, &group->works);
+
+        g_object_unref(G_OBJECT(work));
 
-    G_OBJECT_CLASS(g_typed_queue_parent_class)->dispose(G_OBJECT(queue));
+    }
+
+    g_mutex_clear(&group->mutex);
+    g_cond_clear(&group->cond);
+    g_cond_clear(&group->wait_cond);
+
+    G_OBJECT_CLASS(g_work_group_parent_class)->dispose(G_OBJECT(group));
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : queue = instance d'objet GLib à traiter.                     *
+*  Paramètres  : group = instance d'objet GLib à traiter.                     *
 *                                                                             *
 *  Description : Procède à la libération totale de la mémoire.                *
 *                                                                             *
@@ -396,9 +460,12 @@ static void g_typed_queue_dispose(GTypedQueue *queue)
 *                                                                             *
 ******************************************************************************/
 
-static void g_typed_queue_finalize(GTypedQueue *queue)
+static void g_work_group_finalize(GWorkGroup *group)
 {
-    G_OBJECT_CLASS(g_typed_queue_parent_class)->finalize(G_OBJECT(queue));
+    if (group->threads != NULL)
+        free(group->threads);
+
+    G_OBJECT_CLASS(g_work_group_parent_class)->finalize(G_OBJECT(group));
 
 }
 
@@ -416,13 +483,13 @@ static void g_typed_queue_finalize(GTypedQueue *queue)
 *                                                                             *
 ******************************************************************************/
 
-static GTypedQueue *g_typed_queue_new(GType type, GtkExtStatusBar *statusbar)
+static GWorkGroup *g_work_group_new(wgroup_id_t id, GtkExtStatusBar *statusbar)
 {
-    GTypedQueue *result;                    /* Traiteur à retourner        */
+    GWorkGroup *result;                    /* Traiteur à retourner        */
 
-    result = g_object_new(G_TYPE_TYPED_QUEUE, NULL);
+    result = g_object_new(G_TYPE_WORK_GROUP, NULL);
 
-    result->type = type;
+    result->id = id;
 
     result->statusbar = statusbar;
     g_object_ref(statusbar);
@@ -434,7 +501,26 @@ static GTypedQueue *g_typed_queue_new(GType type, GtkExtStatusBar *statusbar)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : queue = gestionnaire des actions à mener.                    *
+*  Paramètres  : group = gestionnaire des actions à mener.                    *
+*                                                                             *
+*  Description : Fournit l'identifiant associé à un groupe de travail.        *
+*                                                                             *
+*  Retour      : Identifiant unique attribué au groupe de travail.            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static wgroup_id_t g_work_group_get_id(const GWorkGroup *group)
+{
+    return group->id;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : group = gestionnaire des actions à mener.                    *
 *                work  = nouvelle tâche à programmer, puis effectuer.         *
 *                                                                             *
 *  Description : Place une nouvelle tâche en attente dans une file dédiée.    *
@@ -445,22 +531,22 @@ static GTypedQueue *g_typed_queue_new(GType type, GtkExtStatusBar *statusbar)
 *                                                                             *
 ******************************************************************************/
 
-static void g_typed_queue_schedule(GTypedQueue *queue, GDelayedWork *work)
+static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work)
 {
-    g_mutex_lock(&queue->mutex);
+    g_mutex_lock(&group->mutex);
 
-    delayed_work_list_add_tail(work, &queue->works);
+    delayed_work_list_add_tail(work, &group->works);
 
-    g_cond_signal(&queue->cond);
+    g_cond_signal(&group->cond);
 
-    g_mutex_unlock(&queue->mutex);
+    g_mutex_unlock(&group->mutex);
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : queue = gestionnaire des actions à mener.                    *
+*  Paramètres  : group = gestionnaire des actions à mener.                    *
 *                                                                             *
 *  Description : Assure le traitement en différé.                             *
 *                                                                             *
@@ -470,25 +556,30 @@ static void g_typed_queue_schedule(GTypedQueue *queue, GDelayedWork *work)
 *                                                                             *
 ******************************************************************************/
 
-static void *g_typed_queue_process(GTypedQueue *queue)
+static void *g_work_group_process(GWorkGroup *group)
 {
     GDelayedWork *work;                     /* Traitement à mener          */
 
     while (1)
     {
-        g_mutex_lock(&queue->mutex);
+        g_mutex_lock(&group->mutex);
+
+        while (dl_list_empty(group->works) && !group->force_exit)
+            g_cond_wait(&group->cond, &group->mutex);
+
+        if (group->force_exit)
+            break;
 
-        if (dl_list_empty(queue->works))
-            g_cond_wait(&queue->cond, &queue->mutex);
+        work = group->works;
+        delayed_work_list_del(work, &group->works);
 
-        work = queue->works;
-        delayed_work_list_del(work, &queue->works);
+        g_mutex_unlock(&group->mutex);
 
-        g_mutex_unlock(&queue->mutex);
+        g_delayed_work_process(work, group->statusbar);
 
-        g_delayed_work_process(work, queue->statusbar);
+        g_object_unref(G_OBJECT(work));
 
-        /* TODO : delete work */
+        g_cond_broadcast(&group->wait_cond);
 
     }
 
@@ -497,6 +588,31 @@ static void *g_typed_queue_process(GTypedQueue *queue)
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : group = groupe dont les conclusions sont attendues.          *
+*                                                                             *
+*  Description : Attend que toutes les tâches d'un groupe soient traitées.    *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_work_group_wait_for_completion(GWorkGroup *group)
+{
+    g_mutex_lock(&group->mutex);
+
+    while (dl_list_empty(group->works) && !group->force_exit)
+        g_cond_wait(&group->wait_cond, &group->mutex);
+
+    g_mutex_unlock(&group->mutex);
+
+}
+
+
+
 
 /* ---------------------------------------------------------------------------------- */
 /*                           TRAITEMENT DE TACHES DIFFEREES                           */
@@ -521,6 +637,12 @@ G_DEFINE_TYPE(GWorkQueue, g_work_queue, G_TYPE_OBJECT);
 
 static void g_work_queue_class_init(GWorkQueueClass *klass)
 {
+    GObjectClass *object;                   /* Autre version de la classe  */
+
+    object = G_OBJECT_CLASS(klass);
+
+    object->dispose = (GObjectFinalizeFunc/* ! */)g_work_queue_dispose;
+    object->finalize = (GObjectFinalizeFunc)g_work_queue_finalize;
 
 }
 
@@ -539,6 +661,65 @@ static void g_work_queue_class_init(GWorkQueueClass *klass)
 
 static void g_work_queue_init(GWorkQueue *queue)
 {
+    queue->generator = 0;
+
+    queue->groups = NULL;
+    queue->groups_count = 0;
+    g_mutex_init(&queue->mutex);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : queue = instance d'objet GLib à traiter.                     *
+*                                                                             *
+*  Description : Supprime toutes les références externes.                     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_work_queue_dispose(GWorkQueue *queue)
+{
+    size_t i;                               /* Boucle de parcours          */
+
+    g_object_unref(G_OBJECT(queue->statusbar));
+
+    g_mutex_lock(&queue->mutex);
+
+    for (i = 0; i < queue->groups_count; i++)
+        g_object_unref(G_OBJECT(queue->groups[i]));
+
+    g_mutex_unlock(&queue->mutex);
+
+    g_mutex_clear(&queue->mutex);
+
+    G_OBJECT_CLASS(g_work_queue_parent_class)->dispose(G_OBJECT(queue));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : queue = instance d'objet GLib à traiter.                     *
+*                                                                             *
+*  Description : Procède à la libération totale de la mémoire.                *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_work_queue_finalize(GWorkQueue *queue)
+{
+    if (queue->groups != NULL)
+        free(queue->groups);
+
+    G_OBJECT_CLASS(g_work_queue_parent_class)->finalize(G_OBJECT(queue));
 
 }
 
@@ -562,7 +743,10 @@ bool init_work_queue(GObject *ref)
     queue = g_object_new(G_TYPE_WORK_QUEUE, NULL);
 
     if (ref != NULL)
+    {
         queue->statusbar = g_object_get_data(ref, "statusbar");
+        g_object_ref(G_OBJECT(queue->statusbar));
+    }
     else
         queue->statusbar = NULL;
 
@@ -600,8 +784,121 @@ GWorkQueue *_get_work_queue(GWorkQueue *queue)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : queue = gestionnaire de l'ensemble des groupes de travail.   *
+*                id    = identifiant d'un groupe de travail.                  *
+*                                                                             *
+*  Description : Donne l'assurance de l'existence d'un groupe de travail.     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : Le verrou d'accès doit être posé par l'appelant.             *
+*                                                                             *
+******************************************************************************/
+
+static GWorkGroup *g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id)
+{
+    GWorkGroup *result;                     /* Groupe en place à renvoyer  */
+    bool found;                             /* Bilan des recherches        */
+    size_t i;                               /* Boucle de parcours          */
+
+    found = false;
+
+    for (i = 0; i < queue->groups_count && !found; i++)
+    {
+        result = queue->groups[i];
+        found = (g_work_group_get_id(result) == id);
+    }
+
+    if (!found)
+    {
+        queue->groups_count++;
+        queue->groups = (GWorkGroup **)realloc(queue->groups,
+                                               queue->groups_count * sizeof(GWorkGroup *));
+
+        result = g_work_group_new(id, queue->statusbar);
+        queue->groups[i] = result;
+
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : queue = gestionnaire de l'ensemble des groupes de travail.   *
+*                                                                             *
+*  Description : Constitue un nouveau groupe de travail.                      *
+*                                                                             *
+*  Retour      : Nouvel identifiant unique d'un nouveau groupe de travail.    *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+wgroup_id_t g_work_queue_define_work_group(GWorkQueue *queue)
+{
+    wgroup_id_t result;                     /* Valeur à retourner          */
+
+    g_mutex_lock(&queue->mutex);
+
+    result = queue->generator++;
+
+    g_work_queue_ensure_group_exists(queue, result);
+
+    g_mutex_unlock(&queue->mutex);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : queue = gestionnaire de l'ensemble des groupes de travail.   *
+*                id    = identifiant d'un groupe de travail.                  *
+*                                                                             *
+*  Description : Dissout un groupe de travail existant.                       *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id)
+{
+    size_t i;                               /* Boucle de parcours          */
+
+    g_mutex_lock(&queue->mutex);
+
+    for (i = 0; i < queue->groups_count; i++)
+        if (g_work_group_get_id(queue->groups[i]) == id)
+        {
+            memmove(&queue->groups[i], &queue->groups[i + 1],
+                    (queue->groups_count - i - 1) * sizeof(GWorkGroup *));
+
+            queue->groups_count--;
+            queue->groups = (GWorkGroup **)realloc(queue->groups,
+                                                   queue->groups_count * sizeof(GWorkGroup *));
+
+            break;
+
+        }
+
+    assert(i < queue->groups_count);
+
+    g_mutex_unlock(&queue->mutex);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : queue = gestionnaire des actions à mener.                    *
 *                work  = nouvelle tâche à programmer, puis effectuer.         *
+*                id    = identifiant du groupe de travail d'affectation.      *
 *                                                                             *
 *  Description : Place une nouvelle tâche en attente.                         *
 *                                                                             *
@@ -611,26 +908,60 @@ GWorkQueue *_get_work_queue(GWorkQueue *queue)
 *                                                                             *
 ******************************************************************************/
 
-void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work)
+void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id_t id)
 {
-    GType target;                           /* Type de travail à ajouter   */
-    size_t i;                               /* Boucle de traitement        */
+    GWorkGroup *group;                      /* Groupe de travail à attendre*/
 
-    target = G_TYPE_FROM_INSTANCE(work);
+    g_mutex_lock(&queue->mutex);
 
-    for (i = 0; i < queue->threads_count; i++)
-        if (queue->threads[i]->type == target) break;
+    group = g_work_queue_ensure_group_exists(queue, id);
 
-    if (i == queue->threads_count)
-    {
-        queue->threads_count++;
-        queue->threads = (GTypedQueue **)realloc(queue->threads,
-                                                 queue->threads_count * sizeof(GTypedQueue *));
+    g_object_ref(G_OBJECT(group));
+
+    g_mutex_unlock(&queue->mutex);
 
-        queue->threads[i] = g_typed_queue_new(target, queue->statusbar);
+    g_work_group_schedule(group, work);
+
+    g_object_unref(G_OBJECT(group));
+
+}
 
-    }
 
-    g_typed_queue_schedule(queue->threads[i], work);
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : queue = gestionnaire de l'ensemble des groupes de travail.   *
+*                id    = identifiant d'un groupe de travail.                  *
+*                                                                             *
+*  Description : Attend que toutes les tâches d'un groupe soient traitées.    *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id)
+{
+    size_t i;                               /* Boucle de parcours          */
+    GWorkGroup *group;                      /* Groupe de travail à attendre*/
+
+    group = NULL;
+
+    g_mutex_lock(&queue->mutex);
+
+    for (i = 0; i < queue->groups_count; i++)
+        if (g_work_group_get_id(queue->groups[i]) == id)
+        {
+            group = queue->groups[i];
+            g_object_ref(G_OBJECT(group));
+        }
+
+    g_mutex_unlock(&queue->mutex);
+
+    if (group != NULL)
+    {
+        g_work_group_wait_for_completion(group);
+        g_object_unref(G_OBJECT(group));
+    }
 
 }
diff --git a/src/glibext/delayed.h b/src/glibext/delayed.h
index 03aa873..0a27c93 100644
--- a/src/glibext/delayed.h
+++ b/src/glibext/delayed.h
@@ -74,6 +74,13 @@ typedef struct _GWorkQueue GWorkQueue;
 typedef struct _GWorkQueueClass GWorkQueueClass;
 
 
+/* Identifiant unique pour groupe de travail */
+typedef unsigned long long wgroup_id_t;
+
+/* Groupe d'exécution par défaut */
+#define DEFAULT_WORK_GROUP 0
+
+
 #define get_work_queue() _get_work_queue(NULL)
 
 
@@ -86,8 +93,17 @@ bool init_work_queue(GObject *);
 /* Fournit le gestionnaire de traitements parallèles courant. */
 GWorkQueue *_get_work_queue(GWorkQueue *);
 
+/* Constitue un nouveau groupe de travail. */
+wgroup_id_t g_work_queue_define_work_group(GWorkQueue *);
+
+/* Dissout un groupe de travail existant. */
+void g_work_queue_delete_work_group(GWorkQueue *, wgroup_id_t);
+
 /* Place une nouvelle tâche en attente. */
-void g_work_queue_schedule_work(GWorkQueue *, GDelayedWork *);
+void g_work_queue_schedule_work(GWorkQueue *, GDelayedWork *, wgroup_id_t);
+
+/* Attend que toutes les tâches d'un groupe soient traitées. */
+void g_work_queue_wait_for_completion(GWorkQueue *, wgroup_id_t);
 
 
 
diff --git a/src/glibext/gcodebuffer.c b/src/glibext/gcodebuffer.c
index a27b202..c676b7c 100644
--- a/src/glibext/gcodebuffer.c
+++ b/src/glibext/gcodebuffer.c
@@ -222,6 +222,11 @@ G_DEFINE_TYPE(GBufferScan, g_buffer_scan, G_TYPE_DELAYED_WORK);
 
 static void g_buffer_scan_class_init(GBufferScanClass *klass)
 {
+    GDelayedWorkClass *work;                /* Version en classe parente   */
+
+    work = G_DELAYED_WORK_CLASS(klass);
+
+    work->run = (run_task_fc)g_buffer_scan_process;
 
 }
 
@@ -240,7 +245,6 @@ static void g_buffer_scan_class_init(GBufferScanClass *klass)
 
 static void g_buffer_scan_init(GBufferScan *disass)
 {
-    G_DELAYED_WORK(disass)->run = (run_task_fc)g_buffer_scan_process;
 
 }
 
@@ -887,7 +891,7 @@ void g_buffer_code_scan(GCodeBuffer *buffer, vmpa_t start, vmpa_t end, const cha
     scan = g_buffer_scan_new(buffer, start, end, message, process, data);
 
     queue = get_work_queue();
-    g_work_queue_schedule_work(queue, G_DELAYED_WORK(scan));
+    g_work_queue_schedule_work(queue, G_DELAYED_WORK(scan), DEFAULT_WORK_GROUP);
 
 }
 
diff --git a/src/gtkext/gtkextstatusbar.c b/src/gtkext/gtkextstatusbar.c
index 8051d41..62d4950 100644
--- a/src/gtkext/gtkextstatusbar.c
+++ b/src/gtkext/gtkextstatusbar.c
@@ -57,7 +57,7 @@ struct _GtkExtStatusBar
 
     bar_item *stack;                        /* Pile de statut de la barre  */
     size_t stack_size;                      /* Taille de la pile           */
-    GMutex stack_access;                    /* Accès en à la pile          */
+    GMutex stack_access;                    /* Accès à la pile             */
 
 };
 
-- 
cgit v0.11.2-87-g4458