Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
4f6907be5ece7a27d269cde89606445749d5153e
[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_thread.h"
17
18 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ctx, xbt, "Context");
19
20 #define VOIRP(expr) DEBUG1("  {" #expr " = %p }", expr)
21
22 static xbt_context_t current_context = NULL;
23 static xbt_context_t init_context = NULL;
24 static xbt_swag_t context_to_destroy = NULL;
25 static xbt_swag_t context_living = NULL;
26
27 static void __xbt_context_yield(xbt_context_t context)
28 {
29         xbt_assert0(current_context,"You have to call context_init() first.");
30
31         DEBUG2("--------- current_context (%p) is yielding to context(%p) ---------",current_context,context);
32    
33         #ifdef CONTEXT_THREADS
34         if (context){
35                 xbt_context_t self = current_context;
36                 DEBUG0("**** Locking ****");
37                 xbt_mutex_lock(context->mutex);
38                 DEBUG0("**** Updating current_context ****");
39                 current_context = context;
40                 DEBUG0("**** Releasing the prisonner ****");
41                 xbt_thcond_signal(context->cond);
42                 DEBUG0("**** Going to jail ****");
43                 xbt_thcond_wait(context->cond, context->mutex);
44                 DEBUG0("**** Unlocking ****");
45                 xbt_mutex_unlock(context->mutex);
46                 DEBUG0("**** Updating current_context ****");
47                 current_context = self;
48         }
49         #else /* use SUSv2 contexts */
50         if(current_context)
51                 VOIRP(current_context->save);
52         
53         VOIRP(context);
54         
55         if(context) 
56                 VOIRP(context->save);
57         
58         if (context){
59         
60                 int return_value = 0;
61                 
62                 if(context->save==NULL){
63                 
64                         DEBUG0("**** Yielding to somebody else ****");
65                         DEBUG2("Saving current_context value (%p) to context(%p)->save",current_context,context);
66                         context->save = current_context ;
67                         DEBUG1("current_context becomes  context(%p) ",context);
68                         current_context = context ;
69                         DEBUG1("Current position memorized (context->save). Jumping to context (%p)",context);
70                         return_value = swapcontext (&(context->save->uc), &(context->uc));
71                         xbt_assert0((return_value==0),"Context swapping failure");
72                         DEBUG1("I am (%p). Coming back\n",context);
73                 } else {
74                         xbt_context_t old_context = context->save ;
75                         DEBUG0("**** Back ! ****");
76                         DEBUG2("Setting current_context (%p) to context(%p)->save",current_context,context);
77                         current_context = context->save ;
78                         DEBUG1("Setting context(%p)->save to NULL",context);
79                         context->save = NULL ;
80                         DEBUG2("Current position memorized (%p). Jumping to context (%p)",context,old_context);
81                         return_value = swapcontext (&(context->uc), &(old_context->uc));
82                         xbt_assert0((return_value==0),"Context swapping failure");
83                         DEBUG1("I am (%p). Coming back\n",context);
84                 
85                 }
86         }
87         #endif
88         return;
89 }
90
91 static void xbt_context_free(xbt_context_t context)
92 {
93         if (!context) return;
94         DEBUG1("Freeing %p",context);
95 #ifdef CONTEXT_THREADS
96         xbt_free(context->thread);
97         xbt_mutex_destroy(context->mutex);
98         xbt_thcond_destroy(context->cond);
99
100         context->thread = NULL;
101         context->mutex = NULL;
102         context->cond = NULL;
103 #endif
104         
105         if(context->exception) 
106                 free(context->exception);
107                 
108         free(context);
109         return;
110 }
111         
112 static void __context_exit(xbt_context_t context ,int value)
113 {
114         int i;
115         DEBUG0("Freeing arguments");
116         for(i=0;i<context->argc; i++) 
117                 if(context->argv[i]) 
118                         free(context->argv[i]);
119         
120         if(context->argv) 
121                 free(context->argv);
122         
123         if(context->cleanup_func){
124                 DEBUG0("Calling cleanup function");
125                 context->cleanup_func(context->cleanup_arg);
126         }
127         
128         DEBUG0("Putting context in the to_destroy set");
129         xbt_swag_remove(context, context_living);
130         xbt_swag_insert(context, context_to_destroy);
131         DEBUG0("Context put in the to_destroy set");
132         DEBUG0("Yielding");
133         
134         #ifdef CONTEXT_THREADS
135         DEBUG0("**** Locking ****");
136         xbt_mutex_lock(context->mutex);
137         DEBUG0("**** Updating current_context ****");
138         current_context = context;
139         DEBUG0("**** Releasing the prisonner ****");
140         xbt_thcond_signal(context->cond);
141         DEBUG0("**** Unlocking ****");
142         xbt_mutex_unlock(context->mutex);
143         DEBUG0("**** Exiting ****");
144         xbt_thread_exit(0);
145         #else
146         __xbt_context_yield(context);
147         #endif
148         xbt_assert0(0,"You can't be here!");
149 }
150
151 static void *
152 __context_wrapper(void* c) {
153         xbt_context_t context = (xbt_context_t)c;
154         
155         #ifdef CONTEXT_THREADS
156         context->thread = xbt_thread_self();
157         
158         DEBUG2("**[%p:%p]** Lock ****",context,(void*)xbt_thread_self());
159         xbt_mutex_lock(context->mutex);
160         
161         DEBUG2("**[%p:%p]** Releasing the prisonner ****",context,(void*)xbt_thread_self());
162         xbt_thcond_signal(context->cond);
163         
164         DEBUG2("**[%p:%p]** Going to Jail ****",context,(void*)xbt_thread_self());
165         xbt_thcond_wait(context->cond, context->mutex);
166         
167         DEBUG2("**[%p:%p]** Unlocking ****",context,(void*)xbt_thread_self());
168         xbt_mutex_unlock(context->mutex);
169         
170         #endif
171         
172         if(context->startup_func)
173                 context->startup_func(context->startup_arg);
174         
175         DEBUG0("Calling the main function");
176         
177         __context_exit(context, (context->code) (context->argc,context->argv));
178         return NULL;
179 }
180
181 /* callback: context fetching */
182 static ex_ctx_t *__context_ex_ctx(void)
183 {
184         return current_context->exception;
185 }
186
187 /* callback: termination */
188 static void __context_ex_terminate(xbt_ex_t *e) {
189   xbt_ex_display(e);
190
191   abort();
192    /* FIXME: there should be a configuration variable to choose this
193   if(current_context!=init_context) 
194     __context_exit(current_context, e->value);
195   else
196     abort();
197     */
198 }
199
200 /** \name Functions 
201  *  \ingroup XBT_context
202  */
203 /* @{ */
204 /** Context module initialization
205  *
206  * \warning It has to be called before using any other function of this module.
207  */
208 void xbt_context_init(void)
209 {
210         if(!current_context){
211                 current_context = init_context = xbt_new0(s_xbt_context_t,1);
212                 DEBUG1("Init Context (%p)",init_context);
213                 
214                 init_context->exception = xbt_new(ex_ctx_t,1);
215                 XBT_CTX_INITIALIZE(init_context->exception);
216                 __xbt_ex_ctx       = __context_ex_ctx;
217                 __xbt_ex_terminate = __context_ex_terminate;
218                 context_to_destroy = xbt_swag_new(xbt_swag_offset(*current_context,hookup));
219                 context_living = xbt_swag_new(xbt_swag_offset(*current_context,hookup));
220                 xbt_swag_insert(init_context, context_living);
221         }
222 }
223
224 /** Garbage collection
225  *
226  * Should be called some time to time to free the memory allocated for contexts
227  * that have finished executing their main functions.
228  */
229 void xbt_context_empty_trash(void)
230 {
231         xbt_context_t context=NULL;
232         DEBUG0("Emptying trashbin");
233         while((context=xbt_swag_extract(context_to_destroy)))
234                 xbt_context_free(context);
235 }
236
237 /** 
238  * \param context the context to start
239  * 
240  * Calling this function prepares \a context to be run. It will 
241    however run effectively only when calling #xbt_context_schedule
242  */
243 void xbt_context_start(xbt_context_t context) 
244 {
245         #ifdef CONTEXT_THREADS
246         /* Launch the thread */
247         DEBUG1("**[%p]** Locking ****",context);
248         xbt_mutex_lock(context->mutex);
249    
250         DEBUG1("**[%p]** Thread create ****",context);
251         context->thread = xbt_thread_create(__context_wrapper, context);   
252         DEBUG2("**[%p]** Thread created : %p ****",context,context->thread);
253    
254         DEBUG1("**[%p]** Going to jail ****",context);
255         xbt_thcond_wait(context->cond, context->mutex);
256         DEBUG1("**[%p]** Unlocking ****",context);
257         xbt_mutex_unlock(context->mutex);
258         #else
259         makecontext (&(context->uc), (void (*) (void)) __context_wrapper,1, context);
260         #endif
261         return;
262 }
263
264 /** 
265  * \param code a main function
266  * \param startup_func a function to call when running the context for
267  *      the first time and just before the main function \a code
268  * \param startup_arg the argument passed to the previous function (\a startup_func)
269  * \param cleanup_func a function to call when running the context, just after 
270         the termination of the main function \a code
271  * \param cleanup_arg the argument passed to the previous function (\a cleanup_func)
272  * \param argc first argument of function \a code
273  * \param argv seconde argument of function \a code
274  */
275 xbt_context_t xbt_context_new(xbt_context_function_t code, 
276                               void_f_pvoid_t startup_func, void *startup_arg,
277                               void_f_pvoid_t cleanup_func, void *cleanup_arg,
278                               int argc, char *argv[])
279 {
280         xbt_context_t res = NULL;
281         
282         res = xbt_new0(s_xbt_context_t,1);
283         
284         res->code = code;
285         #ifdef CONTEXT_THREADS
286         res->mutex = xbt_mutex_init();
287         res->cond = xbt_thcond_init();
288         #else 
289         /* FIXME: strerror is not thread safe */
290         xbt_assert2(getcontext(&(res->uc))==0,"Error in context saving: %d (%s)", errno, strerror(errno));
291         res->uc.uc_link = NULL;
292         /*   res->uc.uc_link = &(current_context->uc); */
293         /* WARNING : when this context is over, the current_context (i.e. the 
294         father), is awaken... Theorically, the wrapper should prevent using 
295         this feature. */
296         res->uc.uc_stack.ss_sp = pth_skaddr_makecontext(res->stack,STACK_SIZE);
297         res->uc.uc_stack.ss_size = pth_sksize_makecontext(res->stack,STACK_SIZE);
298         #endif /* CONTEXT_THREADS or not */
299         
300         res->argc = argc;
301         res->argv = argv;
302         res->startup_func = startup_func;
303         res->startup_arg = startup_arg;
304         res->cleanup_func = cleanup_func;
305         res->cleanup_arg = cleanup_arg;
306         res->exception = xbt_new(ex_ctx_t,1);
307         XBT_CTX_INITIALIZE(res->exception);
308         
309         xbt_swag_insert(res, context_living);
310         
311         return res;
312 }
313
314 /** 
315  * Calling this function makes the current context yield. The context
316  * that scheduled it returns from xbt_context_schedule as if nothing
317  * had happened.
318  */
319 void xbt_context_yield(void)
320 {
321         __xbt_context_yield(current_context);
322 }
323
324 /** 
325  * \param context the winner
326  *
327  * Calling this function blocks the current context and schedule \a context.  
328  * When \a context will call xbt_context_yield, it will return
329  * to this function as if nothing had happened.
330  */
331 void xbt_context_schedule(xbt_context_t context)
332 {
333         DEBUG1("Scheduling %p",context);
334         xbt_assert0((current_context==init_context),"You are not supposed to run this function here!");
335         __xbt_context_yield(context);
336 }
337
338 /** 
339  * This function kill all existing context and free all the memory
340  * that has been allocated in this module.
341  */
342 void xbt_context_exit(void) {
343         xbt_context_t context=NULL;
344
345         xbt_context_empty_trash();
346         xbt_swag_free(context_to_destroy);
347         
348         while((context=xbt_swag_extract(context_living))) {
349           if(context!=init_context) xbt_context_kill(context);
350         }
351         xbt_context_kill(init_context);
352         xbt_swag_free(context_living);
353         
354         init_context = current_context = NULL ;
355 }
356
357 /** 
358  * \param context poor victim
359  *
360  * This function simply kills \a context... scarry isn't it ?
361  */
362 void xbt_context_kill(xbt_context_t context)
363 {
364         int i ;
365         
366         DEBUG1("Killing %p", context);
367    
368         xbt_swag_remove(context, context_living);  
369         
370         if(context->cleanup_func) {
371           DEBUG1("Calling cleanup function %p", context->cleanup_func);
372           context->cleanup_func(context->cleanup_arg);
373         }
374
375         DEBUG0("Freeing arguments");
376         for(i=0;i<context->argc; i++) 
377                 if(context->argv[i]) 
378                         free(context->argv[i]);
379         
380         if(context->argv) 
381                 free(context->argv);
382         
383         xbt_context_free(context);
384         
385         return;
386 }
387 /* @} */