Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
c96a2cde821358d88d5a0c34a6743a19c9a84338
[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         /*#ifdef USE_WIN_THREADS
276         win_thread_mutex_init(&(current_context->mutex));
277             win_thread_cond_init(&(current_context->cond));
278         #endif*/
279
280                 init_context->exception = xbt_new(ex_ctx_t,1);
281                 XBT_CTX_INITIALIZE(init_context->exception);
282                 __xbt_ex_ctx       = __context_ex_ctx;
283                 __xbt_ex_terminate = __context_ex_terminate;
284                 context_to_destroy = xbt_swag_new(xbt_swag_offset(*current_context,hookup));
285                 context_living = xbt_swag_new(xbt_swag_offset(*current_context,hookup));
286                 xbt_swag_insert(init_context, context_living);
287         }
288 }
289
290 /** Garbage collection
291  *
292  * Should be called some time to time to free the memory allocated for contexts
293  * that have finished executing their main functions.
294  */
295 void xbt_context_empty_trash(void)
296 {
297         xbt_context_t context=NULL;
298         
299         while((context=xbt_swag_extract(context_to_destroy)))
300                 xbt_context_destroy(context);
301 }
302
303 /** 
304  * \param context the context to start
305  * 
306  * Calling this function prepares \a context to be run. It will 
307    however run effectively only when calling #xbt_context_schedule
308  */
309 void xbt_context_start(xbt_context_t context) 
310 {
311         #ifdef USE_PTHREADS
312         /* Launch the thread */
313         DEBUG1("**[%p]** Locking ****",context);
314         pthread_mutex_lock(&(context->mutex));
315         DEBUG1("**[%p]** Pthread create ****",context);
316         xbt_assert0(!pthread_create(context->thread, NULL, __context_wrapper, context),"Unable to create a thread.");
317         DEBUG2("**[%p]** Pthread created : %p ****",context,(void*)(*(context->thread)));
318         DEBUG1("**[%p]** Going to jail ****",context);
319         pthread_cond_wait(&(context->cond), &(context->mutex));
320         DEBUG1("**[%p]** Unlocking ****",context);
321         pthread_mutex_unlock(&(context->mutex)); 
322         #elif defined(USE_WIN_THREADS) 
323         /* Launch the thread */
324         DEBUG1("**[%p]** Locking ****",context);
325         win_thread_mutex_lock(&(context->mutex));
326         DEBUG1("**[%p]** Windows thread create ****",context);
327         xbt_assert0(!win_thread_create(&(context->thread), __context_wrapper, context),"Unable to create a thread.");
328         DEBUG2("**[%p]** Windows created : %p ****",context,(void*)(context->thread));
329         DEBUG1("**[%p]** Going to jail ****",context);
330         win_thread_cond_wait(&(context->cond), &(context->mutex));
331         DEBUG1("**[%p]** Unlocking ****",context);
332         win_thread_mutex_unlock(&(context->mutex)); 
333         #else
334         makecontext (&(context->uc), (void (*) (void)) __context_wrapper,1, context);
335         #endif
336         return;
337 }
338
339 /** 
340  * \param code a main function
341  * \param startup_func a function to call when running the context for
342  *      the first time and just before the main function \a code
343  * \param startup_arg the argument passed to the previous function (\a startup_func)
344  * \param cleanup_func a function to call when running the context, just after 
345         the termination of the main function \a code
346  * \param cleanup_arg the argument passed to the previous function (\a cleanup_func)
347  * \param argc first argument of function \a code
348  * \param argv seconde argument of function \a code
349  */
350 xbt_context_t xbt_context_new(xbt_context_function_t code, 
351                               void_f_pvoid_t startup_func, void *startup_arg,
352                               void_f_pvoid_t cleanup_func, void *cleanup_arg,
353                               int argc, char *argv[])
354 {
355         xbt_context_t res = NULL;
356         
357         res = xbt_new0(s_xbt_context_t,1);
358         
359         res->code = code;
360         #ifdef USE_PTHREADS
361         res->thread = xbt_new0(pthread_t,1);
362         xbt_assert0(!pthread_mutex_init(&(res->mutex), NULL), "Mutex initialization error");
363         xbt_assert0(!pthread_cond_init(&(res->cond), NULL), "Condition initialization error");
364         #elif defined(USE_WIN_THREADS)
365         /*res->thread = xbt_new0(pthread_t,1);*/
366         xbt_assert0(!win_thread_mutex_init(&(res->mutex)), "Mutex initialization error");
367         xbt_assert0(!win_thread_cond_init(&(res->cond)), "Condition initialization error");
368         #else 
369         /* FIXME: strerror is not thread safe */
370         xbt_assert2(getcontext(&(res->uc))==0,"Error in context saving: %d (%s)", errno, strerror(errno));
371         res->uc.uc_link = NULL;
372         /*   res->uc.uc_link = &(current_context->uc); */
373         /* WARNING : when this context is over, the current_context (i.e. the 
374         father), is awaken... Theorically, the wrapper should prevent using 
375         this feature. */
376         # ifdef USE_WIN_CONTEXT    
377         res->uc.uc_stack.ss_sp = xbt_malloc(STACK_SIZE);
378         res->uc.uc_stack.ss_size = STACK_SIZE ;
379         #else
380         res->uc.uc_stack.ss_sp = pth_skaddr_makecontext(res->stack,STACK_SIZE);
381         res->uc.uc_stack.ss_size = pth_sksize_makecontext(res->stack,STACK_SIZE);
382         # endif /* USE_WIN_CONTEXT */
383         #endif /* USE_PTHREADS or not */
384         
385         res->argc = argc;
386         res->argv = argv;
387         res->startup_func = startup_func;
388         res->startup_arg = startup_arg;
389         res->cleanup_func = cleanup_func;
390         res->cleanup_arg = cleanup_arg;
391         res->exception = xbt_new(ex_ctx_t,1);
392         XBT_CTX_INITIALIZE(res->exception);
393         
394         xbt_swag_insert(res, context_living);
395         
396         return res;
397 }
398
399 /** 
400  * Calling this function makes the current context yield. The context
401  * that scheduled it returns from xbt_context_schedule as if nothing
402  * had happened.
403  */
404 void xbt_context_yield(void)
405 {
406         __xbt_context_yield(current_context);
407 }
408
409 /** 
410  * \param context the winner
411  *
412  * Calling this function blocks the current context and schedule \a context.  
413  * When \a context will call xbt_context_yield, it will return
414  * to this function as if nothing had happened.
415  */
416 void xbt_context_schedule(xbt_context_t context)
417 {
418         DEBUG1("Scheduling %p",context);
419         xbt_assert0((current_context==init_context),"You are not supposed to run this function here!");
420         __xbt_context_yield(context);
421 }
422
423 /** 
424  * This function kill all existing context and free all the memory
425  * that has been allocated in this module.
426  */
427 void xbt_context_exit(void) {
428         xbt_context_t context=NULL;
429
430     /*#ifdef USE_WIN_THREADS
431     win_thread_mutex_destroy(&(current_context->mutex));
432         win_thread_cond_destroy(&(current_context->cond));
433     #endif*/
434
435         xbt_context_empty_trash();
436         xbt_swag_free(context_to_destroy);
437         
438         while((context=xbt_swag_extract(context_living)))
439                 xbt_context_free(context);
440         
441         xbt_swag_free(context_living);
442         
443         init_context = current_context = NULL ;
444 }
445
446 /** 
447  * \param context poor victim
448  *
449  * This function simply kills \a context... scarry isn't it ?
450  */
451 void xbt_context_free(xbt_context_t context)
452 {
453         int i ;
454         
455         xbt_swag_remove(context, context_living);  
456         
457         for(i=0;i<context->argc; i++) 
458                 if(context->argv[i]) 
459                         free(context->argv[i]);
460         
461         if(context->argv) 
462                 free(context->argv);
463         
464         if(context->cleanup_func)
465                 context->cleanup_func(context->cleanup_arg);
466         
467         xbt_context_destroy(context);
468         
469         return;
470 }
471 /* @} */