Logo AND Algorithmique Numérique Distribuée

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