Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
ContextRaw: add header file with class definitions.
[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 #include "src/internal_config.h"
10 #include "xbt/parmap.hpp"
11
12 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
13
14 // ***** Loads of static stuff
15
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 */
21 #endif
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;
25
26 static bool raw_context_parallel = false;
27
28 // ***** Raw context routines
29
30 typedef void (*rawctx_entry_point_t)(void *);
31
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);
36
37 // TODO, we should handle FP, MMX and the x87 control-word (for x86 and x86_64)
38
39 #if SIMGRID_PROCESSOR_x86_64
40 __asm__ (
41 #if defined(__APPLE__)
42    ".text\n"
43    ".globl _raw_makecontext\n"
44    "_raw_makecontext:\n"
45 #elif defined(_WIN32)
46    ".text\n"
47    ".globl raw_makecontext\n"
48    "raw_makecontext:\n"
49 #else
50    ".text\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 */
54 #endif
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 */
72    "   sub $112,%rax\n"
73    "   ret\n"
74 );
75
76 __asm__ (
77 #if defined(__APPLE__)
78    ".text\n"
79    ".globl _raw_swapcontext\n"
80    "_raw_swapcontext:\n"
81 #elif defined(_WIN32)
82    ".text\n"
83    ".globl raw_swapcontext\n"
84    "raw_swapcontext:\n"
85 #else
86    ".text\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 */
90 #endif
91    "   push %rdi\n"
92    "   push %rsi\n"
93    "   push %rdx\n"
94    "   push %rcx\n"
95    "   push %r8\n"
96    "   push %r9\n"
97    "   push %rbp\n"
98    "   push %rbx\n"
99    "   push %r12\n"
100    "   push %r13\n"
101    "   push %r14\n"
102    "   push %r15\n"
103    "   mov %rsp,(%rdi)\n" /* old */
104    "   mov %rsi,%rsp\n" /* new */
105    "   pop %r15\n"
106    "   pop %r14\n"
107    "   pop %r13\n"
108    "   pop %r12\n"
109    "   pop %rbx\n"
110    "   pop %rbp\n"
111    "   pop %r9\n"
112    "   pop %r8\n"
113    "   pop %rcx\n"
114    "   pop %rdx\n"
115    "   pop %rsi\n"
116    "   pop %rdi\n"
117    "   ret\n"
118 );
119 #elif SIMGRID_PROCESSOR_i686
120 __asm__ (
121 #if defined(__APPLE__) || defined(_WIN32)
122    ".text\n"
123    ".globl _raw_makecontext\n"
124    "_raw_makecontext:\n"
125 #else
126    ".text\n"
127    ".globl raw_makecontext\n"
128    ".type raw_makecontext,@function\n"
129    "raw_makecontext:\n"
130 #endif
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 */
143    "   subl $28,%eax\n"
144    "   retl\n"
145 );
146
147 __asm__ (
148 #if defined(__APPLE__) || defined(_WIN32)
149    ".text\n"
150    ".globl _raw_swapcontext\n"
151    "_raw_swapcontext:\n"
152 #else
153    ".text\n"
154    ".globl raw_swapcontext\n"
155    ".type raw_swapcontext,@function\n"
156    "raw_swapcontext:\n"
157 #endif
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:
162    "   pushl %ebp\n"
163    "   pushl %ebx\n"
164    "   pushl %esi\n"
165    "   pushl %edi\n"
166    // Save the current context (stack pointer) in *old:
167    "   movl %esp,(%eax)\n"
168    // Switch to the stack of the new context:
169    "   movl %edx,%esp\n"
170    // Pop the values of the new context:
171    "   popl %edi\n"
172    "   popl %esi\n"
173    "   popl %ebx\n"
174    "   popl %ebp\n"
175    // Return using the return address of the new context:
176    "   retl\n"
177 );
178 #else
179
180
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 */
183
184 raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
185                             rawctx_entry_point_t entry_point, void* arg) {
186    THROW_UNIMPLEMENTED;
187 }
188
189 void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context) {
190    THROW_UNIMPLEMENTED;
191 }
192
193 #endif
194
195 // ***** Method definitions
196
197 namespace simgrid {
198 namespace kernel {
199 namespace context {
200
201 RawContextFactory::RawContextFactory()
202   : ContextFactory("RawContextFactory")
203 {
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);
209     // TODO, lazily init
210     raw_parmap = nullptr;
211     raw_workers_context = new RawContext*[nthreads];
212     raw_maestro_context = nullptr;
213 #endif
214     // TODO: choose dynamically when SIMIX_context_get_parallel_threshold() > 1
215   }
216 }
217
218 RawContextFactory::~RawContextFactory()
219 {
220 #if HAVE_THREAD_CONTEXTS
221   delete raw_parmap;
222   delete[] raw_workers_context;
223 #endif
224 }
225
226 RawContext* RawContextFactory::create_context(std::function<void()> code,
227     void_pfn_smxprocess_t cleanup, smx_actor_t process)
228 {
229   return this->new_context<RawContext>(std::move(code), cleanup, process);
230 }
231
232 void RawContext::wrapper(void* arg)
233 {
234   RawContext* context = static_cast<RawContext*>(arg);
235   try {
236     (*context)();
237     context->Context::stop();
238   } catch (StopRequest const&) {
239     XBT_DEBUG("Caught a StopRequest");
240   }
241   context->suspend();
242 }
243
244 RawContext::RawContext(std::function<void()> code,
245     void_pfn_smxprocess_t cleanup, smx_actor_t process)
246   : Context(std::move(code), cleanup, process)
247 {
248    if (has_code()) {
249      this->stack_ = SIMIX_context_stack_new();
250      this->stack_top_ = raw_makecontext(this->stack_,
251                          smx_context_usable_stack_size,
252                          RawContext::wrapper,
253                          this);
254    } else {
255      if(process != nullptr && raw_maestro_context == nullptr)
256        raw_maestro_context = this;
257      if (MC_is_active())
258        MC_ignore_heap(
259          &raw_maestro_context->stack_top_,
260          sizeof(raw_maestro_context->stack_top_));
261    }
262 }
263
264 RawContext::~RawContext()
265 {
266   SIMIX_context_stack_delete(this->stack_);
267 }
268
269 void RawContext::stop()
270 {
271   Context::stop();
272   throw StopRequest();
273 }
274
275 void RawContextFactory::run_all()
276 {
277   if (raw_context_parallel)
278     run_all_parallel();
279   else
280     run_all_serial();
281 }
282
283 void RawContextFactory::run_all_serial()
284 {
285   if (simix_global->process_to_run.empty())
286     return;
287
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();
291 }
292
293 void RawContextFactory::run_all_parallel()
294 {
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());
299   raw_parmap->apply(
300       [](smx_actor_t process) {
301         RawContext* context = static_cast<RawContext*>(process->context);
302         context->resume_parallel();
303       },
304       simix_global->process_to_run);
305 #else
306   xbt_die("You asked for a parallel execution, but you don't have any threads.");
307 #endif
308 }
309
310 void RawContext::suspend()
311 {
312   if (raw_context_parallel)
313     RawContext::suspend_parallel();
314   else
315     RawContext::suspend_serial();
316 }
317
318 void RawContext::suspend_serial()
319 {
320   /* determine the next context */
321   RawContext* next_context = nullptr;
322   unsigned long int i      = raw_process_index;
323   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);
328   } else {
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);
332   }
333   SIMIX_context_set_current(next_context);
334   raw_swapcontext(&this->stack_top_, next_context->stack_top_);
335 }
336
337 void RawContext::suspend_parallel()
338 {
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;
343   if (next_work) {
344     /* there is a next process to resume */
345     XBT_DEBUG("Run next process");
346     next_context = static_cast<RawContext*>(next_work.get()->context);
347   } else {
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);
355   }
356
357   SIMIX_context_set_current(next_context);
358   raw_swapcontext(&this->stack_top_, next_context->stack_top_);
359 #endif
360 }
361
362 void RawContext::resume()
363 {
364   if (raw_context_parallel)
365     resume_parallel();
366   else
367     resume_serial();
368 }
369
370 void RawContext::resume_serial()
371 {
372   SIMIX_context_set_current(this);
373   raw_swapcontext(&raw_maestro_context->stack_top_, this->stack_top_);
374 }
375
376 void RawContext::resume_parallel()
377 {
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_);
386 #else
387   xbt_die("Parallel execution disabled");
388 #endif
389 }
390
391 ContextFactory* raw_factory()
392 {
393   XBT_VERB("Using raw contexts. Because the glibc is just not good enough for us.");
394   return new RawContextFactory();
395 }
396 }}}