| 570 |
570 |
/* fiber */
|
| 571 |
571 |
/*********/
|
| 572 |
572 |
|
|
573 |
/*
|
|
574 |
* Document-class: Fiber
|
|
575 |
*
|
|
576 |
* Fibers are primitives for implementing light weight cooperative
|
|
577 |
* concurrency in Ruby. Basically they are a means of creating code blocks
|
|
578 |
* that can be paused and resumed, much like threads. The main difference
|
|
579 |
* is that they are never preempted and that the scheduling must be done by
|
|
580 |
* the programmer and not the VM.
|
|
581 |
*
|
|
582 |
* As opposed to other stackless light weight concurrency models, each fiber
|
|
583 |
* comes with a small 4KB stack. This enables the fiber to be paused from deeply
|
|
584 |
* nested function calls within the fiber block.
|
|
585 |
*
|
|
586 |
* When a fiber is created it will not run automatically. Rather it must be
|
|
587 |
* be explicitly asked to run using the <code>Fiber#resume</code> method.
|
|
588 |
* The code running inside the fibe can give up control by calling
|
|
589 |
* <code>Fiber.yield</code> in which case it yields control back to caller
|
|
590 |
* (the caller of the <code>Fiber#resume</code>).
|
|
591 |
*
|
|
592 |
* Upon yielding or termination the Fiber returns the value of the last
|
|
593 |
* executed expression
|
|
594 |
*
|
|
595 |
* For instance:
|
|
596 |
*
|
|
597 |
* fiber = Fiber.new do
|
|
598 |
* Fiber.yield 1
|
|
599 |
* 2
|
|
600 |
* end
|
|
601 |
*
|
|
602 |
* puts fiber.resume
|
|
603 |
* puts fiber.resume
|
|
604 |
* puts fiber.resume
|
|
605 |
*
|
|
606 |
* <em>produces</em>
|
|
607 |
*
|
|
608 |
* 1
|
|
609 |
* 2
|
|
610 |
* FiberError: dead fiber called
|
|
611 |
*
|
|
612 |
* The <code>Fiber#resume</code> method accepts an arbitary number of
|
|
613 |
* parameters, if it is the first call to <code>resume</code> then they
|
|
614 |
* will be passed as block arguments. Other wise they will be the return
|
|
615 |
* value of the call to <code>Fiber.yield</code>
|
|
616 |
*
|
|
617 |
* Example:
|
|
618 |
*
|
|
619 |
* fiber = Fiber.new do |first|
|
|
620 |
* second = Fiber.yield first + 2
|
|
621 |
* end
|
|
622 |
*
|
|
623 |
* puts fiber.resume 10
|
|
624 |
* puts fiber.resume 14
|
|
625 |
* puts fiber.resume 18
|
|
626 |
*
|
|
627 |
* <em>produces</em>
|
|
628 |
*
|
|
629 |
* 12
|
|
630 |
* 14
|
|
631 |
* FiberError: dead fiber called
|
|
632 |
*
|
|
633 |
*/
|
|
634 |
|
| 573 |
635 |
#define FIBER_VM_STACK_SIZE (4 * 1024)
|
| 574 |
636 |
|
| 575 |
637 |
static VALUE
|
| ... | ... | |
| 840 |
902 |
return rb_fiber_transfer(return_fiber(), argc, argv);
|
| 841 |
903 |
}
|
| 842 |
904 |
|
|
905 |
/*
|
|
906 |
* call-seq:
|
|
907 |
* fiber.alive?
|
|
908 |
*
|
|
909 |
* Returns true if the fiber can still be resumed (or transferred to).
|
|
910 |
* After finishing execution of the fiber block this method will always
|
|
911 |
* return false.
|
|
912 |
*/
|
| 843 |
913 |
VALUE
|
| 844 |
914 |
rb_fiber_alive_p(VALUE fibval)
|
| 845 |
915 |
{
|
| ... | ... | |
| 848 |
918 |
return fib->status != TERMINATED;
|
| 849 |
919 |
}
|
| 850 |
920 |
|
|
921 |
/*
|
|
922 |
* call-seq:
|
|
923 |
* fiber.resume(args, ...)
|
|
924 |
*
|
|
925 |
* Resumes the fiber from the point at which the last <code>Fiber.yield</code>
|
|
926 |
* was called, or starts running it if it is the first call to
|
|
927 |
* <code>resume</code>. Arguments passed to resume will be the value of
|
|
928 |
* the <code>Fiber.yield</code> expression or will be passed as block
|
|
929 |
* parameters to the fiber's block if this is the first <code>resume</code>.
|
|
930 |
*
|
|
931 |
* Alternatively, when resume is called it evaluates to the arguments passed
|
|
932 |
* to the next <code>Fiber.yield</code> statement inside the fiber's block
|
|
933 |
* or to the block value if it runs to completion without any
|
|
934 |
* <code>Fiber.yield</code>
|
|
935 |
*/
|
| 851 |
936 |
static VALUE
|
| 852 |
937 |
rb_fiber_m_resume(int argc, VALUE *argv, VALUE fib)
|
| 853 |
938 |
{
|
| 854 |
939 |
return rb_fiber_resume(fib, argc, argv);
|
| 855 |
940 |
}
|
| 856 |
941 |
|
|
942 |
/*
|
|
943 |
* call-seq:
|
|
944 |
* fiber.transfer(args, ...)
|
|
945 |
*
|
|
946 |
* Transfer control to another fiber, resuming it from where it last
|
|
947 |
* stopped or starting it if it was not resumed before. The calling
|
|
948 |
* fiber will be suspended much like in a call to <code>Fiber.yield</code>.
|
|
949 |
*
|
|
950 |
* The fiber which recieves the transfer call is treats it much like
|
|
951 |
* a resume call. Arguments passed to transfer are treated like those
|
|
952 |
* passed to resume.
|
|
953 |
*
|
|
954 |
* You cannot resume a fiber that transferred control to another one.
|
|
955 |
* This will cause a double resume error. You need to transfer control
|
|
956 |
* back to this fiber before it can yield and resume.
|
|
957 |
*/
|
| 857 |
958 |
static VALUE
|
| 858 |
959 |
rb_fiber_m_transfer(int argc, VALUE *argv, VALUE fib)
|
| 859 |
960 |
{
|
| 860 |
961 |
return rb_fiber_transfer(fib, argc, argv);
|
| 861 |
962 |
}
|
| 862 |
963 |
|
|
964 |
/*
|
|
965 |
* call-seq:
|
|
966 |
* Fiber.yield(args, ...)
|
|
967 |
*
|
|
968 |
* Yields control back to the context that resumed the fiber, passing
|
|
969 |
* along any arguments that were passed to it. The fiber will resume
|
|
970 |
* processing at this point when <code>resume</code> is called next.
|
|
971 |
* Any arguments passed to the next <code>resume</code> will be the
|
|
972 |
* value that this <code>Fiber.yield</code> expression evaluates to.
|
|
973 |
*/
|
| 863 |
974 |
static VALUE
|
| 864 |
975 |
rb_fiber_s_yield(int argc, VALUE *argv, VALUE klass)
|
| 865 |
976 |
{
|
| 866 |
977 |
return rb_fiber_yield(argc, argv);
|
| 867 |
978 |
}
|
| 868 |
979 |
|
|
980 |
/*
|
|
981 |
* call-seq:
|
|
982 |
* Fiber.current()
|
|
983 |
*
|
|
984 |
* Returns the current fiber. You need to <code>require 'fiber'</code>
|
|
985 |
* before using this method. If you are not running in the context of
|
|
986 |
* a fiber this method will return the root fiber.
|
|
987 |
*/
|
| 869 |
988 |
static VALUE
|
| 870 |
989 |
rb_fiber_s_current(VALUE klass)
|
| 871 |
990 |
{
|