Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of scm.gforge.inria.fr:/gitroot/simgrid/simgrid
[simgrid.git] / src / simix / ContextBoost.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 <functional>
12 #include <utility>
13 #include <vector>
14
15 #include <boost/context/all.hpp>
16
17 #include <xbt/log.h>
18 #include <xbt/xbt_os_thread.h>
19
20 #include "smx_private.h"
21 #include "src/internal_config.h"
22 #include "src/simix/ContextBoost.hpp"
23
24 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
25
26 namespace simgrid {
27 namespace simix {
28
29 class BoostSerialContext : public BoostContext {
30 public:
31   BoostSerialContext(std::function<void()> code,
32       void_pfn_smxprocess_t cleanup_func,
33       smx_process_t process)
34     : BoostContext(std::move(code), cleanup_func, process) {}
35   void stop() override;
36   void suspend() override;
37   void resume();
38 };
39
40 #if HAVE_THREAD_CONTEXTS
41 class BoostParallelContext : public BoostContext {
42 public:
43   BoostParallelContext(std::function<void()> code,
44       void_pfn_smxprocess_t cleanup_func,
45       smx_process_t process)
46     : BoostContext(std::move(code), cleanup_func, process) {}
47   void stop() override;
48   void suspend() override;
49   void resume();
50 };
51 #endif
52
53 // BoostContextFactory
54
55 bool                BoostContext::parallel_        = false;
56 xbt_parmap_t        BoostContext::parmap_          = nullptr;
57 uintptr_t           BoostContext::threads_working_ = 0;
58 xbt_os_thread_key_t BoostContext::worker_id_key_;
59 unsigned long       BoostContext::process_index_   = 0;
60 BoostContext*       BoostContext::maestro_context_ = nullptr;
61 std::vector<BoostContext*> BoostContext::workers_context_;
62
63 BoostContextFactory::BoostContextFactory()
64   : ContextFactory("BoostContextFactory")
65 {
66   BoostContext::parallel_ = SIMIX_context_is_parallel();
67   if (BoostContext::parallel_) {
68 #if !HAVE_THREAD_CONTEXTS
69     xbt_die("No thread support for parallel context execution");
70 #else
71     int nthreads = SIMIX_context_get_nthreads();
72     BoostContext::parmap_ = xbt_parmap_new(nthreads, SIMIX_context_get_parallel_mode());
73     BoostContext::workers_context_.clear();
74     BoostContext::workers_context_.resize(nthreads, nullptr);
75     BoostContext::maestro_context_ = nullptr;
76     xbt_os_thread_key_create(&BoostContext::worker_id_key_);
77 #endif
78   }
79 }
80
81 BoostContextFactory::~BoostContextFactory()
82 {
83 #if HAVE_THREAD_CONTEXTS
84   if (BoostContext::parmap_) {
85     xbt_parmap_destroy(BoostContext::parmap_);
86     BoostContext::parmap_ = nullptr;
87   }
88   BoostContext::workers_context_.clear();
89 #endif
90 }
91
92 smx_context_t BoostContextFactory::create_context(std::function<void()>  code,
93   void_pfn_smxprocess_t cleanup_func, smx_process_t process)
94 {
95   BoostContext* context = nullptr;
96   if (BoostContext::parallel_)
97 #if HAVE_THREAD_CONTEXTS
98     context = this->new_context<BoostParallelContext>(
99       std::move(code), cleanup_func, process);
100 #else
101     xbt_die("No support for parallel execution");
102 #endif
103   else
104     context = this->new_context<BoostSerialContext>(
105       std::move(code), cleanup_func, process);
106   return context;
107 }
108
109 void BoostContextFactory::run_all()
110 {
111 #if HAVE_THREAD_CONTEXTS
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(std::function<void()> code,
143     void_pfn_smxprocess_t cleanup_func, smx_process_t process)
144   : Context(std::move(code), cleanup_func, process)
145 {
146
147   /* if the user provided a function for the process then use it,
148      otherwise it is the context for maestro */
149   if (has_code()) {
150     this->stack_ = SIMIX_context_stack_new();
151     // We need to pass the bottom of the stack to make_fcontext,
152     // depending on the stack direction it may be the lower or higher address:
153   #if PTH_STACKGROWTH == -1
154     void* stack = (char*) this->stack_ + smx_context_usable_stack_size - 1;
155   #else
156     void* stack = this->stack_;
157   #endif
158     this->fc_ = boost::context::make_fcontext(
159                       stack,
160                       smx_context_usable_stack_size,
161                       smx_ctx_boost_wrapper);
162   } else {
163     #if HAVE_BOOST_CONTEXTS == 1
164     this->fc_ = new boost::context::fcontext_t();
165     #endif
166     if (BoostContext::maestro_context_ == nullptr)
167       BoostContext::maestro_context_ = this;
168   }
169 }
170
171 BoostContext::~BoostContext()
172 {
173 #if HAVE_BOOST_CONTEXTS == 1
174   if (!this->stack_)
175     delete this->fc_;
176 #endif
177   if (this == maestro_context_)
178     maestro_context_ = nullptr;
179   SIMIX_context_stack_delete(this->stack_);
180 }
181
182 // BoostSerialContext
183
184 void BoostContext::resume()
185 {
186   SIMIX_context_set_current(this);
187 #if HAVE_BOOST_CONTEXTS == 1
188   boost::context::jump_fcontext(
189     maestro_context_->fc_, this->fc_,
190     (intptr_t) this);
191 #else
192   boost::context::jump_fcontext(
193     &maestro_context_->fc_, this->fc_,
194     (intptr_t) this);
195 #endif
196 }
197
198 void BoostSerialContext::suspend()
199 {
200   /* determine the next context */
201   BoostSerialContext* next_context = nullptr;
202   unsigned long int i = process_index_++;
203
204   if (i < xbt_dynar_length(simix_global->process_to_run)) {
205     /* execute the next process */
206     XBT_DEBUG("Run next process");
207     next_context = static_cast<BoostSerialContext*>(xbt_dynar_get_as(
208         simix_global->process_to_run, i, smx_process_t)->context);
209   }
210   else {
211     /* all processes were run, return to maestro */
212     XBT_DEBUG("No more process to run");
213     next_context = static_cast<BoostSerialContext*>(
214       maestro_context_);
215   }
216   SIMIX_context_set_current((smx_context_t) next_context);
217   #if HAVE_BOOST_CONTEXTS == 1
218   boost::context::jump_fcontext(
219     this->fc_, next_context->fc_, (intptr_t) next_context);
220   #else
221   boost::context::jump_fcontext(
222     &this->fc_, next_context->fc_, (intptr_t) next_context);
223   #endif
224 }
225
226 void BoostSerialContext::stop()
227 {
228   BoostContext::stop();
229   this->suspend();
230 }
231
232 // BoostParallelContext
233
234 #if HAVE_THREAD_CONTEXTS
235
236 void BoostParallelContext::suspend()
237 {
238   smx_process_t next_work = (smx_process_t) xbt_parmap_next(parmap_);
239   BoostParallelContext* next_context = nullptr;
240
241   if (next_work != nullptr) {
242     XBT_DEBUG("Run next process");
243     next_context = static_cast<BoostParallelContext*>(next_work->context);
244   }
245   else {
246     XBT_DEBUG("No more processes to run");
247     uintptr_t worker_id =
248       (uintptr_t) xbt_os_thread_get_specific(worker_id_key_);
249     next_context = static_cast<BoostParallelContext*>(
250       workers_context_[worker_id]);
251   }
252
253   SIMIX_context_set_current((smx_context_t) next_context);
254 #if HAVE_BOOST_CONTEXTS == 1
255   boost::context::jump_fcontext(
256     this->fc_, next_context->fc_, (intptr_t)next_context);
257 #else
258   boost::context::jump_fcontext(
259     &this->fc_, next_context->fc_, (intptr_t)next_context);
260 #endif
261 }
262
263 void BoostParallelContext::stop()
264 {
265   BoostContext::stop();
266   this->suspend();
267 }
268
269 void BoostParallelContext::resume()
270 {
271   uintptr_t worker_id = __sync_fetch_and_add(&threads_working_, 1);
272   xbt_os_thread_set_specific(worker_id_key_, (void*) worker_id);
273
274   BoostParallelContext* worker_context =
275     static_cast<BoostParallelContext*>(SIMIX_context_self());
276   workers_context_[worker_id] = worker_context;
277
278   SIMIX_context_set_current(this);
279 #if HAVE_BOOST_CONTEXTS == 1
280   boost::context::jump_fcontext(
281     worker_context->fc_, this->fc_, (intptr_t) this);
282 #else
283   boost::context::jump_fcontext(
284     &worker_context->fc_, this->fc_, (intptr_t) this);
285 #endif
286 }
287
288 #endif
289
290 XBT_PRIVATE ContextFactory* boost_factory()
291 {
292   XBT_VERB("Using Boost contexts. Welcome to the 21th century.");
293   return new BoostContextFactory();
294 }
295
296 }
297 }