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 */
11 #include <boost/context/all.hpp>
14 #include <xbt/xbt_os_thread.h>
16 #include "smx_private.h"
17 #include "smx_private.hpp"
18 #include "src/internal_config.h"
19 #include "src/simix/BoostContext.hpp"
21 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
26 class BoostSerialContext : public BoostContext {
28 BoostSerialContext(xbt_main_func_t code,
29 int argc, char **argv,
30 void_pfn_smxprocess_t cleanup_func,
31 smx_process_t process)
32 : BoostContext(code, argc, argv, cleanup_func, process) {}
34 void suspend() override;
38 #ifdef CONTEXT_THREADS
39 class BoostParallelContext : public BoostContext {
41 BoostParallelContext(xbt_main_func_t code,
42 int argc, char **argv,
43 void_pfn_smxprocess_t cleanup_func,
44 smx_process_t process)
45 : BoostContext(code, argc, argv, cleanup_func, process) {}
47 void suspend() override;
52 // BoostContextFactory
54 bool BoostContext::parallel_ = false;
55 xbt_parmap_t BoostContext::parmap_ = nullptr;
56 unsigned long BoostContext::threads_working_ = 0;
57 xbt_os_thread_key_t BoostContext::worker_id_key_;
58 unsigned long BoostContext::process_index_ = 0;
59 BoostContext* BoostContext::maestro_context_ = nullptr;
60 std::vector<BoostContext*> BoostContext::workers_context_;
62 BoostContextFactory::BoostContextFactory()
63 : ContextFactory("BoostContextFactory")
65 BoostContext::parallel_ = SIMIX_context_is_parallel();
66 if (BoostContext::parallel_) {
67 #ifndef CONTEXT_THREADS
68 xbt_die("No thread support for parallel context execution");
70 int nthreads = SIMIX_context_get_nthreads();
71 BoostContext::parmap_ = xbt_parmap_new(nthreads, SIMIX_context_get_parallel_mode());
72 BoostContext::workers_context_.clear();
73 BoostContext::workers_context_.resize(nthreads, nullptr);
74 BoostContext::maestro_context_ = nullptr;
75 xbt_os_thread_key_create(&BoostContext::worker_id_key_);
80 BoostContextFactory::~BoostContextFactory()
82 #ifdef CONTEXT_THREADS
83 if (BoostContext::parmap_) {
84 xbt_parmap_destroy(BoostContext::parmap_);
85 BoostContext::parmap_ = nullptr;
87 BoostContext::workers_context_.clear();
91 smx_context_t BoostContextFactory::create_context(
92 xbt_main_func_t code, int argc, char ** argv,
93 void_pfn_smxprocess_t cleanup_func, smx_process_t process)
95 BoostContext* context = nullptr;
96 if (BoostContext::parallel_)
97 #ifdef CONTEXT_THREADS
98 context = this->new_context<BoostParallelContext>(
99 code, argc, argv, cleanup_func, process);
101 xbt_die("No support for parallel execution");
104 context = this->new_context<BoostSerialContext>(
105 code, argc, argv, cleanup_func, process);
109 void BoostContextFactory::run_all()
111 #ifdef CONTEXT_THREADS
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(xbt_main_func_t code,
143 int argc, char **argv,
144 void_pfn_smxprocess_t cleanup_func,
145 smx_process_t process)
146 : Context(code, argc, argv, cleanup_func, process)
149 /* if the user provided a function for the process then use it,
150 otherwise it is the context for maestro */
152 this->stack_ = SIMIX_context_stack_new();
153 // We need to pass the bottom of the stack to make_fcontext,
154 // depending on the stack direction it may be the lower or higher address:
155 #if PTH_STACKGROWTH == -1
156 void* stack = (char*) this->stack_ + smx_context_usable_stack_size - 1;
158 void* stack = this->stack_;
160 this->fc_ = boost::context::make_fcontext(
162 smx_context_usable_stack_size,
163 smx_ctx_boost_wrapper);
165 #if HAVE_BOOST_CONTEXT == 1
166 this->fc_ = new boost::context::fcontext_t();
168 if (BoostContext::maestro_context_ == nullptr)
169 BoostContext::maestro_context_ = this;
173 BoostContext::~BoostContext()
175 #if HAVE_BOOST_CONTEXT == 1
179 if (this == maestro_context_)
180 maestro_context_ = nullptr;
181 SIMIX_context_stack_delete(this->stack_);
184 // BoostSerialContext
186 void BoostContext::resume()
188 SIMIX_context_set_current(this);
189 #if HAVE_BOOST_CONTEXT == 1
190 boost::context::jump_fcontext(
191 maestro_context_->fc_, this->fc_,
194 boost::context::jump_fcontext(
195 &boost_maestro_context_->fc_, this->fc_,
200 void BoostSerialContext::suspend()
202 /* determine the next context */
203 BoostSerialContext* next_context = nullptr;
204 unsigned long int i = process_index_++;
206 if (i < xbt_dynar_length(simix_global->process_to_run)) {
207 /* execute the next process */
208 XBT_DEBUG("Run next process");
209 next_context = static_cast<BoostSerialContext*>(xbt_dynar_get_as(
210 simix_global->process_to_run, i, smx_process_t)->context);
213 /* all processes were run, return to maestro */
214 XBT_DEBUG("No more process to run");
215 next_context = static_cast<BoostSerialContext*>(
218 SIMIX_context_set_current((smx_context_t) next_context);
219 #if HAVE_BOOST_CONTEXT == 1
220 boost::context::jump_fcontext(
221 this->fc_, next_context->fc_, (intptr_t) next_context);
223 boost::context::jump_fcontext(
224 &this->fc_, next_context->fc_, (intptr_t) next_context);
228 void BoostSerialContext::stop()
230 BoostContext::stop();
234 // BoostParallelContext
236 #ifdef CONTEXT_THREADS
238 void BoostParallelContext::suspend()
240 smx_process_t next_work = (smx_process_t) xbt_parmap_next(parmap_);
241 BoostParallelContext* next_context = nullptr;
243 if (next_work != nullptr) {
244 XBT_DEBUG("Run next process");
245 next_context = static_cast<BoostParallelContext*>(next_work->context);
248 XBT_DEBUG("No more processes to run");
249 unsigned long worker_id =
250 (unsigned long) xbt_os_thread_get_specific(worker_id_key_);
251 next_context = static_cast<BoostParallelContext*>(
252 workers_context_[worker_id]);
255 SIMIX_context_set_current((smx_context_t) next_context);
256 #if HAVE_BOOST_CONTEXT == 1
257 boost::context::jump_fcontext(
258 this->fc_, next_context->fc_, (intptr_t)next_context);
260 boost::context::jump_fcontext(
261 &this->fc_, next_context->fc_, (intptr_t)next_context);
265 void BoostParallelContext::stop()
267 BoostContext::stop();
271 void BoostParallelContext::resume()
273 unsigned long worker_id = __sync_fetch_and_add(&threads_working_, 1);
274 xbt_os_thread_set_specific(worker_id_key_, (void*) worker_id);
276 BoostParallelContext* worker_context =
277 static_cast<BoostParallelContext*>(SIMIX_context_self());
278 workers_context_[worker_id] = worker_context;
280 SIMIX_context_set_current(this);
281 #if HAVE_BOOST_CONTEXT == 1
282 boost::context::jump_fcontext(
283 worker_context->fc_, this->fc_, (intptr_t) this);
285 boost::context::jump_fcontext(
286 &worker_context->fc_, this->fc_, (intptr_t) this);
292 XBT_PRIVATE ContextFactory* boost_factory()
294 return new BoostContextFactory();