Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
ops, wasn't compilable under windows
[simgrid.git] / src / xbt / context.c
1 /*      $Id$     */
2
3 /* a fast and simple context switching library                              */
4
5 /* Copyright (c) 2004 Arnaud Legrand.                                       */
6 /* Copyright (c) 2004, 2005 Martin Quinson.                                 */
7 /* All rights reserved.                                                     */
8
9 /* This program is free software; you can redistribute it and/or modify it
10  * under the terms of the license (GNU LGPL) which comes with this package. */
11
12 #include "portable.h"
13 #include "context_private.h"
14 #include "xbt/log.h"
15 #include "xbt/dynar.h"
16 #include "xbt/xbt_os_thread.h"
17
18 #ifdef CONTEXT_THREADS
19  /* This file (context.c) is only loaded in libsimgrid, not libgras.
20   * xbt_os_thread is only loaded in libgras explicitly, and we need it in 
21   *    libsimgrid, but only when it is the backend used to implement the 
22   *    xbt_context. So, do load it on need.
23   */
24 #include "xbt/xbt_os_thread.c"
25 #else
26 /* if not, load stubs so that all symbols get resolved anyway */
27 #include "xbt/xbt_os_thread_stubs.c"
28 #endif
29
30 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ctx, xbt, "Context");
31
32 #define VOIRP(expr) DEBUG1("  {" #expr " = %p }", expr)
33
34 static xbt_context_t current_context = NULL;
35 static xbt_context_t init_context = NULL;
36 static xbt_swag_t context_to_destroy = NULL;
37 static xbt_swag_t context_living = NULL;
38 #ifdef CONTEXT_THREADS
39 static xbt_os_mutex_t creation_mutex;
40 static xbt_os_cond_t creation_cond;
41 #endif
42
43 static void __context_exit(xbt_context_t context ,int value);
44 static void __xbt_context_yield(xbt_context_t context)
45 {
46         xbt_assert0(current_context,"You have to call context_init() first.");
47
48         DEBUG2("--------- current_context (%p) is yielding to context(%p) ---------",current_context,context);
49    
50         #ifdef CONTEXT_THREADS
51         if (context){
52                 xbt_context_t self = current_context;
53                 DEBUG2("[%p] **** Locking ctx %p ****", self, context);
54                 xbt_os_mutex_lock(context->mutex);
55                 DEBUG1("[%p] **** Updating current_context ****", self);
56                 current_context = context;
57                 DEBUG1("[%p] **** Releasing the prisonner ****", self);
58                 xbt_os_cond_signal(context->cond);
59                 DEBUG3("[%p] **** Going to jail on individual %p/%p ****", self,context->cond, context->mutex);
60                 xbt_os_cond_wait(context->cond, context->mutex);
61                 DEBUG2("[%p] **** Unlocking individual %p ****", self,context->mutex);
62                 xbt_os_mutex_unlock(context->mutex);
63                 DEBUG1("[%p] **** Updating current_context ****", self);
64                 current_context = self;
65         }
66         #else /* use SUSv2 contexts */
67         if(current_context)
68                 VOIRP(current_context->save);
69         
70         VOIRP(context);
71         
72         if(context) 
73                 VOIRP(context->save);
74         
75         if (context){
76         
77                 int return_value = 0;
78                 
79                 if(context->save==NULL){
80                 
81                   DEBUG1("[%p] **** Yielding to somebody else ****", current_context);
82                         DEBUG2("Saving current_context value (%p) to context(%p)->save",current_context,context);
83                         context->save = current_context ;
84                         DEBUG1("current_context becomes  context(%p) ",context);
85                         current_context = context ;
86                         DEBUG1("Current position memorized (context->save). Jumping to context (%p)",context);
87                         return_value = swapcontext (&(context->save->uc), &(context->uc));
88                         xbt_assert0((return_value==0),"Context swapping failure");
89                         DEBUG1("I am (%p). Coming back\n",context);
90                 } else {
91                         xbt_context_t old_context = context->save ;
92                         DEBUG1("[%p] **** Back ! ****", context);
93                         DEBUG2("Setting current_context (%p) to context(%p)->save",current_context,context);
94                         current_context = context->save ;
95                         DEBUG1("Setting context(%p)->save to NULL",context);
96                         context->save = NULL ;
97                         DEBUG2("Current position memorized (%p). Jumping to context (%p)",context,old_context);
98                         return_value = swapcontext (&(context->uc), &(old_context->uc));
99                         xbt_assert0((return_value==0),"Context swapping failure");
100                         DEBUG1("I am (%p). Coming back\n",context);
101                 
102                 }
103         }
104         #endif
105         if(current_context->iwannadie)
106           __context_exit(current_context, 1);
107
108         return;
109 }
110
111 static void xbt_context_free(xbt_context_t context)
112 {
113         if (!context) return;
114         DEBUG1("Freeing %p",context);
115 #ifdef CONTEXT_THREADS
116         /*DEBUG1("\t joining %p",(void *)context->thread->t);*/
117         DEBUG1("\t joining %p",(void *)context->thread);
118
119         xbt_os_thread_join(context->thread,NULL);
120
121         DEBUG1("\t mutex_destroy %p",(void *)context->mutex);
122         xbt_os_mutex_destroy(context->mutex);
123         DEBUG1("\t cond_destroy %p",(void *)context->cond);
124         xbt_os_cond_destroy(context->cond);
125
126         context->thread = NULL;
127         context->mutex = NULL;
128         context->cond = NULL;
129 #endif
130         
131         if(context->exception) 
132                 free(context->exception);
133                 
134         free(context);
135         return;
136 }
137         
138 static void __context_exit(xbt_context_t context ,int value)
139 {
140         int i;
141
142         DEBUG1("--------- %p is exiting ---------",context);
143
144         DEBUG0("Calling cleanup functions");
145         if(context->cleanup_func){
146                 DEBUG0("Calling cleanup function");
147                 context->cleanup_func(context->cleanup_arg);
148         }
149
150         DEBUG0("Freeing arguments");
151         for(i=0;i<context->argc; i++) 
152                 if(context->argv[i]) 
153                         free(context->argv[i]);
154         
155         if(context->argv) 
156                 free(context->argv);
157                 
158         DEBUG0("Putting context in the to_destroy set");
159         xbt_swag_remove(context, context_living);
160         xbt_swag_insert(context, context_to_destroy);
161         DEBUG0("Context put in the to_destroy set");
162
163         DEBUG0("Yielding");
164         
165         #ifdef CONTEXT_THREADS
166         DEBUG2("[%p] **** Locking %p ****", context, context->mutex);
167         xbt_os_mutex_lock(context->mutex);
168 /*      DEBUG1("[%p] **** Updating current_context ****"); */
169 /*      current_context = context; */
170         DEBUG1("[%p] **** Releasing the prisonner ****", context);
171         xbt_os_cond_signal(context->cond);
172         DEBUG2("[%p] **** Unlocking individual %p ****", context, context->mutex);
173         xbt_os_mutex_unlock(context->mutex);
174         DEBUG1("[%p] **** Exiting ****", context);
175         xbt_os_thread_exit(NULL); // We should provide return value in case other wants it
176         #else
177         __xbt_context_yield(context);
178         #endif
179         xbt_assert0(0,"You can't be here!");
180 }
181
182 static void *
183 __context_wrapper(void* c) {
184         xbt_context_t context = current_context;
185         
186         #ifdef CONTEXT_THREADS
187         context = (xbt_context_t)c;
188         context->thread = xbt_os_thread_self();
189         
190         DEBUG3("**[ctx:%p;self:%p]** Lock creation_mutex %p ****",context,(void*)xbt_os_thread_self(), creation_mutex);
191         xbt_os_mutex_lock(creation_mutex);
192         xbt_os_mutex_lock(context->mutex);
193         
194         DEBUG4("**[ctx:%p;self:%p]** Releasing the creator (creation_cond %p,%p) ****",
195                context,(void*)xbt_os_thread_self(),creation_cond,creation_mutex);
196         xbt_os_cond_signal(creation_cond);
197         xbt_os_mutex_unlock(creation_mutex);
198         
199         DEBUG4("**[ctx:%p;self:%p]** Going to Jail on lock %p and cond %p ****",
200                context,(void*)xbt_os_thread_self(),context->mutex,context->cond);
201         xbt_os_cond_wait(context->cond, context->mutex);
202         
203         DEBUG3("**[ctx:%p;self:%p]** Unlocking individual %p ****",
204                context,(void*)xbt_os_thread_self(),context->mutex);
205         xbt_os_mutex_unlock(context->mutex);
206         
207         #endif
208         
209         if(context->startup_func)
210                 context->startup_func(context->startup_arg);
211         
212         DEBUG0("Calling the main function");
213         
214         __context_exit(context, (context->code) (context->argc,context->argv));
215         return NULL;
216 }
217
218 /* callback: context fetching */
219 static ex_ctx_t *__context_ex_ctx(void)
220 {
221         return current_context->exception;
222 }
223
224 /* callback: termination */
225 static void __context_ex_terminate(xbt_ex_t *e) {
226   xbt_ex_display(e);
227
228   abort();
229    /* FIXME: there should be a configuration variable to choose this
230   if(current_context!=init_context) 
231     __context_exit(current_context, e->value);
232   else
233     abort();
234     */
235 }
236
237 /** \name Functions 
238  *  \ingroup XBT_context
239  */
240 /* @{ */
241 /** Context module initialization
242  *
243  * \warning It has to be called before using any other function of this module.
244  */
245 void xbt_context_init(void)
246 {
247         if(!current_context){
248                 current_context = init_context = xbt_new0(s_xbt_context_t,1);
249                 DEBUG1("Init Context (%p)",init_context);
250                 
251                 init_context->exception = xbt_new(ex_ctx_t,1);
252                 XBT_CTX_INITIALIZE(init_context->exception);
253                 __xbt_ex_ctx       = __context_ex_ctx;
254                 __xbt_ex_terminate = __context_ex_terminate;
255                 context_to_destroy = xbt_swag_new(xbt_swag_offset(*current_context,hookup));
256                 context_living = xbt_swag_new(xbt_swag_offset(*current_context,hookup));
257                 xbt_swag_insert(init_context, context_living);
258 #ifdef CONTEXT_THREADS     
259                 creation_mutex = xbt_os_mutex_init();
260                 creation_cond = xbt_os_cond_init();
261 #endif     
262         }
263 }
264
265 /** Garbage collection
266  *
267  * Should be called some time to time to free the memory allocated for contexts
268  * that have finished executing their main functions.
269  */
270 void xbt_context_empty_trash(void)
271 {
272         xbt_context_t context=NULL;
273         DEBUG1("Emptying trashbin (%d contexts to free)",
274                xbt_swag_size(context_to_destroy));
275         while((context=xbt_swag_extract(context_to_destroy)))
276                 xbt_context_free(context);
277 }
278
279 /** 
280  * \param context the context to start
281  * 
282  * Calling this function prepares \a context to be run. It will 
283    however run effectively only when calling #xbt_context_schedule
284  */
285 void xbt_context_start(xbt_context_t context) 
286 {
287         #ifdef CONTEXT_THREADS
288         /* Launch the thread */
289         DEBUG3("**[ctx:%p;self:%p]** Locking creation_mutex %p ****",context,xbt_os_thread_self(),creation_mutex);
290         xbt_os_mutex_lock(creation_mutex);
291    
292         DEBUG2("**[ctx:%p;self:%p]** Thread create ****",context,xbt_os_thread_self());
293         context->thread = xbt_os_thread_create(__context_wrapper, context);   
294         DEBUG3("**[ctx:%p;self:%p]** Thread created : %p ****",context,xbt_os_thread_self(),context->thread);
295    
296         DEBUG4("**[ctx:%p;self:%p]** Going to jail on creation_cond/mutex (%p,%p) ****",
297                context,xbt_os_thread_self(),creation_cond, creation_mutex);
298         xbt_os_cond_wait(creation_cond, creation_mutex);
299         DEBUG3("**[ctx:%p;self:%p]** Unlocking creation %p ****",context, xbt_os_thread_self(),creation_mutex);
300         xbt_os_mutex_unlock(creation_mutex);
301         #else
302         makecontext (&(context->uc), (void (*) (void)) __context_wrapper,1, context);
303         #endif
304         return;
305 }
306
307 /** 
308  * \param code a main function
309  * \param startup_func a function to call when running the context for
310  *      the first time and just before the main function \a code
311  * \param startup_arg the argument passed to the previous function (\a startup_func)
312  * \param cleanup_func a function to call when running the context, just after 
313         the termination of the main function \a code
314  * \param cleanup_arg the argument passed to the previous function (\a cleanup_func)
315  * \param argc first argument of function \a code
316  * \param argv seconde argument of function \a code
317  */
318 xbt_context_t xbt_context_new(xbt_context_function_t code, 
319                               void_f_pvoid_t startup_func, void *startup_arg,
320                               void_f_pvoid_t cleanup_func, void *cleanup_arg,
321                               int argc, char *argv[])
322 {
323         xbt_context_t res = NULL;
324         
325         res = xbt_new0(s_xbt_context_t,1);
326         
327         res->code = code;
328         #ifdef CONTEXT_THREADS
329         res->mutex = xbt_os_mutex_init();
330         res->cond = xbt_os_cond_init();
331         #else 
332
333         xbt_assert2(getcontext(&(res->uc))==0,"Error in context saving: %d (%s)", errno, strerror(errno));
334         res->uc.uc_link = NULL;
335         /*   res->uc.uc_link = &(current_context->uc); */
336         /* WARNING : when this context is over, the current_context (i.e. the 
337         father), is awaken... Theorically, the wrapper should prevent using 
338         this feature. */
339         res->uc.uc_stack.ss_sp = pth_skaddr_makecontext(res->stack,STACK_SIZE);
340         res->uc.uc_stack.ss_size = pth_sksize_makecontext(res->stack,STACK_SIZE);
341         #endif /* CONTEXT_THREADS or not */
342         
343         res->argc = argc;
344         res->argv = argv;
345         res->startup_func = startup_func;
346         res->startup_arg = startup_arg;
347         res->cleanup_func = cleanup_func;
348         res->cleanup_arg = cleanup_arg;
349         res->exception = xbt_new(ex_ctx_t,1);
350         XBT_CTX_INITIALIZE(res->exception);
351         
352         xbt_swag_insert(res, context_living);
353         
354         return res;
355 }
356
357 /** 
358  * Calling this function makes the current context yield. The context
359  * that scheduled it returns from xbt_context_schedule as if nothing
360  * had happened.
361  */
362 void xbt_context_yield(void)
363 {
364         __xbt_context_yield(current_context);
365 }
366
367 /** 
368  * \param context the winner
369  *
370  * Calling this function blocks the current context and schedule \a context.  
371  * When \a context will call xbt_context_yield, it will return
372  * to this function as if nothing had happened.
373  */
374 void xbt_context_schedule(xbt_context_t context)
375 {
376         DEBUG1("Scheduling %p",context);
377         xbt_assert0((current_context==init_context),"You are not supposed to run this function here!");
378         __xbt_context_yield(context);
379 }
380
381 /** 
382  * This function kill all existing context and free all the memory
383  * that has been allocated in this module.
384  */
385 void xbt_context_exit(void) {
386         xbt_context_t context=NULL;
387         
388         xbt_context_empty_trash();
389         while((context=xbt_swag_extract(context_living))) {
390           if(context!=init_context) {
391             xbt_context_kill(context);
392           }
393         }
394         free(init_context->exception);   
395         free(init_context);   
396         init_context = current_context = NULL ;
397
398         xbt_context_empty_trash();
399         xbt_swag_free(context_to_destroy);
400         xbt_swag_free(context_living);
401         
402 #ifdef CONTEXT_THREADS     
403         xbt_os_mutex_destroy(creation_mutex);
404         xbt_os_cond_destroy(creation_cond);
405 #endif   
406 }
407
408 /** 
409  * \param context poor victim
410  *
411  * This function simply kills \a context... scarry isn't it ?
412  */
413 void xbt_context_kill(xbt_context_t context)
414 {
415         DEBUG1("Killing %p", context);
416
417         context->iwannadie=1;
418         DEBUG1("Scheduling %p",context);
419         __xbt_context_yield(context);
420         DEBUG1("End of Scheduling %p",context);
421         
422         return;
423 }
424 /* @} */
425
426 /* Stub of the stuff to interact with JAVA threads; not used in native lib */
427 void  xbt_context_set_jprocess(xbt_context_t context, void *jp) {}
428 void* xbt_context_get_jprocess(xbt_context_t context)           { return NULL; }
429
430 void  xbt_context_set_jmutex(xbt_context_t context,void *jm)    {}
431 void* xbt_context_get_jmutex(xbt_context_t context)             { return NULL; }
432
433 void  xbt_context_set_jcond(xbt_context_t context,void *jc)     {}
434 void* xbt_context_get_jcond(xbt_context_t context)              { return NULL; }
435
436 void  xbt_context_set_jenv(xbt_context_t context,void* je)      {}
437 void* xbt_context_get_jenv(xbt_context_t context)               { return NULL; }