$stdout.sync = true
objects = [1, 2, 3]
fiber = Fiber.new do
loop do
objects.each { |obj| Fiber.yield(obj) }
end
end
def run(obj)
fork do
puts obj
end
end
def on_child_exit(obj)
begin
while Process.wait(-1, Process::WNOHANG)
run(obj)
end
rescue Errno::ECHILD
end
end
trap(:CHLD) { on_child_exit(fiber.resume) }
4.times { run(fiber.resume) }
sleep
fiber_process.rb:26:in `resume': fiber called across stack rewinding barrier (FiberError)
There is a race condition when two or more children exit. Now I know I can implement
this differently, but this still made me curious. Is this a bug? Let's say I would
need to use a Fiber, then there is no way how I can do the synchronization manually,
or is there? Using a Mutex to synchronize the Fiber#resume will fail due to the
non-reentrant behaviour of Mutex#lock (I'll get "in `lock': deadlock; recursive
locking (ThreadError)"). Is there a way to do this or should Fibers not be used in
this context?
No. You don't need Mutex at all.
You only need to use variables (such as global variables).
Now I'm confused. How would I write the example code without getting the FiberErrors? Since I have no control over when a child process exits, I can't control when the 'trap(:CHLD)' block calls Fiber#resume, no? I thought I would have to do some form of manual synchronization there, to avoid the race condition. Sorry to bug you :)
No. You don't need Mutex at all.
You only need to use variables (such as global variables).
Now I'm confused. How would I write the example code without getting the FiberErrors? Since I have no control over when a child process exits, I can't control when the 'trap(:CHLD)' block calls Fiber#resume, no? I thought I would have to do some form of manual synchronization there, to avoid the race condition. Sorry to bug you :)
Now, I understand your issue. This is not a Fiber problem, but
concurrency problem with signal.
I recommend that you shouldn't use Fiber.resume in a trap handler. In
the trap handler, you should only set a flag and make flag sense in main.