+/* Scenario for the end of a context:
+ *
+ * CASE 1: death after end of function
+ * __context_wrapper, called by os thread, calls xbt_context_stop after user code stops
+ * xbt_context_stop calls user cleanup_func if any (in context settings),
+ * add current to trashbin
+ * yields back to maestro (destroy os thread on need)
+ * From time to time, maestro calls xbt_context_empty_trash,
+ * which maps xbt_context_free on the content
+ * xbt_context_free frees some more memory,
+ * joins os thread
+ *
+ * CASE 2: brutal death
+ * xbt_context_kill (from any context)
+ * set context->wannadie to 1
+ * yields to the context
+ * the context is awaken in the middle of __yield.
+ * At the end of it, it checks that wannadie == 1, and call xbt_context_stop
+ * (same than first case afterward)
+ */
+
+
+/* Argument must be stopped first -- runs in maestro context */
+static void xbt_context_free(xbt_context_t context)
+{
+ int i;
+
+ if (!context)
+ return;
+
+ DEBUG1("Freeing %p", context);
+ free(context->name);
+
+ DEBUG0("Freeing arguments");
+
+ for (i = 0; i < context->argc; i++)
+ if (context->argv[i])
+ free(context->argv[i]);
+
+ if (context->argv)
+ free(context->argv);
+
+#ifdef CONTEXT_THREADS
+ DEBUG1("\t joining %p", (void *) context->thread);
+
+ xbt_os_thread_join(context->thread, NULL);
+
+ /* destroy the semaphore used to schedule/unshedule the process */
+ xbt_os_sem_destroy(context->begin);
+ xbt_os_sem_destroy(context->end);
+
+ context->thread = NULL;
+ context->begin = NULL;
+ context->end = NULL;
+#else
+ if (context->exception)
+ free(context->exception);
+#endif
+
+ free(context);
+}
+
+/************************/
+/* Start/stop a context */
+/************************/
+static void xbt_context_stop(int retvalue);
+static void __xbt_context_yield(xbt_context_t context);
+
+static void *__context_wrapper(void *c)
+{
+ xbt_context_t context = current_context;
+
+#ifdef CONTEXT_THREADS
+ context = (xbt_context_t) c;
+ /*context->thread = xbt_os_thread_self();*/
+
+ /* signal its starting to the maestro and wait to start its job*/
+ yield(context);
+
+#endif
+
+ if (context->startup_func)
+ context->startup_func(context->startup_arg);
+
+ DEBUG0("Calling the main function");
+
+ xbt_context_stop((context->code) (context->argc, context->argv));
+ return NULL;
+}
+/**
+ * \param context the context to start
+ *
+ * Calling this function prepares \a context to be run. It will
+ however run effectively only when calling #xbt_context_schedule
+ */
+void xbt_context_start(xbt_context_t context)
+{
+#ifdef CONTEXT_THREADS
+ /* create the process and start it */
+ context->thread = xbt_os_thread_create(context->name,__context_wrapper, context);
+
+ /* wait the starting of the newly created process */
+ xbt_os_sem_acquire(context->end);
+#else
+ makecontext(&(context->uc), (void (*)(void)) __context_wrapper, 1, context);
+#endif
+}
+
+/* Stops current context: calls user's cleanup function, kills os thread, and yields back to maestro */
+static void xbt_context_stop(int retvalue)
+{
+ DEBUG1("--------- %p is exiting ---------", current_context);
+
+ if (current_context->cleanup_func) {
+ DEBUG0("Calling cleanup function");
+ current_context->cleanup_func(current_context->cleanup_arg);
+ }
+
+ DEBUG0("Putting context in the to_destroy set");
+ xbt_swag_remove(current_context, context_living);
+ xbt_swag_insert(current_context, context_to_destroy);
+
+ DEBUG0("Yielding");
+
+#ifdef CONTEXT_THREADS
+ /* signal to the maestro that it has finished */
+ xbt_os_sem_release(current_context->end);
+ /* exit*/
+ xbt_os_thread_exit(NULL); /* We should provide return value in case other wants it */
+#else
+ __xbt_context_yield(current_context);
+#endif
+ THROW_IMPOSSIBLE;
+}
+
+
+
+/** Garbage collection
+ *
+ * Should be called some time to time to free the memory allocated for contexts
+ * that have finished executing their main functions.
+ */
+void xbt_context_empty_trash(void)
+{
+ xbt_context_t context = NULL;
+
+ DEBUG1("Emptying trashbin (%d contexts to free)",
+ xbt_swag_size(context_to_destroy));
+
+ while ((context = xbt_swag_extract(context_to_destroy)))
+ xbt_context_free(context);
+}
+
+/*********************/
+/* context switching */
+/*********************/
+
+static void __xbt_context_yield(xbt_context_t context)
+{
+ xbt_assert0(current_context, "You have to call context_init() first.");
+ xbt_assert0(context, "Invalid argument");
+
+ if (current_context == context) {
+ DEBUG1
+ ("--------- current_context (%p) is yielding back to maestro ---------",
+ context);
+ } else {
+ DEBUG2
+ ("--------- current_context (%p) is yielding to context(%p) ---------",
+ current_context, context);
+ }
+
+#ifdef CONTEXT_THREADS
+
+ if(current_context != init_context && !context->iwannadie)
+ {/* it's a process and it doesn't wants to die (xbt_context_yield()) */
+
+ /* save the current context */
+ xbt_context_t self = current_context;
+
+ /* update the current context to this context */
+ current_context = context;
+
+ /* yield itself */
+ yield(context);
+
+ /* restore the current context to the previously saved context */
+ current_context = self;
+ }
+ else
+ { /* maestro wants to schedule a process or a process wants to die (xbt_context_schedule() or xbt_context_kill())*/
+
+ /* save the current context */
+ xbt_context_t self = current_context;
+
+ /* update the current context */
+ current_context = context;
+
+ /* schedule the process associated with this context */
+ schedule(context);
+
+ /* restore the current context to the previously saved context */
+ current_context = self;
+ }
+
+#else /* use SUSv2 contexts */
+ VOIRP(current_context);
+ VOIRP(current_context->save);
+
+ VOIRP(context);
+ VOIRP(context->save);
+
+ int return_value = 0;
+
+ if (context->save == NULL) {
+ DEBUG1("[%p] **** Yielding to somebody else ****", current_context);
+ DEBUG2("Saving current_context value (%p) to context(%p)->save",
+ current_context, context);
+ context->save = current_context;
+ DEBUG1("current_context becomes context(%p) ", context);
+ current_context = context;
+ DEBUG1
+ ("Current position memorized (context->save). Jumping to context (%p)",
+ context);
+ return_value = swapcontext(&(context->save->uc), &(context->uc));
+ xbt_assert0((return_value == 0), "Context swapping failure");
+ DEBUG1("I am (%p). Coming back\n", context);
+ } else {
+ xbt_context_t old_context = context->save;
+
+ DEBUG1("[%p] **** Back ! ****", context);
+ DEBUG2("Setting current_context (%p) to context(%p)->save",
+ current_context, context);
+ current_context = context->save;
+ DEBUG1("Setting context(%p)->save to NULL", context);
+ context->save = NULL;
+ DEBUG2("Current position memorized (%p). Jumping to context (%p)",
+ context, old_context);
+ return_value = swapcontext(&(context->uc), &(old_context->uc));
+ xbt_assert0((return_value == 0), "Context swapping failure");
+ DEBUG1("I am (%p). Coming back\n", context);
+ }
+#endif
+
+ if (current_context->iwannadie)
+ xbt_context_stop(1);
+}
+