Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
9d313f5c830ea0baf8e3a161cb87ef2f25194e05
[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 #include "xbt/ex_interface.h"
18
19 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ctx, xbt, "Context");
20
21 #define VOIRP(expr) DEBUG1("  {" #expr " = %p }", expr)
22
23 static xbt_context_t current_context = NULL;
24 static xbt_context_t init_context = NULL;
25 static xbt_swag_t context_to_destroy = NULL;
26 static xbt_swag_t context_living = NULL;
27 #ifdef CONTEXT_THREADS
28 static xbt_os_mutex_t creation_mutex;
29 static xbt_os_cond_t creation_cond;
30 static xbt_os_mutex_t master_mutex;
31 static xbt_os_cond_t master_cond;
32
33 #ifdef WIN32
34 static DWORD __current_thread_id = 0;
35 #else
36 static pthread_t __current_thread_id;
37 #endif
38
39 static int
40 is_main_thread(void);
41
42 static int
43 is_main_thread(void)
44 {
45         #ifdef WIN32
46         return (GetCurrentThreadId() == __current_thread_id) ? 1 : 0;
47         #else
48         return (pthread_self() == __current_thread_id) ? 1 : 0;
49         #endif
50 }
51
52 #endif
53
54
55 /********************/
56 /* Module init/exit */
57 /********************/
58 #ifndef CONTEXT_THREADS
59 /* callback: context fetching (used only with ucontext, os_thread deal with it
60                                for us otherwise) */
61 static ex_ctx_t *_context_ex_ctx(void) {
62   return current_context->exception;
63 }
64
65 /* callback: termination */
66 static void _context_ex_terminate(xbt_ex_t * e) {
67   xbt_ex_display(e);
68
69   abort();
70   /* FIXME: there should be a configuration variable to 
71      choose to kill everyone or only this one */
72 }
73 #endif 
74
75 /** \name Functions 
76  *  \ingroup XBT_context
77  */
78 /* @{ */
79 /** Context module initialization
80  *
81  * \warning It has to be called before using any other function of this module.
82  */
83 void 
84 xbt_context_init(void)
85 {
86         if (!current_context) 
87         {
88                 current_context = init_context = xbt_new0(s_xbt_context_t, 1);
89                 DEBUG1("Init Context (%p)", init_context);
90                 init_context->iwannadie = 0; /* useless but makes valgrind happy */
91                 context_to_destroy = xbt_swag_new(xbt_swag_offset(*current_context, hookup));
92                 context_living =  xbt_swag_new(xbt_swag_offset(*current_context, hookup));
93                 xbt_swag_insert(init_context, context_living);
94                 
95                 #ifdef CONTEXT_THREADS
96                   /* only used during the creation of the processes */
97                   creation_mutex = xbt_os_mutex_init();
98                   creation_cond = xbt_os_cond_init();
99
100                   /* used to schedule/unschedule the processes */
101                   master_mutex = xbt_os_mutex_init();
102                   master_cond = xbt_os_cond_init();
103
104                   #ifdef WIN32
105                     __current_thread_id = GetCurrentThreadId();
106                   #else
107                     __current_thread_id = pthread_self();
108                   #endif
109                 #else
110                   init_context->exception = xbt_new(ex_ctx_t, 1);
111                   XBT_CTX_INITIALIZE(init_context->exception);
112                   __xbt_ex_ctx = _context_ex_ctx;
113                   __xbt_ex_terminate = _context_ex_terminate;
114                 #endif
115         }
116 }
117
118 /** 
119  * This function kill all existing context and free all the memory
120  * that has been allocated in this module.
121  */
122 void 
123 xbt_context_exit(void)
124 {
125         xbt_context_t context = NULL;
126
127         xbt_context_empty_trash();
128         
129         while ((context = xbt_swag_extract(context_living))) 
130         {
131                 if (context != init_context) 
132                 {
133                         xbt_context_kill(context);
134                 }
135         }
136         
137         #ifdef CONTEXT_THREADS
138           xbt_os_mutex_destroy(creation_mutex);
139           xbt_os_cond_destroy(creation_cond);
140           xbt_os_mutex_destroy(master_mutex);
141           xbt_os_cond_destroy(master_cond);
142         #else   
143           free(init_context->exception);
144         #endif
145         
146         free(init_context);
147         init_context = current_context = NULL;
148
149         xbt_context_empty_trash();
150         xbt_swag_free(context_to_destroy);
151         xbt_swag_free(context_living);
152
153 }
154
155 /*******************************/
156 /* Object creation/destruction */
157 /*******************************/
158 /** 
159  * \param code a main function
160  * \param startup_func a function to call when running the context for
161  *      the first time and just before the main function \a code
162  * \param startup_arg the argument passed to the previous function (\a startup_func)
163  * \param cleanup_func a function to call when running the context, just after 
164         the termination of the main function \a code
165  * \param cleanup_arg the argument passed to the previous function (\a cleanup_func)
166  * \param argc first argument of function \a code
167  * \param argv seconde argument of function \a code
168  */
169 xbt_context_t 
170 xbt_context_new(        const char *name,
171                                         xbt_main_func_t code,
172                                         void_f_pvoid_t startup_func,
173                                         void *startup_arg,
174                                         void_f_pvoid_t cleanup_func,
175                                         void *cleanup_arg, 
176                                         int argc, 
177                                         char *argv[]
178 )
179 {
180         xbt_context_t res = NULL;
181
182         res = xbt_new0(s_xbt_context_t, 1);
183
184         res->code = code;
185         res->name = xbt_strdup(name);
186
187         #ifdef CONTEXT_THREADS
188           res->mutex = xbt_os_mutex_init();
189           res->cond = xbt_os_cond_init();
190         #else
191
192           xbt_assert2(getcontext(&(res->uc)) == 0,
193           "Error in context saving: %d (%s)", errno, strerror(errno));
194           res->uc.uc_link = NULL;
195           /*   res->uc.uc_link = &(current_context->uc); */
196           /* WARNING : when this context is over, the current_context (i.e. the 
197           father), is awaken... Theorically, the wrapper should prevent using 
198           this feature. */
199           res->uc.uc_stack.ss_sp = pth_skaddr_makecontext(res->stack, STACK_SIZE);
200           res->uc.uc_stack.ss_size =
201           pth_sksize_makecontext(res->stack, STACK_SIZE);
202
203           res->exception = xbt_new(ex_ctx_t, 1);
204           XBT_CTX_INITIALIZE(res->exception);
205         #endif                          /* CONTEXT_THREADS or not */
206
207         res->iwannadie = 0; /* useless but makes valgrind happy */
208
209         res->argc = argc;
210         res->argv = argv;
211         res->startup_func = startup_func;
212         res->startup_arg = startup_arg;
213         res->cleanup_func = cleanup_func;
214         res->cleanup_arg = cleanup_arg;
215
216         xbt_swag_insert(res, context_living);
217
218         return res;
219 }
220
221 /* Scenario for the end of a context:
222  * 
223  * CASE 1: death after end of function
224  *   __context_wrapper, called by os thread, calls xbt_context_stop after user code stops
225  *   xbt_context_stop calls user cleanup_func if any (in context settings),
226  *                    add current to trashbin
227  *                    yields back to maestro (destroy os thread on need)
228  *   From time to time, maestro calls xbt_context_empty_trash, 
229  *       which maps xbt_context_free on the content
230  *   xbt_context_free frees some more memory, 
231  *                    joins os thread
232  * 
233  * CASE 2: brutal death
234  *   xbt_context_kill (from any context)
235  *                    set context->wannadie to 1
236  *                    yields to the context
237  *   the context is awaken in the middle of __yield. 
238  *   At the end of it, it checks that wannadie == 1, and call xbt_context_stop
239  *   (same than first case afterward)
240  */
241
242 /* Argument must be stopped first -- runs in maestro context */
243 static void 
244 xbt_context_free(xbt_context_t context) 
245 {
246         int i;
247
248         if (!context)
249                 return;
250
251         DEBUG1("Freeing %p", context);
252         free(context->name);
253
254         DEBUG0("Freeing arguments");
255
256         for (i = 0; i < context->argc; i++)
257                 if (context->argv[i])
258                         free(context->argv[i]);
259         
260         if (context->argv)
261                 free(context->argv);
262
263         #ifdef CONTEXT_THREADS
264           DEBUG1("\t joining %p", (void *) context->thread);
265
266           xbt_os_thread_join(context->thread, NULL);
267
268           xbt_os_mutex_destroy(context->mutex);
269           xbt_os_cond_destroy(context->cond);
270
271           context->thread = NULL;
272           context->mutex = NULL;
273           context->cond = NULL;
274         #else
275           if (context->exception)
276           free(context->exception);
277         #endif
278
279         free(context);
280         return;
281 }
282
283 /************************/
284 /* Start/stop a context */
285 /************************/
286 static void xbt_context_stop(int retvalue);
287 static void __xbt_context_yield(xbt_context_t context);
288
289 static void *
290 __context_wrapper(void *c)
291 {
292         xbt_context_t context = current_context;
293
294         #ifdef CONTEXT_THREADS
295           context = (xbt_context_t) c;
296           context->thread = xbt_os_thread_self();
297
298           DEBUG3("**[ctx:%p;self:%p]** Lock creation_mutex %p ****", context,(void *) xbt_os_thread_self(), creation_mutex);
299           xbt_os_mutex_lock(creation_mutex);
300           xbt_os_mutex_lock(context->mutex);
301
302           DEBUG4("**[ctx:%p;self:%p]** Releasing the creator (creation_cond %p,%p) ****",context, (void *) xbt_os_thread_self(), creation_cond,creation_mutex);
303           xbt_os_cond_signal(creation_cond);
304           xbt_os_mutex_unlock(creation_mutex);
305
306           DEBUG4("**[ctx:%p;self:%p]** Going to Jail on lock %p and cond %p ****",context, (void *) xbt_os_thread_self(), context->mutex,context->cond);
307           xbt_os_cond_wait(context->cond, context->mutex);
308
309           DEBUG3("**[ctx:%p;self:%p]** Unlocking individual %p ****",context, (void *) xbt_os_thread_self(), context->mutex);
310           xbt_os_mutex_unlock(context->mutex);
311
312         #endif
313
314         if (context->startup_func)
315                 context->startup_func(context->startup_arg);
316
317         DEBUG0("Calling the main function");
318
319         xbt_context_stop((context->code) (context->argc, context->argv));
320         return NULL;
321 }
322 /** 
323  * \param context the context to start
324  * 
325  * Calling this function prepares \a context to be run. It will 
326    however run effectively only when calling #xbt_context_schedule
327  */
328 void 
329 xbt_context_start(xbt_context_t context)
330 {
331         #ifdef CONTEXT_THREADS
332         /* Launch the thread */
333
334           DEBUG3("**[ctx:%p;self:%p]** Locking creation_mutex %p ****", context,
335           xbt_os_thread_self(), creation_mutex);
336           xbt_os_mutex_lock(creation_mutex);
337
338           DEBUG2("**[ctx:%p;self:%p]** Thread create ****", context,xbt_os_thread_self());
339           context->thread = xbt_os_thread_create(context->name,__context_wrapper, context);
340           DEBUG3("**[ctx:%p;self:%p]** Thread created : %p ****", context,xbt_os_thread_self(), context->thread);
341
342           DEBUG4("**[ctx:%p;self:%p]** Going to jail on creation_cond/mutex (%p,%p) ****",context, xbt_os_thread_self(), creation_cond, creation_mutex);
343           xbt_os_cond_wait(creation_cond, creation_mutex);
344           DEBUG3("**[ctx:%p;self:%p]** Unlocking creation %p ****", context,xbt_os_thread_self(), creation_mutex);
345           xbt_os_mutex_unlock(creation_mutex);
346         #else
347           makecontext(&(context->uc), (void (*)(void)) __context_wrapper, 1, context);
348         #endif
349 }
350
351 /* Stops current context: calls user's cleanup function, kills os thread, and yields back to maestro */
352 static void 
353 xbt_context_stop(int retvalue) 
354 {
355         DEBUG1("--------- %p is exiting ---------", current_context);
356
357         if (current_context->cleanup_func) 
358         {
359                 DEBUG0("Calling cleanup function");
360                 current_context->cleanup_func(current_context->cleanup_arg);
361         }
362
363         DEBUG0("Putting context in the to_destroy set");
364         xbt_swag_remove(current_context, context_living);
365         xbt_swag_insert(current_context, context_to_destroy);
366
367         DEBUG0("Yielding");
368
369         #ifdef CONTEXT_THREADS
370           /* a java thread has called this function
371                          * - update the current context
372                          * - signal the condition of the main thread
373                          * - wait on its condition
374                          * - restore thr current contex
375                          */
376
377                         xbt_os_mutex_lock(master_mutex);
378                         xbt_os_cond_signal(master_cond);
379                         xbt_os_mutex_unlock(master_mutex);
380                         xbt_os_thread_exit(NULL);       /* We should provide return value in case other wants it */
381                 
382                         
383         #else
384           __xbt_context_yield(current_context);
385         #endif
386
387         xbt_assert0(0, "You can't be here!");
388 }
389
390
391
392 /** Garbage collection
393  *
394  * Should be called some time to time to free the memory allocated for contexts
395  * that have finished executing their main functions.
396  */
397 void 
398 xbt_context_empty_trash(void)
399 {
400         xbt_context_t context = NULL;
401         DEBUG1("Emptying trashbin (%d contexts to free)",xbt_swag_size(context_to_destroy));
402         
403         while ((context = xbt_swag_extract(context_to_destroy)))
404                 xbt_context_free(context);
405 }
406
407 /*********************/
408 /* context switching */
409 /*********************/
410
411 static void 
412 __xbt_context_yield(xbt_context_t context)
413 {
414         #ifdef CONTEXT_THREADS
415           xbt_context_t self;
416         #endif  
417         
418           xbt_assert0(current_context, "You have to call context_init() first.");       
419         xbt_assert0(context,"Invalid argument");
420
421         if (current_context == context) 
422         {
423                 DEBUG1("--------- current_context (%p) is yielding back to maestro ---------",context);
424         } 
425         else 
426         {
427                 DEBUG2("--------- current_context (%p) is yielding to context(%p) ---------",current_context, context);
428         }
429
430         #ifdef CONTEXT_THREADS
431                 
432           self = current_context;
433         
434           if(is_main_thread())
435           {
436                         /* the main thread has called this function
437                          * - update the current context
438                          * - signal the condition of the process to run
439                          * - wait on its condition
440                          * - restore thr current contex
441                          */
442
443                         xbt_os_mutex_lock(master_mutex);
444                         xbt_os_mutex_lock(context->mutex);
445                         
446                         /* update the current context */
447                         current_context = context;
448                         xbt_os_cond_signal(context->cond);
449                         xbt_os_mutex_unlock(context->mutex);
450                         
451                         xbt_os_cond_wait(master_cond, master_mutex);
452                         xbt_os_mutex_unlock(master_mutex);
453                         /* retore the current context */
454                         current_context = self;
455                         
456                 }
457                 else
458                 {
459                         /* a java thread has called this function
460                          * - update the current context
461                          * - signal the condition of the main thread
462                          * - wait on its condition
463                          * - restore thr current contex
464                          */
465
466                         xbt_os_mutex_lock(master_mutex);
467                         xbt_os_mutex_lock(context->mutex);
468                         /* update the current context */
469                         current_context = context;
470                         xbt_os_cond_signal(master_cond);
471                         xbt_os_mutex_unlock(master_mutex);
472                         xbt_os_cond_wait(context->cond, context->mutex);
473                         xbt_os_mutex_unlock(context->mutex);
474                         /* retore the current context */
475                         current_context = self;
476                 }
477
478         #else                           /* use SUSv2 contexts */
479           VOIRP(current_context);
480           VOIRP(current_context->save);
481
482           VOIRP(context);
483           VOIRP(context->save);
484
485           int return_value = 0;
486
487         if (context->save == NULL) 
488         {
489           xbt_assert(context == current_context);
490           DEBUG1("[%p] **** Yielding to somebody else ****", current_context);
491           DEBUG2("Saving current_context value (%p) to context(%p)->save",current_context, context);
492           context->save = current_context;
493         DEBUG1("current_context becomes  context(%p) ", context);
494         current_context = context;
495         DEBUG1
496         ("Current position memorized (context->save). Jumping to context (%p)",
497         context);
498         return_value = swapcontext(&(context->save->uc), &(context->uc));
499         xbt_assert0((return_value == 0), "Context swapping failure");
500         DEBUG1("I am (%p). Coming back\n", context);
501         } else {
502         xbt_context_t old_context = context->save;
503         DEBUG1("[%p] **** Back ! ****", context);
504         DEBUG2("Setting current_context (%p) to context(%p)->save",
505         current_context, context);
506         current_context = context->save;
507         DEBUG1("Setting context(%p)->save to NULL", context);
508         context->save = NULL;
509         DEBUG2("Current position memorized (%p). Jumping to context (%p)",
510         context, old_context);
511         return_value = swapcontext(&(context->uc), &(old_context->uc));
512         xbt_assert0((return_value == 0), "Context swapping failure");
513         DEBUG1("I am (%p). Coming back\n", context);
514         }
515         #endif
516         
517         if (current_context->iwannadie)
518                 xbt_context_stop(1);
519
520         return;
521 }
522
523
524
525
526
527 /** 
528  * Calling this function makes the current context yield. The context
529  * that scheduled it returns from xbt_context_schedule as if nothing
530  * had happened.
531  * 
532  * Only the processes can call this function, giving back the control
533  * to the maestro
534  */
535 void 
536 xbt_context_yield(void)
537 {
538   __xbt_context_yield(current_context);
539 }
540
541 /** 
542  * \param context the winner
543  *
544  * Calling this function blocks the current context and schedule \a context.  
545  * When \a context will call xbt_context_yield, it will return
546  * to this function as if nothing had happened.
547  * 
548  * Only the maestro can call this function to run a given process.
549  */
550 void 
551 xbt_context_schedule(xbt_context_t context)
552 {
553   DEBUG1("Scheduling %p", context);
554   xbt_assert0((current_context == init_context),"You are not supposed to run this function here!");
555   __xbt_context_yield(context);
556 }
557
558
559 /** 
560  * \param context poor victim
561  *
562  * This function simply kills \a context... scarry isn't it ?
563  */
564 void 
565 xbt_context_kill(xbt_context_t context)
566 {
567   DEBUG1("Killing %p", context);
568
569   context->iwannadie = 1;
570   
571   DEBUG1("Scheduling %p", context);
572   __xbt_context_yield(context);
573   DEBUG1("End of Scheduling %p", context);
574
575   return;
576 }
577
578 /* Java cruft I'm gonna kill in the next cleanup round */
579 void  xbt_context_set_jprocess(xbt_context_t context, void *jp){}
580 void* xbt_context_get_jprocess(xbt_context_t context){return NULL;}
581 void  xbt_context_set_jmutex(xbt_context_t context,void *jm){}
582 void* xbt_context_get_jmutex(xbt_context_t context){return NULL;}
583 void  xbt_context_set_jcond(xbt_context_t context,void *jc){}
584 void* xbt_context_get_jcond(xbt_context_t context){return NULL;}
585 void  xbt_context_set_jenv(xbt_context_t context,void* je){}
586 void* xbt_context_get_jenv(xbt_context_t context){return NULL;}
587
588 /* @} */