1 /* Copyright (c) 2009-2017. The SimGrid Team. All rights reserved. */
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. */
6 #include "src/internal_config.h"
8 #include "xbt/parmap.h"
10 #include "src/simix/smx_private.h"
12 #include "src/mc/mc_ignore.h"
14 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
16 // ***** Class definitions
23 class RawContextFactory;
25 /** @brief Fast context switching inspired from SystemV ucontexts.
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.
30 class RawContext : public Context {
32 void* stack_ = nullptr;
33 /** pointer to top the stack stack */
34 void* stack_top_ = nullptr;
36 friend class RawContextFactory;
37 RawContext(std::function<void()> code,
38 void_pfn_smxprocess_t cleanup_func,
40 ~RawContext() override;
42 static void wrapper(void* arg);
44 void suspend() override;
47 void suspend_serial();
48 void suspend_parallel();
50 void resume_parallel();
53 class RawContextFactory : public ContextFactory {
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;
61 void run_all_adaptative();
62 void run_all_serial();
63 void run_all_parallel();
66 ContextFactory* raw_factory()
68 XBT_VERB("Using raw contexts. Because the glibc is just not good enough for us.");
69 return new RawContextFactory();
74 // ***** Loads of static stuff
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 */
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;
86 static bool raw_context_parallel = false;
88 // ***** Raw context routines
90 typedef void (*rawctx_entry_point_t)(void *);
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);
97 // TODO, we should handle FP, MMX and the x87 control-word (for x86 and x86_64)
99 #if SIMGRID_PROCESSOR_x86_64
101 #if defined(__APPLE__)
103 ".globl _raw_makecontext\n"
104 "_raw_makecontext:\n"
105 #elif defined(_WIN32)
107 ".globl raw_makecontext\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 */
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 */
137 #if defined(__APPLE__)
139 ".globl _raw_swapcontext\n"
140 "_raw_swapcontext:\n"
141 #elif defined(_WIN32)
143 ".globl raw_swapcontext\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 */
163 " mov %rsp,(%rdi)\n" /* old */
164 " mov %rsi,%rsp\n" /* new */
179 #elif SIMGRID_PROCESSOR_i686
181 #if defined(__APPLE__) || defined(_WIN32)
183 ".globl _raw_makecontext\n"
184 "_raw_makecontext:\n"
187 ".globl raw_makecontext\n"
188 ".type raw_makecontext,@function\n"
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 */
208 #if defined(__APPLE__) || defined(_WIN32)
210 ".globl _raw_swapcontext\n"
211 "_raw_swapcontext:\n"
214 ".globl raw_swapcontext\n"
215 ".type raw_swapcontext,@function\n"
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:
226 // Save the current context (stack pointer) in *old:
227 " movl %esp,(%eax)\n"
228 // Switch to the stack of the new context:
230 // Pop the values of the new context:
235 // Return using the return address of the new context:
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 */
244 raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
245 rawctx_entry_point_t entry_point, void* arg) {
249 void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context) {
255 // ***** Method definitions
261 RawContextFactory::RawContextFactory()
262 : ContextFactory("RawContextFactory")
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);
270 raw_parmap = nullptr;
271 raw_workers_context = xbt_new(RawContext*, nthreads);
272 raw_maestro_context = nullptr;
274 // TODO, if(SIMIX_context_get_parallel_threshold() > 1) => choose dynamically
278 RawContextFactory::~RawContextFactory()
280 #if HAVE_THREAD_CONTEXTS
282 xbt_parmap_destroy(raw_parmap);
283 xbt_free(raw_workers_context);
287 RawContext* RawContextFactory::create_context(std::function<void()> code,
288 void_pfn_smxprocess_t cleanup, smx_actor_t process)
290 return this->new_context<RawContext>(std::move(code), cleanup, process);
293 void RawContext::wrapper(void* arg)
295 RawContext* context = static_cast<RawContext*>(arg);
300 RawContext::RawContext(std::function<void()> code,
301 void_pfn_smxprocess_t cleanup, smx_actor_t process)
302 : Context(std::move(code), cleanup, process)
305 this->stack_ = SIMIX_context_stack_new();
306 this->stack_top_ = raw_makecontext(this->stack_,
307 smx_context_usable_stack_size,
311 if(process != nullptr && raw_maestro_context == nullptr)
312 raw_maestro_context = this;
315 &raw_maestro_context->stack_top_,
316 sizeof(raw_maestro_context->stack_top_));
320 RawContext::~RawContext()
322 SIMIX_context_stack_delete(this->stack_);
325 void RawContext::stop()
331 void RawContextFactory::run_all()
333 if (raw_context_parallel)
339 void RawContextFactory::run_all_serial()
341 if (xbt_dynar_is_empty(simix_global->process_to_run))
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();
350 void RawContextFactory::run_all_parallel()
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,
359 smx_actor_t process = static_cast<smx_actor_t>(arg);
360 RawContext* context = static_cast<RawContext*>(process->context);
361 context->resume_parallel();
363 simix_global->process_to_run);
365 xbt_die("You asked for a parallel execution, but you don't have any threads.");
369 void RawContext::suspend()
371 if (raw_context_parallel)
372 RawContext::suspend_parallel();
374 RawContext::suspend_serial();
377 void RawContext::suspend_serial()
379 /* determine the next context */
380 RawContext* next_context = nullptr;
381 unsigned long int i = 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);
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);
392 SIMIX_context_set_current(next_context);
393 raw_swapcontext(&this->stack_top_, next_context->stack_top_);
396 void RawContext::suspend_parallel()
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;
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);
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);
417 SIMIX_context_set_current(next_context);
418 raw_swapcontext(&this->stack_top_, next_context->stack_top_);
422 void RawContext::resume()
424 if (raw_context_parallel)
430 void RawContext::resume_serial()
432 SIMIX_context_set_current(this);
433 raw_swapcontext(&raw_maestro_context->stack_top_, this->stack_top_);
436 void RawContext::resume_parallel()
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_);
447 xbt_die("Parallel execution disabled");
451 /** @brief Resumes all processes ready to run. */
452 void RawContextFactory::run_all_adaptative()
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();
461 XBT_DEBUG("Runall serial %lu", nb_processes);
462 raw_context_parallel = false;
463 this->run_all_serial();