Problems With Fibers in Threads

C

Chris Wailes

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.
 
T

Tony Arcieri

[Note: parts of this message were removed to make it a legal post.]

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

7stud --

Chris said:
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
 
7

7stud --

Tony said:
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:in `block in <main>'
 
C

Chris Wailes

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?
 
C

Christopher Dicely

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. =C2=A0Is = the
inability to call a fiber across different threads a 'feature', or
simply a consequence of implementation? =C2=A0Is this going to remain the
situation in the future? =C2=A0Is there a way around it? =C2=A0Where is t= his
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 Thomas.)
 
C

Chris Wailes

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.
 
C

Christopher Dicely

What I'm creating is a job queue. =C2=A0A thread from a central thread po= ol
grabs a job and then executes it. =C2=A0In very special circumstances, wh= en 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. =C2=A0The proble= m
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.

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.)
 
T

Tony Arcieri

[Note: parts of this message were removed to make it a legal post.]

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:

http://github.com/tarcieri/revactor/blob/master/lib/revactor/http_fetcher.rb

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top