Logo AND Algorithmique Numérique Distribuée

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