Logo AND Algorithmique Numérique Distribuée

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