1 /* Copyright (c) 2015. The SimGrid Team.
2 * All rights reserved. */
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. */
7 /** @file BoostContext.cpp Userspace context switching implementation based on Boost.Context */
15 #include <boost/context/all.hpp>
18 #include <xbt/xbt_os_thread.h>
20 #include "smx_private.h"
21 #include "smx_private.hpp"
22 #include "src/internal_config.h"
23 #include "src/simix/BoostContext.hpp"
25 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
30 class BoostSerialContext : public BoostContext {
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) {}
37 void suspend() override;
41 #if HAVE_THREAD_CONTEXTS
42 class BoostParallelContext : public BoostContext {
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) {}
49 void suspend() override;
54 // BoostContextFactory
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_;
64 BoostContextFactory::BoostContextFactory()
65 : ContextFactory("BoostContextFactory")
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");
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_);
82 BoostContextFactory::~BoostContextFactory()
84 #if HAVE_THREAD_CONTEXTS
85 if (BoostContext::parmap_) {
86 xbt_parmap_destroy(BoostContext::parmap_);
87 BoostContext::parmap_ = nullptr;
89 BoostContext::workers_context_.clear();
93 smx_context_t BoostContextFactory::create_context(std::function<void()> code,
94 void_pfn_smxprocess_t cleanup_func, smx_process_t process)
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);
102 xbt_die("No support for parallel execution");
105 context = this->new_context<BoostSerialContext>(
106 std::move(code), cleanup_func, process);
110 void BoostContextFactory::run_all()
112 #if HAVE_THREAD_CONTEXTS
113 if (BoostContext::parallel_) {
114 BoostContext::threads_working_ = 0;
115 xbt_parmap_apply(BoostContext::parmap_,
117 smx_process_t process = static_cast<smx_process_t>(arg);
118 BoostContext* context = static_cast<BoostContext*>(process->context);
119 return context->resume();
121 simix_global->process_to_run);
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();
136 static void smx_ctx_boost_wrapper(std::intptr_t arg)
138 BoostContext* context = (BoostContext*) arg;
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)
148 /* if the user provided a function for the process then use it,
149 otherwise it is the context for maestro */
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;
157 void* stack = this->stack_;
159 this->fc_ = boost::context::make_fcontext(
161 smx_context_usable_stack_size,
162 smx_ctx_boost_wrapper);
164 #if HAVE_BOOST_CONTEXTS == 1
165 this->fc_ = new boost::context::fcontext_t();
167 if (BoostContext::maestro_context_ == nullptr)
168 BoostContext::maestro_context_ = this;
172 BoostContext::~BoostContext()
174 #if HAVE_BOOST_CONTEXTS == 1
178 if (this == maestro_context_)
179 maestro_context_ = nullptr;
180 SIMIX_context_stack_delete(this->stack_);
183 // BoostSerialContext
185 void BoostContext::resume()
187 SIMIX_context_set_current(this);
188 #if HAVE_BOOST_CONTEXTS == 1
189 boost::context::jump_fcontext(
190 maestro_context_->fc_, this->fc_,
193 boost::context::jump_fcontext(
194 &maestro_context_->fc_, this->fc_,
199 void BoostSerialContext::suspend()
201 /* determine the next context */
202 BoostSerialContext* next_context = nullptr;
203 unsigned long int i = process_index_++;
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);
212 /* all processes were run, return to maestro */
213 XBT_DEBUG("No more process to run");
214 next_context = static_cast<BoostSerialContext*>(
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);
222 boost::context::jump_fcontext(
223 &this->fc_, next_context->fc_, (intptr_t) next_context);
227 void BoostSerialContext::stop()
229 BoostContext::stop();
233 // BoostParallelContext
235 #if HAVE_THREAD_CONTEXTS
237 void BoostParallelContext::suspend()
239 smx_process_t next_work = (smx_process_t) xbt_parmap_next(parmap_);
240 BoostParallelContext* next_context = nullptr;
242 if (next_work != nullptr) {
243 XBT_DEBUG("Run next process");
244 next_context = static_cast<BoostParallelContext*>(next_work->context);
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]);
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);
259 boost::context::jump_fcontext(
260 &this->fc_, next_context->fc_, (intptr_t)next_context);
264 void BoostParallelContext::stop()
266 BoostContext::stop();
270 void BoostParallelContext::resume()
272 uintptr_t worker_id = __sync_fetch_and_add(&threads_working_, 1);
273 xbt_os_thread_set_specific(worker_id_key_, (void*) worker_id);
275 BoostParallelContext* worker_context =
276 static_cast<BoostParallelContext*>(SIMIX_context_self());
277 workers_context_[worker_id] = worker_context;
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);
284 boost::context::jump_fcontext(
285 &worker_context->fc_, this->fc_, (intptr_t) this);
291 XBT_PRIVATE ContextFactory* boost_factory()
293 XBT_VERB("Using Boost contexts. Welcome to the 21th century.");
294 return new BoostContextFactory();