From dc35ba75078e8ed895386c9be316774d3235f869 Mon Sep 17 00:00:00 2001 From: mquinson Date: Wed, 19 Jul 2006 14:05:17 +0000 Subject: [PATCH] New, proper, module mecanism. Still missing dependency tracking, but already allow modules to declare init/exit functions and per-process globals initialized during some join/leave functions. Once every module switch to that model, libdata can be killed (but both can coexist for now) git-svn-id: svn+ssh://scm.gforge.inria.fr/svn/simgrid/simgrid/trunk@2625 48e7efb5-ca39-0410-a469-dd3cf9ba447f --- include/gras/module.h | 21 ++++ src/gras/Virtu/gras_module.c | 231 +++++++++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+) create mode 100644 include/gras/module.h create mode 100644 src/gras/Virtu/gras_module.c diff --git a/include/gras/module.h b/include/gras/module.h new file mode 100644 index 0000000000..df21bc34a5 --- /dev/null +++ b/include/gras/module.h @@ -0,0 +1,21 @@ + +#ifndef GRAS_MODULE_H +#define GRAS_MODULE_H + +#include + +/* Function users of module should use */ +void gras_module_join(const char *name); +void gras_module_leave(const char *name); + + +/* Functions module implementor should use */ +void gras_module_add(const char *name, unsigned int data_size, int *ID, + void_f_void_t *init_f, void_f_void_t *exit_f, + void_f_pvoid_t *join_f, void_f_pvoid_t *leave_f) ; + + +void *gras_moddata_by_id(unsigned int ID); + + +#endif /* GRAS_MODULE_H */ diff --git a/src/gras/Virtu/gras_module.c b/src/gras/Virtu/gras_module.c new file mode 100644 index 0000000000..2f7fb48394 --- /dev/null +++ b/src/gras/Virtu/gras_module.c @@ -0,0 +1,231 @@ +/* $Id$ */ +/* module - module handling, along with module dependencies and local state */ + +/* Copyright (C) 2006 Martin Quinson. All rights reserved. */ + +/* This program is free software; you can redistribute it and/or modify it + * under the terms of the license (GNU LGPL) which comes with this package. */ + +#include "xbt.h" +#include "gras/module.h" +#include "virtu_private.h" + +XBT_LOG_NEW_DEFAULT_SUBCATEGORY(gras_modules,gras,"Module and moddata handling"); + +/* IMPLEMENTATION NOTE + +The goal of this module is quite difficult. We want to have several modules, +each of them having globals which have to be initialized in any unix +process. Let's call them world-wide globals. An example may be the messages +dictionnary, which is world-wide to detect definition discrepencies when +running in SG. + +And then, the module may want to have some globals specific to a gras process, +just like userdata stuff allows to have per-process globals. Let's call them +process-wide globals. An example may be the list of attached callbacks. + +That is why we have 4 functions per module: the join function is called by any +process, and is in charge of creating the process-wide globals. If it is the +first time a process uses the module, it also calls the init function, in +charge of initializing the world-wide globals. We have the symetric functions +leave, called by any process not using the module anymore, and exit, in charge +of freeing the world-wide globals when no process use it anymore. + +One can see this as a factory of factories. the init function is a factory +creating the module, which contains a factory (the join function) able to +create process-wide globals. The fact that indeed the calling sequence goes +from join to init and not the other side is just an implementation bias ;) + +Then again, we want these functionnalities to be quick. We want to access +the process-wide globals by providing their rank in a dynar, we don't want to +search them in a dictionnary. This is especially true in the module +implementation, where each functions is likely to require them to work. The +position could be a stored in a global variable only visible from the module +implementation. + +The need for an array in which to store the globals does not hold for +world-wide globals: only one instance of them can exist in the same unix naming +space. Thus, there is no need to put them in an array, we can actually declare +them as regular C globals. + +The next trick comes from the fact that the user may (and will) mess up and not +get all modules initialized in the same order in every process. So, the rank of +module data cannot be the order in which a process called the join +function. This has to be a world-wide global instead. + +And finally, if we want to get the ability to destroy modules (one day, they +will load their code in as plugin), we want to give the module handling library +(ie, this file) to modify the variable containing the rank of module in the +tables. This is why we have the ID field in the module structure: it points +exactly to the implementation side global. + +Yeah, I know. All this is not that clear. But at least, writing this helped me +to design that crap ;) + +*/ + +typedef struct s_gras_module { + XBT_SET_HEADERS; + + unsigned int datasize; + int refcount; /* Number of processes using this module */ + /* FIXME: we should keep a count of references within a given process to + allow modules initializing other modules while tracking dependencies + properly and leave() only when needed. This would allow dynamic module + loading/unloading */ + + int *p_id; /* where the module stores the libdata ID (a global somewhere), to tweak it on need */ + void_f_void_t *init_f; /* First time the module is referenced. */ + void_f_void_t *exit_f; /* When last process referencing it stops doing so. */ + void_f_pvoid_t *join_f; /* Called by each process in initialization phase (init_f called once for all processes) */ + void_f_pvoid_t *leave_f; /* Called by each process in finalization phase. Should free moddata passed */ +} s_gras_module_t, *gras_module_t; + +static xbt_set_t _gras_modules = NULL; /* content: s_gras_module_t */ + +static void gras_module_freep(void *p) { + free( ((gras_module_t)p) ->name); +} + + +/** + * @brief Declaring a new GRAS module + * @param name: name of the module, of course (beware of dupplicates!) + * @param datasize: the size of your data, ie of the state this module has on each process + * @param ID: address of a global you use as parameter to gras_module_data_by_id + * @param init_f: function called the first time a module gets by a process of the naming space. + * A classical use is to declare some messages the module uses, as well as the initialization + * of module constants (accross processes boundaries in SG). + * @param exit_f: function called when the last process of this naming space unref this module. + * @param join_f: function called each time a process references the module. + * It is passed the moddata already malloced, and should initialize the fields as it wants. + * It can also attach some callbacks to the module messages. + * @param leave_f: function called each time a process unrefs the module. + * It is passed the moddata, and should free any memory allocated by init_f. + * It should alse disconnect any attached callbacks. + */ + +void gras_module_add(const char *name, unsigned int datasize, int *ID, + void_f_void_t *init_f, void_f_void_t *exit_f, + void_f_pvoid_t *join_f, void_f_pvoid_t *leave_f) { + gras_module_t mod; + xbt_ex_t e; + int found = 0; + + if (!_gras_modules) + _gras_modules = xbt_set_new(); + + TRY { + mod=(gras_module_t)xbt_set_get_by_name (_gras_modules,name); + found = 1; + } CATCH(e) { + if (e.category != not_found_error) + RETHROW; + xbt_ex_free(e); + } + + if (found) { + xbt_assert(mod->init_f == init_f); + xbt_assert(mod->exit_f == exit_f); + xbt_assert(mod->join_f == join_f); + xbt_assert(mod->leave_f == leave_f); + xbt_assert(mod->datasize == datasize); + xbt_assert(mod->p_id == ID); + + DEBUG1("Module %s already registered. Ignoring re-registration",name); + return; + } + + VERB1("Register module %s",name); + mod = xbt_new(s_gras_module_t, 1); + mod->name = xbt_strdup(name); + mod->name_len = strlen(name); + + mod->datasize = datasize; + mod->p_id = ID; + mod->init_f = init_f; + mod->exit_f = exit_f; + mod->join_f = join_f; + mod->leave_f = leave_f; + mod->refcount = 0; + + + *mod->p_id = xbt_set_length(_gras_modules); + + xbt_set_add(_gras_modules,(void*)mod,gras_module_freep); +} + +/* Removes & frees a moddata */ +static void moddata_freep(void *p) { + gras_procdata_t *pd=gras_procdata_get(); + int id = xbt_dynar_search (pd->moddata, p); + gras_module_t mod = (gras_module_t)xbt_set_get_by_id(_gras_modules, id); + + (*mod->leave_f)(p); + free(p); +} + +void gras_module_join(const char *name) { + gras_module_t mod = (gras_module_t)xbt_set_get_by_name(_gras_modules, name); + + VERB2("Join to module %s (%p)",name,mod); + + /* NEW */ + if (mod->refcount == 0) { + VERB1("Init module %s",name); + mod->name = xbt_strdup(name); + + (*mod->init_f)(); + } else { + INFO3("Module %s already inited. Refcount=%d ID=%d", + mod->name, mod->refcount,*(mod->p_id)); + } + mod->refcount++; + + /* JOIN */ + gras_procdata_t *pd=gras_procdata_get(); + void *moddata; + + if (!pd->moddata) /* Damn. I must be the first module on this process. Scary ;) */ + pd->moddata = xbt_dynar_new(sizeof(gras_module_t),&moddata_freep); + + moddata = xbt_malloc(mod->datasize); + + xbt_dynar_set(pd->moddata, *(mod->p_id), &moddata); + + (*mod->join_f)(moddata); + + INFO2("Module %s joined successfully (ID=%d)", name,*(mod->p_id)); +} +void gras_module_leave(const char *name) { + gras_module_t mod = (gras_module_t)xbt_set_get_by_name(_gras_modules, name); + + VERB1("Leave module %s",name); + + /* LEAVE */ + void *moddata = gras_moddata_by_id( *(mod->p_id) ); + (*mod->leave_f)(moddata); + + /* EXIT */ + mod->refcount--; + if (!mod->refcount) { + VERB1("Exit module %s",name); + + (*mod->exit_f)(); + + /* Don't remove the module for real, sets don't allow to + + free(mod->name); + free(mod); + */ + } +} + + +void *gras_moddata_by_id(unsigned int ID) { + gras_procdata_t *pd=gras_procdata_get(); + void *p; + + xbt_dynar_get_cpy(pd->moddata, ID, &p); + return p; +} -- 2.20.1