Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
7f426c3160aa4095d5377bb7b7f254efe4eca991
[simgrid.git] / src / gras / Virtu / gras_module.c
1 /* $Id$ */
2 /* module - module handling, along with module dependencies and local state */
3
4 /* Copyright (C) 2006 Martin Quinson. All rights reserved.                  */
5
6 /* This program is free software; you can redistribute it and/or modify it
7  * under the terms of the license (GNU LGPL) which comes with this package. */
8
9 #include "xbt.h"
10 #include "gras/module.h"
11 #include "virtu_private.h"
12
13 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(gras_modules,gras,"Module and moddata handling");
14
15 /* IMPLEMENTATION NOTE
16
17 The goal of this module is quite difficult. We want to have several modules,
18 each of them having globals which have to be initialized in any unix
19 process. Let's call them world-wide globals. An example may be the messages
20 dictionnary, which is world-wide to detect definition discrepencies when
21 running in SG.
22
23 And then, the module may want to have some globals specific to a gras process,
24 just like userdata stuff allows to have per-process globals. Let's call them
25 process-wide globals. An example may be the list of attached callbacks.
26
27 That is why we have 4 functions per module: the join function is called by any
28 process, and is in charge of creating the process-wide globals. If it is the
29 first time a process uses the module, it also calls the init function, in
30 charge of initializing the world-wide globals. We have the symetric functions
31 leave, called by any process not using the module anymore, and exit, in charge
32 of freeing the world-wide globals when no process use it anymore.
33
34 One can see this as a factory of factories. the init function is a factory
35 creating the module, which contains a factory (the join function) able to
36 create process-wide globals. The fact that indeed the calling sequence goes
37 from join to init and not the other side is just an implementation bias ;)
38
39 Then again, we want these functionnalities to be quick. We want to access
40 the process-wide globals by providing their rank in a dynar, we don't want to
41 search them in a dictionnary. This is especially true in the module
42 implementation, where each functions is likely to require them to work. The
43 position could be a stored in a global variable only visible from the module
44 implementation. 
45
46 The need for an array in which to store the globals does not hold for
47 world-wide globals: only one instance of them can exist in the same unix naming
48 space. Thus, there is no need to put them in an array, we can actually declare
49 them as regular C globals.
50
51 The next trick comes from the fact that the user may (and will) mess up and not
52 get all modules initialized in the same order in every process. So, the rank of
53 module data cannot be the order in which a process called the join
54 function. This has to be a world-wide global instead.
55
56 And finally, if we want to get the ability to destroy modules (one day, they
57 will load their code in as plugin), we want to give the module handling library
58 (ie, this file) to modify the variable containing the rank of module in the
59 tables. This is why we have the ID field in the module structure: it points
60 exactly to the implementation side global.
61
62 Yeah, I know. All this is not that clear. But at least, writing this helped me 
63 to design that crap ;)
64
65 */
66
67 typedef struct s_gras_module {
68   XBT_SET_HEADERS;
69
70   unsigned int datasize;
71   int refcount; /* Number of processes using this module */
72   /* FIXME: we should keep a count of references within a given process to
73      allow modules initializing other modules while tracking dependencies
74      properly and leave() only when needed. This would allow dynamic module
75      loading/unloading */
76    
77   int *p_id; /* where the module stores the libdata ID (a global somewhere), to tweak it on need */
78   void_f_void_t *init_f;  /* First time the module is referenced. */
79   void_f_void_t *exit_f;  /* When last process referencing it stops doing so. */
80   void_f_pvoid_t *join_f;  /* Called by each process in initialization phase (init_f called once for all processes) */
81   void_f_pvoid_t *leave_f; /* Called by each process in finalization phase. Should free moddata passed */
82 } s_gras_module_t, *gras_module_t;
83
84 static xbt_set_t _gras_modules = NULL; /* content: s_gras_module_t */
85
86 static void gras_module_freep(void *p) {
87    free( ((gras_module_t)p) ->name);
88 }
89
90
91 /**
92  * @brief Declaring a new GRAS module
93  * @param name: name of the module, of course (beware of dupplicates!)
94  * @param datasize: the size of your data, ie of the state this module has on each process
95  * @param ID: address of a global you use as parameter to gras_module_data_by_id
96  * @param init_f: function called the first time a module gets by a process of the naming space. 
97  *                A classical use is to declare some messages the module uses, as well as the initialization
98  *                of module constants (accross processes boundaries in SG).
99  * @param exit_f: function called when the last process of this naming space unref this module.
100  * @param join_f: function called each time a process references the module. 
101  *                It is passed the moddata already malloced, and should initialize the fields as it wants.
102  *                It can also attach some callbacks to the module messages.
103  * @param leave_f: function called each time a process unrefs the module.
104  *                 It is passed the moddata, and should free any memory allocated by init_f.
105  *                 It should alse disconnect any attached callbacks.
106  */
107
108 void gras_module_add(const char *name, unsigned int datasize, int *ID,
109                      void_f_void_t  *init_f, void_f_void_t  *exit_f,
110                      void_f_pvoid_t *join_f, void_f_pvoid_t *leave_f) {
111   gras_module_t mod=NULL;
112   xbt_ex_t e;
113   volatile int found = 0;
114
115   if (!_gras_modules)
116     _gras_modules = xbt_set_new();
117
118   TRY {
119     mod=(gras_module_t)xbt_set_get_by_name (_gras_modules,name);
120     found = 1;
121   } CATCH(e) {
122     if (e.category != not_found_error)
123       RETHROW;
124     xbt_ex_free(e);
125   }
126
127   if (found) {
128     xbt_assert1(mod->init_f == init_f, 
129                 "Module %s reregistered with a different init_f!", name);
130     xbt_assert1(mod->exit_f == exit_f,
131                 "Module %s reregistered with a different exit_f!", name);
132     xbt_assert1(mod->join_f == join_f,
133                 "Module %s reregistered with a different join_f!", name);
134     xbt_assert1(mod->leave_f == leave_f,
135                 "Module %s reregistered with a different leave_f!", name);
136     xbt_assert1(mod->datasize == datasize, 
137                 "Module %s reregistered with a different datasize!", name);
138     xbt_assert1(mod->p_id == ID,
139                 "Module %s reregistered with a different p_id field!", name);
140     
141     DEBUG1("Module %s already registered. Ignoring re-registration",name);
142     return;
143   }
144
145   VERB1("Register module %s",name);
146   mod = xbt_new(s_gras_module_t, 1);
147   mod->name = xbt_strdup(name);
148   mod->name_len = strlen(name);
149   
150   mod->datasize = datasize;
151   mod->p_id = ID;
152   mod->init_f = init_f;
153   mod->exit_f = exit_f;
154   mod->join_f = join_f;
155   mod->leave_f = leave_f;
156   mod->refcount = 0;
157   
158   *mod->p_id = xbt_set_length(_gras_modules);
159   
160   xbt_set_add(_gras_modules,(void*)mod,gras_module_freep);
161 }
162
163 /* shutdown the module mechanism (world-wide cleanups) */
164 void gras_moddata_exit(void) {
165   xbt_set_free(&_gras_modules);
166 }
167
168 /* frees the moddata on this host (process-wide cleanups) */
169 void gras_moddata_leave(void) {
170   gras_procdata_t *pd=gras_procdata_get();
171
172   xbt_dynar_free(&pd->moddata);
173 }
174
175 /* Removes & frees a given moddata from the current host */
176 static void moddata_freep(void *p) {
177   gras_procdata_t *pd=gras_procdata_get();
178   int id = xbt_dynar_search (pd->moddata, p);
179   gras_module_t mod = (gras_module_t)xbt_set_get_by_id(_gras_modules, id);
180
181   (*mod->leave_f)(gras_moddata_by_id(id));
182 }
183
184 void gras_module_join(const char *name) {
185   gras_procdata_t *pd;
186   void *moddata;
187   gras_module_t mod = (gras_module_t)xbt_set_get_by_name(_gras_modules, name);
188   
189   VERB2("Join to module %s (%p)",name,mod);
190   
191   /* NEW */
192   if (mod->refcount == 0) {
193     VERB1("Init module %s",name);
194     mod->name = xbt_strdup(name);
195
196     (*mod->init_f)();
197   } else {
198     DEBUG3("Module %s already inited. Refcount=%d ID=%d",
199           mod->name, mod->refcount,*(mod->p_id));
200   }
201   mod->refcount++;
202
203   /* JOIN */
204   pd=gras_procdata_get();
205
206   if (!pd->moddata) /* Damn. I must be the first module on this process. Scary ;)*/
207     pd->moddata   = xbt_dynar_new(sizeof(gras_module_t),&moddata_freep);
208
209   moddata = xbt_malloc(mod->datasize);
210
211   xbt_dynar_set(pd->moddata, *(mod->p_id), &moddata);
212
213   (*mod->join_f)(moddata);
214
215   DEBUG2("Module %s joined successfully (ID=%d)", name,*(mod->p_id));
216 }
217 void gras_module_leave(const char *name) {
218   void *moddata;
219   gras_module_t mod = (gras_module_t)xbt_set_get_by_name(_gras_modules, name);
220
221   VERB1("Leave module %s",name);
222
223   /* LEAVE */
224   moddata = gras_moddata_by_id( *(mod->p_id) );
225   (*mod->leave_f)(moddata);
226
227   /* EXIT */
228   mod->refcount--;
229   if (!mod->refcount) {
230     VERB1("Exit module %s",name);
231
232     (*mod->exit_f)();
233
234     /* Don't remove the module for real, sets don't allow to 
235
236        free(mod->name);
237        free(mod);
238     */
239   }
240 }
241
242    
243 void *gras_moddata_by_id(unsigned int ID) {
244   gras_procdata_t *pd=gras_procdata_get();
245   void *p;
246
247   xbt_dynar_get_cpy(pd->moddata, ID, &p);
248   return p;
249 }