Logo AND Algorithmique Numérique Distribuée

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