Logo AND Algorithmique Numérique Distribuée

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