Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
423f13dc0fe4ee048acd2085b90473a09bb3f373
[simgrid.git] / src / kernel / context / ContextRaw.cpp
1 /* Copyright (c) 2009-2018. The SimGrid Team. All rights reserved.          */
2
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. */
5
6 #include "ContextRaw.hpp"
7 #include "context_private.hpp"
8 #include "mc/mc.h"
9 #include "simgrid/Exception.hpp"
10 #include "src/simix/smx_private.hpp"
11
12 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
13
14 // Raw context routines
15
16 typedef void (*rawctx_entry_point_t)(void *);
17
18 typedef void* raw_stack_t;
19 extern "C" raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
20                                    rawctx_entry_point_t entry_point, void* arg);
21 extern "C" void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context);
22
23 // TODO, we should handle FP, MMX and the x87 control-word (for x86 and x86_64)
24
25 #if SIMGRID_PROCESSOR_x86_64
26 __asm__ (
27 #if defined(__APPLE__)
28    ".text\n"
29    ".globl _raw_makecontext\n"
30    "_raw_makecontext:\n"
31 #elif defined(_WIN32)
32    ".text\n"
33    ".globl raw_makecontext\n"
34    "raw_makecontext:\n"
35 #else
36    ".text\n"
37    ".globl raw_makecontext\n"
38    ".type raw_makecontext,@function\n"
39    "raw_makecontext:\n"/* Calling convention sets the arguments in rdi, rsi, rdx and rcx, respectively */
40 #endif
41    "   mov %rdi,%rax\n"      /* stack */
42    "   add %rsi,%rax\n"      /* size  */
43    "   andq $-16, %rax\n"    /* align stack */
44    "   movq $0,   -8(%rax)\n" /* @return for func */
45    "   mov %rdx,-16(%rax)\n" /* func */
46    "   mov %rcx,-24(%rax)\n" /* arg/rdi */
47    "   movq $0,  -32(%rax)\n" /* rsi */
48    "   movq $0,  -40(%rax)\n" /* rdx */
49    "   movq $0,  -48(%rax)\n" /* rcx */
50    "   movq $0,  -56(%rax)\n" /* r8  */
51    "   movq $0,  -64(%rax)\n" /* r9  */
52    "   movq $0,  -72(%rax)\n" /* rbp */
53    "   movq $0,  -80(%rax)\n" /* rbx */
54    "   movq $0,  -88(%rax)\n" /* r12 */
55    "   movq $0,  -96(%rax)\n" /* r13 */
56    "   movq $0, -104(%rax)\n" /* r14 */
57    "   movq $0, -112(%rax)\n" /* r15 */
58    "   sub $112,%rax\n"
59    "   ret\n"
60 );
61
62 __asm__ (
63 #if defined(__APPLE__)
64    ".text\n"
65    ".globl _raw_swapcontext\n"
66    "_raw_swapcontext:\n"
67 #elif defined(_WIN32)
68    ".text\n"
69    ".globl raw_swapcontext\n"
70    "raw_swapcontext:\n"
71 #else
72    ".text\n"
73    ".globl raw_swapcontext\n"
74    ".type raw_swapcontext,@function\n"
75    "raw_swapcontext:\n" /* Calling convention sets the arguments in rdi and rsi, respectively */
76 #endif
77    "   push %rdi\n"
78    "   push %rsi\n"
79    "   push %rdx\n"
80    "   push %rcx\n"
81    "   push %r8\n"
82    "   push %r9\n"
83    "   push %rbp\n"
84    "   push %rbx\n"
85    "   push %r12\n"
86    "   push %r13\n"
87    "   push %r14\n"
88    "   push %r15\n"
89    "   mov %rsp,(%rdi)\n" /* old */
90    "   mov %rsi,%rsp\n" /* new */
91    "   pop %r15\n"
92    "   pop %r14\n"
93    "   pop %r13\n"
94    "   pop %r12\n"
95    "   pop %rbx\n"
96    "   pop %rbp\n"
97    "   pop %r9\n"
98    "   pop %r8\n"
99    "   pop %rcx\n"
100    "   pop %rdx\n"
101    "   pop %rsi\n"
102    "   pop %rdi\n"
103    "   ret\n"
104 );
105 #elif SIMGRID_PROCESSOR_i686
106 __asm__ (
107 #if defined(__APPLE__) || defined(_WIN32)
108    ".text\n"
109    ".globl _raw_makecontext\n"
110    "_raw_makecontext:\n"
111 #else
112    ".text\n"
113    ".globl raw_makecontext\n"
114    ".type raw_makecontext,@function\n"
115    "raw_makecontext:\n"
116 #endif
117    "   movl 4(%esp),%eax\n"   /* stack */
118    "   addl 8(%esp),%eax\n"   /* size  */
119    "   andl $-16, %eax\n"     /* align stack */
120    "   movl 12(%esp),%ecx\n"  /* func  */
121    "   movl 16(%esp),%edx\n"  /* arg   */
122    "   movl %edx, -4(%eax)\n"
123    "   movl $0,   -8(%eax)\n" /* @return for func */
124    "   movl %ecx,-12(%eax)\n"
125    "   movl $0,  -16(%eax)\n" /* ebp */
126    "   movl $0,  -20(%eax)\n" /* ebx */
127    "   movl $0,  -24(%eax)\n" /* esi */
128    "   movl $0,  -28(%eax)\n" /* edi */
129    "   subl $28,%eax\n"
130    "   retl\n"
131 );
132
133 __asm__ (
134 #if defined(__APPLE__) || defined(_WIN32)
135    ".text\n"
136    ".globl _raw_swapcontext\n"
137    "_raw_swapcontext:\n"
138 #else
139    ".text\n"
140    ".globl raw_swapcontext\n"
141    ".type raw_swapcontext,@function\n"
142    "raw_swapcontext:\n"
143 #endif
144    // Fetch the parameters:
145    "   movl 4(%esp),%eax\n" /* old (raw_stack_t*) */
146    "   movl 8(%esp),%edx\n" /* new (raw_stack_t)  */
147    // Save registers of the current context on the stack:
148    "   pushl %ebp\n"
149    "   pushl %ebx\n"
150    "   pushl %esi\n"
151    "   pushl %edi\n"
152    // Save the current context (stack pointer) in *old:
153    "   movl %esp,(%eax)\n"
154    // Switch to the stack of the new context:
155    "   movl %edx,%esp\n"
156    // Pop the values of the new context:
157    "   popl %edi\n"
158    "   popl %esi\n"
159    "   popl %ebx\n"
160    "   popl %ebp\n"
161    // Return using the return address of the new context:
162    "   retl\n"
163 );
164 #else
165
166
167 /* If you implement raw contexts for other processors, don't forget to
168    update the definition of HAVE_RAW_CONTEXTS in tools/cmake/CompleteInFiles.cmake */
169
170 raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
171                             rawctx_entry_point_t entry_point, void* arg) {
172    THROW_UNIMPLEMENTED;
173 }
174
175 void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context) {
176    THROW_UNIMPLEMENTED;
177 }
178
179 #endif
180
181 // ***** Method definitions
182
183 namespace simgrid {
184 namespace kernel {
185 namespace context {
186
187 // RawContextFactory
188
189 RawContextFactory::RawContextFactory() : ContextFactory("RawContextFactory"), parallel_(SIMIX_context_is_parallel())
190 {
191   RawContext::setMaestro(nullptr);
192   if (parallel_) {
193 #if HAVE_THREAD_CONTEXTS
194     // TODO: choose dynamically when SIMIX_context_get_parallel_threshold() > 1
195     ParallelRawContext::initialize();
196 #else
197     xbt_die("You asked for a parallel execution, but you don't have any threads.");
198 #endif
199   }
200 }
201
202 RawContextFactory::~RawContextFactory()
203 {
204 #if HAVE_THREAD_CONTEXTS
205   if (parallel_)
206     ParallelRawContext::finalize();
207 #endif
208 }
209
210 Context* RawContextFactory::create_context(std::function<void()> code, void_pfn_smxprocess_t cleanup_func,
211                                            smx_actor_t process)
212 {
213 #if HAVE_THREAD_CONTEXTS
214   if (parallel_)
215     return this->new_context<ParallelRawContext>(std::move(code), cleanup_func, process);
216 #endif
217
218   return this->new_context<SerialRawContext>(std::move(code), cleanup_func, process);
219 }
220
221 void RawContextFactory::run_all()
222 {
223 #if HAVE_THREAD_CONTEXTS
224   if (parallel_)
225     ParallelRawContext::run_all();
226   else
227 #endif
228     SerialRawContext::run_all();
229 }
230
231 // RawContext
232
233 RawContext* RawContext::maestro_context_ = nullptr;
234
235 RawContext::RawContext(std::function<void()> code, void_pfn_smxprocess_t cleanup, smx_actor_t process)
236     : Context(std::move(code), cleanup, process)
237 {
238    if (has_code()) {
239      this->stack_ = SIMIX_context_stack_new();
240 #if PTH_STACKGROWTH == -1
241      ASAN_ONLY(this->asan_stack_ = static_cast<char*>(this->stack_) + smx_context_usable_stack_size);
242 #else
243      ASAN_ONLY(this->asan_stack_ = this->stack_);
244 #endif
245      this->stack_top_ = raw_makecontext(this->stack_, smx_context_usable_stack_size, RawContext::wrapper, this);
246    } else {
247      if (process != nullptr && maestro_context_ == nullptr)
248        maestro_context_ = this;
249      if (MC_is_active())
250        MC_ignore_heap(&maestro_context_->stack_top_, sizeof(maestro_context_->stack_top_));
251    }
252 }
253
254 RawContext::~RawContext()
255 {
256   SIMIX_context_stack_delete(this->stack_);
257 }
258
259 void RawContext::wrapper(void* arg)
260 {
261   RawContext* context = static_cast<RawContext*>(arg);
262   ASAN_FINISH_SWITCH(nullptr, &context->asan_ctx_->asan_stack_, &context->asan_ctx_->asan_stack_size_);
263   try {
264     (*context)();
265   } catch (StopRequest const&) {
266     XBT_DEBUG("Caught a StopRequest");
267   } catch (simgrid::Exception const& e) {
268     XBT_INFO("Actor killed by an uncatched exception %s", simgrid::xbt::demangle(typeid(e).name()).get());
269     throw;
270   }
271   context->Context::stop();
272
273   ASAN_ONLY(context->asan_stop_ = true);
274   context->suspend();
275 }
276
277 inline void RawContext::swap(RawContext* from, RawContext* to)
278 {
279   ASAN_ONLY(void* fake_stack = nullptr);
280   ASAN_ONLY(to->asan_ctx_ = from);
281   ASAN_START_SWITCH(from->asan_stop_ ? nullptr : &fake_stack, to->asan_stack_, to->asan_stack_size_);
282   raw_swapcontext(&from->stack_top_, to->stack_top_);
283   ASAN_FINISH_SWITCH(fake_stack, &from->asan_ctx_->asan_stack_, &from->asan_ctx_->asan_stack_size_);
284 }
285
286 void RawContext::stop()
287 {
288   Context::stop();
289   throw StopRequest();
290 }
291
292 // SerialRawContext
293
294 unsigned long SerialRawContext::process_index_; /* index of the next process to run in the list of runnable processes */
295
296 void SerialRawContext::suspend()
297 {
298   /* determine the next context */
299   SerialRawContext* next_context;
300   unsigned long int i = process_index_;
301   process_index_++;
302   if (i < simix_global->process_to_run.size()) {
303     /* execute the next process */
304     XBT_DEBUG("Run next process");
305     next_context = static_cast<SerialRawContext*>(simix_global->process_to_run[i]->context_);
306   } else {
307     /* all processes were run, return to maestro */
308     XBT_DEBUG("No more process to run");
309     next_context = static_cast<SerialRawContext*>(RawContext::getMaestro());
310   }
311   SIMIX_context_set_current(next_context);
312   RawContext::swap(this, next_context);
313 }
314
315 void SerialRawContext::resume()
316 {
317   SIMIX_context_set_current(this);
318   RawContext::swap(RawContext::getMaestro(), this);
319 }
320
321 void SerialRawContext::run_all()
322 {
323   if (simix_global->process_to_run.empty())
324     return;
325   smx_actor_t first_process = simix_global->process_to_run.front();
326   process_index_            = 1;
327   static_cast<SerialRawContext*>(first_process->context_)->resume();
328 }
329
330 // ParallelRawContext
331
332 #if HAVE_THREAD_CONTEXTS
333
334 simgrid::xbt::Parmap<smx_actor_t>* ParallelRawContext::parmap_;
335 std::atomic<uintptr_t> ParallelRawContext::threads_working_; /* number of threads that have started their work */
336 uintptr_t thread_local ParallelRawContext::worker_id_;       /* thread-specific storage for the thread id */
337 std::vector<ParallelRawContext*> ParallelRawContext::workers_context_; /* space to save the worker context
338                                                                           in each thread */
339
340 void ParallelRawContext::initialize()
341 {
342   parmap_ = nullptr;
343   workers_context_.clear();
344   workers_context_.resize(SIMIX_context_get_nthreads(), nullptr);
345 }
346
347 void ParallelRawContext::finalize()
348 {
349   delete parmap_;
350   parmap_ = nullptr;
351   workers_context_.clear();
352 }
353
354 void ParallelRawContext::run_all()
355 {
356   threads_working_ = 0;
357   if (parmap_ == nullptr)
358     parmap_ = new simgrid::xbt::Parmap<smx_actor_t>(SIMIX_context_get_nthreads(), SIMIX_context_get_parallel_mode());
359   parmap_->apply(
360       [](smx_actor_t process) {
361         ParallelRawContext* context = static_cast<ParallelRawContext*>(process->context_);
362         context->resume();
363       },
364       simix_global->process_to_run);
365 }
366
367 void ParallelRawContext::suspend()
368 {
369   /* determine the next context */
370   boost::optional<smx_actor_t> next_work = parmap_->next();
371   ParallelRawContext* next_context;
372   if (next_work) {
373     /* there is a next process to resume */
374     XBT_DEBUG("Run next process");
375     next_context = static_cast<ParallelRawContext*>(next_work.get()->context_);
376   } else {
377     /* all processes were run, go to the barrier */
378     XBT_DEBUG("No more processes to run");
379     next_context = workers_context_[worker_id_];
380     XBT_DEBUG("Restoring worker stack %zu (working threads = %zu)", worker_id_, threads_working_.load());
381   }
382
383   SIMIX_context_set_current(next_context);
384   RawContext::swap(this, next_context);
385 }
386
387 void ParallelRawContext::resume()
388 {
389   worker_id_                         = threads_working_.fetch_add(1, std::memory_order_relaxed);
390   ParallelRawContext* worker_context = static_cast<ParallelRawContext*>(SIMIX_context_self());
391   workers_context_[worker_id_]       = worker_context;
392   XBT_DEBUG("Saving worker stack %zu", worker_id_);
393   SIMIX_context_set_current(this);
394   RawContext::swap(worker_context, this);
395 }
396
397 #endif
398
399 ContextFactory* raw_factory()
400 {
401   XBT_VERB("Using raw contexts. Because the glibc is just not good enough for us.");
402   return new RawContextFactory();
403 }
404 }}}