Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Make the implementation more robust: don't die if something to destroy is already...
[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
17 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ctx, xbt, "Context");
18
19 #define VOIRP(expr) DEBUG1("  {" #expr " = %p }", expr)
20
21 static xbt_context_t current_context = NULL;
22 static xbt_context_t init_context = NULL;
23 static xbt_swag_t context_to_destroy = NULL;
24 static xbt_swag_t context_living = NULL;
25
26 #if (!defined(USE_PTHREADS) && !defined(USE_WIN_THREADS)) /* USE_PTHREADS and USE_CONTEXT are exclusive */
27 # ifndef USE_UCONTEXT
28 /* don't want to play with conditional compilation in automake tonight, sorry.
29    include directly the c file from here when needed. */
30 #  include "context_win32.c" 
31 #  define USE_WIN_CONTEXT
32 # endif
33 #endif
34
35 static void __xbt_context_yield(xbt_context_t context)
36 {
37         xbt_assert0(current_context,"You have to call context_init() first.");
38         
39         DEBUG2("--------- current_context (%p) is yielding to context(%p) ---------",current_context,context);
40         
41         #ifdef USE_PTHREADS
42         if (context){
43                 xbt_context_t self = current_context;
44                 DEBUG0("**** Locking ****");
45                 pthread_mutex_lock(&(context->mutex));
46                 DEBUG0("**** Updating current_context ****");
47                 current_context = context;
48                 DEBUG0("**** Releasing the prisonner ****");
49                 pthread_cond_signal(&(context->cond));
50                 DEBUG0("**** Going to jail ****");
51                 pthread_cond_wait(&(context->cond), &(context->mutex));
52                 DEBUG0("**** Unlocking ****");
53                 pthread_mutex_unlock(&(context->mutex));
54                 DEBUG0("**** Updating current_context ****");
55                 current_context = self;
56         }
57         #elif defined(USE_WIN_THREADS)
58         if (context){
59                 xbt_context_t self = current_context;
60                 DEBUG0("**** Locking ****");
61         win_thread_mutex_lock(&(context->mutex));
62         DEBUG0("**** Updating current_context ****");
63         current_context = context;
64         DEBUG0("**** Releasing the prisonner ****");
65         win_thread_cond_signal(&(context->cond));
66         DEBUG0("**** Going to jail ****");
67         win_thread_cond_wait(&(context->cond), &(context->mutex));
68         DEBUG0("**** Unlocking ****");
69         win_thread_mutex_unlock(&(context->mutex));
70         DEBUG0("**** Updating current_context ****");
71         current_context = self;
72
73         }
74         #else
75         if(current_context)
76                 VOIRP(current_context->save);
77         
78         VOIRP(context);
79         
80         if(context) 
81                 VOIRP(context->save);
82         
83         if (context){
84         
85                 int return_value = 0;
86                 
87                 if(context->save==NULL){
88                 
89                         DEBUG0("**** Yielding to somebody else ****");
90                         DEBUG2("Saving current_context value (%p) to context(%p)->save",current_context,context);
91                         context->save = current_context ;
92                         DEBUG1("current_context becomes  context(%p) ",context);
93                         current_context = context ;
94                         DEBUG1("Current position memorized (context->save). Jumping to context (%p)",context);
95                         return_value = swapcontext (&(context->save->uc), &(context->uc));
96                         xbt_assert0((return_value==0),"Context swapping failure");
97                         DEBUG1("I am (%p). Coming back\n",context);
98                 } else {
99                         xbt_context_t old_context = context->save ;
100                         DEBUG0("**** Back ! ****");
101                         DEBUG2("Setting current_context (%p) to context(%p)->save",current_context,context);
102                         current_context = context->save ;
103                         DEBUG1("Setting context(%p)->save to NULL",context);
104                         context->save = NULL ;
105                         DEBUG2("Current position memorized (%p). Jumping to context (%p)",context,old_context);
106                         return_value = swapcontext (&(context->uc), &(old_context->uc));
107                         xbt_assert0((return_value==0),"Context swapping failure");
108                         DEBUG1("I am (%p). Coming back\n",context);
109                 
110                 }
111         }
112         #endif
113         return;
114 }
115
116 static void xbt_context_destroy(xbt_context_t context)
117 {
118         #ifdef USE_PTHREADS
119         xbt_free(context->thread);
120         pthread_mutex_destroy(&(context->mutex));
121         pthread_cond_destroy(&(context->cond));
122         #elif defined(USE_WIN_THREADS)
123         /*xbt_free(context->thread);*/
124
125     if(context->mutex)
126         win_thread_mutex_destroy(&(context->mutex));
127
128
129     if(context->cond)
130             win_thread_cond_destroy(&(context->cond));
131
132
133         #endif
134         
135         if(context->exception) 
136                 free(context->exception);
137         
138         #ifdef USE_WIN_CONTEXT          
139         if(context->uc.uc_stack.ss_sp)
140                 free (context->uc.uc_stack.ss_sp);
141         #endif
142         
143         free(context);
144         return;
145 }
146         
147 static void __context_exit(xbt_context_t context ,int value)
148 {
149         int i;
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         if(context->cleanup_func){
159                 DEBUG0("Calling cleanup function");
160                 context->cleanup_func(context->cleanup_arg);
161         }
162         
163         DEBUG0("Putting context in the to_destroy set");
164         xbt_swag_remove(context, context_living);
165         xbt_swag_insert(context, context_to_destroy);
166         DEBUG0("Context put in the to_destroy set");
167         DEBUG0("Yielding");
168         
169         #ifdef USE_PTHREADS
170         DEBUG0("**** Locking ****");
171         pthread_mutex_lock(&(context->mutex));
172         DEBUG0("**** Updating current_context ****");
173         current_context = context;
174         DEBUG0("**** Releasing the prisonner ****");
175         pthread_cond_signal(&(context->cond));  
176         DEBUG0("**** Unlocking ****");
177         pthread_mutex_unlock(&(context->mutex));
178         DEBUG0("**** Exiting ****");
179         pthread_exit(0);
180         #elif defined(USE_WIN_THREADS)
181         DEBUG0("**** Locking ****");
182         win_thread_mutex_lock(&(context->mutex));
183         DEBUG0("**** Updating current_context ****");
184         current_context = context;
185         DEBUG0("**** Releasing the prisonner ****");
186         win_thread_cond_signal(&(context->cond));  
187         DEBUG0("**** Unlocking ****");
188         win_thread_mutex_unlock(&(context->mutex));
189         DEBUG0("**** Exiting ****");
190         win_thread_exit(context->thread,0);
191         #else
192         __xbt_context_yield(context);
193         #endif
194         xbt_assert0(0,"You can't be here!");
195 }
196
197 #ifdef USE_WIN_THREADS 
198 static DWORD WINAPI     /* special thread proc signature */
199 #else
200 static void *
201 #endif 
202 __context_wrapper(void* c)
203 {
204         xbt_context_t context = (xbt_context_t)c;
205         
206         #ifdef USE_PTHREADS
207         DEBUG2("**[%p:%p]** Lock ****",context,(void*)pthread_self());
208         pthread_mutex_lock(&(context->mutex));
209         
210         DEBUG2("**[%p:%p]** Releasing the prisonner ****",context,(void*)pthread_self());
211         pthread_cond_signal(&(context->cond));
212         
213         DEBUG2("**[%p:%p]** Going to Jail ****",context,(void*)pthread_self());
214         pthread_cond_wait(&(context->cond), &(context->mutex));
215         
216         DEBUG2("**[%p:%p]** Unlocking ****",context,(void*)pthread_self());
217         pthread_mutex_unlock(&(context->mutex));
218         
219         #elif defined(USE_WIN_THREADS)
220         DEBUG2("**[%p:%p]** Lock ****",context,(void*)win_thread_self());
221         win_thread_mutex_lock(&(context->mutex));
222
223         DEBUG2("**[%p:%p]** Releasing the prisonner ****",context,(void*)win_thread_self());
224         win_thread_cond_signal(&(context->cond));
225
226         DEBUG2("**[%p:%p]** Going to Jail ****",context,(void*)win_thread_self());
227         win_thread_cond_wait(&(context->cond), &(context->mutex));
228
229         DEBUG2("**[%p:%p]** Unlocking ****",context,(void*)win_thread_self());
230         win_thread_mutex_unlock(&(context->mutex));
231
232         #endif
233         
234         if(context->startup_func)
235                 context->startup_func(context->startup_arg);
236         
237         DEBUG0("Calling the main function");
238         
239         __context_exit(context, (context->code) (context->argc,context->argv));
240         return NULL;
241 }
242
243 /* callback: context fetching */
244 static ex_ctx_t *__context_ex_ctx(void)
245 {
246         return current_context->exception;
247 }
248
249 /* callback: termination */
250 static void __context_ex_terminate(xbt_ex_t *e) {
251   xbt_ex_display(e);
252
253   abort();
254    /* FIXME: there should be a configuration variable to choose this
255   if(current_context!=init_context) 
256     __context_exit(current_context, e->value);
257   else
258     abort();
259     */
260 }
261
262 /** \name Functions 
263  *  \ingroup XBT_context
264  */
265 /* @{ */
266 /** Context module initialization
267  *
268  * \warning It has to be called before using any other function of this module.
269  */
270 void xbt_context_init(void)
271 {
272         if(!current_context){
273                 current_context = init_context = xbt_new0(s_xbt_context_t,1);
274
275                 init_context->exception = xbt_new(ex_ctx_t,1);
276                 XBT_CTX_INITIALIZE(init_context->exception);
277                 __xbt_ex_ctx       = __context_ex_ctx;
278                 __xbt_ex_terminate = __context_ex_terminate;
279                 context_to_destroy = xbt_swag_new(xbt_swag_offset(*current_context,hookup));
280                 context_living = xbt_swag_new(xbt_swag_offset(*current_context,hookup));
281                 xbt_swag_insert(init_context, context_living);
282         }
283 }
284
285 /** Garbage collection
286  *
287  * Should be called some time to time to free the memory allocated for contexts
288  * that have finished executing their main functions.
289  */
290 void xbt_context_empty_trash(void)
291 {
292         xbt_context_t context=NULL;
293         
294         while((context=xbt_swag_extract(context_to_destroy)))
295                 xbt_context_destroy(context);
296 }
297
298 /** 
299  * \param context the context to start
300  * 
301  * Calling this function prepares \a context to be run. It will 
302    however run effectively only when calling #xbt_context_schedule
303  */
304 void xbt_context_start(xbt_context_t context) 
305 {
306         #ifdef USE_PTHREADS
307         /* Launch the thread */
308         DEBUG1("**[%p]** Locking ****",context);
309         pthread_mutex_lock(&(context->mutex));
310         DEBUG1("**[%p]** Pthread create ****",context);
311         xbt_assert0(!pthread_create(context->thread, NULL, __context_wrapper, context),"Unable to create a thread.");
312         DEBUG2("**[%p]** Pthread created : %p ****",context,(void*)(*(context->thread)));
313         DEBUG1("**[%p]** Going to jail ****",context);
314         pthread_cond_wait(&(context->cond), &(context->mutex));
315         DEBUG1("**[%p]** Unlocking ****",context);
316         pthread_mutex_unlock(&(context->mutex)); 
317         #elif defined(USE_WIN_THREADS) 
318         /* Launch the thread */
319         DEBUG1("**[%p]** Locking ****",context);
320         win_thread_mutex_lock(&(context->mutex));
321         DEBUG1("**[%p]** Windows thread create ****",context);
322         xbt_assert0(!win_thread_create(&(context->thread), __context_wrapper, context),"Unable to create a thread.");
323         DEBUG2("**[%p]** Windows created : %p ****",context,(void*)(context->thread));
324         DEBUG1("**[%p]** Going to jail ****",context);
325         win_thread_cond_wait(&(context->cond), &(context->mutex));
326         DEBUG1("**[%p]** Unlocking ****",context);
327         win_thread_mutex_unlock(&(context->mutex)); 
328         #else
329         makecontext (&(context->uc), (void (*) (void)) __context_wrapper,1, context);
330         #endif
331         return;
332 }
333
334 /** 
335  * \param code a main function
336  * \param startup_func a function to call when running the context for
337  *      the first time and just before the main function \a code
338  * \param startup_arg the argument passed to the previous function (\a startup_func)
339  * \param cleanup_func a function to call when running the context, just after 
340         the termination of the main function \a code
341  * \param cleanup_arg the argument passed to the previous function (\a cleanup_func)
342  * \param argc first argument of function \a code
343  * \param argv seconde argument of function \a code
344  */
345 xbt_context_t xbt_context_new(xbt_context_function_t code, 
346                               void_f_pvoid_t startup_func, void *startup_arg,
347                               void_f_pvoid_t cleanup_func, void *cleanup_arg,
348                               int argc, char *argv[])
349 {
350         xbt_context_t res = NULL;
351         
352         res = xbt_new0(s_xbt_context_t,1);
353         
354         res->code = code;
355         #ifdef USE_PTHREADS
356         res->thread = xbt_new0(pthread_t,1);
357         xbt_assert0(!pthread_mutex_init(&(res->mutex), NULL), "Mutex initialization error");
358         xbt_assert0(!pthread_cond_init(&(res->cond), NULL), "Condition initialization error");
359         #elif defined(USE_WIN_THREADS)
360         /*res->thread = xbt_new0(pthread_t,1);*/
361         xbt_assert0(!win_thread_mutex_init(&(res->mutex)), "Mutex initialization error");
362         xbt_assert0(!win_thread_cond_init(&(res->cond)), "Condition initialization error");
363         #else 
364         /* FIXME: strerror is not thread safe */
365         xbt_assert2(getcontext(&(res->uc))==0,"Error in context saving: %d (%s)", errno, strerror(errno));
366         res->uc.uc_link = NULL;
367         /*   res->uc.uc_link = &(current_context->uc); */
368         /* WARNING : when this context is over, the current_context (i.e. the 
369         father), is awaken... Theorically, the wrapper should prevent using 
370         this feature. */
371         # ifdef USE_WIN_CONTEXT    
372         res->uc.uc_stack.ss_sp = xbt_malloc(STACK_SIZE);
373         res->uc.uc_stack.ss_size = STACK_SIZE ;
374         #else
375         res->uc.uc_stack.ss_sp = pth_skaddr_makecontext(res->stack,STACK_SIZE);
376         res->uc.uc_stack.ss_size = pth_sksize_makecontext(res->stack,STACK_SIZE);
377         # endif /* USE_WIN_CONTEXT */
378         #endif /* USE_PTHREADS or not */
379         
380         res->argc = argc;
381         res->argv = argv;
382         res->startup_func = startup_func;
383         res->startup_arg = startup_arg;
384         res->cleanup_func = cleanup_func;
385         res->cleanup_arg = cleanup_arg;
386         res->exception = xbt_new(ex_ctx_t,1);
387         XBT_CTX_INITIALIZE(res->exception);
388         
389         xbt_swag_insert(res, context_living);
390         
391         return res;
392 }
393
394 /** 
395  * Calling this function makes the current context yield. The context
396  * that scheduled it returns from xbt_context_schedule as if nothing
397  * had happened.
398  */
399 void xbt_context_yield(void)
400 {
401         __xbt_context_yield(current_context);
402 }
403
404 /** 
405  * \param context the winner
406  *
407  * Calling this function blocks the current context and schedule \a context.  
408  * When \a context will call xbt_context_yield, it will return
409  * to this function as if nothing had happened.
410  */
411 void xbt_context_schedule(xbt_context_t context)
412 {
413         DEBUG1("Scheduling %p",context);
414         xbt_assert0((current_context==init_context),"You are not supposed to run this function here!");
415         __xbt_context_yield(context);
416 }
417
418 /** 
419  * This function kill all existing context and free all the memory
420  * that has been allocated in this module.
421  */
422 void xbt_context_exit(void) {
423         xbt_context_t context=NULL;
424
425     
426
427         xbt_context_empty_trash();
428         xbt_swag_free(context_to_destroy);
429         
430         while((context=xbt_swag_extract(context_living)))
431                 xbt_context_free(context);
432         
433         xbt_swag_free(context_living);
434         
435         init_context = current_context = NULL ;
436 }
437
438 /** 
439  * \param context poor victim
440  *
441  * This function simply kills \a context... scarry isn't it ?
442  */
443 void xbt_context_free(xbt_context_t context)
444 {
445         int i ;
446         
447         xbt_swag_remove(context, context_living);  
448         
449         for(i=0;i<context->argc; i++) 
450                 if(context->argv[i]) 
451                         free(context->argv[i]);
452         
453         if(context->argv) 
454                 free(context->argv);
455         
456         if(context->cleanup_func)
457                 context->cleanup_func(context->cleanup_arg);
458         
459         xbt_context_destroy(context);
460         
461         return;
462 }
463 /* @} */