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 8[#scheduling] 9[section:scheduling Scheduling] 10 11The fibers in a thread are coordinated by a fiber manager. Fibers trade 12control cooperatively, rather than preemptively: the currently-running fiber 13retains control until it invokes some operation that passes control to the 14manager. Each time a fiber suspends (or yields), the fiber manager consults a 15scheduler to determine which fiber will run next. 16 17__boost_fiber__ provides the fiber manager, but the scheduler is a 18customization point. (See [link custom Customization].) 19 20Each thread has its own scheduler. Different threads in a process may use 21different schedulers. By default, __boost_fiber__ implicitly instantiates 22[class_link round_robin] as the scheduler for each thread. 23 24You are explicitly permitted to code your own __algo__ subclass. For the most 25part, your `algorithm` subclass need not defend against cross-thread 26calls: the fiber manager intercepts and defers such calls. Most 27`algorithm` methods are only ever directly called from the thread whose 28fibers it is managing [mdash] with exceptions as documented below. 29 30Your `algorithm` subclass is engaged on a particular thread by calling 31[function_link use_scheduling_algorithm]: 32 33 void thread_fn() { 34 boost::fibers::use_scheduling_algorithm< my_fiber_scheduler >(); 35 ... 36 } 37 38A scheduler class must implement interface __algo__. __boost_fiber__ provides 39schedulers: __round_robin__, __work_stealing__, __numa_work_stealing__ and 40__shared_work__. 41 42 void thread( std::uint32_t thread_count) { 43 // thread registers itself at work-stealing scheduler 44 boost::fibers::use_scheduling_algorithm< boost::fibers::algo::work_stealing >( thread_count); 45 ... 46 } 47 48 // count of logical cpus 49 std::uint32_t thread_count = std::thread::hardware_concurrency(); 50 // start worker-threads first 51 std::vector< std::thread > threads; 52 for ( std::uint32_t i = 1 /* count start-thread */; i < thread_count; ++i) { 53 // spawn thread 54 threads.emplace_back( thread, thread_count); 55 } 56 // start-thread registers itself at work-stealing scheduler 57 boost::fibers::use_scheduling_algorithm< boost::fibers::algo::work_stealing >( thread_count); 58 ... 59 60The example spawns as many threads as `std::thread::hardware_concurrency()` 61returns. 62Each thread runs a __work_stealing__ scheduler. Each instance of this 63scheduler needs to know how many threads run the work-stealing scheduler in the 64program. 65If the local queue of one thread runs out of ready fibers, the thread tries to 66steal a ready fiber from another thread running this scheduler. 67 68 69[class_heading algorithm] 70 71`algorithm` is the abstract base class defining the interface that a 72fiber scheduler must implement. 73 74 #include <boost/fiber/algo/algorithm.hpp> 75 76 namespace boost { 77 namespace fibers { 78 namespace algo { 79 80 struct algorithm { 81 virtual ~algorithm(); 82 83 virtual void awakened( context *) noexcept = 0; 84 85 virtual context * pick_next() noexcept = 0; 86 87 virtual bool has_ready_fibers() const noexcept = 0; 88 89 virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept = 0; 90 91 virtual void notify() noexcept = 0; 92 }; 93 94 }}} 95 96[member_heading algorithm..awakened] 97 98 virtual void awakened( context * f) noexcept = 0; 99 100[variablelist 101[[Effects:] [Informs the scheduler that fiber `f` is ready to run. Fiber `f` 102might be newly launched, or it might have been blocked but has just been 103awakened, or it might have called [ns_function_link this_fiber..yield].]] 104[[Note:] [This method advises the scheduler to add fiber `f` to its collection 105of fibers ready to run. A typical scheduler implementation places `f` into a 106queue.]] 107[[See also:] [[class_link round_robin]]] 108] 109 110[member_heading algorithm..pick_next] 111 112 virtual context * pick_next() noexcept = 0; 113 114[variablelist 115[[Returns:] [the fiber which is to be resumed next, or `nullptr` if there is no 116ready fiber.]] 117[[Note:] [This is where the scheduler actually specifies the fiber which is to 118run next. A typical scheduler implementation chooses the head of the ready 119queue.]] 120[[See also:] [[class_link round_robin]]] 121] 122 123[member_heading algorithm..has_ready_fibers] 124 125 virtual bool has_ready_fibers() const noexcept = 0; 126 127[variablelist 128[[Returns:] [`true` if scheduler has fibers ready to run.]] 129] 130 131[member_heading algorithm..suspend_until] 132 133 virtual void suspend_until( std::chrono::steady_clock::time_point const& abs_time) noexcept = 0; 134 135[variablelist 136[[Effects:] [Informs the scheduler that no fiber will be ready until 137time-point `abs_time`.]] 138[[Note:] [This method allows a custom scheduler to yield control to the 139containing environment in whatever way makes sense. The fiber manager is 140stating that `suspend_until()` need not return until `abs_time` [mdash] or 141[member_link algorithm..notify] is called [mdash] whichever comes first. 142The interaction with `notify()` means that, for instance, calling 143[@http://en.cppreference.com/w/cpp/thread/sleep_until 144`std::this_thread::sleep_until(abs_time)`] would be too simplistic. 145[member_link round_robin..suspend_until] uses a 146[@http://en.cppreference.com/w/cpp/thread/condition_variable 147`std::condition_variable`] to coordinate with [member_link 148round_robin..notify].]] 149[[Note:] [Given that `notify()` might be called from another thread, your 150`suspend_until()` implementation [mdash] like the rest of your 151`algorithm` implementation [mdash] must guard any data it shares with 152your `notify()` implementation.]] 153] 154 155[member_heading algorithm..notify] 156 157 virtual void notify() noexcept = 0; 158 159[variablelist 160[[Effects:] [Requests the scheduler to return from a pending call to 161[member_link algorithm..suspend_until].]] 162[[Note:] [Alone among the `algorithm` methods, `notify()` may be called 163from another thread. Your `notify()` implementation must guard any data it 164shares with the rest of your `algorithm` implementation.]] 165] 166 167[class_heading round_robin] 168 169This class implements __algo__, scheduling fibers in round-robin fashion. 170 171 #include <boost/fiber/algo/round_robin.hpp> 172 173 namespace boost { 174 namespace fibers { 175 namespace algo { 176 177 class round_robin : public algorithm { 178 virtual void awakened( context *) noexcept; 179 180 virtual context * pick_next() noexcept; 181 182 virtual bool has_ready_fibers() const noexcept; 183 184 virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept; 185 186 virtual void notify() noexcept; 187 }; 188 189 }}} 190 191[member_heading round_robin..awakened] 192 193 virtual void awakened( context * f) noexcept; 194 195[variablelist 196[[Effects:] [Enqueues fiber `f` onto a ready queue.]] 197[[Throws:] [Nothing.]] 198] 199 200[member_heading round_robin..pick_next] 201 202 virtual context * pick_next() noexcept; 203 204[variablelist 205[[Returns:] [the fiber at the head of the ready queue, or `nullptr` if the 206queue is empty.]] 207[[Throws:] [Nothing.]] 208[[Note:] [Placing ready fibers onto the tail of a queue, and returning them 209from the head of that queue, shares the thread between ready fibers in 210round-robin fashion.]] 211] 212 213[member_heading round_robin..has_ready_fibers] 214 215 virtual bool has_ready_fibers() const noexcept; 216 217[variablelist 218[[Returns:] [`true` if scheduler has fibers ready to run.]] 219[[Throws:] [Nothing.]] 220] 221 222[member_heading round_robin..suspend_until] 223 224 virtual void suspend_until( std::chrono::steady_clock::time_point const& abs_time) noexcept; 225 226[variablelist 227[[Effects:] [Informs `round_robin` that no ready fiber will be available until 228time-point `abs_time`. This implementation blocks in 229[@http://en.cppreference.com/w/cpp/thread/condition_variable/wait_until 230`std::condition_variable::wait_until()`].]] 231[[Throws:] [Nothing.]] 232] 233 234[member_heading round_robin..notify] 235 236 virtual void notify() noexcept = 0; 237 238[variablelist 239[[Effects:] [Wake up a pending call to [member_link 240round_robin..suspend_until], some fibers might be ready. This implementation 241wakes `suspend_until()` via 242[@http://en.cppreference.com/w/cpp/thread/condition_variable/notify_all 243`std::condition_variable::notify_all()`].]] 244[[Throws:] [Nothing.]] 245] 246 247 248 249[class_heading work_stealing] 250 251This class implements __algo__; if the local ready-queue runs out of ready fibers, ready fibers are stolen 252from other schedulers.[br] 253The victim scheduler (from which a ready fiber is stolen) is selected at random. 254 255[note Worker-threads are stored in a static variable, dynamically adding/removing worker threads is not supported.] 256 257 #include <boost/fiber/algo/work_stealing.hpp> 258 259 namespace boost { 260 namespace fibers { 261 namespace algo { 262 263 class work_stealing : public algorithm { 264 public: 265 work_stealing( std::uint32_t thread_count, bool suspend = false); 266 267 work_stealing( work_stealing const&) = delete; 268 work_stealing( work_stealing &&) = delete; 269 270 work_stealing & operator=( work_stealing const&) = delete; 271 work_stealing & operator=( work_stealing &&) = delete; 272 273 virtual void awakened( context *) noexcept; 274 275 virtual context * pick_next() noexcept; 276 277 virtual bool has_ready_fibers() const noexcept; 278 279 virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept; 280 281 virtual void notify() noexcept; 282 }; 283 284 }}} 285 286[heading Constructor] 287 288 work_stealing( std::uint32_t thread_count, bool suspend = false); 289 290[variablelist 291[[Effects:] [Constructs work-stealing scheduling algorithm. `thread_count` represents the number of threads 292running this algorithm.]] 293[[Throws:] [`system_error`]] 294[[Note:][If `suspend` is set to `true`, then the scheduler suspends if no ready fiber could be stolen. 295The scheduler will by woken up if a sleeping fiber times out or it was notified from remote (other thread or 296fiber scheduler).]] 297] 298 299[member_heading work_stealing..awakened] 300 301 virtual void awakened( context * f) noexcept; 302 303[variablelist 304[[Effects:] [Enqueues fiber `f` onto the shared ready queue.]] 305[[Throws:] [Nothing.]] 306] 307 308[member_heading work_stealing..pick_next] 309 310 virtual context * pick_next() noexcept; 311 312[variablelist 313[[Returns:] [the fiber at the head of the ready queue, or `nullptr` if the 314queue is empty.]] 315[[Throws:] [Nothing.]] 316[[Note:] [Placing ready fibers onto the tail of the sahred queue, and returning them 317from the head of that queue, shares the thread between ready fibers in 318round-robin fashion.]] 319] 320 321[member_heading work_stealing..has_ready_fibers] 322 323 virtual bool has_ready_fibers() const noexcept; 324 325[variablelist 326[[Returns:] [`true` if scheduler has fibers ready to run.]] 327[[Throws:] [Nothing.]] 328] 329 330[member_heading work_stealing..suspend_until] 331 332 virtual void suspend_until( std::chrono::steady_clock::time_point const& abs_time) noexcept; 333 334[variablelist 335[[Effects:] [Informs `work_stealing` that no ready fiber will be available until 336time-point `abs_time`. This implementation blocks in 337[@http://en.cppreference.com/w/cpp/thread/condition_variable/wait_until 338`std::condition_variable::wait_until()`].]] 339[[Throws:] [Nothing.]] 340] 341 342[member_heading work_stealing..notify] 343 344 virtual void notify() noexcept = 0; 345 346[variablelist 347[[Effects:] [Wake up a pending call to [member_link 348work_stealing..suspend_until], some fibers might be ready. This implementation 349wakes `suspend_until()` via 350[@http://en.cppreference.com/w/cpp/thread/condition_variable/notify_all 351`std::condition_variable::notify_all()`].]] 352[[Throws:] [Nothing.]] 353] 354 355 356[class_heading shared_work] 357 358[note Because of the non-locality of data, ['shared_work] is less performant 359than __work_stealing__.] 360 361This class implements __algo__, scheduling fibers in round-robin fashion. 362Ready fibers are shared between all instances (running on different threads) 363of shared_work, thus the work is distributed equally over all threads. 364 365[note Worker-threads are stored in a static variable, dynamically adding/removing worker threads is not supported.] 366 367 #include <boost/fiber/algo/shared_work.hpp> 368 369 namespace boost { 370 namespace fibers { 371 namespace algo { 372 373 class shared_work : public algorithm { 374 shared_work(); 375 shared_work( bool suspend); 376 377 virtual void awakened( context *) noexcept; 378 379 virtual context * pick_next() noexcept; 380 381 virtual bool has_ready_fibers() const noexcept; 382 383 virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept; 384 385 virtual void notify() noexcept; 386 }; 387 388 }}} 389 390[heading Constructor] 391 392 shared_work(); 393 shared_work( bool suspend); 394 395[variablelist 396[[Effects:] [Constructs work-sharing scheduling algorithm.]] 397[[Throws:] [`system_error`]] 398[[Note:][If `suspend` is set to `true` (default is `false`), then the scheduler suspends if no ready fiber 399could be stolen. The scheduler will by woken up if a sleeping fiber times out or it was notified from remote 400(other thread or fiber scheduler).]] 401] 402 403[member_heading shared_work..awakened] 404 405 virtual void awakened( context * f) noexcept; 406 407[variablelist 408[[Effects:] [Enqueues fiber `f` onto the shared ready queue.]] 409[[Throws:] [Nothing.]] 410] 411 412[member_heading shared_work..pick_next] 413 414 virtual context * pick_next() noexcept; 415 416[variablelist 417[[Returns:] [the fiber at the head of the ready queue, or `nullptr` if the 418queue is empty.]] 419[[Throws:] [Nothing.]] 420[[Note:] [Placing ready fibers onto the tail of the shared queue, and returning them 421from the head of that queue, shares the thread between ready fibers in 422round-robin fashion.]] 423] 424 425[member_heading shared_work..has_ready_fibers] 426 427 virtual bool has_ready_fibers() const noexcept; 428 429[variablelist 430[[Returns:] [`true` if scheduler has fibers ready to run.]] 431[[Throws:] [Nothing.]] 432] 433 434[member_heading shared_work..suspend_until] 435 436 virtual void suspend_until( std::chrono::steady_clock::time_point const& abs_time) noexcept; 437 438[variablelist 439[[Effects:] [Informs `shared_work` that no ready fiber will be available until 440time-point `abs_time`. This implementation blocks in 441[@http://en.cppreference.com/w/cpp/thread/condition_variable/wait_until 442`std::condition_variable::wait_until()`].]] 443[[Throws:] [Nothing.]] 444] 445 446[member_heading shared_work..notify] 447 448 virtual void notify() noexcept = 0; 449 450[variablelist 451[[Effects:] [Wake up a pending call to [member_link 452shared_work..suspend_until], some fibers might be ready. This implementation 453wakes `suspend_until()` via 454[@http://en.cppreference.com/w/cpp/thread/condition_variable/notify_all 455`std::condition_variable::notify_all()`].]] 456[[Throws:] [Nothing.]] 457] 458 459 460[heading Custom Scheduler Fiber Properties] 461 462A scheduler class directly derived from __algo__ can use any information 463available from [class_link context] to implement the `algorithm` 464interface. But a custom scheduler might need to track additional properties 465for a fiber. For instance, a priority-based scheduler would need to track a 466fiber[s] priority. 467 468__boost_fiber__ provides a mechanism by which your custom scheduler can 469associate custom properties with each fiber. 470 471[class_heading fiber_properties] 472 473A custom fiber properties class must be derived from `fiber_properties`. 474 475 #include <boost/fiber/properties.hpp> 476 477 namespace boost { 478 namespace fibers { 479 480 class fiber_properties { 481 public: 482 fiber_properties( context *) noexcept; 483 484 virtual ~fiber_properties(); 485 486 protected: 487 void notify() noexcept; 488 }; 489 490 }} 491 492[heading Constructor] 493 494 fiber_properties( context * f) noexcept; 495 496[variablelist 497[[Effects:] [Constructs base-class component of custom subclass.]] 498[[Throws:] [Nothing.]] 499[[Note:] [Your subclass constructor must accept a `context*` and pass it 500to the base-class `fiber_properties` constructor.]] 501] 502 503[member_heading fiber_properties..notify] 504 505 void notify() noexcept; 506 507[variablelist 508[[Effects:] [Pass control to the custom [template_link 509algorithm_with_properties] subclass[s] [member_link 510algorithm_with_properties..property_change] method.]] 511[[Throws:] [Nothing.]] 512[[Note:] [A custom scheduler[s] [member_link 513algorithm_with_properties..pick_next] method might dynamically select 514from the ready fibers, or [member_link 515algorithm_with_properties..awakened] might instead insert each ready 516fiber into some form of ready queue for `pick_next()`. In the latter case, if 517application code modifies a fiber property (e.g. priority) that should affect 518that fiber[s] relationship to other ready fibers, the custom scheduler must be 519given the opportunity to reorder its ready queue. The custom property subclass 520should implement an access method to modify such a property; that access 521method should call `notify()` once the new property value has been stored. 522This passes control to the custom scheduler[s] `property_change()` method, 523allowing the custom scheduler to reorder its ready queue appropriately. Use at 524your discretion. Of course, if you define a property which does not affect the 525behavior of the `pick_next()` method, you need not call `notify()` when that 526property is modified.]] 527] 528 529[template_heading algorithm_with_properties] 530 531A custom scheduler that depends on a custom properties class `PROPS` should be 532derived from `algorithm_with_properties<PROPS>`. `PROPS` should be 533derived from [class_link fiber_properties]. 534 535 #include <boost/fiber/algorithm.hpp> 536 537 namespace boost { 538 namespace fibers { 539 namespace algo { 540 541 template< typename PROPS > 542 struct algorithm_with_properties { 543 virtual void awakened( context *, PROPS &) noexcept = 0; 544 545 virtual context * pick_next() noexcept; 546 547 virtual bool has_ready_fibers() const noexcept; 548 549 virtual void suspend_until( std::chrono::steady_clock::time_point const&) noexcept = 0; 550 551 virtual void notify() noexcept = 0; 552 553 PROPS & properties( context *) noexcept; 554 555 virtual void property_change( context *, PROPS &) noexcept; 556 557 virtual fiber_properties * new_properties( context *); 558 }; 559 560 }}} 561 562[member_heading algorithm_with_properties..awakened] 563 564 virtual void awakened( context * f, PROPS & properties) noexcept; 565 566[variablelist 567[[Effects:] [Informs the scheduler that fiber `f` is ready to run, like 568[member_link algorithm..awakened]. Passes the fiber[s] associated `PROPS` 569instance.]] 570[[Throws:] [Nothing.]] 571[[Note:] [An `algorithm_with_properties<>` subclass must override this 572method instead of `algorithm::awakened()`.]] 573] 574 575[member_heading algorithm_with_properties..pick_next] 576 577 virtual context * pick_next() noexcept; 578 579[variablelist 580[[Returns:] [the fiber which is to be resumed next, or `nullptr` if there is no 581ready fiber.]] 582[[Throws:] [Nothing.]] 583[[Note:] [same as [member_link algorithm..pick_next]]] 584] 585 586[member_heading algorithm_with_properties..has_ready_fibers] 587 588 virtual bool has_ready_fibers() const noexcept; 589 590[variablelist 591[[Returns:] [`true` if scheduler has fibers ready to run.]] 592[[Throws:] [Nothing.]] 593[[Note:] [same as [member_link algorithm..has_ready_fibers]]] 594] 595 596[member_heading algorithm_with_properties..suspend_until] 597 598 virtual void suspend_until( std::chrono::steady_clock::time_point const& abs_time) noexcept = 0; 599 600[variablelist 601[[Effects:] [Informs the scheduler that no fiber will be ready until 602time-point `abs_time`.]] 603[[Note:] [same as [member_link algorithm..suspend_until]]] 604] 605 606[member_heading algorithm_with_properties..notify] 607 608 virtual void notify() noexcept = 0; 609 610[variablelist 611[[Effects:] [Requests the scheduler to return from a pending call to 612[member_link algorithm_with_properties..suspend_until].]] 613[[Note:] [same as [member_link algorithm..notify]]] 614] 615 616[member_heading algorithm_with_properties..properties] 617 618 PROPS& properties( context * f) noexcept; 619 620[variablelist 621[[Returns:] [the `PROPS` instance associated with fiber `f`.]] 622[[Throws:] [Nothing.]] 623[[Note:] [The fiber[s] associated `PROPS` instance is already passed to 624[member_link algorithm_with_properties..awakened] and [member_link 625algorithm_with_properties..property_change]. However, every [class_link 626algorithm] subclass is expected to track a collection of ready 627[class_link context] instances. This method allows your custom scheduler 628to retrieve the [class_link fiber_properties] subclass instance for any 629`context` in its collection.]] 630] 631 632[member_heading algorithm_with_properties..property_change] 633 634 virtual void property_change( context * f, PROPS & properties) noexcept; 635 636[variablelist 637[[Effects:] [Notify the custom scheduler of a possibly-relevant change to a 638property belonging to fiber `f`. `properties` contains the new values of 639all relevant properties.]] 640[[Throws:] [Nothing.]] 641[[Note:] [This method is only called when a custom [class_link 642fiber_properties] subclass explicitly calls [member_link 643fiber_properties..notify].]] 644] 645 646[member_heading algorithm_with_properties..new_properties] 647 648 virtual fiber_properties * new_properties( context * f); 649 650[variablelist 651[[Returns:] [A new instance of [class_link fiber_properties] subclass 652`PROPS`.]] 653[[Note:] [By default, `algorithm_with_properties<>::new_properties()` 654simply returns `new PROPS(f)`, placing the `PROPS` instance on the heap. 655Override this method to allocate `PROPS` some other way. The returned 656`fiber_properties` pointer must point to the `PROPS` instance to be associated 657with fiber `f`.]] 658] 659 660[#context] 661[class_heading context] 662 663While you are free to treat `context*` as an opaque token, certain 664`context` members may be useful to a custom scheduler implementation. 665 666[#ready_queue_t] 667Of particular note is the fact that `context` contains a hook to participate 668in a [@http://www.boost.org/doc/libs/release/doc/html/intrusive/list.html 669`boost::intrusive::list`] [^typedef][,]ed as 670`boost::fibers::scheduler::ready_queue_t`. This hook is reserved for use by 671[class_link algorithm] implementations. (For instance, [class_link 672round_robin] contains a `ready_queue_t` instance to manage its ready fibers.) 673See [member_link context..ready_is_linked], [member_link context..ready_link], 674[member_link context..ready_unlink]. 675 676Your `algorithm` implementation may use any container you desire to 677manage passed `context` instances. `ready_queue_t` avoids some of the overhead 678of typical STL containers. 679 680 #include <boost/fiber/context.hpp> 681 682 namespace boost { 683 namespace fibers { 684 685 enum class type { 686 none = ``['unspecified]``, 687 main_context = ``['unspecified]``, // fiber associated with thread's stack 688 dispatcher_context = ``['unspecified]``, // special fiber for maintenance operations 689 worker_context = ``['unspecified]``, // fiber not special to the library 690 pinned_context = ``['unspecified]`` // fiber must not be migrated to another thread 691 }; 692 693 class context { 694 public: 695 class id; 696 697 static context * active() noexcept; 698 699 context( context const&) = delete; 700 context & operator=( context const&) = delete; 701 702 id get_id() const noexcept; 703 704 void detach() noexcept; 705 void attach( context *) noexcept; 706 707 bool is_context( type) const noexcept; 708 709 bool is_terminated() const noexcept; 710 711 bool ready_is_linked() const noexcept; 712 bool remote_ready_is_linked() const noexcept; 713 bool wait_is_linked() const noexcept; 714 715 template< typename List > 716 void ready_link( List &) noexcept; 717 template< typename List > 718 void remote_ready_link( List &) noexcept; 719 template< typename List > 720 void wait_link( List &) noexcept; 721 722 void ready_unlink() noexcept; 723 void remote_ready_unlink() noexcept; 724 void wait_unlink() noexcept; 725 726 void suspend() noexcept; 727 void schedule( context *) noexcept; 728 }; 729 730 bool operator<( context const& l, context const& r) noexcept; 731 732 }} 733 734[static_member_heading context..active] 735 736 static context * active() noexcept; 737 738[variablelist 739[[Returns:] [Pointer to instance of current fiber.]] 740[[Throws:] [Nothing]] 741] 742 743[member_heading context..get_id] 744 745 context::id get_id() const noexcept; 746 747[variablelist 748[[Returns:] [If `*this` refers to a fiber of execution, an instance of 749__fiber_id__ that represents that fiber. Otherwise returns a 750default-constructed __fiber_id__.]] 751[[Throws:] [Nothing]] 752[[See also:] [[member_link fiber..get_id]]] 753] 754 755[member_heading context..attach] 756 757 void attach( context * f) noexcept; 758 759[variablelist 760[[Precondition:] [`this->get_scheduler() == nullptr`]] 761[[Effects:] [Attach fiber `f` to scheduler running `*this`.]] 762[[Postcondition:] [`this->get_scheduler() != nullptr`]] 763[[Throws:] [Nothing]] 764[[Note:] [A typical call: `boost::fibers::context::active()->attach(f);`]] 765[[Note:] [`f` must not be the running fiber[s] context. It must not be 766__blocked__ or terminated. It must not be a `pinned_context`. It must be 767currently detached. It must not currently be linked into an [class_link 768algorithm] implementation[s] ready queue. Most of these conditions are 769implied by `f` being owned by an `algorithm` implementation: that is, it 770has been passed to [member_link algorithm..awakened] but has not yet 771been returned by [member_link algorithm..pick_next]. Typically a 772`pick_next()` implementation would call `attach()` with the `context*` it is 773about to return. It must first remove `f` from its ready queue. You should 774never pass a `pinned_context` to `attach()` because you should never have 775called its `detach()` method in the first place.]] 776] 777 778[member_heading context..detach] 779 780 void detach() noexcept; 781 782[variablelist 783[[Precondition:] [`(this->get_scheduler() != nullptr) && ! this->is_context(pinned_context)`]] 784[[Effects:] [Detach fiber `*this` from its scheduler running `*this`.]] 785[[Throws:] [Nothing]] 786[[Postcondition:] [`this->get_scheduler() == nullptr`]] 787[[Note:] [This method must be called on the thread with which the fiber is 788currently associated. `*this` must not be the running fiber[s] context. It 789must not be __blocked__ or terminated. It must not be a `pinned_context`. It 790must not be detached already. It must not already be linked into an [class_link 791algorithm] implementation[s] ready queue. Most of these conditions are 792implied by `*this` being passed to [member_link algorithm..awakened]; an 793`awakened()` implementation must, however, test for `pinned_context`. It must 794call `detach()` ['before] linking `*this` into its ready queue.]] 795[[Note:] [In particular, it is erroneous to attempt to migrate a fiber from 796one thread to another by calling both `detach()` and `attach()` in the 797[member_link algorithm..pick_next] method. `pick_next()` is called on 798the intended destination thread. `detach()` must be called on the fiber[s] 799original thread. You must call `detach()` in the corresponding `awakened()` 800method.]] 801[[Note:] [Unless you intend make a fiber available for potential migration to 802a different thread, you should call neither `detach()` nor `attach()` with its 803`context`.]] 804] 805 806[member_heading context..is_context] 807 808 bool is_context( type t) const noexcept; 809 810[variablelist 811[[Returns:] [`true` if `*this` is of the specified type.]] 812[[Throws:] [Nothing]] 813[[Note:] [`type::worker_context` here means any fiber not special to the 814library. For `type::main_context` the `context` is associated with the ["main] 815fiber of the thread: the one implicitly created by the thread itself, rather 816than one explicitly created by __boost_fiber__. For `type::dispatcher_context` 817the `context` is associated with a ["dispatching] fiber, responsible for 818dispatching awakened fibers to a scheduler[s] ready-queue. The ["dispatching] 819fiber is an implementation detail of the fiber manager. The context of the 820["main] or ["dispatching] fiber [mdash] any fiber for which 821`is_context(pinned_context)` is `true` [mdash] must never be passed to 822[member_link context..detach].]] 823] 824 825[member_heading context..is_terminated] 826 827 bool is_terminated() const noexcept; 828 829[variablelist 830[[Returns:] [`true` if `*this` is no longer a valid context.]] 831[[Throws:] [Nothing]] 832[[Note:] [The `context` has returned from its fiber-function and is 833no longer considered a valid context.]] 834] 835 836[member_heading context..ready_is_linked] 837 838 bool ready_is_linked() const noexcept; 839 840[variablelist 841[[Returns:] [`true` if `*this` is stored in an [class_link algorithm] 842implementation[s] ready-queue.]] 843[[Throws:] [Nothing]] 844[[Note:] [Specifically, this method indicates whether [member_link 845context..ready_link] has been called on `*this`. `ready_is_linked()` has 846no information about participation in any other containers.]] 847] 848 849[member_heading context..remote_ready_is_linked] 850 851 bool remote_ready_is_linked() const noexcept; 852 853[variablelist 854[[Returns:] [`true` if `*this` is stored in the fiber manager[s] 855remote-ready-queue.]] 856[[Throws:] [Nothing]] 857[[Note:] [A `context` signaled as ready by another thread is first stored in 858the fiber manager[s] remote-ready-queue. This is the mechanism by which the 859fiber manager protects an [class_link algorithm] implementation from 860cross-thread [member_link algorithm..awakened] calls.]] 861] 862 863[member_heading context..wait_is_linked] 864 865 bool wait_is_linked() const noexcept; 866 867[variablelist 868[[Returns:] [`true` if `*this` is stored in the wait-queue of some 869synchronization object.]] 870[[Throws:] [Nothing]] 871[[Note:] [The `context` of a fiber waiting on a synchronization object (e.g. 872`mutex`, `condition_variable` etc.) is stored in the wait-queue of that 873synchronization object.]] 874] 875 876[member_heading context..ready_link] 877 878 template< typename List > 879 void ready_link( List & lst) noexcept; 880 881[variablelist 882[[Effects:] [Stores `*this` in ready-queue `lst`.]] 883[[Throws:] [Nothing]] 884[[Note:] [Argument `lst` must be a doubly-linked list from 885__boost_intrusive__, e.g. an instance of 886`boost::fibers::scheduler::ready_queue_t`. Specifically, it must be a 887[@http://www.boost.org/doc/libs/release/doc/html/intrusive/list.html 888`boost::intrusive::list`] compatible with the `list_member_hook` stored in the 889`context` object.]] 890] 891 892[member_heading context..remote_ready_link] 893 894 template< typename List > 895 void remote_ready_link( List & lst) noexcept; 896 897[variablelist 898[[Effects:] [Stores `*this` in remote-ready-queue `lst`.]] 899[[Throws:] [Nothing]] 900[[Note:] [Argument `lst` must be a doubly-linked list from 901__boost_intrusive__.]] 902] 903 904[member_heading context..wait_link] 905 906 template< typename List > 907 void wait_link( List & lst) noexcept; 908 909[variablelist 910[[Effects:] [Stores `*this` in wait-queue `lst`.]] 911[[Throws:] [Nothing]] 912[[Note:] [Argument `lst` must be a doubly-linked list from 913__boost_intrusive__.]] 914] 915 916[member_heading context..ready_unlink] 917 918 void ready_unlink() noexcept; 919 920[variablelist 921[[Effects:] [Removes `*this` from ready-queue: undoes the effect of 922[member_link context..ready_link].]] 923[[Throws:] [Nothing]] 924] 925 926[member_heading context..remote_ready_unlink] 927 928 void remote_ready_unlink() noexcept; 929 930[variablelist 931[[Effects:] [Removes `*this` from remote-ready-queue.]] 932[[Throws:] [Nothing]] 933] 934 935[member_heading context..wait_unlink] 936 937 void wait_unlink() noexcept; 938 939[variablelist 940[[Effects:] [Removes `*this` from wait-queue.]] 941[[Throws:] [Nothing]] 942] 943 944[member_heading context..suspend] 945 946 void suspend() noexcept; 947 948[variablelist 949[[Effects:] [Suspends the running fiber (the fiber associated with `*this`) 950until some other fiber passes `this` to [member_link context..schedule]. 951`*this` is marked as not-ready, and control passes to the scheduler to select 952another fiber to run.]] 953[[Throws:] [Nothing]] 954[[Note:] [This is a low-level API potentially useful for integration with 955other frameworks. It is not intended to be directly invoked by a typical 956application program.]] 957[[Note:] [The burden is on the caller to arrange for a call to `schedule()` 958with a pointer to `this` at some future time.]] 959] 960 961[member_heading context..schedule] 962 963 void schedule( context * ctx ) noexcept; 964 965[variablelist 966[[Effects:] [Mark the fiber associated with context `*ctx` as being ready to 967run. This does not immediately resume that fiber; rather it passes the fiber 968to the scheduler for subsequent resumption. If the scheduler is idle (has not 969returned from a call to [member_link algorithm..suspend_until]), 970[member_link algorithm..notify] is called to wake it up.]] 971[[Throws:] [Nothing]] 972[[Note:] [This is a low-level API potentially useful for integration with 973other frameworks. It is not intended to be directly invoked by a typical 974application program.]] 975[[Note:] [It is explicitly supported to call `schedule(ctx)` from a thread 976other than the one on which `*ctx` is currently suspended. The corresponding 977fiber will be resumed on its original thread in due course.]] 978[/[[Note:] [See [member_link context..migrate] for a way to migrate the 979suspended thread to the thread calling `schedule()`.]]] 980] 981 982[hding context_less..Non-member function [`operator<()]] 983 984 bool operator<( context const& l, context const& r) noexcept; 985 986[variablelist 987[[Returns:] [`true` if `l.get_id() < r.get_id()` is `true`, `false` 988otherwise.]] 989[[Throws:] [Nothing.]] 990] 991 992 993[endsect] 994