Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
5059f0ef979f86622773e1b610d56985a1daf0bf
[simgrid.git] / src / simix / smx_context_thread.c
1 /* $Id$ */
2
3 /* context_thread - implementation of context switching with native threads */
4
5 /* Copyright (c) 2004-2008 the SimGrid team. All right reserved */
6
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. */
9
10 #include "xbt/function_types.h"
11 #include "xbt_context_private.h"
12
13 #include "portable.h"           /* loads context system definitions */
14 #include "xbt/swag.h"
15 #include "xbt/xbt_os_thread.h"
16
17 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(xbt_context);
18
19 typedef struct s_xbt_ctx_thread {
20   XBT_CTX_BASE_T;
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;
25
26 static xbt_context_t
27 xbt_ctx_thread_factory_create_context(const char *name, xbt_main_func_t code,
28                                       void_f_pvoid_t startup_func,
29                                       void *startup_arg,
30                                       void_f_pvoid_t cleanup_func,
31                                       void *cleanup_arg, int argc,
32                                       char **argv);
33
34
35 static int
36 xbt_ctx_thread_factory_create_master_context(xbt_context_t * maestro);
37
38 static int xbt_ctx_thread_factory_finalize(xbt_context_factory_t * factory);
39
40 static void xbt_ctx_thread_free(xbt_context_t context);
41
42 static void xbt_ctx_thread_kill(xbt_context_t context);
43
44 static void xbt_ctx_thread_schedule(xbt_context_t context);
45
46 static void xbt_ctx_thread_yield(void);
47
48 static void xbt_ctx_thread_start(xbt_context_t context);
49
50 static void xbt_ctx_thread_stop(int exit_code);
51
52 static void xbt_ctx_thread_swap(xbt_context_t context);
53
54 static void xbt_ctx_thread_schedule(xbt_context_t context);
55
56 static void xbt_ctx_thread_yield(void);
57
58 static void xbt_ctx_thread_suspend(xbt_context_t context);
59
60 static void xbt_ctx_thread_resume(xbt_context_t context);
61
62 static void *xbt_ctx_thread_wrapper(void *param);
63
64 void xbt_ctx_thread_factory_init(xbt_context_factory_t * factory)
65 {
66   *factory = xbt_new0(s_xbt_context_factory_t, 1);
67
68   (*factory)->create_context = xbt_ctx_thread_factory_create_context;
69   (*factory)->finalize = xbt_ctx_thread_factory_finalize;
70   (*factory)->create_maestro_context = xbt_ctx_thread_factory_create_master_context;
71   (*factory)->free = xbt_ctx_thread_free;
72   (*factory)->kill = xbt_ctx_thread_kill;
73   (*factory)->schedule = xbt_ctx_thread_schedule;
74   (*factory)->yield = xbt_ctx_thread_yield;
75   (*factory)->start = xbt_ctx_thread_start;
76   (*factory)->stop = xbt_ctx_thread_stop;
77   (*factory)->name = "ctx_thread_factory";
78 }
79
80 static int
81 xbt_ctx_thread_factory_create_master_context(xbt_context_t * maestro)
82 {
83   *maestro = (xbt_context_t) xbt_new0(s_xbt_ctx_thread_t, 1);
84   (*maestro)->name = (char *) "maestro";
85   return 0;
86 }
87
88 static int xbt_ctx_thread_factory_finalize(xbt_context_factory_t * factory)
89 {
90   free(*factory);
91   *factory = NULL;
92   return 0;
93 }
94
95 static xbt_context_t
96 xbt_ctx_thread_factory_create_context(const char *name, xbt_main_func_t code,
97                                       void_f_pvoid_t startup_func,
98                                       void *startup_arg,
99                                       void_f_pvoid_t cleanup_func,
100                                       void *cleanup_arg, int argc,
101                                       char **argv)
102 {
103   xbt_ctx_thread_t context = xbt_new0(s_xbt_ctx_thread_t, 1);
104
105   VERB1("Create context %s", name);
106   context->code = code;
107   context->name = xbt_strdup(name);
108   context->begin = xbt_os_sem_init(0);
109   context->end = xbt_os_sem_init(0);
110   context->iwannadie = 0;       /* useless but makes valgrind happy */
111   context->argc = argc;
112   context->argv = argv;
113   context->startup_func = startup_func;
114   context->startup_arg = startup_arg;
115   context->cleanup_func = cleanup_func;
116   context->cleanup_arg = cleanup_arg;
117
118   return (xbt_context_t) context;
119 }
120
121 static void xbt_ctx_thread_free(xbt_context_t context)
122 {
123   if (context) {
124     xbt_ctx_thread_t ctx_thread = (xbt_ctx_thread_t) context;
125
126     free(ctx_thread->name);
127
128     if (ctx_thread->argv) {
129       int i;
130
131       for (i = 0; i < ctx_thread->argc; i++)
132         if (ctx_thread->argv[i])
133           free(ctx_thread->argv[i]);
134
135       free(ctx_thread->argv);
136     }
137
138     /* wait about the thread terminason */
139     xbt_os_thread_join(ctx_thread->thread, NULL);
140
141     /* destroy the synchronisation objects */
142     xbt_os_sem_destroy(ctx_thread->begin);
143     xbt_os_sem_destroy(ctx_thread->end);
144
145     /* finally destroy the context */
146     free(context);
147   }
148 }
149
150 static void xbt_ctx_thread_kill(xbt_context_t context)
151 {
152   DEBUG1("Kill context '%s'", context->name);
153   context->iwannadie = 1;
154   xbt_ctx_thread_swap(context);
155 }
156
157 /** 
158  * \param context the winner
159  *
160  * Calling this function blocks the current context and schedule \a context.  
161  * When \a context will call xbt_context_yield, it will return
162  * to this function as if nothing had happened.
163  * 
164  * Only the maestro can call this function to run a given process.
165  */
166 static void xbt_ctx_thread_schedule(xbt_context_t context)
167 {
168   DEBUG1("Schedule context '%s'", context->name);
169   xbt_assert0((current_context == maestro_context),
170               "You are not supposed to run this function here!");
171   xbt_ctx_thread_swap(context);
172 }
173
174 /** 
175  * Calling this function makes the current context yield. The context
176  * that scheduled it returns from xbt_context_schedule as if nothing
177  * had happened.
178  * 
179  * Only the processes can call this function, giving back the control
180  * to the maestro
181  */
182 static void xbt_ctx_thread_yield(void)
183 {
184   DEBUG1("Yield context '%s'", current_context->name);
185   xbt_assert0((current_context != maestro_context),
186               "You are not supposed to run this function here!");
187   xbt_ctx_thread_swap(current_context);
188 }
189
190 static void xbt_ctx_thread_start(xbt_context_t context)
191 {
192   xbt_ctx_thread_t ctx_thread = (xbt_ctx_thread_t) context;
193
194   DEBUG1("Start context '%s'", context->name);
195   /* create and start the process */
196   ctx_thread->thread =
197     xbt_os_thread_create(ctx_thread->name, xbt_ctx_thread_wrapper,
198                          ctx_thread);
199
200   /* wait the starting of the newly created process */
201   xbt_os_sem_acquire(ctx_thread->end);
202 }
203
204 static void xbt_ctx_thread_stop(int exit_code)
205 {
206   /* please no debug here: our procdata was already free'd */
207   if (current_context->cleanup_func)
208     ((*current_context->cleanup_func)) (current_context->cleanup_arg);
209
210   xbt_swag_remove(current_context, context_living);
211   xbt_swag_insert(current_context, context_to_destroy);
212
213   /* signal to the maestro that it has finished */
214   xbt_os_sem_release(((xbt_ctx_thread_t) current_context)->end);
215
216   /* exit */
217   xbt_os_thread_exit(NULL);     /* We should provide return value in case other wants it */
218 }
219
220 static void xbt_ctx_thread_swap(xbt_context_t context)
221 {
222   DEBUG2("Swap context: '%s' -> '%s'", current_context->name, context->name);
223   if ((current_context != maestro_context) && !context->iwannadie) {
224     /* (0) it's not the scheduler and the process doesn't want to die, it just wants to yield */
225
226     /* yield itself, resume the maestro */
227     xbt_ctx_thread_suspend(context);
228   } else {
229     /* (1) the current process is the scheduler and the process doesn't want to die
230      *      <-> the maestro wants to schedule the process
231      *              -> the maestro schedules the process and waits
232      *
233      * (2) the current process is the scheduler and the process wants to die
234      *      <-> the maestro wants to kill the process (has called the function xbt_context_kill())
235      *              -> the maestro schedule the process and waits (xbt_os_sem_acquire(context->end))
236      *              -> if the process stops (xbt_context_stop())
237      *                      -> the process resumes the maestro (xbt_os_sem_release(current_context->end)) and exit (xbt_os_thread_exit())
238      *              -> else the process call xbt_context_yield()
239      *                      -> goto (3.1)
240      *
241      * (3) the current process is not the scheduler and the process wants to die
242      *              -> (3.1) if the current process is the process who wants to die
243      *                      -> (resume not need) goto (4)
244      *              -> (3.2) else the current process is not the process who wants to die
245      *                      <-> the current process wants to kill an other process
246      *                              -> the current process resumes the process to die and waits
247      *                              -> if the process to kill stops
248      *                                      -> it resumes the process who kill it and exit
249      *                              -> else if the process to kill calls to xbt_context_yield()
250      *                                      -> goto (3.1)
251      */
252     /* schedule the process associated with this context */
253     xbt_ctx_thread_resume(context);
254
255   }
256
257   /* (4) the current process wants to die */
258   if (current_context->iwannadie)
259     xbt_ctx_thread_stop(1);
260 }
261
262 static void *xbt_ctx_thread_wrapper(void *param)
263 {
264   xbt_ctx_thread_t context = (xbt_ctx_thread_t) param;
265
266   /* Tell the maestro we are starting, and wait for its green light */
267   xbt_os_sem_release(context->end);
268   xbt_os_sem_acquire(context->begin);
269
270   if (context->startup_func)
271     (*(context->startup_func)) (context->startup_arg);
272
273
274   xbt_ctx_thread_stop((context->code) (context->argc, context->argv));
275   return NULL;
276 }
277
278 static void xbt_ctx_thread_suspend(xbt_context_t context)
279 {
280   /* save the current context */
281   xbt_context_t self = current_context;
282
283   DEBUG1("Suspend context '%s'", context->name);
284
285   /* update the current context to this context */
286   current_context = context;
287
288   xbt_os_sem_release(((xbt_ctx_thread_t) context)->end);
289   xbt_os_sem_acquire(((xbt_ctx_thread_t) context)->begin);
290
291   /* restore the current context to the previously saved context */
292   current_context = self;
293 }
294
295 static void xbt_ctx_thread_resume(xbt_context_t context)
296 {
297   /* save the current context */
298   xbt_context_t self = current_context;
299
300   DEBUG1("Resume context '%s'", context->name);
301
302   /* update the current context */
303   current_context = context;
304
305   xbt_os_sem_release(((xbt_ctx_thread_t) context)->begin);
306   xbt_os_sem_acquire(((xbt_ctx_thread_t) context)->end);
307
308   /* restore the current context to the previously saved context */
309   current_context = self;
310 }