Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
do not load internal_config.h from mc.h
[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,
90   void_pfn_smxprocess_t cleanup_func, 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,
144      otherwise it is the context for maestro */
145   if (has_code()) {
146     this->stack_ = SIMIX_context_stack_new();
147     // We need to pass the bottom of the stack to make_fcontext,
148     // depending on the stack direction it may be the lower or higher address:
149 #if PTH_STACKGROWTH == -1
150     void* stack = static_cast<char*>(this->stack_) + smx_context_usable_stack_size - 1;
151 #else
152     void* stack = this->stack_;
153 #endif
154     this->fc_ = boost::context::make_fcontext(
155                       stack,
156                       smx_context_usable_stack_size,
157                       smx_ctx_boost_wrapper);
158   } else {
159 #if HAVE_BOOST_CONTEXTS == 1
160     this->fc_ = new boost::context::fcontext_t();
161 #endif
162     if (BoostContext::maestro_context_ == nullptr)
163       BoostContext::maestro_context_ = this;
164   }
165 }
166
167 BoostContext::~BoostContext()
168 {
169 #if HAVE_BOOST_CONTEXTS == 1
170   if (!this->stack_)
171     delete this->fc_;
172 #endif
173   if (this == maestro_context_)
174     maestro_context_ = nullptr;
175   SIMIX_context_stack_delete(this->stack_);
176 }
177
178 // BoostSerialContext
179
180 void BoostContext::resume()
181 {
182   SIMIX_context_set_current(this);
183 #if HAVE_BOOST_CONTEXTS == 1
184   boost::context::jump_fcontext(maestro_context_->fc_, this->fc_, (intptr_t) this);
185 #else
186   boost::context::jump_fcontext(&maestro_context_->fc_, this->fc_, (intptr_t) this);
187 #endif
188 }
189
190 void BoostSerialContext::suspend()
191 {
192   /* determine the next context */
193   BoostSerialContext* next_context = nullptr;
194   unsigned long int i = 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 = static_cast<BoostSerialContext*>(xbt_dynar_get_as(
200         simix_global->process_to_run, i, smx_actor_t)->context);
201   }
202   else {
203     /* all processes were run, return to maestro */
204     XBT_DEBUG("No more process to run");
205     next_context = static_cast<BoostSerialContext*>(maestro_context_);
206   }
207   SIMIX_context_set_current((smx_context_t) next_context);
208 #if HAVE_BOOST_CONTEXTS == 1
209   boost::context::jump_fcontext(this->fc_, next_context->fc_, (intptr_t) next_context);
210 #else
211   boost::context::jump_fcontext(&this->fc_, next_context->fc_, (intptr_t) next_context);
212 #endif
213 }
214
215 void BoostSerialContext::stop()
216 {
217   BoostContext::stop();
218   this->suspend();
219 }
220
221 // BoostParallelContext
222
223 #if HAVE_THREAD_CONTEXTS
224
225 void BoostParallelContext::suspend()
226 {
227   smx_actor_t next_work = static_cast<smx_actor_t>(xbt_parmap_next(parmap_));
228   BoostParallelContext* next_context = nullptr;
229
230   if (next_work != nullptr) {
231     XBT_DEBUG("Run next process");
232     next_context = static_cast<BoostParallelContext*>(next_work->context);
233   }
234   else {
235     XBT_DEBUG("No more processes to run");
236     uintptr_t worker_id =
237       (uintptr_t) xbt_os_thread_get_specific(worker_id_key_);
238     next_context = static_cast<BoostParallelContext*>(workers_context_[worker_id]);
239   }
240
241   SIMIX_context_set_current(static_cast<smx_context_t> (next_context));
242 #if HAVE_BOOST_CONTEXTS == 1
243   boost::context::jump_fcontext(this->fc_, next_context->fc_, (intptr_t)(next_context));
244 #else
245   boost::context::jump_fcontext(&this->fc_, next_context->fc_, (intptr_t)(next_context));
246 #endif
247 }
248
249 void BoostParallelContext::stop()
250 {
251   BoostContext::stop();
252   this->suspend();
253 }
254
255 void BoostParallelContext::resume()
256 {
257   uintptr_t worker_id = __sync_fetch_and_add(&threads_working_, 1);
258   xbt_os_thread_set_specific(worker_id_key_, (void*) worker_id);
259
260   BoostParallelContext* worker_context = static_cast<BoostParallelContext*>(SIMIX_context_self());
261   workers_context_[worker_id] = worker_context;
262
263   SIMIX_context_set_current(this);
264 #if HAVE_BOOST_CONTEXTS == 1
265   boost::context::jump_fcontext(worker_context->fc_, this->fc_, (intptr_t) this);
266 #else
267   boost::context::jump_fcontext(&worker_context->fc_, this->fc_, (intptr_t) this);
268 #endif
269 }
270
271 #endif
272
273 XBT_PRIVATE ContextFactory* boost_factory()
274 {
275   XBT_VERB("Using Boost contexts. Welcome to the 21th century.");
276   return new BoostContextFactory();
277 }
278
279 }}} // namespace