1 /* Copyright (c) 2015-2018. The SimGrid Team. All rights reserved. */
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. */
6 #include "ContextBoost.hpp"
7 #include "context_private.hpp"
8 #include "src/simix/smx_private.hpp"
10 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
16 // BoostContextFactory
18 BoostContextFactory::BoostContextFactory()
19 : ContextFactory("BoostContextFactory"), parallel_(SIMIX_context_is_parallel())
21 BoostContext::setMaestro(nullptr);
23 #if HAVE_THREAD_CONTEXTS
24 ParallelBoostContext::initialize();
26 xbt_die("No thread support for parallel context execution");
31 BoostContextFactory::~BoostContextFactory()
33 #if HAVE_THREAD_CONTEXTS
35 ParallelBoostContext::finalize();
39 smx_context_t BoostContextFactory::create_context(std::function<void()> code, void_pfn_smxprocess_t cleanup_func,
42 #if HAVE_THREAD_CONTEXTS
44 return this->new_context<ParallelBoostContext>(std::move(code), cleanup_func, process);
47 return this->new_context<SerialBoostContext>(std::move(code), cleanup_func, process);
50 void BoostContextFactory::run_all()
52 #if HAVE_THREAD_CONTEXTS
54 ParallelBoostContext::run_all();
57 SerialBoostContext::run_all();
62 BoostContext* BoostContext::maestro_context_ = nullptr;
64 BoostContext::BoostContext(std::function<void()> code, void_pfn_smxprocess_t cleanup_func, smx_actor_t process)
65 : Context(std::move(code), cleanup_func, process)
68 /* if the user provided a function for the process then use it, otherwise it is the context for maestro */
70 this->stack_ = SIMIX_context_stack_new();
71 /* We need to pass the bottom of the stack to make_fcontext,
72 depending on the stack direction it may be the lower or higher address: */
73 #if PTH_STACKGROWTH == -1
74 void* stack = static_cast<char*>(this->stack_) + smx_context_usable_stack_size;
76 void* stack = this->stack_;
78 ASAN_EVAL(this->asan_stack_ = stack);
79 #if BOOST_VERSION < 106100
80 this->fc_ = boost::context::make_fcontext(stack, smx_context_usable_stack_size, BoostContext::wrapper);
82 this->fc_ = boost::context::detail::make_fcontext(stack, smx_context_usable_stack_size, BoostContext::wrapper);
85 #if BOOST_VERSION < 105600
86 this->fc_ = new boost::context::fcontext_t();
88 if (BoostContext::maestro_context_ == nullptr)
89 BoostContext::maestro_context_ = this;
93 BoostContext::~BoostContext()
95 #if BOOST_VERSION < 105600
99 if (this == maestro_context_)
100 maestro_context_ = nullptr;
101 SIMIX_context_stack_delete(this->stack_);
104 void BoostContext::wrapper(BoostContext::arg_type arg)
106 #if BOOST_VERSION < 106100
107 BoostContext* context = reinterpret_cast<BoostContext*>(arg);
109 ASAN_FINISH_SWITCH(nullptr, &static_cast<BoostContext**>(arg.data)[0]->asan_stack_,
110 &static_cast<BoostContext**>(arg.data)[0]->asan_stack_size_);
111 static_cast<BoostContext**>(arg.data)[0]->fc_ = arg.fctx;
112 BoostContext* context = static_cast<BoostContext**>(arg.data)[1];
116 context->Context::stop();
117 } catch (StopRequest const&) {
118 XBT_DEBUG("Caught a StopRequest");
120 ASAN_EVAL(context->asan_stop_ = true);
124 inline void BoostContext::swap(BoostContext* from, BoostContext* to)
126 #if BOOST_VERSION < 105600
127 boost::context::jump_fcontext(from->fc_, to->fc_, reinterpret_cast<intptr_t>(to));
128 #elif BOOST_VERSION < 106100
129 boost::context::jump_fcontext(&from->fc_, to->fc_, reinterpret_cast<intptr_t>(to));
131 BoostContext* ctx[2] = {from, to};
132 void* fake_stack = nullptr;
133 ASAN_START_SWITCH(from->asan_stop_ ? nullptr : &fake_stack, to->asan_stack_, to->asan_stack_size_);
134 boost::context::detail::transfer_t arg = boost::context::detail::jump_fcontext(to->fc_, ctx);
135 ASAN_FINISH_SWITCH(fake_stack, &static_cast<BoostContext**>(arg.data)[0]->asan_stack_,
136 &static_cast<BoostContext**>(arg.data)[0]->asan_stack_size_);
137 static_cast<BoostContext**>(arg.data)[0]->fc_ = arg.fctx;
141 void BoostContext::stop()
147 // SerialBoostContext
149 unsigned long SerialBoostContext::process_index_;
151 void SerialBoostContext::suspend()
153 /* determine the next context */
154 SerialBoostContext* next_context;
155 unsigned long int i = process_index_;
158 if (i < simix_global->process_to_run.size()) {
159 /* execute the next process */
160 XBT_DEBUG("Run next process");
161 next_context = static_cast<SerialBoostContext*>(simix_global->process_to_run[i]->context);
163 /* all processes were run, return to maestro */
164 XBT_DEBUG("No more process to run");
165 next_context = static_cast<SerialBoostContext*>(BoostContext::getMaestro());
167 SIMIX_context_set_current(next_context);
168 BoostContext::swap(this, next_context);
171 void SerialBoostContext::resume()
173 SIMIX_context_set_current(this);
174 BoostContext::swap(BoostContext::getMaestro(), this);
177 void SerialBoostContext::run_all()
179 if (simix_global->process_to_run.empty())
181 smx_actor_t first_process = simix_global->process_to_run.front();
183 /* execute the first process */
184 static_cast<SerialBoostContext*>(first_process->context)->resume();
187 // ParallelBoostContext
189 #if HAVE_THREAD_CONTEXTS
191 simgrid::xbt::Parmap<smx_actor_t>* ParallelBoostContext::parmap_;
192 std::atomic<uintptr_t> ParallelBoostContext::threads_working_;
193 xbt_os_thread_key_t ParallelBoostContext::worker_id_key_;
194 std::vector<ParallelBoostContext*> ParallelBoostContext::workers_context_;
196 void ParallelBoostContext::initialize()
199 workers_context_.clear();
200 workers_context_.resize(SIMIX_context_get_nthreads(), nullptr);
201 xbt_os_thread_key_create(&worker_id_key_);
204 void ParallelBoostContext::finalize()
208 workers_context_.clear();
209 xbt_os_thread_key_destroy(worker_id_key_);
212 void ParallelBoostContext::run_all()
214 threads_working_ = 0;
215 if (parmap_ == nullptr)
216 parmap_ = new simgrid::xbt::Parmap<smx_actor_t>(SIMIX_context_get_nthreads(), SIMIX_context_get_parallel_mode());
218 [](smx_actor_t process) {
219 ParallelBoostContext* context = static_cast<ParallelBoostContext*>(process->context);
222 simix_global->process_to_run);
225 void ParallelBoostContext::suspend()
227 boost::optional<smx_actor_t> next_work = parmap_->next();
228 ParallelBoostContext* next_context;
230 XBT_DEBUG("Run next process");
231 next_context = static_cast<ParallelBoostContext*>(next_work.get()->context);
233 XBT_DEBUG("No more processes to run");
234 uintptr_t worker_id = reinterpret_cast<uintptr_t>(xbt_os_thread_get_specific(worker_id_key_));
235 next_context = workers_context_[worker_id];
238 SIMIX_context_set_current(next_context);
239 BoostContext::swap(this, next_context);
242 void ParallelBoostContext::resume()
244 uintptr_t worker_id = threads_working_.fetch_add(1, std::memory_order_relaxed);
245 xbt_os_thread_set_specific(worker_id_key_, reinterpret_cast<void*>(worker_id));
247 ParallelBoostContext* worker_context = static_cast<ParallelBoostContext*>(SIMIX_context_self());
248 workers_context_[worker_id] = worker_context;
250 SIMIX_context_set_current(this);
251 BoostContext::swap(worker_context, this);
256 XBT_PRIVATE ContextFactory* boost_factory()
258 XBT_VERB("Using Boost contexts. Welcome to the 21th century.");
259 return new BoostContextFactory();