Logo AND Algorithmique Numérique Distribuée

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