Bug #933 ยป cont.c.diff
cont.c (working copy) | ||
---|---|---|
/* fiber */
|
||
/*********/
|
||
/*
|
||
* Document-class: Fiber
|
||
*
|
||
* Fibers are primitives for implementing light weight cooperative
|
||
* concurrency in Ruby. Basically they are a means of creating code blocks
|
||
* that can be paused and resumed, much like threads. The main difference
|
||
* is that they are never preempted and that the scheduling must be done by
|
||
* the programmer and not the VM.
|
||
*
|
||
* As opposed to other stackless light weight concurrency models, each fiber
|
||
* comes with a small 4KB stack. This enables the fiber to be paused from deeply
|
||
* nested function calls within the fiber block.
|
||
*
|
||
* When a fiber is created it will not run automatically. Rather it must be
|
||
* be explicitly asked to run using the <code>Fiber#resume</code> method.
|
||
* The code running inside the fibe can give up control by calling
|
||
* <code>Fiber.yield</code> in which case it yields control back to caller
|
||
* (the caller of the <code>Fiber#resume</code>).
|
||
*
|
||
* Upon yielding or termination the Fiber returns the value of the last
|
||
* executed expression
|
||
*
|
||
* For instance:
|
||
*
|
||
* fiber = Fiber.new do
|
||
* Fiber.yield 1
|
||
* 2
|
||
* end
|
||
*
|
||
* puts fiber.resume
|
||
* puts fiber.resume
|
||
* puts fiber.resume
|
||
*
|
||
* <em>produces</em>
|
||
*
|
||
* 1
|
||
* 2
|
||
* FiberError: dead fiber called
|
||
*
|
||
* The <code>Fiber#resume</code> method accepts an arbitary number of
|
||
* parameters, if it is the first call to <code>resume</code> then they
|
||
* will be passed as block arguments. Other wise they will be the return
|
||
* value of the call to <code>Fiber.yield</code>
|
||
*
|
||
* Example:
|
||
*
|
||
* fiber = Fiber.new do |first|
|
||
* second = Fiber.yield first + 2
|
||
* end
|
||
*
|
||
* puts fiber.resume 10
|
||
* puts fiber.resume 14
|
||
* puts fiber.resume 18
|
||
*
|
||
* <em>produces</em>
|
||
*
|
||
* 12
|
||
* 14
|
||
* FiberError: dead fiber called
|
||
*
|
||
*/
|
||
#define FIBER_VM_STACK_SIZE (4 * 1024)
|
||
static VALUE
|
||
... | ... | |
return rb_fiber_transfer(return_fiber(), argc, argv);
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* fiber.alive?
|
||
*
|
||
* Returns true if the fiber can still be resumed (or transferred to).
|
||
* After finishing execution of the fiber block this method will always
|
||
* return false.
|
||
*/
|
||
VALUE
|
||
rb_fiber_alive_p(VALUE fibval)
|
||
{
|
||
... | ... | |
return fib->status != TERMINATED;
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* fiber.resume(args, ...)
|
||
*
|
||
* Resumes the fiber from the point at which the last <code>Fiber.yield</code>
|
||
* was called, or starts running it if it is the first call to
|
||
* <code>resume</code>. Arguments passed to resume will be the value of
|
||
* the <code>Fiber.yield</code> expression or will be passed as block
|
||
* parameters to the fiber's block if this is the first <code>resume</code>.
|
||
*
|
||
* Alternatively, when resume is called it evaluates to the arguments passed
|
||
* to the next <code>Fiber.yield</code> statement inside the fiber's block
|
||
* or to the block value if it runs to completion without any
|
||
* <code>Fiber.yield</code>
|
||
*/
|
||
static VALUE
|
||
rb_fiber_m_resume(int argc, VALUE *argv, VALUE fib)
|
||
{
|
||
return rb_fiber_resume(fib, argc, argv);
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* fiber.transfer(args, ...)
|
||
*
|
||
* Transfer control to another fiber, resuming it from where it last
|
||
* stopped or starting it if it was not resumed before. The calling
|
||
* fiber will be suspended much like in a call to <code>Fiber.yield</code>.
|
||
*
|
||
* The fiber which recieves the transfer call is treats it much like
|
||
* a resume call. Arguments passed to transfer are treated like those
|
||
* passed to resume.
|
||
*
|
||
* You cannot resume a fiber that transferred control to another one.
|
||
* This will cause a double resume error. You need to transfer control
|
||
* back to this fiber before it can yield and resume.
|
||
*/
|
||
static VALUE
|
||
rb_fiber_m_transfer(int argc, VALUE *argv, VALUE fib)
|
||
{
|
||
return rb_fiber_transfer(fib, argc, argv);
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* Fiber.yield(args, ...)
|
||
*
|
||
* Yields control back to the context that resumed the fiber, passing
|
||
* along any arguments that were passed to it. The fiber will resume
|
||
* processing at this point when <code>resume</code> is called next.
|
||
* Any arguments passed to the next <code>resume</code> will be the
|
||
* value that this <code>Fiber.yield</code> expression evaluates to.
|
||
*/
|
||
static VALUE
|
||
rb_fiber_s_yield(int argc, VALUE *argv, VALUE klass)
|
||
{
|
||
return rb_fiber_yield(argc, argv);
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* Fiber.current()
|
||
*
|
||
* Returns the current fiber. You need to <code>require 'fiber'</code>
|
||
* before using this method. If you are not running in the context of
|
||
* a fiber this method will return the root fiber.
|
||
*/
|
||
static VALUE
|
||
rb_fiber_s_current(VALUE klass)
|
||
{
|