summaryrefslogtreecommitdiff
path: root/src/glibext
diff options
context:
space:
mode:
Diffstat (limited to 'src/glibext')
-rw-r--r--src/glibext/Makefile.am2
-rw-r--r--src/glibext/work-int.h (renamed from src/glibext/delayed-int.h)0
-rw-r--r--src/glibext/work.c (renamed from src/glibext/delayed.c)0
-rw-r--r--src/glibext/work.h (renamed from src/glibext/delayed.h)0
-rw-r--r--src/glibext/workgroup-int.h75
-rw-r--r--src/glibext/workgroup.c1353
-rw-r--r--src/glibext/workgroup.h127
-rw-r--r--src/glibext/workwqueue-int.h75
-rw-r--r--src/glibext/workwqueue.c1353
-rw-r--r--src/glibext/workwqueue.h127
10 files changed, 3110 insertions, 2 deletions
diff --git a/src/glibext/Makefile.am b/src/glibext/Makefile.am
index e2c9d69..b5ea0f0 100644
--- a/src/glibext/Makefile.am
+++ b/src/glibext/Makefile.am
@@ -8,8 +8,6 @@ libglibext_la_SOURCES = \
comparison.h comparison.c \
configuration-int.h \
configuration.h configuration.c \
- delayed-int.h \
- delayed.h delayed.c \
gbinarycursor.h gbinarycursor.c \
gbinportion-int.h \
gbinportion.h gbinportion.c \
diff --git a/src/glibext/delayed-int.h b/src/glibext/work-int.h
index 4f84e86..4f84e86 100644
--- a/src/glibext/delayed-int.h
+++ b/src/glibext/work-int.h
diff --git a/src/glibext/delayed.c b/src/glibext/work.c
index 6b5ac35..6b5ac35 100644
--- a/src/glibext/delayed.c
+++ b/src/glibext/work.c
diff --git a/src/glibext/delayed.h b/src/glibext/work.h
index 89eed12..89eed12 100644
--- a/src/glibext/delayed.h
+++ b/src/glibext/work.h
diff --git a/src/glibext/workgroup-int.h b/src/glibext/workgroup-int.h
new file mode 100644
index 0000000..4f84e86
--- /dev/null
+++ b/src/glibext/workgroup-int.h
@@ -0,0 +1,75 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * delayed-int.h - définitions internes pour la gestion des travaux différés
+ *
+ * Copyright (C) 2009-2018 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_DELAYED_INT_H
+#define _GLIBEXT_DELAYED_INT_H
+
+
+#include "delayed.h"
+
+
+#include "notifier.h"
+#include "../common/dllist.h"
+
+
+
+/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
+
+
+/* Traite un travail programmé. */
+typedef void (* run_task_fc) (GDelayedWork *, GtkStatusStack *);
+
+
+/* Travail différé (instance) */
+struct _GDelayedWork
+{
+ GObject parent; /* A laisser en premier */
+
+ DL_LIST_ITEM(link); /* Lien vers les maillons */
+
+ bool completed; /* Fin de la tâche ? */
+ GMutex mutex; /* Accès à la variable */
+ GCond cond; /* Attente de changement */
+
+};
+
+/* Travail différé (classe) */
+struct _GDelayedWorkClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+ run_task_fc run; /* Traitement externalisé */
+
+ /* Signaux */
+
+ void (* work_completed) (GDelayedWork *);
+
+};
+
+
+#define delayed_work_list_add_tail(new, head) dl_list_add_tail(new, head, GDelayedWork, link)
+#define delayed_work_list_del(item, head) dl_list_del(item, head, GDelayedWork, link)
+
+
+
+#endif /* _GLIBEXT_DELAYED_INT_H */
diff --git a/src/glibext/workgroup.c b/src/glibext/workgroup.c
new file mode 100644
index 0000000..6b5ac35
--- /dev/null
+++ b/src/glibext/workgroup.c
@@ -0,0 +1,1353 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * delayed.c - gestion des travaux différés
+ *
+ * Copyright (C) 2009-2018 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "delayed.h"
+
+
+#include <assert.h>
+#include <inttypes.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <string.h>
+
+
+#include "delayed-int.h"
+#include "../core/nproc.h"
+#ifdef INCLUDE_GTK_SUPPORT
+# include "../gui/core/global.h"
+#endif
+
+
+
+/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
+
+
+/* Initialise la classe des travaux différés. */
+static void g_delayed_work_class_init(GDelayedWorkClass *);
+
+/* Initialise une instance de travail différé. */
+static void g_delayed_work_init(GDelayedWork *);
+
+/* Supprime toutes les références externes. */
+static void g_delayed_work_dispose(GDelayedWork *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_delayed_work_finalize(GDelayedWork *);
+
+/* Mène l'opération programmée. */
+static void g_delayed_work_process(GDelayedWork *, GtkStatusStack *);
+
+
+
+/* -------------------------- THREAD DE TRAITEMENTS DEDIES -------------------------- */
+
+
+#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 _GWorkGroup
+{
+ GObject parent; /* A laisser en premier */
+
+ wgroup_id_t id; /* Identifiant de travaux menés*/
+
+ 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 */
+ gint pending; /* Tâches en cours d'exécution */
+
+ GThread **threads; /* Procédure de traitement */
+ guint threads_count; /* Nombre de procédures */
+ bool force_exit; /* Procédure d'arrêt */
+
+ wait_for_incoming_works_cb callback; /* Encadre les attentes de fin */
+ void *data; /* Données à associer */
+
+} GWorkGroup;
+
+/* File de traitement pour un type donné (classe) */
+typedef struct _GWorkGroupClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+} GWorkGroupClass;
+
+
+/* Indique le type défini pour les groupes de travail. */
+static GType g_work_group_get_type(void);
+
+/* Initialise la classe des groupes de travail. */
+static void g_work_group_class_init(GWorkGroupClass *);
+
+/* Initialise une instance de groupe de travail. */
+static void g_work_group_init(GWorkGroup *);
+
+/* Supprime toutes les références externes. */
+static void g_work_group_dispose(GWorkGroup *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_work_group_finalize(GWorkGroup *);
+
+/* Crée un nouveau thread dédié à un type de travaux donné. */
+static GWorkGroup *g_work_group_new(wgroup_id_t, const guint *);
+
+/* 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_work_group_schedule(GWorkGroup *, GDelayedWork *);
+
+/* Assure le traitement en différé. */
+static void *g_work_group_process(GWorkGroup *);
+
+/* Détermine si le groupe est vide de toute programmation. */
+static bool g_work_group_is_empty(GWorkGroup *);
+
+/* Attend que toutes les tâches d'un groupe soient traitées. */
+static void g_work_group_wait_for_completion(GWorkGroup *, GWorkQueue *);
+
+/* Modifie les conditions d'attente des fins d'exécutions. */
+static void g_work_group_set_extra_wait_callback(GWorkGroup *, wait_for_incoming_works_cb, void *);
+
+/* Force un réveil d'une attente en cours pour la confirmer. */
+static void g_work_group_wake_up_waiters(GWorkGroup *);
+
+
+
+/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */
+
+
+/* Gestionnaire des travaux différés (instance) */
+struct _GWorkQueue
+{
+ GObject parent; /* A laisser en premier */
+
+ 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 */
+ GCond wait_all; /* Réveil d'attente globale */
+
+};
+
+/* Gestionnaire des travaux différés (classe) */
+struct _GWorkQueueClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+};
+
+
+/* Initialise la classe des travaux différés. */
+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 bool g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t, const guint *);
+
+/* Fournit le groupe de travail correspondant à un identifiant. */
+static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *, wgroup_id_t);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* TACHE DIFFEREE DANS LE TEMPS */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour les travaux différés. */
+G_DEFINE_TYPE(GDelayedWork, g_delayed_work, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des travaux différés. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_delayed_work_class_init(GDelayedWorkClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_work_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_delayed_work_finalize;
+
+ g_signal_new("work-completed",
+ G_TYPE_DELAYED_WORK,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(GDelayedWorkClass, work_completed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : work = instance à initialiser. *
+* *
+* Description : Initialise une instance de travail différé. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_delayed_work_init(GDelayedWork *work)
+{
+ work->completed = false;
+ g_mutex_init(&work->mutex);
+ g_cond_init(&work->cond);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : work = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_delayed_work_dispose(GDelayedWork *work)
+{
+ g_mutex_clear(&work->mutex);
+ g_cond_clear(&work->cond);
+
+ G_OBJECT_CLASS(g_delayed_work_parent_class)->dispose(G_OBJECT(work));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : work = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_delayed_work_finalize(GDelayedWork *work)
+{
+ G_OBJECT_CLASS(g_delayed_work_parent_class)->finalize(G_OBJECT(work));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : work = travail à effectuer. *
+* status = barre de statut à tenir informée. *
+* *
+* Description : Mène l'opération programmée. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_delayed_work_process(GDelayedWork *work, GtkStatusStack *status)
+{
+ G_DELAYED_WORK_GET_CLASS(work)->run(work, status);
+
+ g_mutex_lock(&work->mutex);
+
+ work->completed = true;
+
+ g_cond_signal(&work->cond);
+ g_mutex_unlock(&work->mutex);
+
+ g_signal_emit_by_name(work, "work-completed");
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : work = travail à surveiller. *
+* *
+* Description : Attend la fin de l'exécution d'une tâche donnée. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_delayed_work_wait_for_completion(GDelayedWork *work)
+{
+ g_mutex_lock(&work->mutex);
+
+ while (!work->completed)
+ g_cond_wait(&work->cond, &work->mutex);
+
+ g_mutex_unlock(&work->mutex);
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* THREADS DES TRAITEMENTS DEDIES */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* 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 groupes de travail. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+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_work_group_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_work_group_finalize;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = instance à initialiser. *
+* *
+* Description : Initialise une instance de groupe de travail. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_group_init(GWorkGroup *group)
+{
+ group->works = NULL;
+
+ g_mutex_init(&group->mutex);
+ g_cond_init(&group->cond);
+ g_cond_init(&group->wait_cond);
+
+ g_atomic_int_set(&group->pending, 0);
+
+ group->threads = NULL;
+ group->threads_count = 0;
+ group->force_exit = false;
+
+ group->callback = NULL;
+ group->data = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_group_dispose(GWorkGroup *group)
+{
+ guint i; /* Boucle de parcours */
+ GDelayedWork *work; /* Travail à oublier */
+
+ group->force_exit = true;
+
+ /**
+ * Concernant la pose du verrou, se référer aux commentaires de la
+ * fonction g_work_group_process().
+ */
+
+ g_mutex_lock(&group->mutex);
+
+ g_cond_broadcast(&group->cond);
+
+ g_mutex_unlock(&group->mutex);
+
+ for (i = 0; i < group->threads_count; i++)
+ g_thread_join(group->threads[i]);
+
+ while (!dl_list_empty(group->works))
+ {
+ work = group->works;
+ delayed_work_list_del(work, &group->works);
+
+ g_object_unref(G_OBJECT(work));
+
+ }
+
+ 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 : group = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_group_finalize(GWorkGroup *group)
+{
+ if (group->threads != NULL)
+ free(group->threads);
+
+ G_OBJECT_CLASS(g_work_group_parent_class)->finalize(G_OBJECT(group));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : id = identifiant accordé au nouveau groupe. *
+* count = quantité de threads à allouer. *
+* *
+* Description : Crée un nouveau thread dédié à un type de travaux donné. *
+* *
+* Retour : Structure associée au thread mise en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static GWorkGroup *g_work_group_new(wgroup_id_t id, const guint *count)
+{
+ GWorkGroup *result; /* Traiteur à retourner */
+ guint i; /* Boucle de parcours */
+ char name[16]; /* Désignation humaine */
+
+ result = g_object_new(G_TYPE_WORK_GROUP, NULL);
+
+ result->id = id;
+
+ result->threads_count = get_max_online_threads();
+
+ if (count != NULL && *count < result->threads_count)
+ result->threads_count = *count;
+
+ result->threads = (GThread **)calloc(result->threads_count, sizeof(GThread *));
+
+ for (i = 0; i < result->threads_count; i++)
+ {
+ snprintf(name, sizeof(name), "wgrp_%" PRIu64 "-%u", id, i);
+
+ result->threads[i] = g_thread_new(name, (GThreadFunc)g_work_group_process, result);
+ if (!result->threads[i])
+ goto start_error;
+
+ }
+
+ start_error:
+
+ result->threads_count = i;
+
+ assert(i > 0);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* 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. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work)
+{
+ g_mutex_lock(&group->mutex);
+
+ g_atomic_int_inc(&group->pending);
+
+ delayed_work_list_add_tail(work, &group->works);
+
+ g_cond_signal(&group->cond);
+
+ g_mutex_unlock(&group->mutex);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = gestionnaire des actions à mener. *
+* *
+* Description : Assure le traitement en différé. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void *g_work_group_process(GWorkGroup *group)
+{
+ GDelayedWork *work; /* Traitement à mener */
+ GtkStatusStack *status; /* Zone d'info éventuelle */
+
+ while (1)
+ {
+ 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)
+ {
+ g_mutex_unlock(&group->mutex);
+ break;
+ }
+
+ work = group->works;
+ delayed_work_list_del(work, &group->works);
+
+ g_mutex_unlock(&group->mutex);
+
+#ifdef INCLUDE_GTK_SUPPORT
+ status = get_global_status();
+#else
+ status = NULL;
+#endif
+ g_delayed_work_process(work, status);
+
+ g_object_unref(G_OBJECT(work));
+
+ /**
+ * Verrou ou pas verrou ?
+ *
+ * La documentation de la GLib indique que ce n'est pas nécessaire :
+ *
+ * '''
+ * It is good practice to lock the same mutex as the waiting threads
+ * while calling this function, though not required.
+ * '''
+ *
+ * Ce conseil se trouve verbatim à l'adresse :
+ *
+ * https://developer.gnome.org/glib/stable/glib-Threads.html#g-cond-broadcast
+ *
+ * Dans la pratique, il peut arriver que l'attente de la fonction
+ * g_work_group_wait_for_completion() ne soit jamais interrompue.
+ *
+ * La documentation POSIX est un peu plus orientée :
+ *
+ * '''
+ * The pthread_cond_broadcast() functions may be called by a thread
+ * whether or not it currently owns the mutex that threads calling
+ * pthread_cond_wait() have associated with the condition variable
+ * during their waits; however, if predictable scheduling behavior is
+ * required, then that mutex shall be locked by the thread calling
+ * pthread_cond_broadcast().
+ * '''
+ *
+ * Ce passage complet est consultable à l'adresse :
+ *
+ * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_broadcast.html
+ *
+ * La page de manuel pthread_cond_broadcast(3) est quant à elle plus
+ * directrice : aucun complément d'information sur le sujet n'est fourni
+ * et les exemples associés utilisent implicement un verrou pendant
+ * sont appel.
+ */
+
+ g_mutex_lock(&group->mutex);
+
+ if (g_atomic_int_dec_and_test(&group->pending))
+ g_cond_broadcast(&group->wait_cond);
+
+ g_mutex_unlock(&group->mutex);
+
+ }
+
+ return NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = gestionnaire des actions à consulter. *
+* *
+* Description : Détermine si le groupe est vide de toute programmation. *
+* *
+* Retour : Etat du groupe de travail. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_work_group_is_empty(GWorkGroup *group)
+{
+ bool result; /* Etat à retourner */
+
+ /**
+ * Pour que le résultat soit exploitable, il ne doit pas varier
+ * en dehors de la zone couverte par le verrou du groupe avant
+ * son utilisation par l'appelant.
+ *
+ * Il doit donc logiquement y avoir un autre verrou en amont et,
+ * comme à priori on ne devrait pas bloquer les groupes principaux
+ * pour un traitement particulier, cette procédure ne devrait concerner
+ * que des groupes dynamiques.
+ */
+
+ g_mutex_lock(&group->mutex);
+
+ result = dl_list_empty(group->works);
+
+ g_mutex_unlock(&group->mutex);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = groupe dont les conclusions sont attendues. *
+* queue = queue d'appartenance pour les appels externes. *
+* *
+* 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, GWorkQueue *queue)
+{
+ wait_for_incoming_works_cb callback; /* Procédure complémentaire */
+
+ bool no_extra_check(GWorkQueue *_q, wgroup_id_t _id, void *_data)
+ {
+ return false;
+ }
+
+ callback = group->callback != NULL ? group->callback : no_extra_check;
+
+ g_mutex_lock(&group->mutex);
+
+ /**
+ * On attend que :
+ * - la liste des tâches programmées soit vide.
+ * - il n'existe plus de tâche en cours.
+ * - rien n'indique que de nouvelles tâches supplémentaires vont arriver.
+ */
+
+ while ((g_atomic_int_get(&group->pending) > 0 || callback(queue, group->id, group->data))
+ && !group->force_exit)
+ {
+ g_cond_wait(&group->wait_cond, &group->mutex);
+ }
+
+ g_mutex_unlock(&group->mutex);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = groupe dont les paramètres sont à modifier. *
+* callback = éventuelle fonction à appeler ou NULL. *
+* data = données devant accompagner l'appel. *
+* *
+* Description : Modifie les conditions d'attente des fins d'exécutions. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_group_set_extra_wait_callback(GWorkGroup *group, wait_for_incoming_works_cb callback, void *data)
+{
+ group->callback = callback;
+ group->data = data;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
+* id = identifiant d'un groupe de travail. *
+* *
+* Description : Force un réveil d'une attente en cours pour la confirmer. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_group_wake_up_waiters(GWorkGroup *group)
+{
+ /**
+ * Concernant la pose du verrou, se référer aux commentaires de la
+ * fonction g_work_group_process().
+ */
+
+ g_mutex_lock(&group->mutex);
+
+ g_cond_broadcast(&group->wait_cond);
+
+ g_mutex_unlock(&group->mutex);
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* TRAITEMENT DE TACHES DIFFEREES */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour le gestionnaire des travaux différés. */
+G_DEFINE_TYPE(GWorkQueue, g_work_queue, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des travaux différés. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+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;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = instance à initialiser. *
+* *
+* Description : Initialise une instance de gestionnaire de travaux différés. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_queue_init(GWorkQueue *queue)
+{
+ queue->generator = 0;
+
+ queue->groups = NULL;
+ queue->groups_count = 0;
+ g_mutex_init(&queue->mutex);
+ g_cond_init(&queue->wait_all);
+
+}
+
+
+/******************************************************************************
+* *
+* 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_mutex_lock(&queue->mutex);
+
+ for (i = 0; i < queue->groups_count; i++)
+ g_clear_object(&queue->groups[i]);
+
+ g_mutex_unlock(&queue->mutex);
+
+ g_mutex_clear(&queue->mutex);
+ g_cond_clear(&queue->wait_all);
+
+ 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));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Créé un nouveau gestionnaire de tâches parallèles. *
+* *
+* Retour : Gestionnaire de traitements mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GWorkQueue *g_work_queue_new(void)
+{
+ GWorkQueue *result; /* Instance à retourner */
+
+ result = g_object_new(G_TYPE_WORK_QUEUE, NULL);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
+* id = identifiant d'un groupe de travail. *
+* count = quantité de threads à allouer. *
+* *
+* Description : Donne l'assurance de l'existence d'un groupe de travail. *
+* *
+* Retour : true si un nouveau groupe a été constitué, false sinon. *
+* *
+* Remarques : Le verrou d'accès doit être posé par l'appelant. *
+* *
+******************************************************************************/
+
+static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, const guint *count)
+{
+ bool found; /* Bilan des recherches */
+ size_t i; /* Boucle de parcours */
+ GWorkGroup *group; /* Groupe à consulter */
+
+ assert(!g_mutex_trylock(&queue->mutex));
+
+ found = false;
+
+ for (i = 0; i < queue->groups_count && !found; i++)
+ {
+ group = queue->groups[i];
+ found = (g_work_group_get_id(group) == id);
+ }
+
+ if (!found)
+ {
+ queue->groups_count++;
+ queue->groups = (GWorkGroup **)realloc(queue->groups,
+ queue->groups_count * sizeof(GWorkGroup *));
+
+ group = g_work_group_new(id, count);
+ queue->groups[queue->groups_count - 1] = group;
+
+ }
+
+ return !found;
+
+}
+
+
+/******************************************************************************
+* *
+* 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 */
+ bool created; /* Bilan d'une tentative */
+
+ g_mutex_lock(&queue->mutex);
+
+ do
+ {
+ result = queue->generator++;
+ created = g_work_queue_ensure_group_exists(queue, result, NULL);
+ }
+ while (!created);
+
+ g_mutex_unlock(&queue->mutex);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
+* count = quantité de threads à allouer. *
+* *
+* Description : Constitue un nouveau petit groupe de travail. *
+* *
+* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count)
+{
+ wgroup_id_t result; /* Valeur à retourner */
+ bool created; /* Bilan d'une tentative */
+
+ g_mutex_lock(&queue->mutex);
+
+ do
+ {
+ result = queue->generator++;
+ created = g_work_queue_ensure_group_exists(queue, result, &count);
+ }
+ while (!created);
+
+ 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 */
+ GWorkGroup *group; /* Groupe de travail manipulé */
+#ifndef NDEBUG
+ bool found; /* Repérage du groupe visé */
+#endif
+
+#ifndef NDEBUG
+ found = false;
+#endif
+
+ g_mutex_lock(&queue->mutex);
+
+ for (i = 0; i < queue->groups_count; i++)
+ {
+ group = queue->groups[i];
+
+ if (g_work_group_get_id(group) == id)
+ {
+ g_object_unref(G_OBJECT(group));
+
+ 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 *));
+
+#ifndef NDEBUG
+ found = true;
+#endif
+
+ break;
+
+ }
+
+ }
+
+ assert(found);
+
+ g_cond_broadcast(&queue->wait_all);
+
+ 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. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id_t id)
+{
+ GWorkGroup *group; /* Groupe de travail à attendre*/
+
+ group = g_work_queue_find_group_for_id(queue, id);
+ assert(group != NULL);
+
+ g_work_group_schedule(group, work);
+
+ g_object_unref(G_OBJECT(group));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
+* id = identifiant d'un groupe de travail. *
+* *
+* Description : Fournit le groupe de travail correspondant à un identifiant. *
+* *
+* Retour : Eventuel groupe existant trouvé ou NULL si aucun. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *queue, wgroup_id_t id)
+{
+ GWorkGroup *result; /* Trouvaille à retourner */
+ size_t i; /* Boucle de parcours */
+
+ result = NULL;
+
+ g_mutex_lock(&queue->mutex);
+
+ for (i = 0; i < queue->groups_count; i++)
+ if (g_work_group_get_id(queue->groups[i]) == id)
+ {
+ result = queue->groups[i];
+ g_object_ref(G_OBJECT(result));
+ break;
+ }
+
+ 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 : Détermine si un groupe est vide de toute programmation. *
+* *
+* Retour : Etat du groupe de travail. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_work_queue_is_empty(GWorkQueue *queue, wgroup_id_t id)
+{
+ bool result; /* Etat à retourner */
+ GWorkGroup *group; /* Groupe de travail à attendre*/
+
+ group = g_work_queue_find_group_for_id(queue, id);
+
+ if (group != NULL)
+ {
+ result = g_work_group_is_empty(group);
+ g_object_unref(G_OBJECT(group));
+ }
+
+ else
+ result = true;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* 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)
+{
+ GWorkGroup *group; /* Groupe de travail à attendre*/
+
+ group = g_work_queue_find_group_for_id(queue, id);
+
+ if (group != NULL)
+ {
+ g_work_group_wait_for_completion(group, queue);
+ g_object_unref(G_OBJECT(group));
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
+* gb_ids = identifiants de groupes globaux. *
+* gb_count = nombre de ces groupes globaux. *
+* *
+* Description : Attend que toutes les tâches de tout groupe soient traitées. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_work_queue_wait_for_all_completions(GWorkQueue *queue, const wgroup_id_t *gb_ids, size_t gb_count)
+{
+ size_t i; /* Boucle de parcours */
+
+ g_mutex_lock(&queue->mutex);
+
+ wait_again:
+
+ /**
+ * Attente d'éventuels groupes isolés.
+ */
+
+ while (queue->groups_count > gb_count)
+ g_cond_wait(&queue->wait_all, &queue->mutex);
+
+ g_mutex_unlock(&queue->mutex);
+
+ /**
+ * Attente des groupes principaux.
+ */
+
+ for (i = 0; i < gb_count; i++)
+ g_work_queue_wait_for_completion(queue, gb_ids[i]);
+
+ /**
+ * Si le groupe par défaut a généré de nouveaux groupes, on recommence !
+ */
+
+ g_mutex_lock(&queue->mutex);
+
+ if (queue->groups_count > gb_count)
+ goto wait_again;
+
+ g_mutex_unlock(&queue->mutex);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
+* id = identifiant d'un groupe de travail. *
+* callback = éventuelle fonction à appeler ou NULL. *
+* data = données devant accompagner l'appel. *
+* *
+* Description : Modifie les conditions d'attente des fins d'exécutions. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wait_for_incoming_works_cb callback, void *data)
+{
+ GWorkGroup *group; /* Groupe de travail à traiter */
+
+ group = g_work_queue_find_group_for_id(queue, id);
+
+ if (group != NULL)
+ {
+ g_work_group_set_extra_wait_callback(group, callback, data);
+ g_object_unref(G_OBJECT(group));
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
+* id = identifiant d'un groupe de travail. *
+* *
+* Description : Force un réveil d'une attente en cours pour la confirmer. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_work_queue_wake_up_waiters(GWorkQueue *queue, wgroup_id_t id)
+{
+ GWorkGroup *group; /* Groupe de travail à traiter */
+
+ group = g_work_queue_find_group_for_id(queue, id);
+
+ if (group != NULL)
+ {
+ g_work_group_wake_up_waiters(group);
+ g_object_unref(G_OBJECT(group));
+ }
+
+}
diff --git a/src/glibext/workgroup.h b/src/glibext/workgroup.h
new file mode 100644
index 0000000..89eed12
--- /dev/null
+++ b/src/glibext/workgroup.h
@@ -0,0 +1,127 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * delayed.h - prototypes pour la gestion des travaux différés
+ *
+ * Copyright (C) 2009-2018 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_DELAYED_H
+#define _GLIBEXT_DELAYED_H
+
+
+#include <glib-object.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+
+
+/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
+
+
+#define G_TYPE_DELAYED_WORK g_delayed_work_get_type()
+#define G_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_delayed_work_get_type(), GDelayedWork))
+#define G_IS_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_delayed_work_get_type()))
+#define G_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DELAYED_WORK, GDelayedWorkClass))
+#define G_IS_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DELAYED_WORK))
+#define G_DELAYED_WORK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DELAYED_WORK, GDelayedWorkClass))
+
+
+/* Travail différé (instance) */
+typedef struct _GDelayedWork GDelayedWork;
+
+/* Travail différé (classe) */
+typedef struct _GDelayedWorkClass GDelayedWorkClass;
+
+
+/* Indique le type défini pour les travaux différés. */
+GType g_delayed_work_get_type(void);
+
+/* Attend la fin de l'exécution d'une tâche donnée. */
+void g_delayed_work_wait_for_completion(GDelayedWork *);
+
+
+
+/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */
+
+
+#define G_TYPE_WORK_QUEUE g_work_queue_get_type()
+#define G_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_queue_get_type(), GWorkQueue))
+#define G_IS_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_queue_get_type()))
+#define G_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_QUEUE, GWorkQueueClass))
+#define G_IS_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_QUEUE))
+#define G_WORK_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_QUEUE, GWorkQueueClass))
+
+
+/* Gestionnaire des travaux différés (instance) */
+typedef struct _GWorkQueue GWorkQueue;
+
+/* Gestionnaire des travaux différés (classe) */
+typedef struct _GWorkQueueClass GWorkQueueClass;
+
+
+/**
+ * Identifiant unique pour groupe de travail.
+ *
+ * Le nombre de bits est forcé à 64 bits car glib-genmarshal ne reconnait
+ * pas explicitement le type 'unsigned long long'.
+ */
+typedef uint64_t wgroup_id_t;
+
+
+/* Indique le type défini pour le gestionnaire des travaux différés. */
+GType g_work_queue_get_type(void);
+
+/* Créé un nouveau gestionnaire de tâches parallèles. */
+GWorkQueue *g_work_queue_new(void);
+
+/* Constitue un nouveau groupe de travail. */
+wgroup_id_t g_work_queue_define_work_group(GWorkQueue *);
+
+/* Constitue un nouveau petit groupe de travail. */
+wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *, guint);
+
+/* 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 *, wgroup_id_t);
+
+/* Détermine si un groupe est vide de toute programmation. */
+bool g_work_queue_is_empty(GWorkQueue *, 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);
+
+/* Attend que toutes les tâches de tout groupe soient traitées. */
+void g_work_queue_wait_for_all_completions(GWorkQueue *, const wgroup_id_t *, size_t);
+
+
+/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */
+typedef bool (* wait_for_incoming_works_cb) (GWorkQueue *, wgroup_id_t, void *);
+
+
+/* Modifie les conditions d'attente des fins d'exécutions. */
+void g_work_queue_set_extra_wait_callback(GWorkQueue *, wgroup_id_t, wait_for_incoming_works_cb, void *);
+
+/* Force un réveil d'une attente en cours pour la confirmer. */
+void g_work_queue_wake_up_waiters(GWorkQueue *, wgroup_id_t);
+
+
+
+#endif /* _GLIBEXT_DELAYED_H */
diff --git a/src/glibext/workwqueue-int.h b/src/glibext/workwqueue-int.h
new file mode 100644
index 0000000..4f84e86
--- /dev/null
+++ b/src/glibext/workwqueue-int.h
@@ -0,0 +1,75 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * delayed-int.h - définitions internes pour la gestion des travaux différés
+ *
+ * Copyright (C) 2009-2018 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_DELAYED_INT_H
+#define _GLIBEXT_DELAYED_INT_H
+
+
+#include "delayed.h"
+
+
+#include "notifier.h"
+#include "../common/dllist.h"
+
+
+
+/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
+
+
+/* Traite un travail programmé. */
+typedef void (* run_task_fc) (GDelayedWork *, GtkStatusStack *);
+
+
+/* Travail différé (instance) */
+struct _GDelayedWork
+{
+ GObject parent; /* A laisser en premier */
+
+ DL_LIST_ITEM(link); /* Lien vers les maillons */
+
+ bool completed; /* Fin de la tâche ? */
+ GMutex mutex; /* Accès à la variable */
+ GCond cond; /* Attente de changement */
+
+};
+
+/* Travail différé (classe) */
+struct _GDelayedWorkClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+ run_task_fc run; /* Traitement externalisé */
+
+ /* Signaux */
+
+ void (* work_completed) (GDelayedWork *);
+
+};
+
+
+#define delayed_work_list_add_tail(new, head) dl_list_add_tail(new, head, GDelayedWork, link)
+#define delayed_work_list_del(item, head) dl_list_del(item, head, GDelayedWork, link)
+
+
+
+#endif /* _GLIBEXT_DELAYED_INT_H */
diff --git a/src/glibext/workwqueue.c b/src/glibext/workwqueue.c
new file mode 100644
index 0000000..6b5ac35
--- /dev/null
+++ b/src/glibext/workwqueue.c
@@ -0,0 +1,1353 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * delayed.c - gestion des travaux différés
+ *
+ * Copyright (C) 2009-2018 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "delayed.h"
+
+
+#include <assert.h>
+#include <inttypes.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <string.h>
+
+
+#include "delayed-int.h"
+#include "../core/nproc.h"
+#ifdef INCLUDE_GTK_SUPPORT
+# include "../gui/core/global.h"
+#endif
+
+
+
+/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
+
+
+/* Initialise la classe des travaux différés. */
+static void g_delayed_work_class_init(GDelayedWorkClass *);
+
+/* Initialise une instance de travail différé. */
+static void g_delayed_work_init(GDelayedWork *);
+
+/* Supprime toutes les références externes. */
+static void g_delayed_work_dispose(GDelayedWork *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_delayed_work_finalize(GDelayedWork *);
+
+/* Mène l'opération programmée. */
+static void g_delayed_work_process(GDelayedWork *, GtkStatusStack *);
+
+
+
+/* -------------------------- THREAD DE TRAITEMENTS DEDIES -------------------------- */
+
+
+#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 _GWorkGroup
+{
+ GObject parent; /* A laisser en premier */
+
+ wgroup_id_t id; /* Identifiant de travaux menés*/
+
+ 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 */
+ gint pending; /* Tâches en cours d'exécution */
+
+ GThread **threads; /* Procédure de traitement */
+ guint threads_count; /* Nombre de procédures */
+ bool force_exit; /* Procédure d'arrêt */
+
+ wait_for_incoming_works_cb callback; /* Encadre les attentes de fin */
+ void *data; /* Données à associer */
+
+} GWorkGroup;
+
+/* File de traitement pour un type donné (classe) */
+typedef struct _GWorkGroupClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+} GWorkGroupClass;
+
+
+/* Indique le type défini pour les groupes de travail. */
+static GType g_work_group_get_type(void);
+
+/* Initialise la classe des groupes de travail. */
+static void g_work_group_class_init(GWorkGroupClass *);
+
+/* Initialise une instance de groupe de travail. */
+static void g_work_group_init(GWorkGroup *);
+
+/* Supprime toutes les références externes. */
+static void g_work_group_dispose(GWorkGroup *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_work_group_finalize(GWorkGroup *);
+
+/* Crée un nouveau thread dédié à un type de travaux donné. */
+static GWorkGroup *g_work_group_new(wgroup_id_t, const guint *);
+
+/* 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_work_group_schedule(GWorkGroup *, GDelayedWork *);
+
+/* Assure le traitement en différé. */
+static void *g_work_group_process(GWorkGroup *);
+
+/* Détermine si le groupe est vide de toute programmation. */
+static bool g_work_group_is_empty(GWorkGroup *);
+
+/* Attend que toutes les tâches d'un groupe soient traitées. */
+static void g_work_group_wait_for_completion(GWorkGroup *, GWorkQueue *);
+
+/* Modifie les conditions d'attente des fins d'exécutions. */
+static void g_work_group_set_extra_wait_callback(GWorkGroup *, wait_for_incoming_works_cb, void *);
+
+/* Force un réveil d'une attente en cours pour la confirmer. */
+static void g_work_group_wake_up_waiters(GWorkGroup *);
+
+
+
+/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */
+
+
+/* Gestionnaire des travaux différés (instance) */
+struct _GWorkQueue
+{
+ GObject parent; /* A laisser en premier */
+
+ 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 */
+ GCond wait_all; /* Réveil d'attente globale */
+
+};
+
+/* Gestionnaire des travaux différés (classe) */
+struct _GWorkQueueClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+};
+
+
+/* Initialise la classe des travaux différés. */
+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 bool g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t, const guint *);
+
+/* Fournit le groupe de travail correspondant à un identifiant. */
+static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *, wgroup_id_t);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* TACHE DIFFEREE DANS LE TEMPS */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour les travaux différés. */
+G_DEFINE_TYPE(GDelayedWork, g_delayed_work, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des travaux différés. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_delayed_work_class_init(GDelayedWorkClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_work_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_delayed_work_finalize;
+
+ g_signal_new("work-completed",
+ G_TYPE_DELAYED_WORK,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(GDelayedWorkClass, work_completed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : work = instance à initialiser. *
+* *
+* Description : Initialise une instance de travail différé. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_delayed_work_init(GDelayedWork *work)
+{
+ work->completed = false;
+ g_mutex_init(&work->mutex);
+ g_cond_init(&work->cond);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : work = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_delayed_work_dispose(GDelayedWork *work)
+{
+ g_mutex_clear(&work->mutex);
+ g_cond_clear(&work->cond);
+
+ G_OBJECT_CLASS(g_delayed_work_parent_class)->dispose(G_OBJECT(work));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : work = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_delayed_work_finalize(GDelayedWork *work)
+{
+ G_OBJECT_CLASS(g_delayed_work_parent_class)->finalize(G_OBJECT(work));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : work = travail à effectuer. *
+* status = barre de statut à tenir informée. *
+* *
+* Description : Mène l'opération programmée. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_delayed_work_process(GDelayedWork *work, GtkStatusStack *status)
+{
+ G_DELAYED_WORK_GET_CLASS(work)->run(work, status);
+
+ g_mutex_lock(&work->mutex);
+
+ work->completed = true;
+
+ g_cond_signal(&work->cond);
+ g_mutex_unlock(&work->mutex);
+
+ g_signal_emit_by_name(work, "work-completed");
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : work = travail à surveiller. *
+* *
+* Description : Attend la fin de l'exécution d'une tâche donnée. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_delayed_work_wait_for_completion(GDelayedWork *work)
+{
+ g_mutex_lock(&work->mutex);
+
+ while (!work->completed)
+ g_cond_wait(&work->cond, &work->mutex);
+
+ g_mutex_unlock(&work->mutex);
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* THREADS DES TRAITEMENTS DEDIES */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* 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 groupes de travail. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+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_work_group_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_work_group_finalize;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = instance à initialiser. *
+* *
+* Description : Initialise une instance de groupe de travail. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_group_init(GWorkGroup *group)
+{
+ group->works = NULL;
+
+ g_mutex_init(&group->mutex);
+ g_cond_init(&group->cond);
+ g_cond_init(&group->wait_cond);
+
+ g_atomic_int_set(&group->pending, 0);
+
+ group->threads = NULL;
+ group->threads_count = 0;
+ group->force_exit = false;
+
+ group->callback = NULL;
+ group->data = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_group_dispose(GWorkGroup *group)
+{
+ guint i; /* Boucle de parcours */
+ GDelayedWork *work; /* Travail à oublier */
+
+ group->force_exit = true;
+
+ /**
+ * Concernant la pose du verrou, se référer aux commentaires de la
+ * fonction g_work_group_process().
+ */
+
+ g_mutex_lock(&group->mutex);
+
+ g_cond_broadcast(&group->cond);
+
+ g_mutex_unlock(&group->mutex);
+
+ for (i = 0; i < group->threads_count; i++)
+ g_thread_join(group->threads[i]);
+
+ while (!dl_list_empty(group->works))
+ {
+ work = group->works;
+ delayed_work_list_del(work, &group->works);
+
+ g_object_unref(G_OBJECT(work));
+
+ }
+
+ 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 : group = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_group_finalize(GWorkGroup *group)
+{
+ if (group->threads != NULL)
+ free(group->threads);
+
+ G_OBJECT_CLASS(g_work_group_parent_class)->finalize(G_OBJECT(group));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : id = identifiant accordé au nouveau groupe. *
+* count = quantité de threads à allouer. *
+* *
+* Description : Crée un nouveau thread dédié à un type de travaux donné. *
+* *
+* Retour : Structure associée au thread mise en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static GWorkGroup *g_work_group_new(wgroup_id_t id, const guint *count)
+{
+ GWorkGroup *result; /* Traiteur à retourner */
+ guint i; /* Boucle de parcours */
+ char name[16]; /* Désignation humaine */
+
+ result = g_object_new(G_TYPE_WORK_GROUP, NULL);
+
+ result->id = id;
+
+ result->threads_count = get_max_online_threads();
+
+ if (count != NULL && *count < result->threads_count)
+ result->threads_count = *count;
+
+ result->threads = (GThread **)calloc(result->threads_count, sizeof(GThread *));
+
+ for (i = 0; i < result->threads_count; i++)
+ {
+ snprintf(name, sizeof(name), "wgrp_%" PRIu64 "-%u", id, i);
+
+ result->threads[i] = g_thread_new(name, (GThreadFunc)g_work_group_process, result);
+ if (!result->threads[i])
+ goto start_error;
+
+ }
+
+ start_error:
+
+ result->threads_count = i;
+
+ assert(i > 0);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* 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. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work)
+{
+ g_mutex_lock(&group->mutex);
+
+ g_atomic_int_inc(&group->pending);
+
+ delayed_work_list_add_tail(work, &group->works);
+
+ g_cond_signal(&group->cond);
+
+ g_mutex_unlock(&group->mutex);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = gestionnaire des actions à mener. *
+* *
+* Description : Assure le traitement en différé. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void *g_work_group_process(GWorkGroup *group)
+{
+ GDelayedWork *work; /* Traitement à mener */
+ GtkStatusStack *status; /* Zone d'info éventuelle */
+
+ while (1)
+ {
+ 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)
+ {
+ g_mutex_unlock(&group->mutex);
+ break;
+ }
+
+ work = group->works;
+ delayed_work_list_del(work, &group->works);
+
+ g_mutex_unlock(&group->mutex);
+
+#ifdef INCLUDE_GTK_SUPPORT
+ status = get_global_status();
+#else
+ status = NULL;
+#endif
+ g_delayed_work_process(work, status);
+
+ g_object_unref(G_OBJECT(work));
+
+ /**
+ * Verrou ou pas verrou ?
+ *
+ * La documentation de la GLib indique que ce n'est pas nécessaire :
+ *
+ * '''
+ * It is good practice to lock the same mutex as the waiting threads
+ * while calling this function, though not required.
+ * '''
+ *
+ * Ce conseil se trouve verbatim à l'adresse :
+ *
+ * https://developer.gnome.org/glib/stable/glib-Threads.html#g-cond-broadcast
+ *
+ * Dans la pratique, il peut arriver que l'attente de la fonction
+ * g_work_group_wait_for_completion() ne soit jamais interrompue.
+ *
+ * La documentation POSIX est un peu plus orientée :
+ *
+ * '''
+ * The pthread_cond_broadcast() functions may be called by a thread
+ * whether or not it currently owns the mutex that threads calling
+ * pthread_cond_wait() have associated with the condition variable
+ * during their waits; however, if predictable scheduling behavior is
+ * required, then that mutex shall be locked by the thread calling
+ * pthread_cond_broadcast().
+ * '''
+ *
+ * Ce passage complet est consultable à l'adresse :
+ *
+ * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_broadcast.html
+ *
+ * La page de manuel pthread_cond_broadcast(3) est quant à elle plus
+ * directrice : aucun complément d'information sur le sujet n'est fourni
+ * et les exemples associés utilisent implicement un verrou pendant
+ * sont appel.
+ */
+
+ g_mutex_lock(&group->mutex);
+
+ if (g_atomic_int_dec_and_test(&group->pending))
+ g_cond_broadcast(&group->wait_cond);
+
+ g_mutex_unlock(&group->mutex);
+
+ }
+
+ return NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = gestionnaire des actions à consulter. *
+* *
+* Description : Détermine si le groupe est vide de toute programmation. *
+* *
+* Retour : Etat du groupe de travail. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_work_group_is_empty(GWorkGroup *group)
+{
+ bool result; /* Etat à retourner */
+
+ /**
+ * Pour que le résultat soit exploitable, il ne doit pas varier
+ * en dehors de la zone couverte par le verrou du groupe avant
+ * son utilisation par l'appelant.
+ *
+ * Il doit donc logiquement y avoir un autre verrou en amont et,
+ * comme à priori on ne devrait pas bloquer les groupes principaux
+ * pour un traitement particulier, cette procédure ne devrait concerner
+ * que des groupes dynamiques.
+ */
+
+ g_mutex_lock(&group->mutex);
+
+ result = dl_list_empty(group->works);
+
+ g_mutex_unlock(&group->mutex);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = groupe dont les conclusions sont attendues. *
+* queue = queue d'appartenance pour les appels externes. *
+* *
+* 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, GWorkQueue *queue)
+{
+ wait_for_incoming_works_cb callback; /* Procédure complémentaire */
+
+ bool no_extra_check(GWorkQueue *_q, wgroup_id_t _id, void *_data)
+ {
+ return false;
+ }
+
+ callback = group->callback != NULL ? group->callback : no_extra_check;
+
+ g_mutex_lock(&group->mutex);
+
+ /**
+ * On attend que :
+ * - la liste des tâches programmées soit vide.
+ * - il n'existe plus de tâche en cours.
+ * - rien n'indique que de nouvelles tâches supplémentaires vont arriver.
+ */
+
+ while ((g_atomic_int_get(&group->pending) > 0 || callback(queue, group->id, group->data))
+ && !group->force_exit)
+ {
+ g_cond_wait(&group->wait_cond, &group->mutex);
+ }
+
+ g_mutex_unlock(&group->mutex);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = groupe dont les paramètres sont à modifier. *
+* callback = éventuelle fonction à appeler ou NULL. *
+* data = données devant accompagner l'appel. *
+* *
+* Description : Modifie les conditions d'attente des fins d'exécutions. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_group_set_extra_wait_callback(GWorkGroup *group, wait_for_incoming_works_cb callback, void *data)
+{
+ group->callback = callback;
+ group->data = data;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
+* id = identifiant d'un groupe de travail. *
+* *
+* Description : Force un réveil d'une attente en cours pour la confirmer. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_group_wake_up_waiters(GWorkGroup *group)
+{
+ /**
+ * Concernant la pose du verrou, se référer aux commentaires de la
+ * fonction g_work_group_process().
+ */
+
+ g_mutex_lock(&group->mutex);
+
+ g_cond_broadcast(&group->wait_cond);
+
+ g_mutex_unlock(&group->mutex);
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* TRAITEMENT DE TACHES DIFFEREES */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour le gestionnaire des travaux différés. */
+G_DEFINE_TYPE(GWorkQueue, g_work_queue, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des travaux différés. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+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;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = instance à initialiser. *
+* *
+* Description : Initialise une instance de gestionnaire de travaux différés. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_queue_init(GWorkQueue *queue)
+{
+ queue->generator = 0;
+
+ queue->groups = NULL;
+ queue->groups_count = 0;
+ g_mutex_init(&queue->mutex);
+ g_cond_init(&queue->wait_all);
+
+}
+
+
+/******************************************************************************
+* *
+* 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_mutex_lock(&queue->mutex);
+
+ for (i = 0; i < queue->groups_count; i++)
+ g_clear_object(&queue->groups[i]);
+
+ g_mutex_unlock(&queue->mutex);
+
+ g_mutex_clear(&queue->mutex);
+ g_cond_clear(&queue->wait_all);
+
+ 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));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Créé un nouveau gestionnaire de tâches parallèles. *
+* *
+* Retour : Gestionnaire de traitements mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GWorkQueue *g_work_queue_new(void)
+{
+ GWorkQueue *result; /* Instance à retourner */
+
+ result = g_object_new(G_TYPE_WORK_QUEUE, NULL);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
+* id = identifiant d'un groupe de travail. *
+* count = quantité de threads à allouer. *
+* *
+* Description : Donne l'assurance de l'existence d'un groupe de travail. *
+* *
+* Retour : true si un nouveau groupe a été constitué, false sinon. *
+* *
+* Remarques : Le verrou d'accès doit être posé par l'appelant. *
+* *
+******************************************************************************/
+
+static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, const guint *count)
+{
+ bool found; /* Bilan des recherches */
+ size_t i; /* Boucle de parcours */
+ GWorkGroup *group; /* Groupe à consulter */
+
+ assert(!g_mutex_trylock(&queue->mutex));
+
+ found = false;
+
+ for (i = 0; i < queue->groups_count && !found; i++)
+ {
+ group = queue->groups[i];
+ found = (g_work_group_get_id(group) == id);
+ }
+
+ if (!found)
+ {
+ queue->groups_count++;
+ queue->groups = (GWorkGroup **)realloc(queue->groups,
+ queue->groups_count * sizeof(GWorkGroup *));
+
+ group = g_work_group_new(id, count);
+ queue->groups[queue->groups_count - 1] = group;
+
+ }
+
+ return !found;
+
+}
+
+
+/******************************************************************************
+* *
+* 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 */
+ bool created; /* Bilan d'une tentative */
+
+ g_mutex_lock(&queue->mutex);
+
+ do
+ {
+ result = queue->generator++;
+ created = g_work_queue_ensure_group_exists(queue, result, NULL);
+ }
+ while (!created);
+
+ g_mutex_unlock(&queue->mutex);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
+* count = quantité de threads à allouer. *
+* *
+* Description : Constitue un nouveau petit groupe de travail. *
+* *
+* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count)
+{
+ wgroup_id_t result; /* Valeur à retourner */
+ bool created; /* Bilan d'une tentative */
+
+ g_mutex_lock(&queue->mutex);
+
+ do
+ {
+ result = queue->generator++;
+ created = g_work_queue_ensure_group_exists(queue, result, &count);
+ }
+ while (!created);
+
+ 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 */
+ GWorkGroup *group; /* Groupe de travail manipulé */
+#ifndef NDEBUG
+ bool found; /* Repérage du groupe visé */
+#endif
+
+#ifndef NDEBUG
+ found = false;
+#endif
+
+ g_mutex_lock(&queue->mutex);
+
+ for (i = 0; i < queue->groups_count; i++)
+ {
+ group = queue->groups[i];
+
+ if (g_work_group_get_id(group) == id)
+ {
+ g_object_unref(G_OBJECT(group));
+
+ 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 *));
+
+#ifndef NDEBUG
+ found = true;
+#endif
+
+ break;
+
+ }
+
+ }
+
+ assert(found);
+
+ g_cond_broadcast(&queue->wait_all);
+
+ 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. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id_t id)
+{
+ GWorkGroup *group; /* Groupe de travail à attendre*/
+
+ group = g_work_queue_find_group_for_id(queue, id);
+ assert(group != NULL);
+
+ g_work_group_schedule(group, work);
+
+ g_object_unref(G_OBJECT(group));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
+* id = identifiant d'un groupe de travail. *
+* *
+* Description : Fournit le groupe de travail correspondant à un identifiant. *
+* *
+* Retour : Eventuel groupe existant trouvé ou NULL si aucun. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *queue, wgroup_id_t id)
+{
+ GWorkGroup *result; /* Trouvaille à retourner */
+ size_t i; /* Boucle de parcours */
+
+ result = NULL;
+
+ g_mutex_lock(&queue->mutex);
+
+ for (i = 0; i < queue->groups_count; i++)
+ if (g_work_group_get_id(queue->groups[i]) == id)
+ {
+ result = queue->groups[i];
+ g_object_ref(G_OBJECT(result));
+ break;
+ }
+
+ 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 : Détermine si un groupe est vide de toute programmation. *
+* *
+* Retour : Etat du groupe de travail. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_work_queue_is_empty(GWorkQueue *queue, wgroup_id_t id)
+{
+ bool result; /* Etat à retourner */
+ GWorkGroup *group; /* Groupe de travail à attendre*/
+
+ group = g_work_queue_find_group_for_id(queue, id);
+
+ if (group != NULL)
+ {
+ result = g_work_group_is_empty(group);
+ g_object_unref(G_OBJECT(group));
+ }
+
+ else
+ result = true;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* 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)
+{
+ GWorkGroup *group; /* Groupe de travail à attendre*/
+
+ group = g_work_queue_find_group_for_id(queue, id);
+
+ if (group != NULL)
+ {
+ g_work_group_wait_for_completion(group, queue);
+ g_object_unref(G_OBJECT(group));
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
+* gb_ids = identifiants de groupes globaux. *
+* gb_count = nombre de ces groupes globaux. *
+* *
+* Description : Attend que toutes les tâches de tout groupe soient traitées. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_work_queue_wait_for_all_completions(GWorkQueue *queue, const wgroup_id_t *gb_ids, size_t gb_count)
+{
+ size_t i; /* Boucle de parcours */
+
+ g_mutex_lock(&queue->mutex);
+
+ wait_again:
+
+ /**
+ * Attente d'éventuels groupes isolés.
+ */
+
+ while (queue->groups_count > gb_count)
+ g_cond_wait(&queue->wait_all, &queue->mutex);
+
+ g_mutex_unlock(&queue->mutex);
+
+ /**
+ * Attente des groupes principaux.
+ */
+
+ for (i = 0; i < gb_count; i++)
+ g_work_queue_wait_for_completion(queue, gb_ids[i]);
+
+ /**
+ * Si le groupe par défaut a généré de nouveaux groupes, on recommence !
+ */
+
+ g_mutex_lock(&queue->mutex);
+
+ if (queue->groups_count > gb_count)
+ goto wait_again;
+
+ g_mutex_unlock(&queue->mutex);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
+* id = identifiant d'un groupe de travail. *
+* callback = éventuelle fonction à appeler ou NULL. *
+* data = données devant accompagner l'appel. *
+* *
+* Description : Modifie les conditions d'attente des fins d'exécutions. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wait_for_incoming_works_cb callback, void *data)
+{
+ GWorkGroup *group; /* Groupe de travail à traiter */
+
+ group = g_work_queue_find_group_for_id(queue, id);
+
+ if (group != NULL)
+ {
+ g_work_group_set_extra_wait_callback(group, callback, data);
+ g_object_unref(G_OBJECT(group));
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
+* id = identifiant d'un groupe de travail. *
+* *
+* Description : Force un réveil d'une attente en cours pour la confirmer. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_work_queue_wake_up_waiters(GWorkQueue *queue, wgroup_id_t id)
+{
+ GWorkGroup *group; /* Groupe de travail à traiter */
+
+ group = g_work_queue_find_group_for_id(queue, id);
+
+ if (group != NULL)
+ {
+ g_work_group_wake_up_waiters(group);
+ g_object_unref(G_OBJECT(group));
+ }
+
+}
diff --git a/src/glibext/workwqueue.h b/src/glibext/workwqueue.h
new file mode 100644
index 0000000..89eed12
--- /dev/null
+++ b/src/glibext/workwqueue.h
@@ -0,0 +1,127 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * delayed.h - prototypes pour la gestion des travaux différés
+ *
+ * Copyright (C) 2009-2018 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_DELAYED_H
+#define _GLIBEXT_DELAYED_H
+
+
+#include <glib-object.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+
+
+/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */
+
+
+#define G_TYPE_DELAYED_WORK g_delayed_work_get_type()
+#define G_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_delayed_work_get_type(), GDelayedWork))
+#define G_IS_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_delayed_work_get_type()))
+#define G_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DELAYED_WORK, GDelayedWorkClass))
+#define G_IS_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DELAYED_WORK))
+#define G_DELAYED_WORK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DELAYED_WORK, GDelayedWorkClass))
+
+
+/* Travail différé (instance) */
+typedef struct _GDelayedWork GDelayedWork;
+
+/* Travail différé (classe) */
+typedef struct _GDelayedWorkClass GDelayedWorkClass;
+
+
+/* Indique le type défini pour les travaux différés. */
+GType g_delayed_work_get_type(void);
+
+/* Attend la fin de l'exécution d'une tâche donnée. */
+void g_delayed_work_wait_for_completion(GDelayedWork *);
+
+
+
+/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */
+
+
+#define G_TYPE_WORK_QUEUE g_work_queue_get_type()
+#define G_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_queue_get_type(), GWorkQueue))
+#define G_IS_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_queue_get_type()))
+#define G_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_QUEUE, GWorkQueueClass))
+#define G_IS_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_QUEUE))
+#define G_WORK_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_QUEUE, GWorkQueueClass))
+
+
+/* Gestionnaire des travaux différés (instance) */
+typedef struct _GWorkQueue GWorkQueue;
+
+/* Gestionnaire des travaux différés (classe) */
+typedef struct _GWorkQueueClass GWorkQueueClass;
+
+
+/**
+ * Identifiant unique pour groupe de travail.
+ *
+ * Le nombre de bits est forcé à 64 bits car glib-genmarshal ne reconnait
+ * pas explicitement le type 'unsigned long long'.
+ */
+typedef uint64_t wgroup_id_t;
+
+
+/* Indique le type défini pour le gestionnaire des travaux différés. */
+GType g_work_queue_get_type(void);
+
+/* Créé un nouveau gestionnaire de tâches parallèles. */
+GWorkQueue *g_work_queue_new(void);
+
+/* Constitue un nouveau groupe de travail. */
+wgroup_id_t g_work_queue_define_work_group(GWorkQueue *);
+
+/* Constitue un nouveau petit groupe de travail. */
+wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *, guint);
+
+/* 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 *, wgroup_id_t);
+
+/* Détermine si un groupe est vide de toute programmation. */
+bool g_work_queue_is_empty(GWorkQueue *, 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);
+
+/* Attend que toutes les tâches de tout groupe soient traitées. */
+void g_work_queue_wait_for_all_completions(GWorkQueue *, const wgroup_id_t *, size_t);
+
+
+/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */
+typedef bool (* wait_for_incoming_works_cb) (GWorkQueue *, wgroup_id_t, void *);
+
+
+/* Modifie les conditions d'attente des fins d'exécutions. */
+void g_work_queue_set_extra_wait_callback(GWorkQueue *, wgroup_id_t, wait_for_incoming_works_cb, void *);
+
+/* Force un réveil d'une attente en cours pour la confirmer. */
+void g_work_queue_wake_up_waiters(GWorkQueue *, wgroup_id_t);
+
+
+
+#endif /* _GLIBEXT_DELAYED_H */