Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Cover with a test Mailbox::ready() method introduced in commit 1ed0e64dc405fbb627ff8a...
[simgrid.git] / src / kernel / context / ContextBoost.cpp
1 /* Copyright (c) 2015-2018. The SimGrid Team. All rights reserved.          */
2
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. */
5
6 #include "ContextBoost.hpp"
7 #include "context_private.hpp"
8 #include "src/simix/smx_private.hpp"
9
10 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
11
12 namespace simgrid {
13 namespace kernel {
14 namespace context {
15
16 // BoostContextFactory
17
18 BoostContextFactory::BoostContextFactory()
19     : ContextFactory("BoostContextFactory"), parallel_(SIMIX_context_is_parallel())
20 {
21   BoostContext::setMaestro(nullptr);
22   if (parallel_) {
23 #if HAVE_THREAD_CONTEXTS
24     ParallelBoostContext::initialize();
25 #else
26     xbt_die("No thread support for parallel context execution");
27 #endif
28   }
29 }
30
31 BoostContextFactory::~BoostContextFactory()
32 {
33 #if HAVE_THREAD_CONTEXTS
34   if (parallel_)
35     ParallelBoostContext::finalize();
36 #endif
37 }
38
39 smx_context_t BoostContextFactory::create_context(std::function<void()> code, void_pfn_smxprocess_t cleanup_func,
40                                                   smx_actor_t process)
41 {
42 #if HAVE_THREAD_CONTEXTS
43   if (parallel_)
44     return this->new_context<ParallelBoostContext>(std::move(code), cleanup_func, process);
45 #endif
46
47   return this->new_context<SerialBoostContext>(std::move(code), cleanup_func, process);
48 }
49
50 void BoostContextFactory::run_all()
51 {
52 #if HAVE_THREAD_CONTEXTS
53   if (parallel_)
54     ParallelBoostContext::run_all();
55   else
56 #endif
57     SerialBoostContext::run_all();
58 }
59
60 // BoostContext
61
62 BoostContext* BoostContext::maestro_context_ = nullptr;
63
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)
66 {
67
68   /* if the user provided a function for the process then use it, otherwise it is the context for maestro */
69   if (has_code()) {
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;
75 #else
76     void* stack = this->stack_;
77 #endif
78     ASAN_ONLY(this->asan_stack_ = stack);
79 #if BOOST_VERSION < 106100
80     this->fc_ = boost::context::make_fcontext(stack, smx_context_usable_stack_size, BoostContext::wrapper);
81 #else
82     this->fc_ = boost::context::detail::make_fcontext(stack, smx_context_usable_stack_size, BoostContext::wrapper);
83 #endif
84   } else {
85 #if BOOST_VERSION < 105600
86     this->fc_ = new boost::context::fcontext_t();
87 #endif
88     if (BoostContext::maestro_context_ == nullptr)
89       BoostContext::maestro_context_ = this;
90   }
91 }
92
93 BoostContext::~BoostContext()
94 {
95 #if BOOST_VERSION < 105600
96   if (not this->stack_)
97     delete this->fc_;
98 #endif
99   if (this == maestro_context_)
100     maestro_context_ = nullptr;
101   SIMIX_context_stack_delete(this->stack_);
102 }
103
104 void BoostContext::wrapper(BoostContext::arg_type arg)
105 {
106 #if BOOST_VERSION < 106100
107   BoostContext* context = reinterpret_cast<BoostContext*>(arg);
108 #else
109   BoostContext* context = static_cast<BoostContext**>(arg.data)[1];
110   ASAN_ONLY(xbt_assert(context->asan_ctx_ == static_cast<BoostContext**>(arg.data)[0]));
111   ASAN_FINISH_SWITCH(nullptr, &context->asan_ctx_->asan_stack_, &context->asan_ctx_->asan_stack_size_);
112   static_cast<BoostContext**>(arg.data)[0]->fc_ = arg.fctx;
113 #endif
114   try {
115     (*context)();
116     context->Context::stop();
117   } catch (StopRequest const&) {
118     XBT_DEBUG("Caught a StopRequest");
119   }
120   ASAN_ONLY(context->asan_stop_ = true);
121   context->suspend();
122 }
123
124 inline void BoostContext::swap(BoostContext* from, BoostContext* to)
125 {
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));
130 #else
131   BoostContext* ctx[2] = {from, to};
132   ASAN_ONLY(void* fake_stack = nullptr);
133   ASAN_ONLY(to->asan_ctx_ = from);
134   ASAN_START_SWITCH(from->asan_stop_ ? nullptr : &fake_stack, to->asan_stack_, to->asan_stack_size_);
135   boost::context::detail::transfer_t arg = boost::context::detail::jump_fcontext(to->fc_, ctx);
136   ASAN_ONLY(xbt_assert(from->asan_ctx_ == static_cast<BoostContext**>(arg.data)[0]));
137   ASAN_FINISH_SWITCH(fake_stack, &from->asan_ctx_->asan_stack_, &from->asan_ctx_->asan_stack_size_);
138   static_cast<BoostContext**>(arg.data)[0]->fc_ = arg.fctx;
139 #endif
140 }
141
142 void BoostContext::stop()
143 {
144   Context::stop();
145   throw StopRequest();
146 }
147
148 // SerialBoostContext
149
150 unsigned long SerialBoostContext::process_index_;
151
152 void SerialBoostContext::suspend()
153 {
154   /* determine the next context */
155   SerialBoostContext* next_context;
156   unsigned long int i = process_index_;
157   process_index_++;
158
159   if (i < simix_global->process_to_run.size()) {
160     /* execute the next process */
161     XBT_DEBUG("Run next process");
162     next_context = static_cast<SerialBoostContext*>(simix_global->process_to_run[i]->context_);
163   } else {
164     /* all processes were run, return to maestro */
165     XBT_DEBUG("No more process to run");
166     next_context = static_cast<SerialBoostContext*>(BoostContext::getMaestro());
167   }
168   SIMIX_context_set_current(next_context);
169   BoostContext::swap(this, next_context);
170 }
171
172 void SerialBoostContext::resume()
173 {
174   SIMIX_context_set_current(this);
175   BoostContext::swap(BoostContext::getMaestro(), this);
176 }
177
178 void SerialBoostContext::run_all()
179 {
180   if (simix_global->process_to_run.empty())
181     return;
182   smx_actor_t first_process = simix_global->process_to_run.front();
183   process_index_            = 1;
184   /* execute the first process */
185   static_cast<SerialBoostContext*>(first_process->context_)->resume();
186 }
187
188 // ParallelBoostContext
189
190 #if HAVE_THREAD_CONTEXTS
191
192 simgrid::xbt::Parmap<smx_actor_t>* ParallelBoostContext::parmap_;
193 std::atomic<uintptr_t> ParallelBoostContext::threads_working_;
194 thread_local uintptr_t ParallelBoostContext::worker_id_;
195 std::vector<ParallelBoostContext*> ParallelBoostContext::workers_context_;
196
197 void ParallelBoostContext::initialize()
198 {
199   parmap_ = nullptr;
200   workers_context_.clear();
201   workers_context_.resize(SIMIX_context_get_nthreads(), nullptr);
202 }
203
204 void ParallelBoostContext::finalize()
205 {
206   delete parmap_;
207   parmap_ = nullptr;
208   workers_context_.clear();
209 }
210
211 void ParallelBoostContext::run_all()
212 {
213   threads_working_ = 0;
214   if (parmap_ == nullptr)
215     parmap_ = new simgrid::xbt::Parmap<smx_actor_t>(SIMIX_context_get_nthreads(), SIMIX_context_get_parallel_mode());
216   parmap_->apply(
217       [](smx_actor_t process) {
218         ParallelBoostContext* context = static_cast<ParallelBoostContext*>(process->context_);
219         context->resume();
220       },
221       simix_global->process_to_run);
222 }
223
224 void ParallelBoostContext::suspend()
225 {
226   boost::optional<smx_actor_t> next_work = parmap_->next();
227   ParallelBoostContext* next_context;
228   if (next_work) {
229     XBT_DEBUG("Run next process");
230     next_context = static_cast<ParallelBoostContext*>(next_work.get()->context_);
231   } else {
232     XBT_DEBUG("No more processes to run");
233     next_context = workers_context_[worker_id_];
234   }
235
236   SIMIX_context_set_current(next_context);
237   BoostContext::swap(this, next_context);
238 }
239
240 void ParallelBoostContext::resume()
241 {
242   worker_id_ = threads_working_.fetch_add(1, std::memory_order_relaxed);
243
244   ParallelBoostContext* worker_context = static_cast<ParallelBoostContext*>(SIMIX_context_self());
245   workers_context_[worker_id_]         = worker_context;
246
247   SIMIX_context_set_current(this);
248   BoostContext::swap(worker_context, this);
249 }
250
251 #endif
252
253 XBT_PRIVATE ContextFactory* boost_factory()
254 {
255   XBT_VERB("Using Boost contexts. Welcome to the 21th century.");
256   return new BoostContextFactory();
257 }
258 }}} // namespace