Problems With Fibers in Threads

I know it’s an odd combination, but I have a situation where a thread
pulls a fiber off of a queue and then calls resume on it. I used to
use Proc objects but I need the ability to stop and start the jobs while
they are being executed so I switched to Fibers.

The problem I am having is that the thread hangs once it calls resume.
As far as I can tell nothing inside the fiber gets executed. Does
anyone know why this might be happening?

I’m currently using Ruby v 1.9.1p243.

Are you trying to reschedule a fiber from a different thread? Afaik
that
doesn’t work.

Chris W. wrote:

I know it’s an odd combination, but I have a situation where a thread
pulls a fiber off of a queue and then calls resume on it. I used to
use Proc objects but I need the ability to stop and start the jobs while
they are being executed so I switched to Fibers.

The problem I am having is that the thread hangs once it calls resume.
As far as I can tell nothing inside the fiber gets executed. Does
anyone know why this might be happening?

I’m currently using Ruby v 1.9.1p243.

$ ruby19 -v
ruby 1.9.1p243 (2009-07-16 revision 24175) [i386-darwin8.11.1]

require ‘thread’

q = Queue.new
thr = Thread.new do

f1 = Fiber.new do
puts “hello”
Fiber.yield
puts “world”
end

f2 = Fiber.new do
puts “goodbye”
Fiber.yield
puts “world”
end

q.push(f1)
q.push(f2)
q.push(q)

while (f = q.pop) != q
f.resume
f.resume
end
end

thr.join

–output:–
hello
world
goodbye
world

Tony A. wrote:

Are you trying to reschedule a fiber from a different thread? Afaik
that
doesn’t work.

…for instance:

require ‘thread’

q = Queue.new

f1 = Fiber.new do
puts “hello”
Fiber.yield
puts “world”
end

q.push(f1)

thr = Thread.new do
f = q.pop
f.resume
f.resume
end

thr.join

–output:–
r1test.rb:15:in resume': fiber called across threads (FiberError) from r1test.rb:15:inblock in ’

On Wed, Oct 14, 2009 at 12:13 PM, Chris W. [email protected]
wrote:

OK, that does appear to be the problem as the Fibers are created in
various threads then executed by a thread from the thread pool. Â Is the
inability to call a fiber across different threads a ‘feature’, or
simply a consequence of implementation? Â Is this going to remain the
situation in the future? Â Is there a way around it? Â Where is this
behavior mentioned?

It seems pretty fundamental. You might be able to work around it by
using Procs to store the jobs but then having the thread that runs
each Proc create a Fiber to run the Proc when it is run (I haven’t
tested it, but it seems like it should work.) Its not mentioned in the
documentation for Fiber (though it should be), but it is mentioned on
page 186 of the most recent edition of the Pickaxe (Programming Ruby
1.9: The Pragmatic Programmers’ Guide
, by Dave T…)

What I’m creating is a job queue. A thread from a central thread pool
grabs a job and then executes it. In very special circumstances, when a
job on the queue is waiting for a response from another job on the
queue, the job needs to pause and be put back onto the queue to wait for
a value.

With fibers I could call Fiber.yield() and that would kick control back
out to the thread who would push it back onto the queue. The problem
with your solution, in this particular case, is that the fiber couldn’t
be resumed from where it last exited and would have to be re-ran from
the beginning.

Thanks for the pointer to the book.

Christopher D. wrote:

It seems pretty fundamental. You might be able to work around it by
using Procs to store the jobs but then having the thread that runs
each Proc create a Fiber to run the Proc when it is run (I haven’t
tested it, but it seems like it should work.) Its not mentioned in the
documentation for Fiber (though it should be), but it is mentioned on
page 186 of the most recent edition of the Pickaxe (Programming Ruby
1.9: The Pragmatic Programmers’ Guide
, by Dave T…)

OK, that does appear to be the problem as the Fibers are created in
various threads then executed by a thread from the thread pool. Is the
inability to call a fiber across different threads a ‘feature’, or
simply a consequence of implementation? Is this going to remain the
situation in the future? Is there a way around it? Where is this
behavior mentioned?

7stud – wrote:

Tony A. wrote:

Are you trying to reschedule a fiber from a different thread? Afaik
that
doesn’t work.

…for instance:

require ‘thread’

q = Queue.new

f1 = Fiber.new do
puts “hello”
Fiber.yield
puts “world”
end

q.push(f1)

thr = Thread.new do
f = q.pop
f.resume
f.resume
end

thr.join

–output:–
r1test.rb:15:in resume': fiber called across threads (FiberError) from r1test.rb:15:inblock in ’

On Thu, Oct 15, 2009 at 9:51 AM, Chris W. [email protected]
wrote:

the beginning.
Ah, I got that you needed it to get picked up by a different thread
then the creating thread and paused and resumed once it was running, I
missed that you needed to have one thread pause it once it was started
and another resume it.

I’m pretty sure there is no easy way to do this in existing Ruby
implementations save maybe Rubinius; the problem seems approximately
equivalent to implementing serializable continuations. IIRC Rubinius
plans to have serializable continuations (don’t know if they are
implemented yet), so it might be doable under Rubinius (if you have
serializable continuations, you achieve what you want by serializing
the continuation where you would pause the Fiber, then passing the
serialized continuation back to the queue, where a later thread picks
it up and reinstantiates it.)

On Thu, Oct 15, 2009 at 10:51 AM, Chris W.
[email protected]wrote:

What I’m creating is a job queue. A thread from a central thread pool
grabs a job and then executes it. In very special circumstances, when a
job on the queue is waiting for a response from another job on the
queue, the job needs to pause and be put back onto the queue to wait for
a value.

(excuse the shameless self-promotion, but…)

You might look at Revactor, or the actor model in general:

http://revactor.org/

Revactor is a Fiber-based implementation of the Actor model, full of
baked-in sync/async I/O.

Here’s an example of doing scatter/gather-type processing of a job
queue.
In this case, there are multiple workers handling synchronous I/O
events,
pulling from a central job queue:

What type of jobs are you trying to run? Are they I/O bound or CPU
bound?

Revactor can help you in the I/O bound case but not in the CPU bound
case.
However there are many tools available for doing scatter/gather job
queues
among a pool of Ruby processes. That will let you distribute the load
of a
job across multiple CPU cores.