/* Chrysalide - Outil d'analyse de fichiers binaires * delayed.c - gestion des travaux différés * * Copyright (C) 2009-2013 Cyrille Bagard * * This file is part of Chrysalide. * * OpenIDA 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. * * OpenIDA 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 Foobar. If not, see . */ #include "delayed.h" #include #include #include #include #include "delayed-int.h" /* -------------------------- 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 *, GtkExtStatusBar *); /* -------------------------- 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*/ 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 **threads; /* Procédure de traitement */ guint threads_count; /* Nombre de procédures */ bool force_exit; /* Procédure d'arrêt */ } 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, 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_work_group_schedule(GWorkGroup *, GDelayedWork *); /* Assure le traitement en différé. */ 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 *); /* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */ /* Gestionnaire des travaux différés (instance) */ struct _GWorkQueue { GObject parent; /* A laisser en premier */ GtkExtStatusBar *statusbar; /* Barre de statut principale */ 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 */ }; /* 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 GWorkGroup *g_work_queue_ensure_group_exists(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. * * statusbar = barre de statut à tenir informée. * * * * Description : Mène l'opération programmée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_delayed_work_process(GDelayedWork *work, GtkExtStatusBar *statusbar) { G_DELAYED_WORK_GET_CLASS(work)->run(work, statusbar); 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) { 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 *)); 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; } gwgi_error: group->threads_count = i; assert(i > 0); group->force_exit = false; } /****************************************************************************** * * * 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; 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_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 : type = type dont seront marqués tous les travaux donnés.* * statusbar = barre de statut à tenir informée. * * * * 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, GtkExtStatusBar *statusbar) { GWorkGroup *result; /* Traiteur à retourner */ result = g_object_new(G_TYPE_WORK_GROUP, NULL); result->id = id; result->statusbar = statusbar; g_object_ref(statusbar); 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); 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 */ 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) break; work = group->works; delayed_work_list_del(work, &group->works); g_mutex_unlock(&group->mutex); g_delayed_work_process(work, group->statusbar); g_object_unref(G_OBJECT(work)); g_cond_broadcast(&group->wait_cond); } return NULL; } /****************************************************************************** * * * 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 */ /* ---------------------------------------------------------------------------------- */ /* 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); } /****************************************************************************** * * * 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)); } /****************************************************************************** * * * Paramètres : ref = espace de référencements global. * * * * Description : Procède au chargement du gestionnaire d'analyse différées. * * * * Retour : true pour indiquer un chargement réussi, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool init_work_queue(GObject *ref) { GWorkQueue *queue; /* Singleton à mettre en place */ 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; if (queue != NULL) _get_work_queue(queue); return (queue != NULL); } /****************************************************************************** * * * Paramètres : queue = nouveau gestionnaire à mémoriser ou NULL. * * * * Description : Fournit le gestionnaire de traitements parallèles courant. * * * * Retour : Gestionnaire de traitements parallèles courant. * * * * Remarques : - * * * ******************************************************************************/ GWorkQueue *_get_work_queue(GWorkQueue *queue) { static GWorkQueue *result = NULL; /* Singleton à retourner */ if (queue != NULL) result = queue; return result; } /****************************************************************************** * * * 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. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id_t id) { GWorkGroup *group; /* Groupe de travail à attendre*/ g_mutex_lock(&queue->mutex); group = g_work_queue_ensure_group_exists(queue, id); g_object_ref(G_OBJECT(group)); g_mutex_unlock(&queue->mutex); 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 : 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)); } }