3 /* context_thread - implementation of context switching with native threads */
5 /* Copyright (c) 2004-2008 the SimGrid team. All right reserved */
7 /* This program is free software; you can redistribute it and/or modify it
8 * under the terms of the license (GNU LGPL) which comes with this package. */
10 #include "xbt/function_types.h"
11 #include "xbt/xbt_context_private.h"
13 #include "portable.h" /* loads context system definitions */
15 #include "xbt/xbt_os_thread.h"
17 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(xbt_context);
19 typedef struct s_xbt_ctx_thread {
21 xbt_os_thread_t thread; /* a plain dumb thread (portable to posix or windows) */
22 xbt_os_sem_t begin; /* this semaphore is used to schedule/yield the process */
23 xbt_os_sem_t end; /* this semaphore is used to schedule/unschedule the process */
24 } s_xbt_ctx_thread_t, *xbt_ctx_thread_t;
27 xbt_ctx_thread_factory_create_context(const char *name, xbt_main_func_t code,
28 void_f_pvoid_t startup_func,
30 void_f_pvoid_t cleanup_func,
31 void *cleanup_arg, int argc,
36 xbt_ctx_thread_factory_create_master_context(xbt_context_t * maestro);
38 static int xbt_ctx_thread_factory_finalize(xbt_context_factory_t * factory);
40 static void xbt_ctx_thread_free(xbt_context_t context);
42 static void xbt_ctx_thread_kill(xbt_context_t context);
44 static void xbt_ctx_thread_schedule(xbt_context_t context);
46 static void xbt_ctx_thread_yield(void);
48 static void xbt_ctx_thread_start(xbt_context_t context);
50 static void xbt_ctx_thread_stop(int exit_code);
52 static void xbt_ctx_thread_swap(xbt_context_t context);
54 static void xbt_ctx_thread_schedule(xbt_context_t context);
56 static void xbt_ctx_thread_yield(void);
58 static void xbt_ctx_thread_suspend(xbt_context_t context);
60 static void xbt_ctx_thread_resume(xbt_context_t context);
62 static void *xbt_ctx_thread_wrapper(void *param);
64 void xbt_ctx_thread_factory_init(xbt_context_factory_t * factory)
66 *factory = xbt_new0(s_xbt_context_factory_t, 1);
68 (*factory)->create_context = xbt_ctx_thread_factory_create_context;
69 (*factory)->finalize = xbt_ctx_thread_factory_finalize;
70 (*factory)->create_maestro_context =
71 xbt_ctx_thread_factory_create_master_context;
72 (*factory)->name = "ctx_thread_factory";
76 xbt_ctx_thread_factory_create_master_context(xbt_context_t * maestro)
78 *maestro = (xbt_context_t) xbt_new0(s_xbt_ctx_thread_t, 1);
79 (*maestro)->name = (char *) "maestro";
83 static int xbt_ctx_thread_factory_finalize(xbt_context_factory_t * factory)
91 xbt_ctx_thread_factory_create_context(const char *name, xbt_main_func_t code,
92 void_f_pvoid_t startup_func,
94 void_f_pvoid_t cleanup_func,
95 void *cleanup_arg, int argc,
98 xbt_ctx_thread_t context = xbt_new0(s_xbt_ctx_thread_t, 1);
100 VERB1("Create context %s", name);
101 context->code = code;
102 context->name = xbt_strdup(name);
103 context->begin = xbt_os_sem_init(0);
104 context->end = xbt_os_sem_init(0);
105 context->iwannadie = 0; /* useless but makes valgrind happy */
106 context->argc = argc;
107 context->argv = argv;
108 context->startup_func = startup_func;
109 context->startup_arg = startup_arg;
110 context->cleanup_func = cleanup_func;
111 context->cleanup_arg = cleanup_arg;
113 context->free = xbt_ctx_thread_free;
114 context->kill = xbt_ctx_thread_kill;
115 context->schedule = xbt_ctx_thread_schedule;
116 context->yield = xbt_ctx_thread_yield;
117 context->start = xbt_ctx_thread_start;
118 context->stop = xbt_ctx_thread_stop;
120 return (xbt_context_t) context;
123 static void xbt_ctx_thread_free(xbt_context_t context)
126 xbt_ctx_thread_t ctx_thread = (xbt_ctx_thread_t) context;
128 free(ctx_thread->name);
130 if (ctx_thread->argv) {
133 for (i = 0; i < ctx_thread->argc; i++)
134 if (ctx_thread->argv[i])
135 free(ctx_thread->argv[i]);
137 free(ctx_thread->argv);
140 /* wait about the thread terminason */
141 xbt_os_thread_join(ctx_thread->thread, NULL);
143 /* destroy the synchronisation objects */
144 xbt_os_sem_destroy(ctx_thread->begin);
145 xbt_os_sem_destroy(ctx_thread->end);
147 /* finally destroy the context */
152 static void xbt_ctx_thread_kill(xbt_context_t context)
154 DEBUG1("Kill context '%s'", context->name);
155 context->iwannadie = 1;
156 xbt_ctx_thread_swap(context);
160 * \param context the winner
162 * Calling this function blocks the current context and schedule \a context.
163 * When \a context will call xbt_context_yield, it will return
164 * to this function as if nothing had happened.
166 * Only the maestro can call this function to run a given process.
168 static void xbt_ctx_thread_schedule(xbt_context_t context)
170 DEBUG1("Schedule context '%s'", context->name);
171 xbt_assert0((current_context == maestro_context),
172 "You are not supposed to run this function here!");
173 xbt_ctx_thread_swap(context);
177 * Calling this function makes the current context yield. The context
178 * that scheduled it returns from xbt_context_schedule as if nothing
181 * Only the processes can call this function, giving back the control
184 static void xbt_ctx_thread_yield(void)
186 DEBUG1("Yield context '%s'", current_context->name);
187 xbt_assert0((current_context != maestro_context),
188 "You are not supposed to run this function here!");
189 xbt_ctx_thread_swap(current_context);
192 static void xbt_ctx_thread_start(xbt_context_t context)
194 xbt_ctx_thread_t ctx_thread = (xbt_ctx_thread_t) context;
196 DEBUG1("Start context '%s'", context->name);
197 /* create and start the process */
199 xbt_os_thread_create(ctx_thread->name, xbt_ctx_thread_wrapper,
202 /* wait the starting of the newly created process */
203 xbt_os_sem_acquire(ctx_thread->end);
206 static void xbt_ctx_thread_stop(int exit_code)
208 /* please no debug here: our procdata was already free'd */
209 if (current_context->cleanup_func)
210 ((*current_context->cleanup_func)) (current_context->cleanup_arg);
212 xbt_swag_remove(current_context, context_living);
213 xbt_swag_insert(current_context, context_to_destroy);
215 /* signal to the maestro that it has finished */
216 xbt_os_sem_release(((xbt_ctx_thread_t) current_context)->end);
219 xbt_os_thread_exit(NULL); /* We should provide return value in case other wants it */
222 static void xbt_ctx_thread_swap(xbt_context_t context)
224 DEBUG2("Swap context: '%s' -> '%s'", current_context->name, context->name);
225 if ((current_context != maestro_context) && !context->iwannadie) {
226 /* (0) it's not the scheduler and the process doesn't want to die, it just wants to yield */
228 /* yield itself, resume the maestro */
229 xbt_ctx_thread_suspend(context);
231 /* (1) the current process is the scheduler and the process doesn't want to die
232 * <-> the maestro wants to schedule the process
233 * -> the maestro schedules the process and waits
235 * (2) the current process is the scheduler and the process wants to die
236 * <-> the maestro wants to kill the process (has called the function xbt_context_kill())
237 * -> the maestro schedule the process and waits (xbt_os_sem_acquire(context->end))
238 * -> if the process stops (xbt_context_stop())
239 * -> the process resumes the maestro (xbt_os_sem_release(current_context->end)) and exit (xbt_os_thread_exit())
240 * -> else the process call xbt_context_yield()
243 * (3) the current process is not the scheduler and the process wants to die
244 * -> (3.1) if the current process is the process who wants to die
245 * -> (resume not need) goto (4)
246 * -> (3.2) else the current process is not the process who wants to die
247 * <-> the current process wants to kill an other process
248 * -> the current process resumes the process to die and waits
249 * -> if the process to kill stops
250 * -> it resumes the process who kill it and exit
251 * -> else if the process to kill calls to xbt_context_yield()
254 /* schedule the process associated with this context */
255 xbt_ctx_thread_resume(context);
259 /* (4) the current process wants to die */
260 if (current_context->iwannadie)
261 xbt_ctx_thread_stop(1);
264 static void *xbt_ctx_thread_wrapper(void *param)
266 xbt_ctx_thread_t context = (xbt_ctx_thread_t) param;
268 /* Tell the maestro we are starting, and wait for its green light */
269 xbt_os_sem_release(context->end);
270 xbt_os_sem_acquire(context->begin);
272 if (context->startup_func)
273 (*(context->startup_func)) (context->startup_arg);
276 xbt_ctx_thread_stop((context->code) (context->argc, context->argv));
280 static void xbt_ctx_thread_suspend(xbt_context_t context)
282 /* save the current context */
283 xbt_context_t self = current_context;
285 DEBUG1("Suspend context '%s'", context->name);
287 /* update the current context to this context */
288 current_context = context;
290 xbt_os_sem_release(((xbt_ctx_thread_t) context)->end);
291 xbt_os_sem_acquire(((xbt_ctx_thread_t) context)->begin);
293 /* restore the current context to the previously saved context */
294 current_context = self;
297 static void xbt_ctx_thread_resume(xbt_context_t context)
299 /* save the current context */
300 xbt_context_t self = current_context;
302 DEBUG1("Resume context '%s'", context->name);
304 /* update the current context */
305 current_context = context;
307 xbt_os_sem_release(((xbt_ctx_thread_t) context)->begin);
308 xbt_os_sem_acquire(((xbt_ctx_thread_t) context)->end);
310 /* restore the current context to the previously saved context */
311 current_context = self;