Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Replace sprintf by snprintf.
[simgrid.git] / src / simix / 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 "smx_private.h"
18 #include "src/internal_config.h"
19 #include "src/simix/ContextBoost.hpp"
20
21 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
22
23 namespace simgrid {
24 namespace simix {
25
26 class BoostSerialContext : public BoostContext {
27 public:
28   BoostSerialContext(std::function<void()> code,
29       void_pfn_smxprocess_t cleanup_func,
30       smx_process_t process)
31     : BoostContext(std::move(code), cleanup_func, process) {}
32   void stop() override;
33   void suspend() override;
34 };
35
36 #if HAVE_THREAD_CONTEXTS
37 class BoostParallelContext : public BoostContext {
38 public:
39   BoostParallelContext(std::function<void()> code,
40       void_pfn_smxprocess_t cleanup_func,
41       smx_process_t process)
42     : BoostContext(std::move(code), cleanup_func, process) {}
43   void stop() override;
44   void suspend() override;
45   void resume() override;
46 };
47 #endif
48
49 // BoostContextFactory
50
51 bool                BoostContext::parallel_        = false;
52 xbt_parmap_t        BoostContext::parmap_          = nullptr;
53 uintptr_t           BoostContext::threads_working_ = 0;
54 xbt_os_thread_key_t BoostContext::worker_id_key_;
55 unsigned long       BoostContext::process_index_   = 0;
56 BoostContext*       BoostContext::maestro_context_ = nullptr;
57 std::vector<BoostContext*> BoostContext::workers_context_;
58
59 BoostContextFactory::BoostContextFactory()
60   : ContextFactory("BoostContextFactory")
61 {
62   BoostContext::parallel_ = SIMIX_context_is_parallel();
63   if (BoostContext::parallel_) {
64 #if !HAVE_THREAD_CONTEXTS
65     xbt_die("No thread support for parallel context execution");
66 #else
67     int nthreads = SIMIX_context_get_nthreads();
68     BoostContext::parmap_ = xbt_parmap_new(nthreads, SIMIX_context_get_parallel_mode());
69     BoostContext::workers_context_.clear();
70     BoostContext::workers_context_.resize(nthreads, nullptr);
71     BoostContext::maestro_context_ = nullptr;
72     xbt_os_thread_key_create(&BoostContext::worker_id_key_);
73 #endif
74   }
75 }
76
77 BoostContextFactory::~BoostContextFactory()
78 {
79 #if HAVE_THREAD_CONTEXTS
80   if (BoostContext::parmap_) {
81     xbt_parmap_destroy(BoostContext::parmap_);
82     BoostContext::parmap_ = nullptr;
83   }
84   BoostContext::workers_context_.clear();
85 #endif
86 }
87
88 smx_context_t BoostContextFactory::create_context(std::function<void()>  code,
89   void_pfn_smxprocess_t cleanup_func, smx_process_t process)
90 {
91   BoostContext* context = nullptr;
92   if (BoostContext::parallel_)
93 #if HAVE_THREAD_CONTEXTS
94     context = this->new_context<BoostParallelContext>(
95       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>(
101       std::move(code), cleanup_func, process);
102   return context;
103 }
104
105 void BoostContextFactory::run_all()
106 {
107 #if HAVE_THREAD_CONTEXTS
108   if (BoostContext::parallel_) {
109     BoostContext::threads_working_ = 0;
110     xbt_parmap_apply(BoostContext::parmap_,
111       [](void* arg) {
112         smx_process_t process = static_cast<smx_process_t>(arg);
113         BoostContext* context  = static_cast<BoostContext*>(process->context);
114         return context->resume();
115       },
116       simix_global->process_to_run);
117   } else
118 #endif
119   {
120     smx_process_t first_process =
121         xbt_dynar_get_as(simix_global->process_to_run, 0, smx_process_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_process_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 = (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(
185     maestro_context_->fc_, this->fc_,
186     (intptr_t) this);
187 #else
188   boost::context::jump_fcontext(
189     &maestro_context_->fc_, this->fc_,
190     (intptr_t) this);
191 #endif
192 }
193
194 void BoostSerialContext::suspend()
195 {
196   /* determine the next context */
197   BoostSerialContext* next_context = nullptr;
198   unsigned long int i = process_index_++;
199
200   if (i < xbt_dynar_length(simix_global->process_to_run)) {
201     /* execute the next process */
202     XBT_DEBUG("Run next process");
203     next_context = static_cast<BoostSerialContext*>(xbt_dynar_get_as(
204         simix_global->process_to_run, i, smx_process_t)->context);
205   }
206   else {
207     /* all processes were run, return to maestro */
208     XBT_DEBUG("No more process to run");
209     next_context = static_cast<BoostSerialContext*>(
210       maestro_context_);
211   }
212   SIMIX_context_set_current((smx_context_t) next_context);
213   #if HAVE_BOOST_CONTEXTS == 1
214   boost::context::jump_fcontext(
215     this->fc_, next_context->fc_, (intptr_t) next_context);
216   #else
217   boost::context::jump_fcontext(
218     &this->fc_, next_context->fc_, (intptr_t) next_context);
219   #endif
220 }
221
222 void BoostSerialContext::stop()
223 {
224   BoostContext::stop();
225   this->suspend();
226 }
227
228 // BoostParallelContext
229
230 #if HAVE_THREAD_CONTEXTS
231
232 void BoostParallelContext::suspend()
233 {
234   smx_process_t next_work = (smx_process_t) xbt_parmap_next(parmap_);
235   BoostParallelContext* next_context = nullptr;
236
237   if (next_work != nullptr) {
238     XBT_DEBUG("Run next process");
239     next_context = static_cast<BoostParallelContext*>(next_work->context);
240   }
241   else {
242     XBT_DEBUG("No more processes to run");
243     uintptr_t worker_id =
244       (uintptr_t) xbt_os_thread_get_specific(worker_id_key_);
245     next_context = static_cast<BoostParallelContext*>(
246       workers_context_[worker_id]);
247   }
248
249   SIMIX_context_set_current((smx_context_t) next_context);
250 #if HAVE_BOOST_CONTEXTS == 1
251   boost::context::jump_fcontext(
252     this->fc_, next_context->fc_, (intptr_t)next_context);
253 #else
254   boost::context::jump_fcontext(
255     &this->fc_, next_context->fc_, (intptr_t)next_context);
256 #endif
257 }
258
259 void BoostParallelContext::stop()
260 {
261   BoostContext::stop();
262   this->suspend();
263 }
264
265 void BoostParallelContext::resume()
266 {
267   uintptr_t worker_id = __sync_fetch_and_add(&threads_working_, 1);
268   xbt_os_thread_set_specific(worker_id_key_, (void*) worker_id);
269
270   BoostParallelContext* worker_context =
271     static_cast<BoostParallelContext*>(SIMIX_context_self());
272   workers_context_[worker_id] = worker_context;
273
274   SIMIX_context_set_current(this);
275 #if HAVE_BOOST_CONTEXTS == 1
276   boost::context::jump_fcontext(
277     worker_context->fc_, this->fc_, (intptr_t) this);
278 #else
279   boost::context::jump_fcontext(
280     &worker_context->fc_, this->fc_, (intptr_t) this);
281 #endif
282 }
283
284 #endif
285
286 XBT_PRIVATE ContextFactory* boost_factory()
287 {
288   XBT_VERB("Using Boost contexts. Welcome to the 21th century.");
289   return new BoostContextFactory();
290 }
291
292 }
293 }