]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | |
2 | // Copyright Oliver Kowalke 2013. | |
3 | // Distributed under the Boost Software License, Version 1.0. | |
4 | // (See accompanying file LICENSE_1_0.txt or copy at | |
5 | // http://www.boost.org/LICENSE_1_0.txt) | |
6 | ||
7 | #include "boost/fiber/context.hpp" | |
8 | ||
9 | #include <cstdlib> | |
10 | #include <mutex> | |
11 | #include <new> | |
12 | ||
13 | #include "boost/fiber/exceptions.hpp" | |
14 | #include "boost/fiber/scheduler.hpp" | |
15 | ||
16 | #ifdef BOOST_HAS_ABI_HEADERS | |
17 | # include BOOST_ABI_PREFIX | |
18 | #endif | |
19 | ||
20 | namespace boost { | |
21 | namespace fibers { | |
22 | ||
b32b8144 FG |
23 | class main_context final : public context { |
24 | public: | |
25 | main_context() noexcept : | |
26 | context{ 1, type::main_context, launch::post } { | |
27 | } | |
28 | }; | |
29 | ||
30 | class dispatcher_context final : public context { | |
31 | private: | |
11fdf7f2 TL |
32 | boost::context::fiber |
33 | run_( boost::context::fiber && c) { | |
34 | #if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB)) | |
35 | std::move( c).resume(); | |
36 | #endif | |
b32b8144 FG |
37 | // execute scheduler::dispatch() |
38 | return get_scheduler()->dispatch(); | |
39 | } | |
40 | ||
41 | public: | |
92f5a8d4 | 42 | dispatcher_context( boost::context::preallocated const& palloc, default_stack && salloc) : |
b32b8144 | 43 | context{ 0, type::dispatcher_context, launch::post } { |
11fdf7f2 TL |
44 | c_ = boost::context::fiber{ std::allocator_arg, palloc, salloc, |
45 | std::bind( & dispatcher_context::run_, this, std::placeholders::_1) }; | |
46 | #if (defined(BOOST_USE_UCONTEXT)||defined(BOOST_USE_WINFIB)) | |
47 | c_ = std::move( c_).resume(); | |
48 | #endif | |
b32b8144 FG |
49 | } |
50 | }; | |
51 | ||
52 | static intrusive_ptr< context > make_dispatcher_context() { | |
7c673cae | 53 | default_stack salloc; // use default satck-size |
b32b8144 | 54 | auto sctx = salloc.allocate(); |
7c673cae | 55 | // reserve space for control structure |
b32b8144 FG |
56 | void * storage = reinterpret_cast< void * >( |
57 | ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( dispatcher_context) ) ) | |
58 | & ~ static_cast< uintptr_t >( 0xff) ); | |
59 | void * stack_bottom = reinterpret_cast< void * >( | |
60 | reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) ); | |
61 | const std::size_t size = reinterpret_cast< uintptr_t >( storage) - reinterpret_cast< uintptr_t >( stack_bottom); | |
7c673cae | 62 | // placement new of context on top of fiber's stack |
b32b8144 FG |
63 | return intrusive_ptr< context >{ |
64 | new ( storage) dispatcher_context{ | |
92f5a8d4 | 65 | boost::context::preallocated{ storage, size, sctx }, std::move( salloc) } }; |
7c673cae FG |
66 | } |
67 | ||
68 | // schwarz counter | |
69 | struct context_initializer { | |
70 | static thread_local context * active_; | |
71 | static thread_local std::size_t counter_; | |
72 | ||
73 | context_initializer() { | |
74 | if ( 0 == counter_++) { | |
7c673cae | 75 | // main fiber context of this thread |
b32b8144 | 76 | context * main_ctx = new main_context{}; |
7c673cae | 77 | // scheduler of this thread |
b32b8144 | 78 | scheduler * sched = new scheduler{}; |
7c673cae FG |
79 | // attach main context to scheduler |
80 | sched->attach_main_context( main_ctx); | |
81 | // create and attach dispatcher context to scheduler | |
b32b8144 | 82 | sched->attach_dispatcher_context( make_dispatcher_context() ); |
7c673cae FG |
83 | // make main context to active context |
84 | active_ = main_ctx; | |
7c673cae FG |
85 | } |
86 | } | |
87 | ||
88 | ~context_initializer() { | |
89 | if ( 0 == --counter_) { | |
90 | context * main_ctx = active_; | |
91 | BOOST_ASSERT( main_ctx->is_context( type::main_context) ); | |
92 | scheduler * sched = main_ctx->get_scheduler(); | |
b32b8144 FG |
93 | delete sched; |
94 | delete main_ctx; | |
7c673cae FG |
95 | } |
96 | } | |
97 | }; | |
98 | ||
99 | // zero-initialization | |
b32b8144 FG |
100 | thread_local context * context_initializer::active_{ nullptr }; |
101 | thread_local std::size_t context_initializer::counter_{ 0 }; | |
7c673cae FG |
102 | |
103 | context * | |
104 | context::active() noexcept { | |
7c673cae FG |
105 | // initialized the first time control passes; per thread |
106 | thread_local static context_initializer ctx_initializer; | |
107 | return context_initializer::active_; | |
108 | } | |
109 | ||
110 | void | |
111 | context::reset_active() noexcept { | |
112 | context_initializer::active_ = nullptr; | |
113 | } | |
114 | ||
7c673cae | 115 | context::~context() { |
b32b8144 FG |
116 | // protect for concurrent access |
117 | std::unique_lock< detail::spinlock > lk{ splk_ }; | |
7c673cae | 118 | BOOST_ASSERT( ! ready_is_linked() ); |
b32b8144 | 119 | BOOST_ASSERT( ! remote_ready_is_linked() ); |
7c673cae FG |
120 | BOOST_ASSERT( ! sleep_is_linked() ); |
121 | BOOST_ASSERT( ! wait_is_linked() ); | |
b32b8144 FG |
122 | if ( is_context( type::dispatcher_context) ) { |
123 | // dispatcher-context is resumed by main-context | |
124 | // while the scheduler is deconstructed | |
125 | #ifdef BOOST_DISABLE_ASSERTS | |
126 | wait_queue_.pop_front(); | |
127 | #else | |
128 | context * ctx = & wait_queue_.front(); | |
129 | wait_queue_.pop_front(); | |
130 | BOOST_ASSERT( ctx->is_context( type::main_context) ); | |
131 | BOOST_ASSERT( nullptr == active() ); | |
132 | #endif | |
133 | } | |
134 | BOOST_ASSERT( wait_queue_.empty() ); | |
7c673cae FG |
135 | delete properties_; |
136 | } | |
137 | ||
7c673cae FG |
138 | context::id |
139 | context::get_id() const noexcept { | |
b32b8144 | 140 | return id{ const_cast< context * >( this) }; |
7c673cae FG |
141 | } |
142 | ||
143 | void | |
144 | context::resume() noexcept { | |
145 | context * prev = this; | |
146 | // context_initializer::active_ will point to `this` | |
147 | // prev will point to previous active context | |
148 | std::swap( context_initializer::active_, prev); | |
b32b8144 | 149 | // pass pointer to the context that resumes `this` |
11fdf7f2 | 150 | std::move( c_).resume_with([prev](boost::context::fiber && c){ |
b32b8144 | 151 | prev->c_ = std::move( c); |
11fdf7f2 | 152 | return boost::context::fiber{}; |
b32b8144 | 153 | }); |
7c673cae FG |
154 | } |
155 | ||
156 | void | |
157 | context::resume( detail::spinlock_lock & lk) noexcept { | |
158 | context * prev = this; | |
159 | // context_initializer::active_ will point to `this` | |
160 | // prev will point to previous active context | |
161 | std::swap( context_initializer::active_, prev); | |
b32b8144 | 162 | // pass pointer to the context that resumes `this` |
11fdf7f2 | 163 | std::move( c_).resume_with([prev,&lk](boost::context::fiber && c){ |
b32b8144 FG |
164 | prev->c_ = std::move( c); |
165 | lk.unlock(); | |
11fdf7f2 | 166 | return boost::context::fiber{}; |
b32b8144 | 167 | }); |
7c673cae FG |
168 | } |
169 | ||
170 | void | |
171 | context::resume( context * ready_ctx) noexcept { | |
172 | context * prev = this; | |
173 | // context_initializer::active_ will point to `this` | |
174 | // prev will point to previous active context | |
175 | std::swap( context_initializer::active_, prev); | |
b32b8144 | 176 | // pass pointer to the context that resumes `this` |
11fdf7f2 | 177 | std::move( c_).resume_with([prev,ready_ctx](boost::context::fiber && c){ |
b32b8144 FG |
178 | prev->c_ = std::move( c); |
179 | context::active()->schedule( ready_ctx); | |
11fdf7f2 | 180 | return boost::context::fiber{}; |
b32b8144 | 181 | }); |
7c673cae FG |
182 | } |
183 | ||
184 | void | |
185 | context::suspend() noexcept { | |
b32b8144 | 186 | get_scheduler()->suspend(); |
7c673cae FG |
187 | } |
188 | ||
189 | void | |
190 | context::suspend( detail::spinlock_lock & lk) noexcept { | |
b32b8144 | 191 | get_scheduler()->suspend( lk); |
7c673cae FG |
192 | } |
193 | ||
194 | void | |
195 | context::join() { | |
196 | // get active context | |
197 | context * active_ctx = context::active(); | |
198 | // protect for concurrent access | |
b32b8144 | 199 | std::unique_lock< detail::spinlock > lk{ splk_ }; |
7c673cae | 200 | // wait for context which is not terminated |
b32b8144 | 201 | if ( ! terminated_) { |
7c673cae FG |
202 | // push active context to wait-queue, member |
203 | // of the context which has to be joined by | |
204 | // the active context | |
205 | active_ctx->wait_link( wait_queue_); | |
7c673cae | 206 | // suspend active context |
b32b8144 | 207 | active_ctx->get_scheduler()->suspend( lk); |
7c673cae FG |
208 | // active context resumed |
209 | BOOST_ASSERT( context::active() == active_ctx); | |
210 | } | |
211 | } | |
212 | ||
213 | void | |
214 | context::yield() noexcept { | |
215 | // yield active context | |
b32b8144 | 216 | get_scheduler()->yield( context::active() ); |
7c673cae FG |
217 | } |
218 | ||
11fdf7f2 | 219 | boost::context::fiber |
7c673cae FG |
220 | context::suspend_with_cc() noexcept { |
221 | context * prev = this; | |
222 | // context_initializer::active_ will point to `this` | |
223 | // prev will point to previous active context | |
224 | std::swap( context_initializer::active_, prev); | |
b32b8144 | 225 | // pass pointer to the context that resumes `this` |
11fdf7f2 | 226 | return std::move( c_).resume_with([prev](boost::context::fiber && c){ |
b32b8144 | 227 | prev->c_ = std::move( c); |
11fdf7f2 | 228 | return boost::context::fiber{}; |
b32b8144 | 229 | }); |
7c673cae | 230 | } |
7c673cae | 231 | |
11fdf7f2 | 232 | boost::context::fiber |
b32b8144 | 233 | context::terminate() noexcept { |
7c673cae | 234 | // protect for concurrent access |
b32b8144 | 235 | std::unique_lock< detail::spinlock > lk{ splk_ }; |
7c673cae | 236 | // mark as terminated |
b32b8144 | 237 | terminated_ = true; |
7c673cae FG |
238 | // notify all waiting fibers |
239 | while ( ! wait_queue_.empty() ) { | |
240 | context * ctx = & wait_queue_.front(); | |
241 | // remove fiber from wait-queue | |
242 | wait_queue_.pop_front(); | |
243 | // notify scheduler | |
b32b8144 | 244 | schedule( ctx); |
7c673cae | 245 | } |
b32b8144 | 246 | BOOST_ASSERT( wait_queue_.empty() ); |
7c673cae FG |
247 | // release fiber-specific-data |
248 | for ( fss_data_t::value_type & data : fss_data_) { | |
249 | data.second.do_cleanup(); | |
250 | } | |
251 | fss_data_.clear(); | |
252 | // switch to another context | |
b32b8144 | 253 | return get_scheduler()->terminate( lk, this); |
7c673cae FG |
254 | } |
255 | ||
256 | bool | |
257 | context::wait_until( std::chrono::steady_clock::time_point const& tp) noexcept { | |
b32b8144 FG |
258 | BOOST_ASSERT( nullptr != get_scheduler() ); |
259 | BOOST_ASSERT( this == active() ); | |
260 | return get_scheduler()->wait_until( this, tp); | |
7c673cae FG |
261 | } |
262 | ||
263 | bool | |
264 | context::wait_until( std::chrono::steady_clock::time_point const& tp, | |
265 | detail::spinlock_lock & lk) noexcept { | |
b32b8144 FG |
266 | BOOST_ASSERT( nullptr != get_scheduler() ); |
267 | BOOST_ASSERT( this == active() ); | |
268 | return get_scheduler()->wait_until( this, tp, lk); | |
7c673cae FG |
269 | } |
270 | ||
271 | void | |
b32b8144 | 272 | context::schedule( context * ctx) noexcept { |
7c673cae FG |
273 | //BOOST_ASSERT( nullptr != ctx); |
274 | BOOST_ASSERT( this != ctx); | |
b32b8144 FG |
275 | BOOST_ASSERT( nullptr != get_scheduler() ); |
276 | BOOST_ASSERT( nullptr != ctx->get_scheduler() ); | |
277 | #if ! defined(BOOST_FIBERS_NO_ATOMICS) | |
7c673cae FG |
278 | // FIXME: comparing scheduler address' must be synchronized? |
279 | // what if ctx is migrated between threads | |
280 | // (other scheduler assigned) | |
b32b8144 | 281 | if ( scheduler_ == ctx->get_scheduler() ) { |
7c673cae | 282 | // local |
b32b8144 | 283 | get_scheduler()->schedule( ctx); |
7c673cae FG |
284 | } else { |
285 | // remote | |
b32b8144 | 286 | ctx->get_scheduler()->schedule_from_remote( ctx); |
7c673cae | 287 | } |
b32b8144 FG |
288 | #else |
289 | BOOST_ASSERT( get_scheduler() == ctx->get_scheduler() ); | |
290 | get_scheduler()->schedule( ctx); | |
291 | #endif | |
7c673cae FG |
292 | } |
293 | ||
294 | void * | |
295 | context::get_fss_data( void const * vp) const { | |
b32b8144 FG |
296 | uintptr_t key = reinterpret_cast< uintptr_t >( vp); |
297 | fss_data_t::const_iterator i = fss_data_.find( key); | |
7c673cae FG |
298 | return fss_data_.end() != i ? i->second.vp : nullptr; |
299 | } | |
300 | ||
301 | void | |
302 | context::set_fss_data( void const * vp, | |
303 | detail::fss_cleanup_function::ptr_t const& cleanup_fn, | |
304 | void * data, | |
305 | bool cleanup_existing) { | |
306 | BOOST_ASSERT( cleanup_fn); | |
b32b8144 FG |
307 | uintptr_t key = reinterpret_cast< uintptr_t >( vp); |
308 | fss_data_t::iterator i = fss_data_.find( key); | |
7c673cae FG |
309 | if ( fss_data_.end() != i) { |
310 | if( cleanup_existing) { | |
311 | i->second.do_cleanup(); | |
312 | } | |
313 | if ( nullptr != data) { | |
92f5a8d4 | 314 | i->second = fss_data{ data, cleanup_fn }; |
7c673cae FG |
315 | } else { |
316 | fss_data_.erase( i); | |
317 | } | |
318 | } else { | |
319 | fss_data_.insert( | |
320 | std::make_pair( | |
321 | key, | |
b32b8144 | 322 | fss_data{ data, cleanup_fn } ) ); |
7c673cae FG |
323 | } |
324 | } | |
325 | ||
326 | void | |
327 | context::set_properties( fiber_properties * props) noexcept { | |
328 | delete properties_; | |
329 | properties_ = props; | |
330 | } | |
331 | ||
332 | bool | |
333 | context::worker_is_linked() const noexcept { | |
334 | return worker_hook_.is_linked(); | |
335 | } | |
336 | ||
337 | bool | |
b32b8144 FG |
338 | context::ready_is_linked() const noexcept { |
339 | return ready_hook_.is_linked(); | |
7c673cae FG |
340 | } |
341 | ||
342 | bool | |
b32b8144 FG |
343 | context::remote_ready_is_linked() const noexcept { |
344 | return remote_ready_hook_.is_linked(); | |
7c673cae FG |
345 | } |
346 | ||
347 | bool | |
348 | context::sleep_is_linked() const noexcept { | |
349 | return sleep_hook_.is_linked(); | |
350 | } | |
351 | ||
b32b8144 FG |
352 | bool |
353 | context::terminated_is_linked() const noexcept { | |
354 | return terminated_hook_.is_linked(); | |
355 | } | |
356 | ||
7c673cae FG |
357 | bool |
358 | context::wait_is_linked() const noexcept { | |
359 | return wait_hook_.is_linked(); | |
360 | } | |
361 | ||
362 | void | |
363 | context::worker_unlink() noexcept { | |
b32b8144 | 364 | BOOST_ASSERT( worker_is_linked() ); |
7c673cae FG |
365 | worker_hook_.unlink(); |
366 | } | |
367 | ||
368 | void | |
369 | context::ready_unlink() noexcept { | |
b32b8144 | 370 | BOOST_ASSERT( ready_is_linked() ); |
7c673cae FG |
371 | ready_hook_.unlink(); |
372 | } | |
373 | ||
374 | void | |
375 | context::sleep_unlink() noexcept { | |
b32b8144 | 376 | BOOST_ASSERT( sleep_is_linked() ); |
7c673cae FG |
377 | sleep_hook_.unlink(); |
378 | } | |
379 | ||
380 | void | |
381 | context::wait_unlink() noexcept { | |
b32b8144 | 382 | BOOST_ASSERT( wait_is_linked() ); |
7c673cae FG |
383 | wait_hook_.unlink(); |
384 | } | |
385 | ||
386 | void | |
387 | context::detach() noexcept { | |
388 | BOOST_ASSERT( context::active() != this); | |
b32b8144 | 389 | get_scheduler()->detach_worker_context( this); |
7c673cae FG |
390 | } |
391 | ||
392 | void | |
393 | context::attach( context * ctx) noexcept { | |
394 | BOOST_ASSERT( nullptr != ctx); | |
b32b8144 | 395 | get_scheduler()->attach_worker_context( ctx); |
7c673cae FG |
396 | } |
397 | ||
398 | }} | |
399 | ||
400 | #ifdef BOOST_HAS_ABI_HEADERS | |
401 | # include BOOST_ABI_SUFFIX | |
402 | #endif |