Logo AND Algorithmique Numérique Distribuée

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