Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
87b490ea803b7b575c14db76e9ec064249667e66
[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_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);
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   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];
113 #endif
114   try {
115     (*context)();
116     context->Context::stop();
117   } catch (StopRequest const&) {
118     XBT_DEBUG("Caught a StopRequest");
119   }
120   ASAN_EVAL(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   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;
138 #endif
139 }
140
141 void BoostContext::stop()
142 {
143   Context::stop();
144   throw StopRequest();
145 }
146
147 // SerialBoostContext
148
149 unsigned long SerialBoostContext::process_index_;
150
151 void SerialBoostContext::suspend()
152 {
153   /* determine the next context */
154   SerialBoostContext* next_context;
155   unsigned long int i = process_index_;
156   process_index_++;
157
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);
162   } else {
163     /* all processes were run, return to maestro */
164     XBT_DEBUG("No more process to run");
165     next_context = static_cast<SerialBoostContext*>(BoostContext::getMaestro());
166   }
167   SIMIX_context_set_current(next_context);
168   BoostContext::swap(this, next_context);
169 }
170
171 void SerialBoostContext::resume()
172 {
173   SIMIX_context_set_current(this);
174   BoostContext::swap(BoostContext::getMaestro(), this);
175 }
176
177 void SerialBoostContext::run_all()
178 {
179   if (simix_global->process_to_run.empty())
180     return;
181   smx_actor_t first_process = simix_global->process_to_run.front();
182   process_index_            = 1;
183   /* execute the first process */
184   static_cast<SerialBoostContext*>(first_process->context)->resume();
185 }
186
187 // ParallelBoostContext
188
189 #if HAVE_THREAD_CONTEXTS
190
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_;
195
196 void ParallelBoostContext::initialize()
197 {
198   parmap_ = nullptr;
199   workers_context_.clear();
200   workers_context_.resize(SIMIX_context_get_nthreads(), nullptr);
201   xbt_os_thread_key_create(&worker_id_key_);
202 }
203
204 void ParallelBoostContext::finalize()
205 {
206   delete parmap_;
207   parmap_ = nullptr;
208   workers_context_.clear();
209   xbt_os_thread_key_destroy(worker_id_key_);
210 }
211
212 void ParallelBoostContext::run_all()
213 {
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());
217   parmap_->apply(
218       [](smx_actor_t process) {
219         ParallelBoostContext* context = static_cast<ParallelBoostContext*>(process->context);
220         context->resume();
221       },
222       simix_global->process_to_run);
223 }
224
225 void ParallelBoostContext::suspend()
226 {
227   boost::optional<smx_actor_t> next_work = parmap_->next();
228   ParallelBoostContext* next_context;
229   if (next_work) {
230     XBT_DEBUG("Run next process");
231     next_context = static_cast<ParallelBoostContext*>(next_work.get()->context);
232   } else {
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];
236   }
237
238   SIMIX_context_set_current(next_context);
239   BoostContext::swap(this, next_context);
240 }
241
242 void ParallelBoostContext::resume()
243 {
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));
246
247   ParallelBoostContext* worker_context = static_cast<ParallelBoostContext*>(SIMIX_context_self());
248   workers_context_[worker_id]          = worker_context;
249
250   SIMIX_context_set_current(this);
251   BoostContext::swap(worker_context, this);
252 }
253
254 #endif
255
256 XBT_PRIVATE ContextFactory* boost_factory()
257 {
258   XBT_VERB("Using Boost contexts. Welcome to the 21th century.");
259   return new BoostContextFactory();
260 }
261 }}} // namespace