/* Chrysalide - Outil d'analyse de fichiers binaires
* manager.c - collecte et gestion des instances partagées
*
* Copyright (C) 2016 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 Foobar. If not, see .
*/
#include "manager.h"
#include
#ifdef DEBUG_DUMP_STATS
# include
#endif
/* Gestionnaire d'instances de type identique partagées (instance) */
struct _GShareManager
{
GObject parent; /* A laisser en premier */
GHashTable *table; /* Collection de partages */
GMutex access; /* Accès à la table */
GType managed; /* Type d'instances gérées */
};
/* Gestionnaire d'instances de type identique partagées (classe) */
struct _GShareManagerClass
{
GObjectClass parent; /* A laisser en premier */
};
/* Procède à l'initialisation d'une classe de suivi de largeurs. */
static void g_share_manager_class_init(GShareManagerClass *);
/* Procède à l'initialisation d'un suivi de largeurs de lignes. */
static void g_share_manager_init(GShareManager *);
/* Supprime toutes les références externes. */
static void g_share_manager_dispose(GShareManager *);
/* Procède à la libération totale de la mémoire. */
static void g_share_manager_finalize(GShareManager *);
/* Détermine le type du gestionnaire d'instances partagées. */
G_DEFINE_TYPE(GShareManager, g_share_manager, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : class = classe de composant GTK à initialiser. *
* *
* Description : Procède à l'initialisation d'une classe de suivi de largeurs.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_share_manager_class_init(GShareManagerClass *class)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(class);
object->dispose = (GObjectFinalizeFunc/* ! */)g_share_manager_dispose;
object->finalize = (GObjectFinalizeFunc)g_share_manager_finalize;
}
/******************************************************************************
* *
* Paramètres : manager = composant GLib à initialiser. *
* *
* Description : Procède à l'initialisation d'un suivi de largeurs de lignes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_share_manager_init(GShareManager *manager)
{
g_mutex_init(&manager->access);
}
/******************************************************************************
* *
* Paramètres : manager = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_share_manager_dispose(GShareManager *manager)
{
g_hash_table_unref(manager->table);
g_mutex_clear(&manager->access);
G_OBJECT_CLASS(g_share_manager_parent_class)->dispose(G_OBJECT(manager));
}
/******************************************************************************
* *
* Paramètres : manager = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_share_manager_finalize(GShareManager *manager)
{
G_OBJECT_CLASS(g_share_manager_parent_class)->finalize(G_OBJECT(manager));
}
/******************************************************************************
* *
* Paramètres : type = type d'instances encadrées ici. *
* *
* Description : Crée un nouveau gestionnaire d'instances partagées. *
* *
* Retour : Composant GLib créé. *
* *
* Remarques : - *
* *
******************************************************************************/
GShareManager *g_share_manager_new(GType type)
{
GShareManager *result; /* Gestionnaire à renvoyer */
result = g_object_new(G_TYPE_SHARE_MANAGER, NULL);
result->table = g_hash_table_new_full((GHashFunc)g_direct_hash,
(GEqualFunc)g_shared_instance_is_equal,
(GDestroyNotify)g_object_unref,
(GDestroyNotify)NULL);
result->managed = type;
return result;
}
/******************************************************************************
* *
* Paramètres : manager = gestionnaire d'instance à consulter. *
* info = informations à retrouver intégralement. *
* *
* Description : Retrouve ou crée une instance partagée. *
* *
* Retour : Instance existante déjà partagée ou nouvellement créée. *
* *
* Remarques : - *
* *
******************************************************************************/
GSharedInstance *g_share_manager_get(GShareManager *manager, const void *info)
{
GSharedInstance *result; /* Trouvaille à retourner */
gpointer found; /* Elément correspondant ? */
bool status; /* Conclusion d'initialisation */
#ifndef NDEBUG
gboolean new; /* Présence de partage existant*/
#endif
gboolean find_shared_matching_info(const GSharedInstance *key, gpointer unused, const void *nfo)
{
return g_shared_instance_compare_info(key, nfo);
}
g_mutex_lock(&manager->access);
found = g_hash_table_find(manager->table, (GHRFunc)find_shared_matching_info, (void *)info);
if (found == NULL)
{
result = g_object_new(manager->managed, NULL);
status = g_shared_instance_init(result, info);
if (!status)
{
g_object_unref(result);
result = NULL;
}
else
{
g_shared_instance_inc_references(result);
#ifndef NDEBUG
new = g_hash_table_add(manager->table, result);
assert(new);
#else
g_hash_table_add(manager->table, result);
#endif
}
}
else
result = G_SHARED_INSTANCE(found);
if (result != NULL)
{
g_object_ref(G_OBJECT(result));
g_shared_instance_inc_references(result);
}
g_mutex_unlock(&manager->access);
return result;
}
/******************************************************************************
* *
* Paramètres : manager = gestionnaire d'instance à consulter. *
* old = ancienne instance partagée à faire évoluer. *
* info = informations à retrouver intégralement. *
* container = propriétaire de l'ancienne version à contacter. *
* *
* Description : Met à jour une instance partagée. *
* *
* Retour : Instance existante déjà partagée ou nouvellement créée. *
* *
* Remarques : - *
* *
******************************************************************************/
GSharedInstance *g_share_manager_update(GShareManager *manager, GSharedInstance *old, const void *info, GShareContainer *container)
{
GSharedInstance *result; /* Nouvelle instance à renvoyer*/
result = g_share_manager_get(manager, info);
if (container != NULL)
g_share_container_replace(container, old, result);
g_share_manager_put(manager, old);
return result;
}
/******************************************************************************
* *
* Paramètres : manager = gestionnaire d'instance à consulter. *
* shared = instance partagée à libérer. *
* *
* Description : Abandonne un usage d'une instance partagée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_share_manager_put(GShareManager *manager, GSharedInstance *shared)
{
#ifndef NDEBUG
gboolean found; /* Présence de partage existant*/
#endif
g_mutex_lock(&manager->access);
g_shared_instance_dec_references(shared);
if (g_shared_instance_get_references(shared) == 1)
{
#ifndef NDEBUG
found = g_hash_table_remove(manager->table, shared);
assert(found);
#else
g_hash_table_remove(manager->table, shared);
#endif
}
g_mutex_unlock(&manager->access);
}
/******************************************************************************
* *
* Paramètres : manager = gestionnaire d'instance à consulter. *
* *
* Description : Imprime des statistiques d'utilisation du gestionnaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
#ifdef DEBUG_DUMP_STATS
void g_share_manager_dump_stats(GShareManager *manager)
{
unsigned int counter; /* Quantité nécessaire */
GTypeQuery query; /* Informations sur un type */
guint size; /* Taille de la table */
void count_shared_instances(const GSharedInstance *key, gpointer unused, unsigned int *c)
{
*c += g_shared_instance_get_references(key);
}
counter = 0;
g_mutex_lock(&manager->access);
g_hash_table_foreach(manager->table, (GHFunc)count_shared_instances, &counter);
size = g_hash_table_size(manager->table);
g_mutex_unlock(&manager->access);
g_type_query(manager->managed, &query);
printf("%s: current = %u / %u - needed = %u / %u (size=%u, saved=%u)\n",
query.type_name,
size, size * query.instance_size,
counter, counter * query.instance_size,
query.instance_size,
(counter - size) * query.instance_size);
}
#endif