Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Introduce log appenders
[simgrid.git] / src / xbt / xbt_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/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 =
71     xbt_ctx_thread_factory_create_master_context;
72   (*factory)->name = "ctx_thread_factory";
73 }
74
75 static int
76 xbt_ctx_thread_factory_create_master_context(xbt_context_t * maestro)
77 {
78   *maestro = (xbt_context_t) xbt_new0(s_xbt_ctx_thread_t, 1);
79   (*maestro)->name = (char *) "maestro";
80   return 0;
81 }
82
83 static int xbt_ctx_thread_factory_finalize(xbt_context_factory_t * factory)
84 {
85   free(*factory);
86   *factory = NULL;
87   return 0;
88 }
89
90 static xbt_context_t
91 xbt_ctx_thread_factory_create_context(const char *name, xbt_main_func_t code,
92                                       void_f_pvoid_t startup_func,
93                                       void *startup_arg,
94                                       void_f_pvoid_t cleanup_func,
95                                       void *cleanup_arg, int argc,
96                                       char **argv)
97 {
98   xbt_ctx_thread_t context = xbt_new0(s_xbt_ctx_thread_t, 1);
99
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;
112
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;
119
120   return (xbt_context_t) context;
121 }
122
123 static void xbt_ctx_thread_free(xbt_context_t context)
124 {
125   if (context) {
126     xbt_ctx_thread_t ctx_thread = (xbt_ctx_thread_t) context;
127
128     free(ctx_thread->name);
129
130     if (ctx_thread->argv) {
131       int i;
132
133       for (i = 0; i < ctx_thread->argc; i++)
134         if (ctx_thread->argv[i])
135           free(ctx_thread->argv[i]);
136
137       free(ctx_thread->argv);
138     }
139
140     /* wait about the thread terminason */
141     xbt_os_thread_join(ctx_thread->thread, NULL);
142
143     /* destroy the synchronisation objects */
144     xbt_os_sem_destroy(ctx_thread->begin);
145     xbt_os_sem_destroy(ctx_thread->end);
146
147     /* finally destroy the context */
148     free(context);
149   }
150 }
151
152 static void xbt_ctx_thread_kill(xbt_context_t context)
153 {
154   DEBUG1("Kill context '%s'", context->name);
155   context->iwannadie = 1;
156   xbt_ctx_thread_swap(context);
157 }
158
159 /** 
160  * \param context the winner
161  *
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.
165  * 
166  * Only the maestro can call this function to run a given process.
167  */
168 static void xbt_ctx_thread_schedule(xbt_context_t context)
169 {
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);
174 }
175
176 /** 
177  * Calling this function makes the current context yield. The context
178  * that scheduled it returns from xbt_context_schedule as if nothing
179  * had happened.
180  * 
181  * Only the processes can call this function, giving back the control
182  * to the maestro
183  */
184 static void xbt_ctx_thread_yield(void)
185 {
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);
190 }
191
192 static void xbt_ctx_thread_start(xbt_context_t context)
193 {
194   xbt_ctx_thread_t ctx_thread = (xbt_ctx_thread_t) context;
195
196   DEBUG1("Start context '%s'", context->name);
197   /* create and start the process */
198   ctx_thread->thread =
199     xbt_os_thread_create(ctx_thread->name, xbt_ctx_thread_wrapper,
200                          ctx_thread);
201
202   /* wait the starting of the newly created process */
203   xbt_os_sem_acquire(ctx_thread->end);
204 }
205
206 static void xbt_ctx_thread_stop(int exit_code)
207 {
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);
211
212   xbt_swag_remove(current_context, context_living);
213   xbt_swag_insert(current_context, context_to_destroy);
214
215   /* signal to the maestro that it has finished */
216   xbt_os_sem_release(((xbt_ctx_thread_t) current_context)->end);
217
218   /* exit */
219   xbt_os_thread_exit(NULL);     /* We should provide return value in case other wants it */
220 }
221
222 static void xbt_ctx_thread_swap(xbt_context_t context)
223 {
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 */
227
228     /* yield itself, resume the maestro */
229     xbt_ctx_thread_suspend(context);
230   } else {
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
234      *
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()
241      *                      -> goto (3.1)
242      *
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()
252      *                                      -> goto (3.1)
253      */
254     /* schedule the process associated with this context */
255     xbt_ctx_thread_resume(context);
256
257   }
258
259   /* (4) the current process wants to die */
260   if (current_context->iwannadie)
261     xbt_ctx_thread_stop(1);
262 }
263
264 static void *xbt_ctx_thread_wrapper(void *param)
265 {
266   xbt_ctx_thread_t context = (xbt_ctx_thread_t) param;
267
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);
271
272   if (context->startup_func)
273     (*(context->startup_func)) (context->startup_arg);
274
275
276   xbt_ctx_thread_stop((context->code) (context->argc, context->argv));
277   return NULL;
278 }
279
280 static void xbt_ctx_thread_suspend(xbt_context_t context)
281 {
282   /* save the current context */
283   xbt_context_t self = current_context;
284
285   DEBUG1("Suspend context '%s'", context->name);
286
287   /* update the current context to this context */
288   current_context = context;
289
290   xbt_os_sem_release(((xbt_ctx_thread_t) context)->end);
291   xbt_os_sem_acquire(((xbt_ctx_thread_t) context)->begin);
292
293   /* restore the current context to the previously saved context */
294   current_context = self;
295 }
296
297 static void xbt_ctx_thread_resume(xbt_context_t context)
298 {
299   /* save the current context */
300   xbt_context_t self = current_context;
301
302   DEBUG1("Resume context '%s'", context->name);
303
304   /* update the current context */
305   current_context = context;
306
307   xbt_os_sem_release(((xbt_ctx_thread_t) context)->begin);
308   xbt_os_sem_acquire(((xbt_ctx_thread_t) context)->end);
309
310   /* restore the current context to the previously saved context */
311   current_context = self;
312 }