]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/fiber/src/context.cpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / libs / fiber / src / context.cpp
CommitLineData
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
20namespace boost {
21namespace fibers {
22
b32b8144
FG
23class main_context final : public context {
24public:
25 main_context() noexcept :
26 context{ 1, type::main_context, launch::post } {
27 }
28};
29
30class dispatcher_context final : public context {
31private:
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
41public:
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
52static 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
69struct 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
100thread_local context * context_initializer::active_{ nullptr };
101thread_local std::size_t context_initializer::counter_{ 0 };
7c673cae
FG
102
103context *
104context::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
110void
111context::reset_active() noexcept {
112 context_initializer::active_ = nullptr;
113}
114
7c673cae 115context::~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
138context::id
139context::get_id() const noexcept {
b32b8144 140 return id{ const_cast< context * >( this) };
7c673cae
FG
141}
142
143void
144context::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
156void
157context::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
170void
171context::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
184void
185context::suspend() noexcept {
b32b8144 186 get_scheduler()->suspend();
7c673cae
FG
187}
188
189void
190context::suspend( detail::spinlock_lock & lk) noexcept {
b32b8144 191 get_scheduler()->suspend( lk);
7c673cae
FG
192}
193
194void
195context::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
213void
214context::yield() noexcept {
215 // yield active context
b32b8144 216 get_scheduler()->yield( context::active() );
7c673cae
FG
217}
218
11fdf7f2 219boost::context::fiber
7c673cae
FG
220context::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 232boost::context::fiber
b32b8144 233context::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
256bool
257context::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
263bool
264context::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
271void
b32b8144 272context::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
294void *
295context::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
301void
302context::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
326void
327context::set_properties( fiber_properties * props) noexcept {
328 delete properties_;
329 properties_ = props;
330}
331
332bool
333context::worker_is_linked() const noexcept {
334 return worker_hook_.is_linked();
335}
336
337bool
b32b8144
FG
338context::ready_is_linked() const noexcept {
339 return ready_hook_.is_linked();
7c673cae
FG
340}
341
342bool
b32b8144
FG
343context::remote_ready_is_linked() const noexcept {
344 return remote_ready_hook_.is_linked();
7c673cae
FG
345}
346
347bool
348context::sleep_is_linked() const noexcept {
349 return sleep_hook_.is_linked();
350}
351
b32b8144
FG
352bool
353context::terminated_is_linked() const noexcept {
354 return terminated_hook_.is_linked();
355}
356
7c673cae
FG
357bool
358context::wait_is_linked() const noexcept {
359 return wait_hook_.is_linked();
360}
361
362void
363context::worker_unlink() noexcept {
b32b8144 364 BOOST_ASSERT( worker_is_linked() );
7c673cae
FG
365 worker_hook_.unlink();
366}
367
368void
369context::ready_unlink() noexcept {
b32b8144 370 BOOST_ASSERT( ready_is_linked() );
7c673cae
FG
371 ready_hook_.unlink();
372}
373
374void
375context::sleep_unlink() noexcept {
b32b8144 376 BOOST_ASSERT( sleep_is_linked() );
7c673cae
FG
377 sleep_hook_.unlink();
378}
379
380void
381context::wait_unlink() noexcept {
b32b8144 382 BOOST_ASSERT( wait_is_linked() );
7c673cae
FG
383 wait_hook_.unlink();
384}
385
386void
387context::detach() noexcept {
388 BOOST_ASSERT( context::active() != this);
b32b8144 389 get_scheduler()->detach_worker_context( this);
7c673cae
FG
390}
391
392void
393context::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