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 "ContextRaw.hpp"
9 #include "src/internal_config.h"
10 #include "xbt/parmap.hpp"
12 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
14 // ***** Loads of static stuff
16 #if HAVE_THREAD_CONTEXTS
17 static simgrid::xbt::Parmap<smx_actor_t>* raw_parmap;
18 static simgrid::kernel::context::RawContext** raw_workers_context; /* space to save the worker context in each thread */
19 static uintptr_t raw_threads_working; /* number of threads that have started their work */
20 static xbt_os_thread_key_t raw_worker_id_key; /* thread-specific storage for the thread id */
22 static unsigned long raw_process_index = 0; /* index of the next process to run in the
23 * list of runnable processes */
24 static simgrid::kernel::context::RawContext* raw_maestro_context;
26 static bool raw_context_parallel = false;
28 // ***** Raw context routines
30 typedef void (*rawctx_entry_point_t)(void *);
32 typedef void* raw_stack_t;
33 extern "C" raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
34 rawctx_entry_point_t entry_point, void* arg);
35 extern "C" void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context);
37 // TODO, we should handle FP, MMX and the x87 control-word (for x86 and x86_64)
39 #if SIMGRID_PROCESSOR_x86_64
41 #if defined(__APPLE__)
43 ".globl _raw_makecontext\n"
47 ".globl raw_makecontext\n"
51 ".globl raw_makecontext\n"
52 ".type raw_makecontext,@function\n"
53 "raw_makecontext:\n"/* Calling convention sets the arguments in rdi, rsi, rdx and rcx, respectively */
55 " mov %rdi,%rax\n" /* stack */
56 " add %rsi,%rax\n" /* size */
57 " andq $-16, %rax\n" /* align stack */
58 " movq $0, -8(%rax)\n" /* @return for func */
59 " mov %rdx,-16(%rax)\n" /* func */
60 " mov %rcx,-24(%rax)\n" /* arg/rdi */
61 " movq $0, -32(%rax)\n" /* rsi */
62 " movq $0, -40(%rax)\n" /* rdx */
63 " movq $0, -48(%rax)\n" /* rcx */
64 " movq $0, -56(%rax)\n" /* r8 */
65 " movq $0, -64(%rax)\n" /* r9 */
66 " movq $0, -72(%rax)\n" /* rbp */
67 " movq $0, -80(%rax)\n" /* rbx */
68 " movq $0, -88(%rax)\n" /* r12 */
69 " movq $0, -96(%rax)\n" /* r13 */
70 " movq $0, -104(%rax)\n" /* r14 */
71 " movq $0, -112(%rax)\n" /* r15 */
77 #if defined(__APPLE__)
79 ".globl _raw_swapcontext\n"
83 ".globl raw_swapcontext\n"
87 ".globl raw_swapcontext\n"
88 ".type raw_swapcontext,@function\n"
89 "raw_swapcontext:\n" /* Calling convention sets the arguments in rdi and rsi, respectively */
103 " mov %rsp,(%rdi)\n" /* old */
104 " mov %rsi,%rsp\n" /* new */
119 #elif SIMGRID_PROCESSOR_i686
121 #if defined(__APPLE__) || defined(_WIN32)
123 ".globl _raw_makecontext\n"
124 "_raw_makecontext:\n"
127 ".globl raw_makecontext\n"
128 ".type raw_makecontext,@function\n"
131 " movl 4(%esp),%eax\n" /* stack */
132 " addl 8(%esp),%eax\n" /* size */
133 " andl $-16, %eax\n" /* align stack */
134 " movl 12(%esp),%ecx\n" /* func */
135 " movl 16(%esp),%edx\n" /* arg */
136 " movl %edx, -4(%eax)\n"
137 " movl $0, -8(%eax)\n" /* @return for func */
138 " movl %ecx,-12(%eax)\n"
139 " movl $0, -16(%eax)\n" /* ebp */
140 " movl $0, -20(%eax)\n" /* ebx */
141 " movl $0, -24(%eax)\n" /* esi */
142 " movl $0, -28(%eax)\n" /* edi */
148 #if defined(__APPLE__) || defined(_WIN32)
150 ".globl _raw_swapcontext\n"
151 "_raw_swapcontext:\n"
154 ".globl raw_swapcontext\n"
155 ".type raw_swapcontext,@function\n"
158 // Fetch the parameters:
159 " movl 4(%esp),%eax\n" /* old (raw_stack_t*) */
160 " movl 8(%esp),%edx\n" /* new (raw_stack_t) */
161 // Save registers of the current context on the stack:
166 // Save the current context (stack pointer) in *old:
167 " movl %esp,(%eax)\n"
168 // Switch to the stack of the new context:
170 // Pop the values of the new context:
175 // Return using the return address of the new context:
181 /* If you implement raw contexts for other processors, don't forget to
182 update the definition of HAVE_RAW_CONTEXTS in tools/cmake/CompleteInFiles.cmake */
184 raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
185 rawctx_entry_point_t entry_point, void* arg) {
189 void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context) {
195 // ***** Method definitions
201 RawContextFactory::RawContextFactory()
202 : ContextFactory("RawContextFactory")
204 raw_context_parallel = SIMIX_context_is_parallel();
205 if (raw_context_parallel) {
206 #if HAVE_THREAD_CONTEXTS
207 int nthreads = SIMIX_context_get_nthreads();
208 xbt_os_thread_key_create(&raw_worker_id_key);
210 raw_parmap = nullptr;
211 raw_workers_context = new RawContext*[nthreads];
212 raw_maestro_context = nullptr;
214 // TODO: choose dynamically when SIMIX_context_get_parallel_threshold() > 1
218 RawContextFactory::~RawContextFactory()
220 #if HAVE_THREAD_CONTEXTS
222 delete[] raw_workers_context;
226 RawContext* RawContextFactory::create_context(std::function<void()> code,
227 void_pfn_smxprocess_t cleanup, smx_actor_t process)
229 return this->new_context<RawContext>(std::move(code), cleanup, process);
232 void RawContext::wrapper(void* arg)
234 RawContext* context = static_cast<RawContext*>(arg);
237 context->Context::stop();
238 } catch (StopRequest const&) {
239 XBT_DEBUG("Caught a StopRequest");
244 RawContext::RawContext(std::function<void()> code,
245 void_pfn_smxprocess_t cleanup, smx_actor_t process)
246 : Context(std::move(code), cleanup, process)
249 this->stack_ = SIMIX_context_stack_new();
250 this->stack_top_ = raw_makecontext(this->stack_,
251 smx_context_usable_stack_size,
255 if(process != nullptr && raw_maestro_context == nullptr)
256 raw_maestro_context = this;
259 &raw_maestro_context->stack_top_,
260 sizeof(raw_maestro_context->stack_top_));
264 RawContext::~RawContext()
266 SIMIX_context_stack_delete(this->stack_);
269 void RawContext::stop()
275 void RawContextFactory::run_all()
277 if (raw_context_parallel)
283 void RawContextFactory::run_all_serial()
285 if (simix_global->process_to_run.empty())
288 smx_actor_t first_process = simix_global->process_to_run.front();
289 raw_process_index = 1;
290 static_cast<RawContext*>(first_process->context)->resume_serial();
293 void RawContextFactory::run_all_parallel()
295 #if HAVE_THREAD_CONTEXTS
296 raw_threads_working = 0;
297 if (raw_parmap == nullptr)
298 raw_parmap = new simgrid::xbt::Parmap<smx_actor_t>(SIMIX_context_get_nthreads(), SIMIX_context_get_parallel_mode());
300 [](smx_actor_t process) {
301 RawContext* context = static_cast<RawContext*>(process->context);
302 context->resume_parallel();
304 simix_global->process_to_run);
306 xbt_die("You asked for a parallel execution, but you don't have any threads.");
310 void RawContext::suspend()
312 if (raw_context_parallel)
313 RawContext::suspend_parallel();
315 RawContext::suspend_serial();
318 void RawContext::suspend_serial()
320 /* determine the next context */
321 RawContext* next_context = nullptr;
322 unsigned long int i = raw_process_index;
324 if (i < simix_global->process_to_run.size()) {
325 /* execute the next process */
326 XBT_DEBUG("Run next process");
327 next_context = static_cast<RawContext*>(simix_global->process_to_run[i]->context);
329 /* all processes were run, return to maestro */
330 XBT_DEBUG("No more process to run");
331 next_context = static_cast<RawContext*>(raw_maestro_context);
333 SIMIX_context_set_current(next_context);
334 raw_swapcontext(&this->stack_top_, next_context->stack_top_);
337 void RawContext::suspend_parallel()
339 #if HAVE_THREAD_CONTEXTS
340 /* determine the next context */
341 boost::optional<smx_actor_t> next_work = raw_parmap->next();
342 RawContext* next_context;
344 /* there is a next process to resume */
345 XBT_DEBUG("Run next process");
346 next_context = static_cast<RawContext*>(next_work.get()->context);
348 /* all processes were run, go to the barrier */
349 XBT_DEBUG("No more processes to run");
350 uintptr_t worker_id = (uintptr_t)
351 xbt_os_thread_get_specific(raw_worker_id_key);
352 next_context = raw_workers_context[worker_id];
353 XBT_DEBUG("Restoring worker stack %zu (working threads = %zu)",
354 worker_id, raw_threads_working);
357 SIMIX_context_set_current(next_context);
358 raw_swapcontext(&this->stack_top_, next_context->stack_top_);
362 void RawContext::resume()
364 if (raw_context_parallel)
370 void RawContext::resume_serial()
372 SIMIX_context_set_current(this);
373 raw_swapcontext(&raw_maestro_context->stack_top_, this->stack_top_);
376 void RawContext::resume_parallel()
378 #if HAVE_THREAD_CONTEXTS
379 uintptr_t worker_id = __sync_fetch_and_add(&raw_threads_working, 1);
380 xbt_os_thread_set_specific(raw_worker_id_key, (void*) worker_id);
381 RawContext* worker_context = static_cast<RawContext*>(SIMIX_context_self());
382 raw_workers_context[worker_id] = worker_context;
383 XBT_DEBUG("Saving worker stack %zu", worker_id);
384 SIMIX_context_set_current(this);
385 raw_swapcontext(&worker_context->stack_top_, this->stack_top_);
387 xbt_die("Parallel execution disabled");
391 ContextFactory* raw_factory()
393 XBT_VERB("Using raw contexts. Because the glibc is just not good enough for us.");
394 return new RawContextFactory();