Logo AND Algorithmique Numérique Distribuée

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