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"
13 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
15 // ***** Class definitions
22 class RawContextFactory;
24 /** @brief Fast context switching inspired from SystemV ucontexts.
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.
29 class RawContext : public Context {
31 void* stack_ = nullptr;
32 /** pointer to top the stack stack */
33 void* stack_top_ = nullptr;
35 friend class RawContextFactory;
36 RawContext(std::function<void()> code,
37 void_pfn_smxprocess_t cleanup_func,
39 ~RawContext() override;
41 static void wrapper(void* arg);
43 void suspend() override;
46 void suspend_serial();
47 void suspend_parallel();
49 void resume_parallel();
52 class RawContextFactory : public ContextFactory {
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;
60 void run_all_adaptative();
61 void run_all_serial();
62 void run_all_parallel();
65 ContextFactory* raw_factory()
67 XBT_VERB("Using raw contexts. Because the glibc is just not good enough for us.");
68 return new RawContextFactory();
73 // ***** Loads of static stuff
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 */
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;
85 static bool raw_context_parallel = false;
87 // ***** Raw context routines
89 typedef void (*rawctx_entry_point_t)(void *);
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);
96 // TODO, we should handle FP, MMX and the x87 control-word (for x86 and x86_64)
98 #if SIMGRID_PROCESSOR_x86_64
100 #if defined(__APPLE__)
102 ".globl _raw_makecontext\n"
103 "_raw_makecontext:\n"
104 #elif defined(_WIN32)
106 ".globl raw_makecontext\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 */
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 */
136 #if defined(__APPLE__)
138 ".globl _raw_swapcontext\n"
139 "_raw_swapcontext:\n"
140 #elif defined(_WIN32)
142 ".globl raw_swapcontext\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 */
162 " mov %rsp,(%rdi)\n" /* old */
163 " mov %rsi,%rsp\n" /* new */
178 #elif SIMGRID_PROCESSOR_i686
180 #if defined(__APPLE__) || defined(_WIN32)
182 ".globl _raw_makecontext\n"
183 "_raw_makecontext:\n"
186 ".globl raw_makecontext\n"
187 ".type raw_makecontext,@function\n"
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 */
207 #if defined(__APPLE__) || defined(_WIN32)
209 ".globl _raw_swapcontext\n"
210 "_raw_swapcontext:\n"
213 ".globl raw_swapcontext\n"
214 ".type raw_swapcontext,@function\n"
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:
225 // Save the current context (stack pointer) in *old:
226 " movl %esp,(%eax)\n"
227 // Switch to the stack of the new context:
229 // Pop the values of the new context:
234 // Return using the return address of the new context:
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 */
243 raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
244 rawctx_entry_point_t entry_point, void* arg) {
248 void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context) {
254 // ***** Method definitions
260 RawContextFactory::RawContextFactory()
261 : ContextFactory("RawContextFactory")
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);
269 raw_parmap = nullptr;
270 raw_workers_context = xbt_new(RawContext*, nthreads);
271 raw_maestro_context = nullptr;
273 // TODO, if(SIMIX_context_get_parallel_threshold() > 1) => choose dynamically
277 RawContextFactory::~RawContextFactory()
279 #if HAVE_THREAD_CONTEXTS
281 xbt_parmap_destroy(raw_parmap);
282 xbt_free(raw_workers_context);
286 RawContext* RawContextFactory::create_context(std::function<void()> code,
287 void_pfn_smxprocess_t cleanup, smx_actor_t process)
289 return this->new_context<RawContext>(std::move(code), cleanup, process);
292 void RawContext::wrapper(void* arg)
294 RawContext* context = static_cast<RawContext*>(arg);
299 RawContext::RawContext(std::function<void()> code,
300 void_pfn_smxprocess_t cleanup, smx_actor_t process)
301 : Context(std::move(code), cleanup, process)
304 this->stack_ = SIMIX_context_stack_new();
305 this->stack_top_ = raw_makecontext(this->stack_,
306 smx_context_usable_stack_size,
310 if(process != nullptr && raw_maestro_context == nullptr)
311 raw_maestro_context = this;
314 &raw_maestro_context->stack_top_,
315 sizeof(raw_maestro_context->stack_top_));
319 RawContext::~RawContext()
321 SIMIX_context_stack_delete(this->stack_);
324 void RawContext::stop()
330 void RawContextFactory::run_all()
332 if (raw_context_parallel)
338 void RawContextFactory::run_all_serial()
340 if (xbt_dynar_is_empty(simix_global->process_to_run))
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();
349 void RawContextFactory::run_all_parallel()
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,
358 smx_actor_t process = static_cast<smx_actor_t>(arg);
359 RawContext* context = static_cast<RawContext*>(process->context);
360 context->resume_parallel();
362 simix_global->process_to_run);
364 xbt_die("You asked for a parallel execution, but you don't have any threads.");
368 void RawContext::suspend()
370 if (raw_context_parallel)
371 RawContext::suspend_parallel();
373 RawContext::suspend_serial();
376 void RawContext::suspend_serial()
378 /* determine the next context */
379 RawContext* next_context = nullptr;
380 unsigned long int i = 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);
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);
391 SIMIX_context_set_current(next_context);
392 raw_swapcontext(&this->stack_top_, next_context->stack_top_);
395 void RawContext::suspend_parallel()
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;
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);
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);
416 SIMIX_context_set_current(next_context);
417 raw_swapcontext(&this->stack_top_, next_context->stack_top_);
421 void RawContext::resume()
423 if (raw_context_parallel)
429 void RawContext::resume_serial()
431 SIMIX_context_set_current(this);
432 raw_swapcontext(&raw_maestro_context->stack_top_, this->stack_top_);
435 void RawContext::resume_parallel()
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_);
446 xbt_die("Parallel execution disabled");
450 /** @brief Resumes all processes ready to run. */
451 void RawContextFactory::run_all_adaptative()
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();
460 XBT_DEBUG("Runall serial %lu", nb_processes);
461 raw_context_parallel = false;
462 this->run_all_serial();