-
Notifications
You must be signed in to change notification settings - Fork 178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: avoid race condition in singleton instance
method
#1305
base: main
Are you sure you want to change the base?
Conversation
|
f3e7e34
to
a1ec590
Compare
@kaylareopelle In response to your question as to whether this is testable, here's a contrived example of a singleton that returns multiple instance when more than one thread is requesting it, and the corresponding fix applied to another test. require 'minitest/autorun'
class SingletonClass
def initialize
Thread.pass
end
class << self
def instance
@instance ||= new
end
end
end
class SafeSingletonClass
MUTEX = Thread::Mutex.new
def initialize
Thread.pass
end
class << self
def instance
@instance || MUTEX.synchronize { @instance ||= new }
end
end
end
describe 'singleton acquisition' do
it 'fails' do
ids = 2.times.map do
Thread.new { SingletonClass.instance.object_id }
end.map(&:join).map(&:value)
_(ids.uniq.count).must_equal(1)
end
it 'works' do
ids = 2.times.map do
Thread.new { SafeSingletonClass.instance.object_id }
end.map(&:join).map(&:value)
_(ids.uniq.count).must_equal(1)
end
end As to how this might be incorporated into this repo — I could probably structure the test around a test-only subclass of Instumentation::Base that has roughly the same characteristic? |
1925a54
to
18dc263
Compare
Instrumentations should be initialized during application boot time, and I am skeptical of programs that perform multi-threaded initialization or Ruby applications. Do you have any examples or bug reports of this happening in applications? |
I haven't seen bug reports for this library specifically. The intention here is to put the guarantee somewhere — in most cases initialization should happen at boot in a single thread, but that doesn't seem to be enforced or otherwise guaranteed (and if it was, it would be in a downstream library, i.e. the sdk, which includes this library as a dependency). The One thing I did not consider until just this moment is that putting the mutex into a constant forces it to be shared amongst all of the sub-classes; in practice that is probably fine but isn't completely correct. It could introduce deadlocks if anything refers to another subclass's |
18dc263
to
fcd48b5
Compare
||=
is not an atomic operation, so two simultaneous callers may be given different instrumentation instances. I do not know whether this ever practically matters, but if the intention is that these should be singletons, then that condition should be avoided by wrapping the assignment portion in a synchronize block.