Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Use C++ atomic instead of compiler builtins.
[simgrid.git] / src / kernel / context / ContextRaw.cpp
1 /* Copyright (c) 2009-2017. 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
8 #include "mc/mc.h"
9
10 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
11
12 // Raw context routines
13
14 typedef void (*rawctx_entry_point_t)(void *);
15
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);
20
21 // TODO, we should handle FP, MMX and the x87 control-word (for x86 and x86_64)
22
23 #if SIMGRID_PROCESSOR_x86_64
24 __asm__ (
25 #if defined(__APPLE__)
26    ".text\n"
27    ".globl _raw_makecontext\n"
28    "_raw_makecontext:\n"
29 #elif defined(_WIN32)
30    ".text\n"
31    ".globl raw_makecontext\n"
32    "raw_makecontext:\n"
33 #else
34    ".text\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 */
38 #endif
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 */
56    "   sub $112,%rax\n"
57    "   ret\n"
58 );
59
60 __asm__ (
61 #if defined(__APPLE__)
62    ".text\n"
63    ".globl _raw_swapcontext\n"
64    "_raw_swapcontext:\n"
65 #elif defined(_WIN32)
66    ".text\n"
67    ".globl raw_swapcontext\n"
68    "raw_swapcontext:\n"
69 #else
70    ".text\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 */
74 #endif
75    "   push %rdi\n"
76    "   push %rsi\n"
77    "   push %rdx\n"
78    "   push %rcx\n"
79    "   push %r8\n"
80    "   push %r9\n"
81    "   push %rbp\n"
82    "   push %rbx\n"
83    "   push %r12\n"
84    "   push %r13\n"
85    "   push %r14\n"
86    "   push %r15\n"
87    "   mov %rsp,(%rdi)\n" /* old */
88    "   mov %rsi,%rsp\n" /* new */
89    "   pop %r15\n"
90    "   pop %r14\n"
91    "   pop %r13\n"
92    "   pop %r12\n"
93    "   pop %rbx\n"
94    "   pop %rbp\n"
95    "   pop %r9\n"
96    "   pop %r8\n"
97    "   pop %rcx\n"
98    "   pop %rdx\n"
99    "   pop %rsi\n"
100    "   pop %rdi\n"
101    "   ret\n"
102 );
103 #elif SIMGRID_PROCESSOR_i686
104 __asm__ (
105 #if defined(__APPLE__) || defined(_WIN32)
106    ".text\n"
107    ".globl _raw_makecontext\n"
108    "_raw_makecontext:\n"
109 #else
110    ".text\n"
111    ".globl raw_makecontext\n"
112    ".type raw_makecontext,@function\n"
113    "raw_makecontext:\n"
114 #endif
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 */
127    "   subl $28,%eax\n"
128    "   retl\n"
129 );
130
131 __asm__ (
132 #if defined(__APPLE__) || defined(_WIN32)
133    ".text\n"
134    ".globl _raw_swapcontext\n"
135    "_raw_swapcontext:\n"
136 #else
137    ".text\n"
138    ".globl raw_swapcontext\n"
139    ".type raw_swapcontext,@function\n"
140    "raw_swapcontext:\n"
141 #endif
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:
146    "   pushl %ebp\n"
147    "   pushl %ebx\n"
148    "   pushl %esi\n"
149    "   pushl %edi\n"
150    // Save the current context (stack pointer) in *old:
151    "   movl %esp,(%eax)\n"
152    // Switch to the stack of the new context:
153    "   movl %edx,%esp\n"
154    // Pop the values of the new context:
155    "   popl %edi\n"
156    "   popl %esi\n"
157    "   popl %ebx\n"
158    "   popl %ebp\n"
159    // Return using the return address of the new context:
160    "   retl\n"
161 );
162 #else
163
164
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 */
167
168 raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
169                             rawctx_entry_point_t entry_point, void* arg) {
170    THROW_UNIMPLEMENTED;
171 }
172
173 void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context) {
174    THROW_UNIMPLEMENTED;
175 }
176
177 #endif
178
179 // ***** Method definitions
180
181 namespace simgrid {
182 namespace kernel {
183 namespace context {
184
185 // RawContextFactory
186
187 RawContextFactory::RawContextFactory() : ContextFactory("RawContextFactory"), parallel_(SIMIX_context_is_parallel())
188 {
189   RawContext::setMaestro(nullptr);
190   if (parallel_) {
191 #if HAVE_THREAD_CONTEXTS
192     // TODO: choose dynamically when SIMIX_context_get_parallel_threshold() > 1
193     ParallelRawContext::initialize();
194 #else
195     xbt_die("You asked for a parallel execution, but you don't have any threads.");
196 #endif
197   }
198 }
199
200 RawContextFactory::~RawContextFactory()
201 {
202 #if HAVE_THREAD_CONTEXTS
203   if (parallel_)
204     ParallelRawContext::finalize();
205 #endif
206 }
207
208 Context* RawContextFactory::create_context(std::function<void()> code, void_pfn_smxprocess_t cleanup_func,
209                                            smx_actor_t process)
210 {
211 #if HAVE_THREAD_CONTEXTS
212   if (parallel_)
213     return this->new_context<ParallelRawContext>(std::move(code), cleanup_func, process);
214 #endif
215
216   return this->new_context<SerialRawContext>(std::move(code), cleanup_func, process);
217 }
218
219 void RawContextFactory::run_all()
220 {
221 #if HAVE_THREAD_CONTEXTS
222   if (parallel_)
223     ParallelRawContext::run_all();
224   else
225 #endif
226     SerialRawContext::run_all();
227 }
228
229 // RawContext
230
231 RawContext* RawContext::maestro_context_ = nullptr;
232
233 RawContext::RawContext(std::function<void()> code, void_pfn_smxprocess_t cleanup, smx_actor_t process)
234     : Context(std::move(code), cleanup, process)
235 {
236    if (has_code()) {
237      this->stack_ = SIMIX_context_stack_new();
238      this->stack_top_ = raw_makecontext(this->stack_, smx_context_usable_stack_size, RawContext::wrapper, this);
239    } else {
240      if (process != nullptr && maestro_context_ == nullptr)
241        maestro_context_ = this;
242      if (MC_is_active())
243        MC_ignore_heap(&maestro_context_->stack_top_, sizeof(maestro_context_->stack_top_));
244    }
245 }
246
247 RawContext::~RawContext()
248 {
249   SIMIX_context_stack_delete(this->stack_);
250 }
251
252 void RawContext::wrapper(void* arg)
253 {
254   RawContext* context = static_cast<RawContext*>(arg);
255   try {
256     (*context)();
257     context->Context::stop();
258   } catch (StopRequest const&) {
259     XBT_DEBUG("Caught a StopRequest");
260   }
261   context->suspend();
262 }
263
264 inline void RawContext::swap(RawContext* from, RawContext* to)
265 {
266   raw_swapcontext(&from->stack_top_, to->stack_top_);
267 }
268
269 void RawContext::stop()
270 {
271   Context::stop();
272   throw StopRequest();
273 }
274
275 // SerialRawContext
276
277 unsigned long SerialRawContext::process_index_; /* index of the next process to run in the list of runnable processes */
278
279 void SerialRawContext::suspend()
280 {
281   /* determine the next context */
282   SerialRawContext* next_context;
283   unsigned long int i = process_index_;
284   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);
289   } else {
290     /* all processes were run, return to maestro */
291     XBT_DEBUG("No more process to run");
292     next_context = static_cast<SerialRawContext*>(RawContext::getMaestro());
293   }
294   SIMIX_context_set_current(next_context);
295   RawContext::swap(this, next_context);
296 }
297
298 void SerialRawContext::resume()
299 {
300   SIMIX_context_set_current(this);
301   RawContext::swap(RawContext::getMaestro(), this);
302 }
303
304 void SerialRawContext::run_all()
305 {
306   if (simix_global->process_to_run.empty())
307     return;
308   smx_actor_t first_process = simix_global->process_to_run.front();
309   process_index_            = 1;
310   static_cast<SerialRawContext*>(first_process->context)->resume();
311 }
312
313 // ParallelRawContext
314
315 #if HAVE_THREAD_CONTEXTS
316
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
321                                                                           in each thread */
322
323 void ParallelRawContext::initialize()
324 {
325   parmap_ = nullptr;
326   workers_context_.clear();
327   workers_context_.resize(SIMIX_context_get_nthreads(), nullptr);
328   xbt_os_thread_key_create(&worker_id_key_);
329 }
330
331 void ParallelRawContext::finalize()
332 {
333   delete parmap_;
334   parmap_ = nullptr;
335   workers_context_.clear();
336   xbt_os_thread_key_destroy(worker_id_key_);
337 }
338
339 void ParallelRawContext::run_all()
340 {
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());
344   parmap_->apply(
345       [](smx_actor_t process) {
346         ParallelRawContext* context = static_cast<ParallelRawContext*>(process->context);
347         context->resume();
348       },
349       simix_global->process_to_run);
350 }
351
352 void ParallelRawContext::suspend()
353 {
354   /* determine the next context */
355   boost::optional<smx_actor_t> next_work = parmap_->next();
356   ParallelRawContext* next_context;
357   if (next_work) {
358     /* there is a next process to resume */
359     XBT_DEBUG("Run next process");
360     next_context = static_cast<ParallelRawContext*>(next_work.get()->context);
361   } else {
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());
367   }
368
369   SIMIX_context_set_current(next_context);
370   RawContext::swap(this, next_context);
371 }
372
373 void ParallelRawContext::resume()
374 {
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);
382 }
383
384 #endif
385
386 ContextFactory* raw_factory()
387 {
388   XBT_VERB("Using raw contexts. Because the glibc is just not good enough for us.");
389   return new RawContextFactory();
390 }
391 }}}