Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Sysv contexts: remove useless indirection.
[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   BoostContext* context = static_cast<BoostContext**>(arg.data)[1];
110   ASAN_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_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_EVAL(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_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 xbt_os_thread_key_t ParallelBoostContext::worker_id_key_;
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   xbt_os_thread_key_create(&worker_id_key_);
203 }
204
205 void ParallelBoostContext::finalize()
206 {
207   delete parmap_;
208   parmap_ = nullptr;
209   workers_context_.clear();
210   xbt_os_thread_key_destroy(worker_id_key_);
211 }
212
213 void ParallelBoostContext::run_all()
214 {
215   threads_working_ = 0;
216   if (parmap_ == nullptr)
217     parmap_ = new simgrid::xbt::Parmap<smx_actor_t>(SIMIX_context_get_nthreads(), SIMIX_context_get_parallel_mode());
218   parmap_->apply(
219       [](smx_actor_t process) {
220         ParallelBoostContext* context = static_cast<ParallelBoostContext*>(process->context);
221         context->resume();
222       },
223       simix_global->process_to_run);
224 }
225
226 void ParallelBoostContext::suspend()
227 {
228   boost::optional<smx_actor_t> next_work = parmap_->next();
229   ParallelBoostContext* next_context;
230   if (next_work) {
231     XBT_DEBUG("Run next process");
232     next_context = static_cast<ParallelBoostContext*>(next_work.get()->context);
233   } else {
234     XBT_DEBUG("No more processes to run");
235     uintptr_t worker_id = reinterpret_cast<uintptr_t>(xbt_os_thread_get_specific(worker_id_key_));
236     next_context        = workers_context_[worker_id];
237   }
238
239   SIMIX_context_set_current(next_context);
240   BoostContext::swap(this, next_context);
241 }
242
243 void ParallelBoostContext::resume()
244 {
245   uintptr_t worker_id = threads_working_.fetch_add(1, std::memory_order_relaxed);
246   xbt_os_thread_set_specific(worker_id_key_, reinterpret_cast<void*>(worker_id));
247
248   ParallelBoostContext* worker_context = static_cast<ParallelBoostContext*>(SIMIX_context_self());
249   workers_context_[worker_id]          = worker_context;
250
251   SIMIX_context_set_current(this);
252   BoostContext::swap(worker_context, this);
253 }
254
255 #endif
256
257 XBT_PRIVATE ContextFactory* boost_factory()
258 {
259   XBT_VERB("Using Boost contexts. Welcome to the 21th century.");
260   return new BoostContextFactory();
261 }
262 }}} // namespace