Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
model-checker : move functions about snapshot comparison in a separate file mc_compare.c
[simgrid.git] / src / simix / smx_context_sysv.c
1 /* context_sysv - context switching with ucontextes from System V           */
2
3 /* Copyright (c) 2009, 2010. The SimGrid Team.
4  * All rights reserved.                                                     */
5
6  /* This program is free software; you can redistribute it and/or modify it
7   * under the terms of the license (GNU LGPL) which comes with this package. */
8
9 #include <stdarg.h>
10
11 #include "xbt/parmap.h"
12 #include "smx_private.h"
13 #include "gras_config.h"
14 #include "context_sysv_config.h"        /* loads context system definitions */
15
16 #ifdef _XBT_WIN32
17 #  include <win32_ucontext.h>     /* context relative declarations */
18 #else
19 #  include <ucontext.h>           /* context relative declarations */
20 #endif
21
22 #ifdef HAVE_VALGRIND_VALGRIND_H
23 #  include <valgrind/valgrind.h>
24 #endif                          /* HAVE_VALGRIND_VALGRIND_H */
25
26 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
27
28 #ifdef CONTEXT_THREADS
29 static xbt_parmap_t sysv_parmap;
30 static ucontext_t* sysv_workers_stacks;        /* space to save the worker's stack in each thread */
31 static unsigned long sysv_threads_working;     /* number of threads that have started their work */
32 static xbt_os_thread_key_t sysv_worker_id_key; /* thread-specific storage for the thread id */
33 #endif
34 static unsigned long sysv_process_index = 0;   /* index of the next process to run in the
35                                                 * list of runnable processes */
36 static smx_ctx_sysv_t sysv_maestro_context;
37
38 static int smx_ctx_sysv_factory_finalize(smx_context_factory_t *factory);
39 static smx_context_t
40 smx_ctx_sysv_create_context_sized(size_t structure_size,
41                                   xbt_main_func_t code, int argc,
42                                   char **argv,
43                                   void_pfn_smxprocess_t cleanup_func,
44                                   void *data);
45 static void smx_ctx_sysv_free(smx_context_t context);
46 static smx_context_t
47 smx_ctx_sysv_create_context(xbt_main_func_t code, int argc, char **argv,
48     void_pfn_smxprocess_t cleanup_func, void* data);
49
50 static void smx_ctx_sysv_wrapper(int count, ...);
51
52 static void smx_ctx_sysv_stop_serial(smx_context_t context);
53 static void smx_ctx_sysv_suspend_serial(smx_context_t context);
54 static void smx_ctx_sysv_resume_serial(smx_process_t first_process);
55 static void smx_ctx_sysv_runall_serial(void);
56
57 static void smx_ctx_sysv_stop_parallel(smx_context_t context);
58 static void smx_ctx_sysv_suspend_parallel(smx_context_t context);
59 static void smx_ctx_sysv_resume_parallel(smx_process_t first_process);
60 static void smx_ctx_sysv_runall_parallel(void);
61
62 /* This is a bit paranoid about sizeof(smx_ctx_sysv_t) not being a multiple of
63  * sizeof(int), but it doesn't harm. */
64 #define CTX_ADDR_LEN                            \
65   (sizeof(smx_ctx_sysv_t) / sizeof(int) +       \
66    !!(sizeof(smx_ctx_sysv_t) % sizeof(int)))
67
68 void SIMIX_ctx_sysv_factory_init(smx_context_factory_t *factory)
69 {
70   smx_ctx_base_factory_init(factory);
71   XBT_VERB("Activating SYSV context factory");
72
73   (*factory)->finalize = smx_ctx_sysv_factory_finalize;
74   (*factory)->create_context = smx_ctx_sysv_create_context;
75   /* Do not overload that method (*factory)->finalize */
76   (*factory)->free = smx_ctx_sysv_free;
77   (*factory)->name = "smx_sysv_context_factory";
78
79   if (SIMIX_context_is_parallel()) {
80 #ifdef CONTEXT_THREADS  /* To use parallel ucontexts a thread pool is needed */
81     int nthreads = SIMIX_context_get_nthreads();
82     sysv_parmap = xbt_parmap_new(nthreads, SIMIX_context_get_parallel_mode());
83     sysv_workers_stacks = xbt_new(ucontext_t, nthreads);
84     xbt_os_thread_key_create(&sysv_worker_id_key);
85     (*factory)->stop = smx_ctx_sysv_stop_parallel;
86     (*factory)->suspend = smx_ctx_sysv_suspend_parallel;
87     (*factory)->runall = smx_ctx_sysv_runall_parallel;
88 #else
89     THROWF(arg_error, 0, "No thread support for parallel context execution");
90 #endif
91   } else {
92     (*factory)->stop = smx_ctx_sysv_stop_serial;
93     (*factory)->suspend = smx_ctx_sysv_suspend_serial;
94     (*factory)->runall = smx_ctx_sysv_runall_serial;
95   }    
96 }
97
98 static int smx_ctx_sysv_factory_finalize(smx_context_factory_t *factory)
99
100 #ifdef CONTEXT_THREADS
101   if (sysv_parmap)
102     xbt_parmap_destroy(sysv_parmap);
103   xbt_free(sysv_workers_stacks);
104 #endif
105   return smx_ctx_base_factory_finalize(factory);
106 }
107
108 static smx_context_t
109 smx_ctx_sysv_create_context_sized(size_t size, xbt_main_func_t code,
110                                   int argc, char **argv,
111                                   void_pfn_smxprocess_t cleanup_func,
112                                   void *data)
113 {
114   int ctx_addr[CTX_ADDR_LEN];
115   smx_ctx_sysv_t context =
116       (smx_ctx_sysv_t) smx_ctx_base_factory_create_context_sized(size,
117                                                                  code,
118                                                                  argc,
119                                                                  argv,
120                                                                  cleanup_func,
121                                                                  data);
122
123   /* if the user provided a function for the process then use it,
124      otherwise it is the context for maestro */
125   if (code) {
126
127     getcontext(&(context->uc));
128
129     context->uc.uc_link = NULL;
130
131     context->uc.uc_stack.ss_sp =
132         pth_skaddr_makecontext(context->stack, smx_context_stack_size);
133
134     context->uc.uc_stack.ss_size =
135         pth_sksize_makecontext(context->stack, smx_context_stack_size);
136
137 #ifdef HAVE_VALGRIND_VALGRIND_H
138     context->valgrind_stack_id =
139         VALGRIND_STACK_REGISTER(context->uc.uc_stack.ss_sp,
140                                 ((char *) context->uc.uc_stack.ss_sp) +
141                                 context->uc.uc_stack.ss_size);
142 #endif                          /* HAVE_VALGRIND_VALGRIND_H */
143     memcpy(ctx_addr, &context, sizeof(smx_ctx_sysv_t));
144     switch (CTX_ADDR_LEN) {
145     case 1:
146       makecontext(&context->uc, (void (*)())smx_ctx_sysv_wrapper,
147                   1, ctx_addr[0]);
148       break;
149     case 2:
150       makecontext(&context->uc, (void (*)())smx_ctx_sysv_wrapper,
151                   2, ctx_addr[0], ctx_addr[1]);
152       break;
153     default:
154       xbt_die("Ucontexts are not supported on this arch yet (addr len = %zu/%zu = %zu)",
155               sizeof(smx_ctx_sysv_t), sizeof(int), CTX_ADDR_LEN);
156     }
157   } else {
158     sysv_maestro_context = context;
159   }
160
161   return (smx_context_t) context;
162 }
163
164 static smx_context_t
165 smx_ctx_sysv_create_context(xbt_main_func_t code, int argc, char **argv,
166     void_pfn_smxprocess_t cleanup_func,
167     void *data)
168 {
169
170   return smx_ctx_sysv_create_context_sized(sizeof(s_smx_ctx_sysv_t) + smx_context_stack_size,
171                                            code, argc, argv, cleanup_func,
172                                            data);
173
174 }
175
176 static void smx_ctx_sysv_free(smx_context_t context)
177 {
178
179   if (context) {
180
181 #ifdef HAVE_VALGRIND_VALGRIND_H
182     VALGRIND_STACK_DEREGISTER(((smx_ctx_sysv_t)
183                                context)->valgrind_stack_id);
184 #endif                          /* HAVE_VALGRIND_VALGRIND_H */
185
186   }
187   smx_ctx_base_free(context);
188 }
189
190 static void smx_ctx_sysv_wrapper(int first, ...)
191
192   int ctx_addr[CTX_ADDR_LEN];
193   smx_ctx_sysv_t context;
194
195   ctx_addr[0] = first;
196   if (CTX_ADDR_LEN > 1) {
197     va_list ap;
198     int i;
199     va_start(ap, first);
200     for (i = 1; i < CTX_ADDR_LEN; i++)
201       ctx_addr[i] = va_arg(ap, int);
202     va_end(ap);
203   }
204   memcpy(&context, ctx_addr, sizeof(smx_ctx_sysv_t));
205   (context->super.code) (context->super.argc, context->super.argv);
206
207   simix_global->context_factory->stop((smx_context_t) context);
208 }
209
210 static void smx_ctx_sysv_stop_serial(smx_context_t context)
211 {
212   smx_ctx_base_stop(context);
213   smx_ctx_sysv_suspend_serial(context);
214 }
215
216 static void smx_ctx_sysv_suspend_serial(smx_context_t context)
217 {
218   /* determine the next context */
219   smx_context_t next_context;
220   unsigned long int i = sysv_process_index++;
221
222   if (i < xbt_dynar_length(simix_global->process_to_run)) {
223     /* execute the next process */
224     XBT_DEBUG("Run next process");
225     next_context = xbt_dynar_get_as(
226         simix_global->process_to_run,i, smx_process_t)->context;
227   }
228   else {
229     /* all processes were run, return to maestro */
230     XBT_DEBUG("No more process to run");
231     next_context = (smx_context_t) sysv_maestro_context;
232   }
233   SIMIX_context_set_current(next_context);
234   swapcontext(&((smx_ctx_sysv_t) context)->uc,
235       &((smx_ctx_sysv_t) next_context)->uc);
236 }
237
238 static void smx_ctx_sysv_resume_serial(smx_process_t first_process)
239 {
240   smx_context_t context = first_process->context;
241   SIMIX_context_set_current(context);
242   swapcontext(&sysv_maestro_context->uc,
243       &((smx_ctx_sysv_t) context)->uc);
244 }
245
246 static void smx_ctx_sysv_runall_serial(void)
247 {
248   if (!xbt_dynar_is_empty(simix_global->process_to_run)) {
249     smx_process_t first_process =
250         xbt_dynar_get_as(simix_global->process_to_run, 0, smx_process_t);
251     sysv_process_index = 1;
252
253     /* execute the first process */
254     smx_ctx_sysv_resume_serial(first_process);
255   }
256 }
257
258 static void smx_ctx_sysv_stop_parallel(smx_context_t context)
259 {
260   smx_ctx_base_stop(context);
261   smx_ctx_sysv_suspend_parallel(context);
262 }
263
264 static void smx_ctx_sysv_suspend_parallel(smx_context_t context)
265 {
266 #ifdef CONTEXT_THREADS
267   /* determine the next context */
268   smx_process_t next_work = xbt_parmap_next(sysv_parmap);
269   smx_context_t next_context;
270   ucontext_t* next_stack;
271
272   if (next_work != NULL) {
273     /* there is a next process to resume */
274     XBT_DEBUG("Run next process");
275     next_context = next_work->context;
276     next_stack = &((smx_ctx_sysv_t) next_context)->uc;
277   }
278   else {
279     /* all processes were run, go to the barrier */
280     XBT_DEBUG("No more processes to run");
281     next_context = (smx_context_t) sysv_maestro_context;
282     unsigned long worker_id =
283         (unsigned long) xbt_os_thread_get_specific(sysv_worker_id_key);
284     next_stack = &sysv_workers_stacks[worker_id];
285   }
286
287   SIMIX_context_set_current(next_context);
288   swapcontext(&((smx_ctx_sysv_t) context)->uc, next_stack);
289 #endif
290 }
291
292 static void smx_ctx_sysv_resume_parallel(smx_process_t first_process)
293 {
294 #ifdef CONTEXT_THREADS
295   unsigned long worker_id = __sync_fetch_and_add(&sysv_threads_working, 1);
296   xbt_os_thread_set_specific(sysv_worker_id_key, (void*) worker_id);
297   ucontext_t* worker_stack = &sysv_workers_stacks[worker_id];
298
299   smx_context_t context = first_process->context;
300   SIMIX_context_set_current(context);
301   swapcontext(worker_stack, &((smx_ctx_sysv_t) context)->uc);
302 #endif
303 }
304
305 static void smx_ctx_sysv_runall_parallel(void)
306 {
307 #ifdef CONTEXT_THREADS
308   sysv_threads_working = 0;
309   xbt_parmap_apply(sysv_parmap, (void_f_pvoid_t) smx_ctx_sysv_resume_parallel,
310       simix_global->process_to_run);
311 #endif
312 }