Skip to content

Fix a RactorLocalSingleton lazy mutex race#39

Open
havenwood wants to merge 1 commit intoruby:masterfrom
havenwood:fix-ractor-local-singleton-race
Open

Fix a RactorLocalSingleton lazy mutex race#39
havenwood wants to merge 1 commit intoruby:masterfrom
havenwood:fix-ractor-local-singleton-race

Conversation

@havenwood
Copy link

RactorLocalSingleton lazily creates the mutex that guards instance creation, but that lazy creation isn't itself guarded. Multiple threads can each see nil, create their own mutex and synchronize on different locks.

This switches to using Ractor.store_if_absent, which makes the separate mutex and double-checked locking unnecessary. Ractor.store_if_absent had not been introduced yet when this code was initially written.

Reproduction:

require "singleton"

class Single
  include RactorLocalSingleton

  def initialize
    count = Ractor.current[:instances] || 0
    Ractor.current[:instances] = count.succ
    sleep 0.1
  end

  class << self
    private

    def set_mutex(v)
      sleep 0.01
      super
    end
  end
end

count = Ractor.new do
  Array.new(10) { Thread.new { Single.instance } }.each(&:join)
  Ractor.current[:instances]
end

pp count.value

Before patch:

10

After patch:

1

`RactorLocalSingleton` lazily creates the mutex that guards instance
creation, but that lazy creation isn't itself guarded. Multiple threads
can each see `nil`, create their own mutex and synchronize on different
locks.

This switches to using `Ractor.store_if_absent`, which makes the
separate mutex and double-checked locking unnecessary.
`Ractor.store_if_absent` had not been introduced yet when this code was
initially written.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant