Logo AND Algorithmique Numérique Distribuée

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