Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Pfff. These functions are actually not available from the high APIs
[simgrid.git] / src / simix / RawContext.cpp
1 /* Copyright (c) 2009-2015. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 /** \file RawContext.cpp 
8   * Fast context switching inspired from SystemV ucontexts.
9   *
10   * In contrast to System V context, it does not touch the signal mask
11   * which avoids making a system call (at least on Linux).
12   */
13
14 #include <math.h>
15
16 #include <utility>
17 #include <functional>
18
19 #include "src/internal_config.h" 
20
21 #include "xbt/log.h"
22 #include "xbt/parmap.h"
23 #include "xbt/dynar.h"
24
25 #include "smx_private.h"
26 #include "smx_private.hpp"
27 #include "mc/mc.h"
28
29 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
30
31 // ***** Class definitions
32
33 namespace simgrid {
34 namespace simix {
35
36 class RawContext;
37 class RawContextFactory;
38
39 class RawContext : public Context {
40 protected:
41   void* stack_ = nullptr; 
42   /** pointer to top the stack stack */
43   void* stack_top_ = nullptr;
44 public:
45   friend class RawContextFactory;
46   RawContext(std::function<void()> code,
47           void_pfn_smxprocess_t cleanup_func,
48           smx_process_t process);
49   ~RawContext();
50 public:
51   static void wrapper(void* arg);
52   void stop() override;
53   void suspend() override;
54   void resume();
55 private:
56   void suspend_serial();
57   void suspend_parallel();
58   void resume_serial();
59   void resume_parallel();
60 };
61
62 class RawContextFactory : public ContextFactory {
63 public:
64   RawContextFactory();
65   ~RawContextFactory();
66   RawContext* create_context(std::function<void()> code,
67     void_pfn_smxprocess_t, smx_process_t process) override;
68   void run_all() override;
69 private:
70   void run_all_adaptative();
71   void run_all_serial();
72   void run_all_parallel();
73 };
74
75 ContextFactory* raw_factory()
76 {
77   XBT_VERB("Using raw contexts. Because the glibc is just not good enough for us.");
78   return new RawContextFactory();
79 }
80
81 }
82 }
83
84 // ***** Loads of static stuff
85
86 #if HAVE_THREAD_CONTEXTS
87 static xbt_parmap_t raw_parmap;
88 static simgrid::simix::RawContext** raw_workers_context;    /* space to save the worker context in each thread */
89 static uintptr_t raw_threads_working;     /* number of threads that have started their work */
90 static xbt_os_thread_key_t raw_worker_id_key; /* thread-specific storage for the thread id */
91 #endif
92 static unsigned long raw_process_index = 0;   /* index of the next process to run in the
93                                                * list of runnable processes */
94 static simgrid::simix::RawContext* raw_maestro_context;
95
96 static bool raw_context_parallel = false;
97
98 // ***** Raw context routines
99
100 typedef void (*rawctx_entry_point_t)(void *);
101
102 typedef void* raw_stack_t;
103 extern "C" raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
104                                    rawctx_entry_point_t entry_point, void* arg);
105 extern "C" void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context);
106
107 // TODO, we should handle FP, MMX and the x87 control-word (for x86 and x86_64)
108
109 #if SIMGRID_PROCESSOR_x86_64
110 __asm__ (
111 #if defined(__APPLE__)
112    ".text\n"
113    ".globl _raw_makecontext\n"
114    "_raw_makecontext:\n"
115 #elif defined(_WIN32)
116    ".text\n"
117    ".globl raw_makecontext\n"
118    "raw_makecontext:\n"
119 #else
120    ".text\n"
121    ".globl raw_makecontext\n"
122    ".type raw_makecontext,@function\n"
123    "raw_makecontext:\n"/* Calling convention sets the arguments in rdi, rsi, rdx and rcx, respectively */
124 #endif
125    "   mov %rdi,%rax\n"      /* stack */
126    "   add %rsi,%rax\n"      /* size  */
127    "   andq $-16, %rax\n"    /* align stack */
128    "   movq $0,   -8(%rax)\n" /* @return for func */
129    "   mov %rdx,-16(%rax)\n" /* func */
130    "   mov %rcx,-24(%rax)\n" /* arg/rdi */
131    "   movq $0,  -32(%rax)\n" /* rsi */
132    "   movq $0,  -40(%rax)\n" /* rdx */
133    "   movq $0,  -48(%rax)\n" /* rcx */
134    "   movq $0,  -56(%rax)\n" /* r8  */
135    "   movq $0,  -64(%rax)\n" /* r9  */
136    "   movq $0,  -72(%rax)\n" /* rbp */
137    "   movq $0,  -80(%rax)\n" /* rbx */
138    "   movq $0,  -88(%rax)\n" /* r12 */
139    "   movq $0,  -96(%rax)\n" /* r13 */
140    "   movq $0, -104(%rax)\n" /* r14 */
141    "   movq $0, -112(%rax)\n" /* r15 */
142    "   sub $112,%rax\n"
143    "   ret\n"
144 );
145
146 __asm__ (
147 #if defined(__APPLE__)
148    ".text\n"
149    ".globl _raw_swapcontext\n"
150    "_raw_swapcontext:\n"
151 #elif defined(_WIN32)
152    ".text\n"
153    ".globl raw_swapcontext\n"
154    "raw_swapcontext:\n"
155 #else
156    ".text\n"
157    ".globl raw_swapcontext\n"
158    ".type raw_swapcontext,@function\n"
159    "raw_swapcontext:\n" /* Calling convention sets the arguments in rdi and rsi, respectively */
160 #endif
161    "   push %rdi\n"
162    "   push %rsi\n"
163    "   push %rdx\n"
164    "   push %rcx\n"
165    "   push %r8\n"
166    "   push %r9\n"
167    "   push %rbp\n"
168    "   push %rbx\n"
169    "   push %r12\n"
170    "   push %r13\n"
171    "   push %r14\n"
172    "   push %r15\n"
173    "   mov %rsp,(%rdi)\n" /* old */
174    "   mov %rsi,%rsp\n" /* new */
175    "   pop %r15\n"
176    "   pop %r14\n"
177    "   pop %r13\n"
178    "   pop %r12\n"
179    "   pop %rbx\n"
180    "   pop %rbp\n"
181    "   pop %r9\n"
182    "   pop %r8\n"
183    "   pop %rcx\n"
184    "   pop %rdx\n"
185    "   pop %rsi\n"
186    "   pop %rdi\n"
187    "   ret\n"
188 );
189 #elif SIMGRID_PROCESSOR_i686
190 __asm__ (
191 #if defined(__APPLE__) || defined(_WIN32)
192    ".text\n"
193    ".globl _raw_makecontext\n"
194    "_raw_makecontext:\n"
195 #else
196    ".text\n"
197    ".globl raw_makecontext\n"
198    ".type raw_makecontext,@function\n"
199    "raw_makecontext:\n"
200 #endif
201    "   movl 4(%esp),%eax\n"   /* stack */
202    "   addl 8(%esp),%eax\n"   /* size  */
203    "   andl $-16, %eax\n"     /* align stack */
204    "   movl 12(%esp),%ecx\n"  /* func  */
205    "   movl 16(%esp),%edx\n"  /* arg   */
206    "   movl %edx, -4(%eax)\n"
207    "   movl $0,   -8(%eax)\n" /* @return for func */
208    "   movl %ecx,-12(%eax)\n"
209    "   movl $0,  -16(%eax)\n" /* ebp */
210    "   movl $0,  -20(%eax)\n" /* ebx */
211    "   movl $0,  -24(%eax)\n" /* esi */
212    "   movl $0,  -28(%eax)\n" /* edi */
213    "   subl $28,%eax\n"
214    "   retl\n"
215 );
216
217 __asm__ (
218 #if defined(__APPLE__) || defined(_WIN32)
219    ".text\n"
220    ".globl _raw_swapcontext\n"
221    "_raw_swapcontext:\n"
222 #else
223    ".text\n"
224    ".globl raw_swapcontext\n"
225    ".type raw_swapcontext,@function\n"
226    "raw_swapcontext:\n"
227 #endif
228    // Fetch the parameters:
229    "   movl 4(%esp),%eax\n" /* old (raw_stack_t*) */
230    "   movl 8(%esp),%edx\n" /* new (raw_stack_t)  */
231    // Save registers of the current context on the stack:
232    "   pushl %ebp\n"
233    "   pushl %ebx\n"
234    "   pushl %esi\n"
235    "   pushl %edi\n"
236    // Save the current context (stack pointer) in *old:
237    "   movl %esp,(%eax)\n"
238    // Switch to the stack of the new context:
239    "   movl %edx,%esp\n"
240    // Pop the values of the new context:
241    "   popl %edi\n"
242    "   popl %esi\n"
243    "   popl %ebx\n"
244    "   popl %ebp\n"
245    // Return using the return address of the new context:
246    "   retl\n"
247 );
248 #else
249
250
251 /* If you implement raw contexts for other processors, don't forget to
252    update the definition of HAVE_RAW_CONTEXTS in tools/cmake/CompleteInFiles.cmake */
253
254 raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
255                             rawctx_entry_point_t entry_point, void* arg) {
256    THROW_UNIMPLEMENTED;
257 }
258
259 void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context) {
260    THROW_UNIMPLEMENTED;
261 }
262
263 #endif
264
265 // ***** Method definitions
266
267 namespace simgrid {
268 namespace simix {
269
270 RawContextFactory::RawContextFactory()
271   : ContextFactory("RawContextFactory")
272 {
273   raw_context_parallel = SIMIX_context_is_parallel();
274   if (raw_context_parallel) {
275 #if HAVE_THREAD_CONTEXTS
276     int nthreads = SIMIX_context_get_nthreads();
277     xbt_os_thread_key_create(&raw_worker_id_key);
278     // TODO, lazily init
279     raw_parmap = nullptr;
280     raw_workers_context = xbt_new(RawContext*, nthreads);
281     raw_maestro_context = nullptr;
282 #endif
283     // TODO, if(SIMIX_context_get_parallel_threshold() > 1) => choose dynamically
284   }
285 }
286
287 RawContextFactory::~RawContextFactory()
288 {
289 #if HAVE_THREAD_CONTEXTS
290   if (raw_parmap)
291     xbt_parmap_destroy(raw_parmap);
292   xbt_free(raw_workers_context);
293 #endif
294 }
295
296 RawContext* RawContextFactory::create_context(std::function<void()> code,
297     void_pfn_smxprocess_t cleanup, smx_process_t process)
298 {
299   return this->new_context<RawContext>(std::move(code),
300     cleanup, process);
301 }
302
303 void RawContext::wrapper(void* arg)
304 {
305   RawContext* context = (RawContext*) arg;
306   (*context)();
307   context->stop();
308 }
309
310 RawContext::RawContext(std::function<void()> code,
311     void_pfn_smxprocess_t cleanup, smx_process_t process)
312   : Context(std::move(code), cleanup, process)
313 {
314    if (has_code()) {
315      this->stack_ = SIMIX_context_stack_new();
316      this->stack_top_ = raw_makecontext(this->stack_,
317                          smx_context_usable_stack_size,
318                          RawContext::wrapper,
319                          this);
320    } else {
321      if(process != nullptr && raw_maestro_context == nullptr)
322        raw_maestro_context = this;
323      if (MC_is_active())
324        MC_ignore_heap(
325          &raw_maestro_context->stack_top_,
326          sizeof(raw_maestro_context->stack_top_));
327    }
328 }
329
330 RawContext::~RawContext()
331 {
332   SIMIX_context_stack_delete(this->stack_);
333 }
334
335 void RawContext::stop()
336 {
337   Context::stop();
338   this->suspend();
339 }
340
341 void RawContextFactory::run_all()
342 {
343   if (raw_context_parallel)
344     run_all_parallel();
345   else
346     run_all_serial();
347 }
348
349 void RawContextFactory::run_all_serial()
350 {
351   smx_process_t first_process =
352       xbt_dynar_get_as(simix_global->process_to_run, 0, smx_process_t);
353   raw_process_index = 1;
354   static_cast<RawContext*>(first_process->context)->resume_serial();
355 }
356
357 void RawContextFactory::run_all_parallel()
358 {
359 #if HAVE_THREAD_CONTEXTS
360   raw_threads_working = 0;
361   if (raw_parmap == nullptr)
362     raw_parmap = xbt_parmap_new(
363       SIMIX_context_get_nthreads(), SIMIX_context_get_parallel_mode());
364   xbt_parmap_apply(raw_parmap,
365       [](void* arg) {
366         smx_process_t process = static_cast<smx_process_t>(arg);
367         RawContext* context = static_cast<RawContext*>(process->context);
368         context->resume_parallel();
369       },
370       simix_global->process_to_run);
371 #else
372   xbt_die("You asked for a parallel execution, but you don't have any threads.");
373 #endif
374 }
375
376 void RawContext::suspend()
377 {
378   if (raw_context_parallel)
379     RawContext::suspend_parallel();
380   else
381     RawContext::suspend_serial();
382 }
383
384 void RawContext::suspend_serial()
385 {
386   /* determine the next context */
387   RawContext* next_context = nullptr;
388   unsigned long int i;
389   i = raw_process_index++;
390   if (i < xbt_dynar_length(simix_global->process_to_run)) {
391     /* execute the next process */
392     XBT_DEBUG("Run next process");
393     next_context = (RawContext*) xbt_dynar_get_as(
394         simix_global->process_to_run, i, smx_process_t)->context;
395   }
396   else {
397     /* all processes were run, return to maestro */
398     XBT_DEBUG("No more process to run");
399     next_context = (RawContext*) raw_maestro_context;
400   }
401   SIMIX_context_set_current(next_context);
402   raw_swapcontext(&this->stack_top_, next_context->stack_top_);
403 }
404
405 void RawContext::suspend_parallel()
406 {
407 #if HAVE_THREAD_CONTEXTS
408   /* determine the next context */
409   smx_process_t next_work = (smx_process_t) xbt_parmap_next(raw_parmap);
410   RawContext* next_context = nullptr;
411
412   if (next_work != NULL) {
413     /* there is a next process to resume */
414     XBT_DEBUG("Run next process");
415     next_context = (RawContext*) next_work->context;
416   }
417   else {
418     /* all processes were run, go to the barrier */
419     XBT_DEBUG("No more processes to run");
420     uintptr_t worker_id = (uintptr_t)
421       xbt_os_thread_get_specific(raw_worker_id_key);
422     next_context = (RawContext*) raw_workers_context[worker_id];
423     XBT_DEBUG("Restoring worker stack %zu (working threads = %zu)",
424         worker_id, raw_threads_working);
425   }
426
427   SIMIX_context_set_current(next_context);
428   raw_swapcontext(&this->stack_top_, next_context->stack_top_);
429 #endif
430 }
431
432 void RawContext::resume()
433 {
434   if (raw_context_parallel)
435     resume_parallel();
436   else
437     resume_serial();
438 }
439
440 void RawContext::resume_serial()
441 {
442   SIMIX_context_set_current(this);
443   raw_swapcontext(&raw_maestro_context->stack_top_, this->stack_top_);
444 }
445
446 void RawContext::resume_parallel()
447 {
448 #if HAVE_THREAD_CONTEXTS
449   uintptr_t worker_id = __sync_fetch_and_add(&raw_threads_working, 1);
450   xbt_os_thread_set_specific(raw_worker_id_key, (void*) worker_id);
451   RawContext* worker_context = (RawContext*) SIMIX_context_self();
452   raw_workers_context[worker_id] = worker_context;
453   XBT_DEBUG("Saving worker stack %zu", worker_id);
454   SIMIX_context_set_current(this);
455   raw_swapcontext(&worker_context->stack_top_, this->stack_top_);
456 #else
457   xbt_die("Parallel execution disabled");
458 #endif
459 }
460
461 /** @brief Resumes all processes ready to run. */
462 void RawContextFactory::run_all_adaptative()
463 {
464   unsigned long nb_processes = xbt_dynar_length(simix_global->process_to_run);
465   if (SIMIX_context_is_parallel()
466     && (unsigned long) SIMIX_context_get_parallel_threshold() < nb_processes) {
467         raw_context_parallel = true;
468         XBT_DEBUG("Runall // %lu", nb_processes);
469         this->run_all_parallel();
470     } else {
471         XBT_DEBUG("Runall serial %lu", nb_processes);
472         raw_context_parallel = false;
473         this->run_all_serial();
474     }
475 }
476
477 }
478 }