3 /* a fast and simple context switching library */
5 /* Copyright (c) 2004 Arnaud Legrand. */
6 /* Copyright (c) 2004, 2005 Martin Quinson. */
7 /* All rights reserved. */
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. */
13 #include "context_private.h"
15 #include "xbt/dynar.h"
16 #include "xbt/xbt_os_thread.h"
17 #include "xbt/ex_interface.h"
19 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ctx, xbt, "Context");
21 #define VOIRP(expr) DEBUG1(" {" #expr " = %p }", expr)
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;
32 static void __context_exit(xbt_context_t context, int value);
33 static void __xbt_context_yield(xbt_context_t context)
35 xbt_assert0(current_context, "You have to call context_init() first.");
38 ("--------- current_context (%p) is yielding to context(%p) ---------",
39 current_context, context);
41 #ifdef CONTEXT_THREADS
43 xbt_context_t self = current_context;
44 DEBUG2("[%p] **** Locking ctx %p ****", self, context);
45 xbt_os_mutex_lock(context->mutex);
46 DEBUG1("[%p] **** Updating current_context ****", self);
47 current_context = context;
48 DEBUG1("[%p] **** Releasing the prisonner ****", self);
49 xbt_os_cond_signal(context->cond);
50 DEBUG3("[%p] **** Going to jail on individual %p/%p ****", self,
51 context->cond, context->mutex);
52 xbt_os_cond_wait(context->cond, context->mutex);
53 DEBUG2("[%p] **** Unlocking individual %p ****", self, context->mutex);
54 xbt_os_mutex_unlock(context->mutex);
55 DEBUG1("[%p] **** Updating current_context ****", self);
56 current_context = self;
58 #else /* use SUSv2 contexts */
59 VOIRP(current_context);
61 VOIRP(current_context->save);
72 if (context->save == NULL) {
74 DEBUG1("[%p] **** Yielding to somebody else ****", current_context);
75 DEBUG2("Saving current_context value (%p) to context(%p)->save",
76 current_context, context);
77 context->save = current_context;
78 DEBUG1("current_context becomes context(%p) ", context);
79 current_context = context;
81 ("Current position memorized (context->save). Jumping to context (%p)",
83 return_value = swapcontext(&(context->save->uc), &(context->uc));
84 xbt_assert0((return_value == 0), "Context swapping failure");
85 DEBUG1("I am (%p). Coming back\n", context);
87 xbt_context_t old_context = context->save;
88 DEBUG1("[%p] **** Back ! ****", context);
89 DEBUG2("Setting current_context (%p) to context(%p)->save",
90 current_context, context);
91 current_context = context->save;
92 DEBUG1("Setting context(%p)->save to NULL", context);
94 DEBUG2("Current position memorized (%p). Jumping to context (%p)",
95 context, old_context);
96 return_value = swapcontext(&(context->uc), &(old_context->uc));
97 xbt_assert0((return_value == 0), "Context swapping failure");
98 DEBUG1("I am (%p). Coming back\n", context);
103 if (current_context->iwannadie)
104 __context_exit(current_context, 1);
109 static void xbt_context_free(xbt_context_t context)
113 DEBUG1("Freeing %p", context);
114 #ifdef CONTEXT_THREADS
115 /*DEBUG1("\t joining %p",(void *)context->thread->t); */
116 DEBUG1("\t joining %p", (void *) context->thread);
118 xbt_os_thread_join(context->thread, NULL);
120 DEBUG1("\t mutex_destroy %p", (void *) context->mutex);
121 xbt_os_mutex_destroy(context->mutex);
122 DEBUG1("\t cond_destroy %p", (void *) context->cond);
123 xbt_os_cond_destroy(context->cond);
125 context->thread = NULL;
126 context->mutex = NULL;
127 context->cond = NULL;
130 if (context->exception)
131 free(context->exception);
137 static void __context_exit(xbt_context_t context, int value)
141 DEBUG1("--------- %p is exiting ---------", context);
143 DEBUG0("Calling cleanup functions");
144 if (context->cleanup_func) {
145 DEBUG0("Calling cleanup function");
146 context->cleanup_func(context->cleanup_arg);
149 DEBUG0("Freeing arguments");
150 for (i = 0; i < context->argc; i++)
151 if (context->argv[i])
152 free(context->argv[i]);
157 DEBUG0("Putting context in the to_destroy set");
158 xbt_swag_remove(context, context_living);
159 xbt_swag_insert(context, context_to_destroy);
160 DEBUG0("Context put in the to_destroy set");
164 #ifdef CONTEXT_THREADS
165 DEBUG2("[%p] **** Locking %p ****", context, context->mutex);
166 xbt_os_mutex_lock(context->mutex);
167 /* DEBUG1("[%p] **** Updating current_context ****"); */
168 /* current_context = context; */
169 DEBUG1("[%p] **** Releasing the prisonner ****", context);
170 xbt_os_cond_signal(context->cond);
171 DEBUG2("[%p] **** Unlocking individual %p ****", context,
173 xbt_os_mutex_unlock(context->mutex);
174 DEBUG1("[%p] **** Exiting ****", context);
175 xbt_os_thread_exit(NULL); // We should provide return value in case other wants it
177 __xbt_context_yield(context);
179 xbt_assert0(0, "You can't be here!");
182 static void *__context_wrapper(void *c)
184 xbt_context_t context = current_context;
186 #ifdef CONTEXT_THREADS
187 context = (xbt_context_t) c;
188 context->thread = xbt_os_thread_self();
190 DEBUG3("**[ctx:%p;self:%p]** Lock creation_mutex %p ****", context,
191 (void *) xbt_os_thread_self(), creation_mutex);
192 xbt_os_mutex_lock(creation_mutex);
193 xbt_os_mutex_lock(context->mutex);
196 ("**[ctx:%p;self:%p]** Releasing the creator (creation_cond %p,%p) ****",
197 context, (void *) xbt_os_thread_self(), creation_cond,
199 xbt_os_cond_signal(creation_cond);
200 xbt_os_mutex_unlock(creation_mutex);
202 DEBUG4("**[ctx:%p;self:%p]** Going to Jail on lock %p and cond %p ****",
203 context, (void *) xbt_os_thread_self(), context->mutex,
205 xbt_os_cond_wait(context->cond, context->mutex);
207 DEBUG3("**[ctx:%p;self:%p]** Unlocking individual %p ****",
208 context, (void *) xbt_os_thread_self(), context->mutex);
209 xbt_os_mutex_unlock(context->mutex);
213 if (context->startup_func)
214 context->startup_func(context->startup_arg);
216 DEBUG0("Calling the main function");
218 __context_exit(context, (context->code) (context->argc, context->argv));
222 /* callback: context fetching */
223 static ex_ctx_t *__context_ex_ctx(void)
225 return current_context->exception;
228 /* callback: termination */
229 static void __context_ex_terminate(xbt_ex_t * e)
234 /* FIXME: there should be a configuration variable to choose this
235 if(current_context!=init_context)
236 __context_exit(current_context, e->value);
243 * \ingroup XBT_context
246 /** Context module initialization
248 * \warning It has to be called before using any other function of this module.
250 void xbt_context_init(void)
252 if (!current_context) {
253 current_context = init_context = xbt_new0(s_xbt_context_t, 1);
254 DEBUG1("Init Context (%p)", init_context);
256 init_context->iwannadie = 0; /* useless but makes valgrind happy */
257 init_context->exception = xbt_new(ex_ctx_t, 1);
258 XBT_CTX_INITIALIZE(init_context->exception);
259 __xbt_ex_ctx = __context_ex_ctx;
260 __xbt_ex_terminate = __context_ex_terminate;
262 xbt_swag_new(xbt_swag_offset(*current_context, hookup));
264 xbt_swag_new(xbt_swag_offset(*current_context, hookup));
265 xbt_swag_insert(init_context, context_living);
266 #ifdef CONTEXT_THREADS
267 creation_mutex = xbt_os_mutex_init();
268 creation_cond = xbt_os_cond_init();
273 /** Garbage collection
275 * Should be called some time to time to free the memory allocated for contexts
276 * that have finished executing their main functions.
278 void xbt_context_empty_trash(void)
280 xbt_context_t context = NULL;
281 DEBUG1("Emptying trashbin (%d contexts to free)",
282 xbt_swag_size(context_to_destroy));
283 while ((context = xbt_swag_extract(context_to_destroy)))
284 xbt_context_free(context);
288 * \param context the context to start
290 * Calling this function prepares \a context to be run. It will
291 however run effectively only when calling #xbt_context_schedule
293 void xbt_context_start(xbt_context_t context)
295 #ifdef CONTEXT_THREADS
296 /* Launch the thread */
297 DEBUG3("**[ctx:%p;self:%p]** Locking creation_mutex %p ****", context,
298 xbt_os_thread_self(), creation_mutex);
299 xbt_os_mutex_lock(creation_mutex);
301 DEBUG2("**[ctx:%p;self:%p]** Thread create ****", context,
302 xbt_os_thread_self());
303 context->thread = xbt_os_thread_create(__context_wrapper, context);
304 DEBUG3("**[ctx:%p;self:%p]** Thread created : %p ****", context,
305 xbt_os_thread_self(), context->thread);
308 ("**[ctx:%p;self:%p]** Going to jail on creation_cond/mutex (%p,%p) ****",
309 context, xbt_os_thread_self(), creation_cond, creation_mutex);
310 xbt_os_cond_wait(creation_cond, creation_mutex);
311 DEBUG3("**[ctx:%p;self:%p]** Unlocking creation %p ****", context,
312 xbt_os_thread_self(), creation_mutex);
313 xbt_os_mutex_unlock(creation_mutex);
315 makecontext(&(context->uc), (void (*)(void)) __context_wrapper, 1,
322 * \param code a main function
323 * \param startup_func a function to call when running the context for
324 * the first time and just before the main function \a code
325 * \param startup_arg the argument passed to the previous function (\a startup_func)
326 * \param cleanup_func a function to call when running the context, just after
327 the termination of the main function \a code
328 * \param cleanup_arg the argument passed to the previous function (\a cleanup_func)
329 * \param argc first argument of function \a code
330 * \param argv seconde argument of function \a code
332 xbt_context_t xbt_context_new(xbt_main_func_t code,
333 void_f_pvoid_t startup_func,
335 void_f_pvoid_t cleanup_func,
336 void *cleanup_arg, int argc, char *argv[])
338 xbt_context_t res = NULL;
340 res = xbt_new0(s_xbt_context_t, 1);
343 #ifdef CONTEXT_THREADS
344 res->mutex = xbt_os_mutex_init();
345 res->cond = xbt_os_cond_init();
348 xbt_assert2(getcontext(&(res->uc)) == 0,
349 "Error in context saving: %d (%s)", errno, strerror(errno));
350 res->uc.uc_link = NULL;
351 /* res->uc.uc_link = &(current_context->uc); */
352 /* WARNING : when this context is over, the current_context (i.e. the
353 father), is awaken... Theorically, the wrapper should prevent using
355 res->uc.uc_stack.ss_sp = pth_skaddr_makecontext(res->stack, STACK_SIZE);
356 res->uc.uc_stack.ss_size =
357 pth_sksize_makecontext(res->stack, STACK_SIZE);
358 #endif /* CONTEXT_THREADS or not */
360 res->iwannadie = 0; /* useless but makes valgrind happy */
364 res->startup_func = startup_func;
365 res->startup_arg = startup_arg;
366 res->cleanup_func = cleanup_func;
367 res->cleanup_arg = cleanup_arg;
368 res->exception = xbt_new(ex_ctx_t, 1);
369 XBT_CTX_INITIALIZE(res->exception);
371 xbt_swag_insert(res, context_living);
377 * Calling this function makes the current context yield. The context
378 * that scheduled it returns from xbt_context_schedule as if nothing
381 void xbt_context_yield(void)
383 __xbt_context_yield(current_context);
387 * \param context the winner
389 * Calling this function blocks the current context and schedule \a context.
390 * When \a context will call xbt_context_yield, it will return
391 * to this function as if nothing had happened.
393 void xbt_context_schedule(xbt_context_t context)
395 DEBUG1("Scheduling %p", context);
396 xbt_assert0((current_context == init_context),
397 "You are not supposed to run this function here!");
398 __xbt_context_yield(context);
402 * This function kill all existing context and free all the memory
403 * that has been allocated in this module.
405 void xbt_context_exit(void)
407 xbt_context_t context = NULL;
409 xbt_context_empty_trash();
410 while ((context = xbt_swag_extract(context_living))) {
411 if (context != init_context) {
412 xbt_context_kill(context);
415 free(init_context->exception);
417 init_context = current_context = NULL;
419 xbt_context_empty_trash();
420 xbt_swag_free(context_to_destroy);
421 xbt_swag_free(context_living);
423 #ifdef CONTEXT_THREADS
424 xbt_os_mutex_destroy(creation_mutex);
425 xbt_os_cond_destroy(creation_cond);
430 * \param context poor victim
432 * This function simply kills \a context... scarry isn't it ?
434 void xbt_context_kill(xbt_context_t context)
436 DEBUG1("Killing %p", context);
438 context->iwannadie = 1;
439 DEBUG1("Scheduling %p", context);
440 __xbt_context_yield(context);
441 DEBUG1("End of Scheduling %p", context);
448 /* Stub of the stuff to interact with JAVA threads; not used in native lib */
449 void xbt_context_set_jprocess(xbt_context_t context, void *jp)
452 void *xbt_context_get_jprocess(xbt_context_t context)
457 void xbt_context_set_jmutex(xbt_context_t context, void *jm)
460 void *xbt_context_get_jmutex(xbt_context_t context)
465 void xbt_context_set_jcond(xbt_context_t context, void *jc)
468 void *xbt_context_get_jcond(xbt_context_t context)
473 void xbt_context_set_jenv(xbt_context_t context, void *je)
476 void *xbt_context_get_jenv(xbt_context_t context)