Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
f2e5ceeeda1fbf5b3a6ed4e9237cd2e083ea72f6
[simgrid.git] / src / simix / BoostContext.cpp
1 /* Copyright (c) 2015. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 /** @file BoostContext.cpp Userspace context switching implementation based on Boost.Context */
8
9 #include <cstdint>
10
11 #include <boost/context/all.hpp>
12
13 #include <xbt/log.h>
14 #include <xbt/xbt_os_thread.h>
15
16 #include "smx_private.h"
17 #include "smx_private.hpp"
18 #include "src/internal_config.h"
19 #include "src/simix/BoostContext.hpp"
20
21 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
22
23 namespace simgrid {
24 namespace simix {
25
26 class BoostSerialContext : public BoostContext {
27 public:
28   BoostSerialContext(xbt_main_func_t code,
29       int argc, char **argv,
30       void_pfn_smxprocess_t cleanup_func,
31       smx_process_t process)
32     : BoostContext(code, argc, argv, cleanup_func, process) {}
33   void stop() override;
34   void suspend() override;
35   void resume();
36 };
37
38 #ifdef CONTEXT_THREADS
39 class BoostParallelContext : public BoostContext {
40 public:
41   BoostParallelContext(xbt_main_func_t code,
42       int argc, char **argv,
43       void_pfn_smxprocess_t cleanup_func,
44       smx_process_t process)
45     : BoostContext(code, argc, argv, cleanup_func, process) {}
46   void stop() override;
47   void suspend() override;
48   void resume();
49 };
50 #endif
51
52 // BoostContextFactory
53
54 bool                BoostContext::parallel_        = false;
55 xbt_parmap_t        BoostContext::parmap_          = nullptr;
56 unsigned long       BoostContext::threads_working_ = 0;
57 xbt_os_thread_key_t BoostContext::worker_id_key_;
58 unsigned long       BoostContext::process_index_   = 0;
59 BoostContext*       BoostContext::maestro_context_ = nullptr;
60 std::vector<BoostContext*> BoostContext::workers_context_;
61
62 BoostContextFactory::BoostContextFactory()
63   : ContextFactory("BoostContextFactory")
64 {
65   BoostContext::parallel_ = SIMIX_context_is_parallel();
66   if (BoostContext::parallel_) {
67 #ifndef CONTEXT_THREADS
68     xbt_die("No thread support for parallel context execution");
69 #else
70     int nthreads = SIMIX_context_get_nthreads();
71     BoostContext::parmap_ = xbt_parmap_new(nthreads, SIMIX_context_get_parallel_mode());
72     BoostContext::workers_context_.clear();
73     BoostContext::workers_context_.resize(nthreads, nullptr);
74     BoostContext::maestro_context_ = nullptr;
75     xbt_os_thread_key_create(&BoostContext::worker_id_key_);
76 #endif
77   }
78 }
79
80 BoostContextFactory::~BoostContextFactory()
81 {
82 #ifdef CONTEXT_THREADS
83   if (BoostContext::parmap_) {
84     xbt_parmap_destroy(BoostContext::parmap_);
85     BoostContext::parmap_ = nullptr;
86   }
87   BoostContext::workers_context_.clear();
88 #endif
89 }
90
91 smx_context_t BoostContextFactory::create_context(
92   xbt_main_func_t code, int argc, char ** argv,
93   void_pfn_smxprocess_t cleanup_func, smx_process_t process)
94 {
95   BoostContext* context = nullptr;
96   if (BoostContext::parallel_)
97 #ifdef CONTEXT_THREADS
98     context = this->new_context<BoostParallelContext>(
99       code, argc, argv, cleanup_func, process);
100 #else
101     xbt_die("No support for parallel execution");
102 #endif
103   else
104     context = this->new_context<BoostSerialContext>(
105       code, argc, argv, cleanup_func, process);
106   return context;
107 }
108
109 void BoostContextFactory::run_all()
110 {
111 #ifdef CONTEXT_THREADS
112   if (BoostContext::parallel_) {
113     BoostContext::threads_working_ = 0;
114     xbt_parmap_apply(BoostContext::parmap_,
115       [](void* arg) {
116         smx_process_t process = static_cast<smx_process_t>(arg);
117         BoostContext* context  = static_cast<BoostContext*>(process->context);
118         return context->resume();
119       },
120       simix_global->process_to_run);
121   } else
122 #endif
123   {
124     smx_process_t first_process =
125         xbt_dynar_get_as(simix_global->process_to_run, 0, smx_process_t);
126     BoostContext::process_index_ = 1;
127     /* execute the first process */
128     static_cast<BoostContext*>(first_process->context)->resume();
129   }
130 }
131
132
133 // BoostContext
134
135 static void smx_ctx_boost_wrapper(std::intptr_t arg)
136 {
137   BoostContext* context = (BoostContext*) arg;
138   (*context)();
139   context->stop();
140 }
141
142 BoostContext::BoostContext(xbt_main_func_t code,
143     int argc, char **argv,
144     void_pfn_smxprocess_t cleanup_func,
145     smx_process_t process)
146   : Context(code, argc, argv, cleanup_func, process)
147 {
148
149   /* if the user provided a function for the process then use it,
150      otherwise it is the context for maestro */
151   if (code) {
152     this->stack_ = SIMIX_context_stack_new();
153     // We need to pass the bottom of the stack to make_fcontext,
154     // depending on the stack direction it may be the lower or higher address:
155   #if PTH_STACKGROWTH == -1
156     void* stack = (char*) this->stack_ + smx_context_usable_stack_size - 1;
157   #else
158     void* stack = this->stack_;
159   #endif
160     this->fc_ = boost::context::make_fcontext(
161                       stack,
162                       smx_context_usable_stack_size,
163                       smx_ctx_boost_wrapper);
164   } else {
165     #if HAVE_BOOST_CONTEXT == 1
166     this->fc_ = new boost::context::fcontext_t();
167     #endif
168     if (BoostContext::maestro_context_ == nullptr)
169       BoostContext::maestro_context_ = this;
170   }
171 }
172
173 BoostContext::~BoostContext()
174 {
175 #if HAVE_BOOST_CONTEXT == 1
176   if (!this->stack_)
177     delete this->fc_;
178 #endif
179   if (this == maestro_context_)
180     maestro_context_ = nullptr;
181   SIMIX_context_stack_delete(this->stack_);
182 }
183
184 // BoostSerialContext
185
186 void BoostContext::resume()
187 {
188   SIMIX_context_set_current(this);
189 #if HAVE_BOOST_CONTEXT == 1
190   boost::context::jump_fcontext(
191     maestro_context_->fc_, this->fc_,
192     (intptr_t) this);
193 #else
194   boost::context::jump_fcontext(
195     &boost_maestro_context_->fc_, this->fc_,
196     (intptr_t) this);
197 #endif
198 }
199
200 void BoostSerialContext::suspend()
201 {
202   /* determine the next context */
203   BoostSerialContext* next_context = nullptr;
204   unsigned long int i = process_index_++;
205
206   if (i < xbt_dynar_length(simix_global->process_to_run)) {
207     /* execute the next process */
208     XBT_DEBUG("Run next process");
209     next_context = static_cast<BoostSerialContext*>(xbt_dynar_get_as(
210         simix_global->process_to_run, i, smx_process_t)->context);
211   }
212   else {
213     /* all processes were run, return to maestro */
214     XBT_DEBUG("No more process to run");
215     next_context = static_cast<BoostSerialContext*>(
216       maestro_context_);
217   }
218   SIMIX_context_set_current((smx_context_t) next_context);
219   #if HAVE_BOOST_CONTEXT == 1
220   boost::context::jump_fcontext(
221     this->fc_, next_context->fc_, (intptr_t) next_context);
222   #else
223   boost::context::jump_fcontext(
224     &this->fc_, next_context->fc_, (intptr_t) next_context);
225   #endif
226 }
227
228 void BoostSerialContext::stop()
229 {
230   BoostContext::stop();
231   this->suspend();
232 }
233
234 // BoostParallelContext
235
236 #ifdef CONTEXT_THREADS
237
238 void BoostParallelContext::suspend()
239 {
240   smx_process_t next_work = (smx_process_t) xbt_parmap_next(parmap_);
241   BoostParallelContext* next_context = nullptr;
242
243   if (next_work != nullptr) {
244     XBT_DEBUG("Run next process");
245     next_context = static_cast<BoostParallelContext*>(next_work->context);
246   }
247   else {
248     XBT_DEBUG("No more processes to run");
249     unsigned long worker_id =
250       (unsigned long) xbt_os_thread_get_specific(worker_id_key_);
251     next_context = static_cast<BoostParallelContext*>(
252       workers_context_[worker_id]);
253   }
254
255   SIMIX_context_set_current((smx_context_t) next_context);
256 #if HAVE_BOOST_CONTEXT == 1
257   boost::context::jump_fcontext(
258     this->fc_, next_context->fc_, (intptr_t)next_context);
259 #else
260   boost::context::jump_fcontext(
261     &this->fc_, next_context->fc_, (intptr_t)next_context);
262 #endif
263 }
264
265 void BoostParallelContext::stop()
266 {
267   BoostContext::stop();
268   this->suspend();
269 }
270
271 void BoostParallelContext::resume()
272 {
273   unsigned long worker_id = __sync_fetch_and_add(&threads_working_, 1);
274   xbt_os_thread_set_specific(worker_id_key_, (void*) worker_id);
275
276   BoostParallelContext* worker_context =
277     static_cast<BoostParallelContext*>(SIMIX_context_self());
278   workers_context_[worker_id] = worker_context;
279
280   SIMIX_context_set_current(this);
281 #if HAVE_BOOST_CONTEXT == 1
282   boost::context::jump_fcontext(
283     worker_context->fc_, this->fc_, (intptr_t) this);
284 #else
285   boost::context::jump_fcontext(
286     &worker_context->fc_, this->fc_, (intptr_t) this);
287 #endif
288 }
289
290 #endif
291
292 XBT_PRIVATE ContextFactory* boost_factory()
293 {
294   return new BoostContextFactory();
295 }
296
297 }
298 }