Skip to content

Commit

Permalink
[support] Use Mutex#owned? to correctly check if the Mutex is owned b…
Browse files Browse the repository at this point in the history
…y the current Thread or Fiber

* In Ruby >= 3, Mutex are held per Fiber, not per Thread.
* Fixes rspec/rspec-support#501

---
This commit was imported from rspec/rspec-support@0d64895.
  • Loading branch information
eregon authored and pirj committed Apr 28, 2021
1 parent 6afa2b7 commit bbf0249
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 10 deletions.
5 changes: 5 additions & 0 deletions rspec-support/Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
### Development

Bug Fixes:
* Fix reentrant mutex for Ruby 3.0. (Benoit Daloze, #504)

### 3.10.2 / 2021-01-28
[Full Changelog](http://github.com/rspec/rspec-support/compare/v3.10.1...v3.10.2)

Expand Down
37 changes: 27 additions & 10 deletions rspec-support/lib/rspec/support/reentrant_mutex.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,34 @@ def synchronize

private

def enter
@mutex.lock if @owner != Thread.current
@owner = Thread.current
@count += 1
end
# This is fixing a bug #501 that is specific to Ruby 3.0. The new implementation
# depends on `owned?` that was introduced in Ruby 2.0, so both should work for Ruby 2.x.
if RUBY_VERSION.to_f >= 3.0
def enter
@mutex.lock unless @mutex.owned?
@count += 1
end

def exit
@count -= 1
return unless @count == 0
@owner = nil
@mutex.unlock
def exit
unless @mutex.owned?
raise ThreadError, "Attempt to unlock a mutex which is locked by another thread/fiber"
end
@count -= 1
@mutex.unlock if @count == 0
end
else
def enter
@mutex.lock if @owner != Thread.current
@owner = Thread.current
@count += 1
end

def exit
@count -= 1
return unless @count == 0
@owner = nil
@mutex.unlock
end
end
end

Expand Down
24 changes: 24 additions & 0 deletions rspec-support/spec/rspec/support/reentrant_mutex_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,28 @@
mutex.synchronize { order.pass_to :thread, :resume_on => :sleep }
order.join_all
end

if RUBY_VERSION >= '3.0'
it 'waits when trying to lock from another Fiber' do
mutex.synchronize do
ready = false
f = Fiber.new do
expect {
ready = true
mutex.send(:enter)
raise 'should reach here: mutex is already locked on different Fiber'
}.to raise_error(Exception, 'waited correctly')
end

main_thread = Thread.current

t = Thread.new do
Thread.pass until ready && main_thread.stop?
main_thread.raise Exception, 'waited correctly'
end
f.resume
t.join
end
end
end
end

0 comments on commit bbf0249

Please sign in to comment.