From: eregontp@... Date: 2021-04-27T16:53:03+00:00 Subject: [ruby-core:103618] [Ruby master Bug#17827] Monitor is not fiber safe Issue #17827 has been updated by Eregon (Benoit Daloze). Fixed in https://github.com/ruby/ruby/commit/3a3b19b2bba49e5d6f1cf13764eb6dd701397be9. This should be backported to 3.0 (I marked it as such). I noticed something else while writing a test for this: On 2.7: ``` ruby -e 'm=Mutex.new; m.lock; p 1; Fiber.new { m.lock }.resume' 1 Traceback (most recent call last): 1: from -e:1:in `block in
' -e:1:in `lock': deadlock; recursive locking (ThreadError) ``` On 3.0: ``` $ ruby -e 'm=Mutex.new; m.lock; p 1; Fiber.new { m.lock }.resume' 1 -e:1:in `lock': No live threads left. Deadlock? (fatal) 1 threads, 1 sleeps current:0x000000000214f080 main thread:0x000000000214f080 * # rb_thread_t:0x000000000214f080 native:0x00007f05edc32b80 int:0 from -e:1:in `block in
' $ ruby -e 'Thread.new {sleep}; sleep 0.1; m=Mutex.new; m.lock; p 1; Fiber.new { m.lock }.resume' 1 => hangs ``` Same behavior on master for `Monitor.new` with my fix (`ruby -rmonitor -e 'm=Monitor.new; m.enter; p 1; Fiber.new { p m.enter }.resume'` => fatal, or hang if there is another Thread). It would be nice to raise a ThreadError when detecting in Mutex#lock that the Mutex is owned by the same Thread but a different Fiber, if there is no Fiber scheduler. That's more of a nice-to-have though. ---------------------------------------- Bug #17827: Monitor is not fiber safe https://bugs.ruby-lang.org/issues/17827#change-91715 * Author: ioquatix (Samuel Williams) * Status: Closed * Priority: Normal * Assignee: Eregon (Benoit Daloze) * Backport: 2.6: DONTNEED, 2.7: DONTNEED, 3.0: REQUIRED ---------------------------------------- According to our discussion here https://github.com/rspec/rspec-support/issues/501 it seems like typical implementation of per-thread reentrant mutex is no longer valid and can lead to some deadlock situation. ``` #!/usr/bin/env ruby require 'monitor' def monitor_failure m = Monitor.new f1 = Fiber.new do m.synchronize do puts "f1 A" Fiber.yield puts "f1 B" end end f2 = Fiber.new do m.synchronize do puts "f2 A" # Fiber.yield f1.resume puts "f2 B" end end f1.resume f2.resume end monitor_failure ``` -- https://bugs.ruby-lang.org/ Unsubscribe: