/* 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 "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_TYPED_QUEUE g_typed_queue_get_type()
#define G_TYPED_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_typed_queue_get_type(), GTypedQueue))
#define G_IS_TYPED_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_typed_queue_get_type()))
#define G_TYPED_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_TYPED_QUEUE, GTypedQueueClass))
#define G_IS_TYPED_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_TYPED_QUEUE))
#define G_TYPED_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_TYPED_QUEUE, GTypedQueueClass))
/* File de traitement pour un type donné (instance) */
typedef struct _GTypedQueue
{
GObject parent; /* A laisser en premier */
GType type; /* Type 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 */
GThread *thread; /* Procédure de traitement */
} GTypedQueue;
/* File de traitement pour un type donné (classe) */
typedef struct _GTypedQueueClass
{
GObjectClass parent; /* A laisser en premier */
} GTypedQueueClass;
/* Indique le type défini pour les travaux typés. */
static GType g_typed_queue_get_type(void);
/* Initialise la classe des travaux typés. */
static void g_typed_queue_class_init(GTypedQueueClass *);
/* Initialise une instance de gestionnaire de travaux typés. */
static void g_typed_queue_init(GTypedQueue *);
/* Supprime toutes les références externes. */
static void g_typed_queue_dispose(GTypedQueue *);
/* Procède à la libération totale de la mémoire. */
static void g_typed_queue_finalize(GTypedQueue *);
/* Crée un nouveau thread dédié à un type de travaux donné. */
static GTypedQueue *g_typed_queue_new(GType, GtkExtStatusBar *);
/* Place une nouvelle tâche en attente dans une file dédiée. */
static void g_typed_queue_schedule(GTypedQueue *, GDelayedWork *);
/* Assure le traitement en différé. */
static void *g_typed_queue_process(GTypedQueue *);
/* ------------------------- 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 */
GTypedQueue **threads; /* Files de traitement */
size_t threads_count; /* Nombre de files internes */
};
/* 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 *);
/* ---------------------------------------------------------------------------------- */
/* 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)
{
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);
}
/* ---------------------------------------------------------------------------------- */
/* THREAD DE TRAITEMENTS DEDIES */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour les travaux typés. */
G_DEFINE_TYPE(GTypedQueue, g_typed_queue, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des travaux typés. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_typed_queue_class_init(GTypedQueueClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_typed_queue_dispose;
object->finalize = (GObjectFinalizeFunc)g_typed_queue_finalize;
}
/******************************************************************************
* *
* Paramètres : queue = instance à initialiser. *
* *
* Description : Initialise une instance de gestionnaire de travaux typés. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_typed_queue_init(GTypedQueue *queue)
{
g_mutex_init(&queue->mutex);
g_cond_init(&queue->cond);
queue->thread = g_thread_new("chrysalide_queue", (GThreadFunc)g_typed_queue_process, queue);
if (!queue->thread)
goto gtqi_error;
gtqi_error:
/* TODO */ return;
}
/******************************************************************************
* *
* Paramètres : queue = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_typed_queue_dispose(GTypedQueue *queue)
{
g_mutex_clear(&queue->mutex);
g_cond_clear(&queue->cond);
G_OBJECT_CLASS(g_typed_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_typed_queue_finalize(GTypedQueue *queue)
{
G_OBJECT_CLASS(g_typed_queue_parent_class)->finalize(G_OBJECT(queue));
}
/******************************************************************************
* *
* 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 GTypedQueue *g_typed_queue_new(GType type, GtkExtStatusBar *statusbar)
{
GTypedQueue *result; /* Traiteur à retourner */
result = g_object_new(G_TYPE_TYPED_QUEUE, NULL);
result->type = type;
result->statusbar = statusbar;
g_object_ref(statusbar);
return result;
}
/******************************************************************************
* *
* Paramètres : queue = 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_typed_queue_schedule(GTypedQueue *queue, GDelayedWork *work)
{
g_mutex_lock(&queue->mutex);
delayed_work_list_add_tail(work, &queue->works);
g_cond_signal(&queue->cond);
g_mutex_unlock(&queue->mutex);
}
/******************************************************************************
* *
* Paramètres : queue = gestionnaire des actions à mener. *
* *
* Description : Assure le traitement en différé. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static void *g_typed_queue_process(GTypedQueue *queue)
{
GDelayedWork *work; /* Traitement à mener */
while (1)
{
g_mutex_lock(&queue->mutex);
if (dl_list_empty(queue->works))
g_cond_wait(&queue->cond, &queue->mutex);
work = queue->works;
delayed_work_list_del(work, &queue->works);
g_mutex_unlock(&queue->mutex);
g_delayed_work_process(work, queue->statusbar);
/* TODO : delete work */
}
return NULL;
}
/* ---------------------------------------------------------------------------------- */
/* 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)
{
}
/******************************************************************************
* *
* 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)
{
}
/******************************************************************************
* *
* 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");
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 des actions à mener. *
* work = nouvelle tâche à programmer, puis effectuer. *
* *
* Description : Place une nouvelle tâche en attente. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work)
{
GType target; /* Type de travail à ajouter */
size_t i; /* Boucle de traitement */
target = G_TYPE_FROM_INSTANCE(work);
for (i = 0; i < queue->threads_count; i++)
if (queue->threads[i]->type == target) break;
if (i == queue->threads_count)
{
queue->threads_count++;
queue->threads = (GTypedQueue **)realloc(queue->threads,
queue->threads_count * sizeof(GTypedQueue *));
queue->threads[i] = g_typed_queue_new(target, queue->statusbar);
}
g_typed_queue_schedule(queue->threads[i], work);
}