1 /* Copyright (c) 2009-2015. The SimGrid Team.
2 * All rights reserved. */
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. */
12 #include "src/internal_config.h"
15 #include "xbt/parmap.h"
16 #include "xbt/dynar.h"
18 #include "src/simix/smx_private.h"
21 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
23 // ***** Class definitions
30 class RawContextFactory;
32 /** @brief Fast context switching inspired from SystemV ucontexts.
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.
37 class RawContext : public Context {
39 void* stack_ = nullptr;
40 /** pointer to top the stack stack */
41 void* stack_top_ = nullptr;
43 friend class RawContextFactory;
44 RawContext(std::function<void()> code,
45 void_pfn_smxprocess_t cleanup_func,
46 smx_process_t process);
47 ~RawContext() override;
49 static void wrapper(void* arg);
51 void suspend() override;
54 void suspend_serial();
55 void suspend_parallel();
57 void resume_parallel();
60 class RawContextFactory : public ContextFactory {
63 ~RawContextFactory() override;
64 RawContext* create_context(std::function<void()> code,
65 void_pfn_smxprocess_t cleanup, smx_process_t process) override;
66 void run_all() override;
68 void run_all_adaptative();
69 void run_all_serial();
70 void run_all_parallel();
73 ContextFactory* raw_factory()
75 XBT_VERB("Using raw contexts. Because the glibc is just not good enough for us.");
76 return new RawContextFactory();
81 // ***** Loads of static stuff
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 */
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;
93 static bool raw_context_parallel = false;
95 // ***** Raw context routines
97 typedef void (*rawctx_entry_point_t)(void *);
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);
104 // TODO, we should handle FP, MMX and the x87 control-word (for x86 and x86_64)
106 #if SIMGRID_PROCESSOR_x86_64
108 #if defined(__APPLE__)
110 ".globl _raw_makecontext\n"
111 "_raw_makecontext:\n"
112 #elif defined(_WIN32)
114 ".globl raw_makecontext\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 */
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 */
144 #if defined(__APPLE__)
146 ".globl _raw_swapcontext\n"
147 "_raw_swapcontext:\n"
148 #elif defined(_WIN32)
150 ".globl raw_swapcontext\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 */
170 " mov %rsp,(%rdi)\n" /* old */
171 " mov %rsi,%rsp\n" /* new */
186 #elif SIMGRID_PROCESSOR_i686
188 #if defined(__APPLE__) || defined(_WIN32)
190 ".globl _raw_makecontext\n"
191 "_raw_makecontext:\n"
194 ".globl raw_makecontext\n"
195 ".type raw_makecontext,@function\n"
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 */
215 #if defined(__APPLE__) || defined(_WIN32)
217 ".globl _raw_swapcontext\n"
218 "_raw_swapcontext:\n"
221 ".globl raw_swapcontext\n"
222 ".type raw_swapcontext,@function\n"
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:
233 // Save the current context (stack pointer) in *old:
234 " movl %esp,(%eax)\n"
235 // Switch to the stack of the new context:
237 // Pop the values of the new context:
242 // Return using the return address of the new context:
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 */
251 raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
252 rawctx_entry_point_t entry_point, void* arg) {
256 void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context) {
262 // ***** Method definitions
268 RawContextFactory::RawContextFactory()
269 : ContextFactory("RawContextFactory")
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);
277 raw_parmap = nullptr;
278 raw_workers_context = xbt_new(RawContext*, nthreads);
279 raw_maestro_context = nullptr;
281 // TODO, if(SIMIX_context_get_parallel_threshold() > 1) => choose dynamically
285 RawContextFactory::~RawContextFactory()
287 #if HAVE_THREAD_CONTEXTS
289 xbt_parmap_destroy(raw_parmap);
290 xbt_free(raw_workers_context);
294 RawContext* RawContextFactory::create_context(std::function<void()> code,
295 void_pfn_smxprocess_t cleanup, smx_process_t process)
297 return this->new_context<RawContext>(std::move(code),
301 void RawContext::wrapper(void* arg)
303 RawContext* context = (RawContext*) arg;
308 RawContext::RawContext(std::function<void()> code,
309 void_pfn_smxprocess_t cleanup, smx_process_t process)
310 : Context(std::move(code), cleanup, process)
313 this->stack_ = SIMIX_context_stack_new();
314 this->stack_top_ = raw_makecontext(this->stack_,
315 smx_context_usable_stack_size,
319 if(process != nullptr && raw_maestro_context == nullptr)
320 raw_maestro_context = this;
323 &raw_maestro_context->stack_top_,
324 sizeof(raw_maestro_context->stack_top_));
328 RawContext::~RawContext()
330 SIMIX_context_stack_delete(this->stack_);
333 void RawContext::stop()
339 void RawContextFactory::run_all()
341 if (raw_context_parallel)
347 void RawContextFactory::run_all_serial()
349 if (xbt_dynar_is_empty(simix_global->process_to_run))
352 smx_process_t first_process =
353 xbt_dynar_get_as(simix_global->process_to_run, 0, smx_process_t);
354 raw_process_index = 1;
355 static_cast<RawContext*>(first_process->context)->resume_serial();
358 void RawContextFactory::run_all_parallel()
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,
367 smx_process_t process = static_cast<smx_process_t>(arg);
368 RawContext* context = static_cast<RawContext*>(process->context);
369 context->resume_parallel();
371 simix_global->process_to_run);
373 xbt_die("You asked for a parallel execution, but you don't have any threads.");
377 void RawContext::suspend()
379 if (raw_context_parallel)
380 RawContext::suspend_parallel();
382 RawContext::suspend_serial();
385 void RawContext::suspend_serial()
387 /* determine the next context */
388 RawContext* next_context = nullptr;
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_process_t)->context;
398 /* all processes were run, return to maestro */
399 XBT_DEBUG("No more process to run");
400 next_context = (RawContext*) raw_maestro_context;
402 SIMIX_context_set_current(next_context);
403 raw_swapcontext(&this->stack_top_, next_context->stack_top_);
406 void RawContext::suspend_parallel()
408 #if HAVE_THREAD_CONTEXTS
409 /* determine the next context */
410 smx_process_t next_work = (smx_process_t) xbt_parmap_next(raw_parmap);
411 RawContext* next_context = nullptr;
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;
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);
428 SIMIX_context_set_current(next_context);
429 raw_swapcontext(&this->stack_top_, next_context->stack_top_);
433 void RawContext::resume()
435 if (raw_context_parallel)
441 void RawContext::resume_serial()
443 SIMIX_context_set_current(this);
444 raw_swapcontext(&raw_maestro_context->stack_top_, this->stack_top_);
447 void RawContext::resume_parallel()
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_);
458 xbt_die("Parallel execution disabled");
462 /** @brief Resumes all processes ready to run. */
463 void RawContextFactory::run_all_adaptative()
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();
472 XBT_DEBUG("Runall serial %lu", nb_processes);
473 raw_context_parallel = false;
474 this->run_all_serial();