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 "src/internal_config.h"
22 #include "src/simix/ContextBoost.hpp"
24 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
29 class BoostSerialContext : public BoostContext {
31 BoostSerialContext(std::function<void()> code,
32 void_pfn_smxprocess_t cleanup_func,
33 smx_process_t process)
34 : BoostContext(std::move(code), cleanup_func, process) {}
36 void suspend() override;
40 #if HAVE_THREAD_CONTEXTS
41 class BoostParallelContext : public BoostContext {
43 BoostParallelContext(std::function<void()> code,
44 void_pfn_smxprocess_t cleanup_func,
45 smx_process_t process)
46 : BoostContext(std::move(code), cleanup_func, process) {}
48 void suspend() override;
53 // BoostContextFactory
55 bool BoostContext::parallel_ = false;
56 xbt_parmap_t BoostContext::parmap_ = nullptr;
57 uintptr_t BoostContext::threads_working_ = 0;
58 xbt_os_thread_key_t BoostContext::worker_id_key_;
59 unsigned long BoostContext::process_index_ = 0;
60 BoostContext* BoostContext::maestro_context_ = nullptr;
61 std::vector<BoostContext*> BoostContext::workers_context_;
63 BoostContextFactory::BoostContextFactory()
64 : ContextFactory("BoostContextFactory")
66 BoostContext::parallel_ = SIMIX_context_is_parallel();
67 if (BoostContext::parallel_) {
68 #if !HAVE_THREAD_CONTEXTS
69 xbt_die("No thread support for parallel context execution");
71 int nthreads = SIMIX_context_get_nthreads();
72 BoostContext::parmap_ = xbt_parmap_new(nthreads, SIMIX_context_get_parallel_mode());
73 BoostContext::workers_context_.clear();
74 BoostContext::workers_context_.resize(nthreads, nullptr);
75 BoostContext::maestro_context_ = nullptr;
76 xbt_os_thread_key_create(&BoostContext::worker_id_key_);
81 BoostContextFactory::~BoostContextFactory()
83 #if HAVE_THREAD_CONTEXTS
84 if (BoostContext::parmap_) {
85 xbt_parmap_destroy(BoostContext::parmap_);
86 BoostContext::parmap_ = nullptr;
88 BoostContext::workers_context_.clear();
92 smx_context_t BoostContextFactory::create_context(std::function<void()> code,
93 void_pfn_smxprocess_t cleanup_func, smx_process_t process)
95 BoostContext* context = nullptr;
96 if (BoostContext::parallel_)
97 #if HAVE_THREAD_CONTEXTS
98 context = this->new_context<BoostParallelContext>(
99 std::move(code), cleanup_func, process);
101 xbt_die("No support for parallel execution");
104 context = this->new_context<BoostSerialContext>(
105 std::move(code), cleanup_func, process);
109 void BoostContextFactory::run_all()
111 #if HAVE_THREAD_CONTEXTS
112 if (BoostContext::parallel_) {
113 BoostContext::threads_working_ = 0;
114 xbt_parmap_apply(BoostContext::parmap_,
116 smx_process_t process = static_cast<smx_process_t>(arg);
117 BoostContext* context = static_cast<BoostContext*>(process->context);
118 return context->resume();
120 simix_global->process_to_run);
124 smx_process_t first_process =
125 xbt_dynar_get_as(simix_global->process_to_run, 0, smx_process_t);
126 BoostContext::process_index_ = 1;
127 /* execute the first process */
128 static_cast<BoostContext*>(first_process->context)->resume();
135 static void smx_ctx_boost_wrapper(std::intptr_t arg)
137 BoostContext* context = (BoostContext*) arg;
142 BoostContext::BoostContext(std::function<void()> code,
143 void_pfn_smxprocess_t cleanup_func, smx_process_t process)
144 : Context(std::move(code), cleanup_func, process)
147 /* if the user provided a function for the process then use it,
148 otherwise it is the context for maestro */
150 this->stack_ = SIMIX_context_stack_new();
151 // We need to pass the bottom of the stack to make_fcontext,
152 // depending on the stack direction it may be the lower or higher address:
153 #if PTH_STACKGROWTH == -1
154 void* stack = (char*) this->stack_ + smx_context_usable_stack_size - 1;
156 void* stack = this->stack_;
158 this->fc_ = boost::context::make_fcontext(
160 smx_context_usable_stack_size,
161 smx_ctx_boost_wrapper);
163 #if HAVE_BOOST_CONTEXTS == 1
164 this->fc_ = new boost::context::fcontext_t();
166 if (BoostContext::maestro_context_ == nullptr)
167 BoostContext::maestro_context_ = this;
171 BoostContext::~BoostContext()
173 #if HAVE_BOOST_CONTEXTS == 1
177 if (this == maestro_context_)
178 maestro_context_ = nullptr;
179 SIMIX_context_stack_delete(this->stack_);
182 // BoostSerialContext
184 void BoostContext::resume()
186 SIMIX_context_set_current(this);
187 #if HAVE_BOOST_CONTEXTS == 1
188 boost::context::jump_fcontext(
189 maestro_context_->fc_, this->fc_,
192 boost::context::jump_fcontext(
193 &maestro_context_->fc_, this->fc_,
198 void BoostSerialContext::suspend()
200 /* determine the next context */
201 BoostSerialContext* next_context = nullptr;
202 unsigned long int i = process_index_++;
204 if (i < xbt_dynar_length(simix_global->process_to_run)) {
205 /* execute the next process */
206 XBT_DEBUG("Run next process");
207 next_context = static_cast<BoostSerialContext*>(xbt_dynar_get_as(
208 simix_global->process_to_run, i, smx_process_t)->context);
211 /* all processes were run, return to maestro */
212 XBT_DEBUG("No more process to run");
213 next_context = static_cast<BoostSerialContext*>(
216 SIMIX_context_set_current((smx_context_t) next_context);
217 #if HAVE_BOOST_CONTEXTS == 1
218 boost::context::jump_fcontext(
219 this->fc_, next_context->fc_, (intptr_t) next_context);
221 boost::context::jump_fcontext(
222 &this->fc_, next_context->fc_, (intptr_t) next_context);
226 void BoostSerialContext::stop()
228 BoostContext::stop();
232 // BoostParallelContext
234 #if HAVE_THREAD_CONTEXTS
236 void BoostParallelContext::suspend()
238 smx_process_t next_work = (smx_process_t) xbt_parmap_next(parmap_);
239 BoostParallelContext* next_context = nullptr;
241 if (next_work != nullptr) {
242 XBT_DEBUG("Run next process");
243 next_context = static_cast<BoostParallelContext*>(next_work->context);
246 XBT_DEBUG("No more processes to run");
247 uintptr_t worker_id =
248 (uintptr_t) xbt_os_thread_get_specific(worker_id_key_);
249 next_context = static_cast<BoostParallelContext*>(
250 workers_context_[worker_id]);
253 SIMIX_context_set_current((smx_context_t) next_context);
254 #if HAVE_BOOST_CONTEXTS == 1
255 boost::context::jump_fcontext(
256 this->fc_, next_context->fc_, (intptr_t)next_context);
258 boost::context::jump_fcontext(
259 &this->fc_, next_context->fc_, (intptr_t)next_context);
263 void BoostParallelContext::stop()
265 BoostContext::stop();
269 void BoostParallelContext::resume()
271 uintptr_t worker_id = __sync_fetch_and_add(&threads_working_, 1);
272 xbt_os_thread_set_specific(worker_id_key_, (void*) worker_id);
274 BoostParallelContext* worker_context =
275 static_cast<BoostParallelContext*>(SIMIX_context_self());
276 workers_context_[worker_id] = worker_context;
278 SIMIX_context_set_current(this);
279 #if HAVE_BOOST_CONTEXTS == 1
280 boost::context::jump_fcontext(
281 worker_context->fc_, this->fc_, (intptr_t) this);
283 boost::context::jump_fcontext(
284 &worker_context->fc_, this->fc_, (intptr_t) this);
290 XBT_PRIVATE ContextFactory* boost_factory()
292 XBT_VERB("Using Boost contexts. Welcome to the 21th century.");
293 return new BoostContextFactory();