Logo AND Algorithmique Numérique Distribuée

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