/* Chrysalide - Outil d'analyse de fichiers binaires * workqueue.c - gestion des travaux différés * * Copyright (C) 2009-2024 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 . */ #include "workqueue.h" #include #include #include "workqueue-int.h" /* 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, guint); /* Fournit le groupe de travail correspondant à un identifiant. */ static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *, wgroup_id_t); /* 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 (0 pour un défaut). * * * * 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, 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 = 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. * * count = quantité de threads à allouer (0 pour un défaut). * * * * 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_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_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) { unref_object(group); memmove(&queue->groups[i], &queue->groups[i + 1], (queue->groups_count - i - 1) * sizeof(GWorkGroup *)); queue->groups_count--; queue->groups = 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 : Bilan, qui correspond à l'existence du groupe ciblé. * * * * Remarques : - * * * ******************************************************************************/ bool g_work_queue_schedule(GWorkQueue *queue, GGenericWork *work, wgroup_id_t id) { bool result; /* Bilan à retourner */ GWorkGroup *group; /* Groupe de travail à attendre*/ group = g_work_queue_find_group_for_id(queue, id); assert(group != NULL); result = (group != NULL); if (result) { g_work_group_schedule(group, work); unref_object(group); } return result; } /****************************************************************************** * * * 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]; ref_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); unref_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); unref_object(group); } } /****************************************************************************** * * * Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * * id = identifiant d'un groupe de travail. * * rel = durée relative à patienter au max. en microsecondes. * * * * Description : Attend que toutes les tâches d'un groupe soient traitées. * * * * Retour : Bilan de l'attente : false en cas d'expiration, true sinon. * * * * Remarques : Cette fonction est originellement dédiée à un usage Python. * * * ******************************************************************************/ bool g_work_queue_wait_timed_for_completion(GWorkQueue *queue, wgroup_id_t id, gint64 rel) { bool result; /* Bilan d'attente à renvoyer */ GWorkGroup *group; /* Groupe de travail à attendre*/ group = g_work_queue_find_group_for_id(queue, id); if (group != NULL) { result = g_work_group_wait_timed_for_completion(group, rel); unref_object(group); } else result = true; return result; } /****************************************************************************** * * * 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. * * * * 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); unref_object(group); } }