Logo AND Algorithmique Numérique Distribuée

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