Logo AND Algorithmique Numérique Distribuée

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