Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
f861f8d1f2850b24e95544957bcdd80f50bb66a5
[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     smx_process_t first_process =
122         xbt_dynar_get_as(simix_global->process_to_run, 0, smx_process_t);
123     BoostContext::process_index_ = 1;
124     /* execute the first process */
125     static_cast<BoostContext*>(first_process->context)->resume();
126   }
127 }
128
129
130 // BoostContext
131
132 static void smx_ctx_boost_wrapper(std::intptr_t arg)
133 {
134   BoostContext* context = (BoostContext*) arg;
135   (*context)();
136   context->stop();
137 }
138
139 BoostContext::BoostContext(std::function<void()> code,
140     void_pfn_smxprocess_t cleanup_func, smx_process_t process)
141   : Context(std::move(code), cleanup_func, process)
142 {
143
144   /* if the user provided a function for the process then use it,
145      otherwise it is the context for maestro */
146   if (has_code()) {
147     this->stack_ = SIMIX_context_stack_new();
148     // We need to pass the bottom of the stack to make_fcontext,
149     // depending on the stack direction it may be the lower or higher address:
150   #if PTH_STACKGROWTH == -1
151     void* stack = (char*) this->stack_ + smx_context_usable_stack_size - 1;
152   #else
153     void* stack = this->stack_;
154   #endif
155     this->fc_ = boost::context::make_fcontext(
156                       stack,
157                       smx_context_usable_stack_size,
158                       smx_ctx_boost_wrapper);
159   } else {
160     #if HAVE_BOOST_CONTEXTS == 1
161     this->fc_ = new boost::context::fcontext_t();
162     #endif
163     if (BoostContext::maestro_context_ == nullptr)
164       BoostContext::maestro_context_ = this;
165   }
166 }
167
168 BoostContext::~BoostContext()
169 {
170 #if HAVE_BOOST_CONTEXTS == 1
171   if (!this->stack_)
172     delete this->fc_;
173 #endif
174   if (this == maestro_context_)
175     maestro_context_ = nullptr;
176   SIMIX_context_stack_delete(this->stack_);
177 }
178
179 // BoostSerialContext
180
181 void BoostContext::resume()
182 {
183   SIMIX_context_set_current(this);
184 #if HAVE_BOOST_CONTEXTS == 1
185   boost::context::jump_fcontext(
186     maestro_context_->fc_, this->fc_,
187     (intptr_t) this);
188 #else
189   boost::context::jump_fcontext(
190     &maestro_context_->fc_, this->fc_,
191     (intptr_t) this);
192 #endif
193 }
194
195 void BoostSerialContext::suspend()
196 {
197   /* determine the next context */
198   BoostSerialContext* next_context = nullptr;
199   unsigned long int i = process_index_++;
200
201   if (i < xbt_dynar_length(simix_global->process_to_run)) {
202     /* execute the next process */
203     XBT_DEBUG("Run next process");
204     next_context = static_cast<BoostSerialContext*>(xbt_dynar_get_as(
205         simix_global->process_to_run, i, smx_process_t)->context);
206   }
207   else {
208     /* all processes were run, return to maestro */
209     XBT_DEBUG("No more process to run");
210     next_context = static_cast<BoostSerialContext*>(
211       maestro_context_);
212   }
213   SIMIX_context_set_current((smx_context_t) next_context);
214   #if HAVE_BOOST_CONTEXTS == 1
215   boost::context::jump_fcontext(
216     this->fc_, next_context->fc_, (intptr_t) next_context);
217   #else
218   boost::context::jump_fcontext(
219     &this->fc_, next_context->fc_, (intptr_t) next_context);
220   #endif
221 }
222
223 void BoostSerialContext::stop()
224 {
225   BoostContext::stop();
226   this->suspend();
227 }
228
229 // BoostParallelContext
230
231 #if HAVE_THREAD_CONTEXTS
232
233 void BoostParallelContext::suspend()
234 {
235   smx_process_t next_work = (smx_process_t) xbt_parmap_next(parmap_);
236   BoostParallelContext* next_context = nullptr;
237
238   if (next_work != nullptr) {
239     XBT_DEBUG("Run next process");
240     next_context = static_cast<BoostParallelContext*>(next_work->context);
241   }
242   else {
243     XBT_DEBUG("No more processes to run");
244     uintptr_t worker_id =
245       (uintptr_t) xbt_os_thread_get_specific(worker_id_key_);
246     next_context = static_cast<BoostParallelContext*>(
247       workers_context_[worker_id]);
248   }
249
250   SIMIX_context_set_current((smx_context_t) next_context);
251 #if HAVE_BOOST_CONTEXTS == 1
252   boost::context::jump_fcontext(
253     this->fc_, next_context->fc_, (intptr_t)next_context);
254 #else
255   boost::context::jump_fcontext(
256     &this->fc_, next_context->fc_, (intptr_t)next_context);
257 #endif
258 }
259
260 void BoostParallelContext::stop()
261 {
262   BoostContext::stop();
263   this->suspend();
264 }
265
266 void BoostParallelContext::resume()
267 {
268   uintptr_t worker_id = __sync_fetch_and_add(&threads_working_, 1);
269   xbt_os_thread_set_specific(worker_id_key_, (void*) worker_id);
270
271   BoostParallelContext* worker_context =
272     static_cast<BoostParallelContext*>(SIMIX_context_self());
273   workers_context_[worker_id] = worker_context;
274
275   SIMIX_context_set_current(this);
276 #if HAVE_BOOST_CONTEXTS == 1
277   boost::context::jump_fcontext(
278     worker_context->fc_, this->fc_, (intptr_t) this);
279 #else
280   boost::context::jump_fcontext(
281     &worker_context->fc_, this->fc_, (intptr_t) this);
282 #endif
283 }
284
285 #endif
286
287 XBT_PRIVATE ContextFactory* boost_factory()
288 {
289   XBT_VERB("Using Boost contexts. Welcome to the 21th century.");
290   return new BoostContextFactory();
291 }
292
293 }}} // namespace