1 /* Copyright (c) 2009-2018. 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"
8 #include "src/simix/smx_private.hpp"
10 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
12 // Raw context routines
14 typedef void (*rawctx_entry_point_t)(void *);
16 typedef void* raw_stack_t;
17 extern "C" raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
18 rawctx_entry_point_t entry_point, void* arg);
19 extern "C" void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context);
21 // TODO, we should handle FP, MMX and the x87 control-word (for x86 and x86_64)
23 #if SIMGRID_PROCESSOR_x86_64
25 #if defined(__APPLE__)
27 ".globl _raw_makecontext\n"
31 ".globl raw_makecontext\n"
35 ".globl raw_makecontext\n"
36 ".type raw_makecontext,@function\n"
37 "raw_makecontext:\n"/* Calling convention sets the arguments in rdi, rsi, rdx and rcx, respectively */
39 " mov %rdi,%rax\n" /* stack */
40 " add %rsi,%rax\n" /* size */
41 " andq $-16, %rax\n" /* align stack */
42 " movq $0, -8(%rax)\n" /* @return for func */
43 " mov %rdx,-16(%rax)\n" /* func */
44 " mov %rcx,-24(%rax)\n" /* arg/rdi */
45 " movq $0, -32(%rax)\n" /* rsi */
46 " movq $0, -40(%rax)\n" /* rdx */
47 " movq $0, -48(%rax)\n" /* rcx */
48 " movq $0, -56(%rax)\n" /* r8 */
49 " movq $0, -64(%rax)\n" /* r9 */
50 " movq $0, -72(%rax)\n" /* rbp */
51 " movq $0, -80(%rax)\n" /* rbx */
52 " movq $0, -88(%rax)\n" /* r12 */
53 " movq $0, -96(%rax)\n" /* r13 */
54 " movq $0, -104(%rax)\n" /* r14 */
55 " movq $0, -112(%rax)\n" /* r15 */
61 #if defined(__APPLE__)
63 ".globl _raw_swapcontext\n"
67 ".globl raw_swapcontext\n"
71 ".globl raw_swapcontext\n"
72 ".type raw_swapcontext,@function\n"
73 "raw_swapcontext:\n" /* Calling convention sets the arguments in rdi and rsi, respectively */
87 " mov %rsp,(%rdi)\n" /* old */
88 " mov %rsi,%rsp\n" /* new */
103 #elif SIMGRID_PROCESSOR_i686
105 #if defined(__APPLE__) || defined(_WIN32)
107 ".globl _raw_makecontext\n"
108 "_raw_makecontext:\n"
111 ".globl raw_makecontext\n"
112 ".type raw_makecontext,@function\n"
115 " movl 4(%esp),%eax\n" /* stack */
116 " addl 8(%esp),%eax\n" /* size */
117 " andl $-16, %eax\n" /* align stack */
118 " movl 12(%esp),%ecx\n" /* func */
119 " movl 16(%esp),%edx\n" /* arg */
120 " movl %edx, -4(%eax)\n"
121 " movl $0, -8(%eax)\n" /* @return for func */
122 " movl %ecx,-12(%eax)\n"
123 " movl $0, -16(%eax)\n" /* ebp */
124 " movl $0, -20(%eax)\n" /* ebx */
125 " movl $0, -24(%eax)\n" /* esi */
126 " movl $0, -28(%eax)\n" /* edi */
132 #if defined(__APPLE__) || defined(_WIN32)
134 ".globl _raw_swapcontext\n"
135 "_raw_swapcontext:\n"
138 ".globl raw_swapcontext\n"
139 ".type raw_swapcontext,@function\n"
142 // Fetch the parameters:
143 " movl 4(%esp),%eax\n" /* old (raw_stack_t*) */
144 " movl 8(%esp),%edx\n" /* new (raw_stack_t) */
145 // Save registers of the current context on the stack:
150 // Save the current context (stack pointer) in *old:
151 " movl %esp,(%eax)\n"
152 // Switch to the stack of the new context:
154 // Pop the values of the new context:
159 // Return using the return address of the new context:
165 /* If you implement raw contexts for other processors, don't forget to
166 update the definition of HAVE_RAW_CONTEXTS in tools/cmake/CompleteInFiles.cmake */
168 raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
169 rawctx_entry_point_t entry_point, void* arg) {
173 void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context) {
179 // ***** Method definitions
187 RawContextFactory::RawContextFactory() : ContextFactory("RawContextFactory"), parallel_(SIMIX_context_is_parallel())
189 RawContext::setMaestro(nullptr);
191 #if HAVE_THREAD_CONTEXTS
192 // TODO: choose dynamically when SIMIX_context_get_parallel_threshold() > 1
193 ParallelRawContext::initialize();
195 xbt_die("You asked for a parallel execution, but you don't have any threads.");
200 RawContextFactory::~RawContextFactory()
202 #if HAVE_THREAD_CONTEXTS
204 ParallelRawContext::finalize();
208 Context* RawContextFactory::create_context(std::function<void()> code, void_pfn_smxprocess_t cleanup_func,
211 #if HAVE_THREAD_CONTEXTS
213 return this->new_context<ParallelRawContext>(std::move(code), cleanup_func, process);
216 return this->new_context<SerialRawContext>(std::move(code), cleanup_func, process);
219 void RawContextFactory::run_all()
221 #if HAVE_THREAD_CONTEXTS
223 ParallelRawContext::run_all();
226 SerialRawContext::run_all();
231 RawContext* RawContext::maestro_context_ = nullptr;
233 RawContext::RawContext(std::function<void()> code, void_pfn_smxprocess_t cleanup, smx_actor_t process)
234 : Context(std::move(code), cleanup, process)
237 this->stack_ = SIMIX_context_stack_new();
238 this->stack_top_ = raw_makecontext(this->stack_, smx_context_usable_stack_size, RawContext::wrapper, this);
240 if (process != nullptr && maestro_context_ == nullptr)
241 maestro_context_ = this;
243 MC_ignore_heap(&maestro_context_->stack_top_, sizeof(maestro_context_->stack_top_));
247 RawContext::~RawContext()
249 SIMIX_context_stack_delete(this->stack_);
252 void RawContext::wrapper(void* arg)
254 RawContext* context = static_cast<RawContext*>(arg);
257 context->Context::stop();
258 } catch (StopRequest const&) {
259 XBT_DEBUG("Caught a StopRequest");
264 inline void RawContext::swap(RawContext* from, RawContext* to)
266 raw_swapcontext(&from->stack_top_, to->stack_top_);
269 void RawContext::stop()
277 unsigned long SerialRawContext::process_index_; /* index of the next process to run in the list of runnable processes */
279 void SerialRawContext::suspend()
281 /* determine the next context */
282 SerialRawContext* next_context;
283 unsigned long int i = process_index_;
285 if (i < simix_global->process_to_run.size()) {
286 /* execute the next process */
287 XBT_DEBUG("Run next process");
288 next_context = static_cast<SerialRawContext*>(simix_global->process_to_run[i]->context);
290 /* all processes were run, return to maestro */
291 XBT_DEBUG("No more process to run");
292 next_context = static_cast<SerialRawContext*>(RawContext::getMaestro());
294 SIMIX_context_set_current(next_context);
295 RawContext::swap(this, next_context);
298 void SerialRawContext::resume()
300 SIMIX_context_set_current(this);
301 RawContext::swap(RawContext::getMaestro(), this);
304 void SerialRawContext::run_all()
306 if (simix_global->process_to_run.empty())
308 smx_actor_t first_process = simix_global->process_to_run.front();
310 static_cast<SerialRawContext*>(first_process->context)->resume();
313 // ParallelRawContext
315 #if HAVE_THREAD_CONTEXTS
317 simgrid::xbt::Parmap<smx_actor_t>* ParallelRawContext::parmap_;
318 std::atomic<uintptr_t> ParallelRawContext::threads_working_; /* number of threads that have started their work */
319 xbt_os_thread_key_t ParallelRawContext::worker_id_key_; /* thread-specific storage for the thread id */
320 std::vector<ParallelRawContext*> ParallelRawContext::workers_context_; /* space to save the worker context
323 void ParallelRawContext::initialize()
326 workers_context_.clear();
327 workers_context_.resize(SIMIX_context_get_nthreads(), nullptr);
328 xbt_os_thread_key_create(&worker_id_key_);
331 void ParallelRawContext::finalize()
335 workers_context_.clear();
336 xbt_os_thread_key_destroy(worker_id_key_);
339 void ParallelRawContext::run_all()
341 threads_working_ = 0;
342 if (parmap_ == nullptr)
343 parmap_ = new simgrid::xbt::Parmap<smx_actor_t>(SIMIX_context_get_nthreads(), SIMIX_context_get_parallel_mode());
345 [](smx_actor_t process) {
346 ParallelRawContext* context = static_cast<ParallelRawContext*>(process->context);
349 simix_global->process_to_run);
352 void ParallelRawContext::suspend()
354 /* determine the next context */
355 boost::optional<smx_actor_t> next_work = parmap_->next();
356 ParallelRawContext* next_context;
358 /* there is a next process to resume */
359 XBT_DEBUG("Run next process");
360 next_context = static_cast<ParallelRawContext*>(next_work.get()->context);
362 /* all processes were run, go to the barrier */
363 XBT_DEBUG("No more processes to run");
364 uintptr_t worker_id = reinterpret_cast<uintptr_t>(xbt_os_thread_get_specific(worker_id_key_));
365 next_context = workers_context_[worker_id];
366 XBT_DEBUG("Restoring worker stack %zu (working threads = %zu)", worker_id, threads_working_.load());
369 SIMIX_context_set_current(next_context);
370 RawContext::swap(this, next_context);
373 void ParallelRawContext::resume()
375 uintptr_t worker_id = threads_working_.fetch_add(1, std::memory_order_relaxed);
376 xbt_os_thread_set_specific(worker_id_key_, reinterpret_cast<void*>(worker_id));
377 ParallelRawContext* worker_context = static_cast<ParallelRawContext*>(SIMIX_context_self());
378 workers_context_[worker_id] = worker_context;
379 XBT_DEBUG("Saving worker stack %zu", worker_id);
380 SIMIX_context_set_current(this);
381 RawContext::swap(worker_context, this);
386 ContextFactory* raw_factory()
388 XBT_VERB("Using raw contexts. Because the glibc is just not good enough for us.");
389 return new RawContextFactory();