Closed wjordan closed 4 years ago
Thanks so much @wjordan for reporting this, it was a one-line fix!
Please note that your example also has a bug. You need to reset $lock
inside of an ensure block, because sleep
might be interrupted with an exception:
def loop_it(number, lock)
puts "child #{number} starting..."
loop do
sleep(rand * 0.2)
lock.synchronize do
raise "[#{number}] still locked by #{$locked}" if $locked
puts "#{number} has the lock"
begin
$locked = number
sleep(rand * 0.05)
ensure
$locked = nil
end
end
end
end
lock = Polyphony::Mutex.new
100.times do |i|
Array.new(3) do |n|
spin { loop_it(i*3 + n, lock) }
end.tap{sleep 0.1}.each_with_index do |f, n|
puts "Terminating #{i*3+n}"
f.terminate
end
end
In general, the problem with the stock Ruby Thread#raise
, Timeout::timeout
etc APIs is that MRI implements them IIRC by using setjmp/longjmp, which can pull the rug from under your feet at any moment, even inside of a C function. Note, however, that your example does not involve multiple threads, and does not use those APIs. At any case Polyphony reimplements Thread#raise
, and Timeout::timeout
to use the scheduler to raise exceptions on arbitrary fibers. Those patches provided by Polyphony remove the problem completely, assuming there are no bugs 😄 . Good catch!
There's a common issue with Ruby's
ensure
blocks where resources can leak (fail to clean up as expected) where an 'asynchronous interrupt' may cause a block to exit before executing critical cleanup code.See Charles Nutter's (2008) post for a description of the issue as it relates to the use of
Thread#raise
/Thread#kill
. This has even caused subtle bugs in Ruby's ownMutex
/ConditionVariable
implementations (see e.g. 14999.It seems that Fiber switchpoints can trigger the same kinds of issues. Here is a small script that breaks
Polyphony::Mutex
along these lines (based onxx-using-a-mutex.rb
):Here's a sample run:
I wanted to raise this issue early since I think a good general solution to this problem in all places where
ensure
blocks are being used for resource cleanup will be crucial to the overall stability of this framework.