mirror of
https://github.com/mperham/sidekiq.git
synced 2022-11-09 13:52:34 -05:00
Synchronize code that reads/undefines methods in class attrs (#3997)
Fixes #3659 and #2302 The problem is that when two classes are accessed from separate threads they can race to undef/read these methods.
This commit is contained in:
parent
b5cad42e81
commit
64dfc280de
2 changed files with 35 additions and 3 deletions
|
@ -66,6 +66,7 @@ module Sidekiq
|
|||
end
|
||||
|
||||
module ClassMethods
|
||||
ACCESSOR_MUTEX = Mutex.new
|
||||
|
||||
def delay(*args)
|
||||
raise ArgumentError, "Do not call .delay on a Sidekiq::Worker class, call .perform_async"
|
||||
|
@ -148,10 +149,18 @@ module Sidekiq
|
|||
instance_writer = true
|
||||
|
||||
attrs.each do |name|
|
||||
synchronized_getter = "__synchronized_#{name}"
|
||||
|
||||
singleton_class.instance_eval do
|
||||
undef_method(name) if method_defined?(name) || private_method_defined?(name)
|
||||
end
|
||||
define_singleton_method(name) { nil }
|
||||
|
||||
define_singleton_method(synchronized_getter) { nil }
|
||||
singleton_class.class_eval do
|
||||
private(synchronized_getter)
|
||||
end
|
||||
|
||||
define_singleton_method(name) { ACCESSOR_MUTEX.synchronize { send synchronized_getter } }
|
||||
|
||||
ivar = "@#{name}"
|
||||
|
||||
|
@ -161,8 +170,10 @@ module Sidekiq
|
|||
end
|
||||
define_singleton_method("#{name}=") do |val|
|
||||
singleton_class.class_eval do
|
||||
undef_method(name) if method_defined?(name) || private_method_defined?(name)
|
||||
define_method(name) { val }
|
||||
ACCESSOR_MUTEX.synchronize do
|
||||
undef_method(synchronized_getter) if method_defined?(synchronized_getter) || private_method_defined?(synchronized_getter)
|
||||
define_method(synchronized_getter) { val }
|
||||
end
|
||||
end
|
||||
|
||||
if singleton_class?
|
||||
|
|
|
@ -287,4 +287,25 @@ class TestClient < Sidekiq::Test
|
|||
assert_equal 12, job['retry']
|
||||
end
|
||||
end
|
||||
|
||||
describe 'class attribute race conditions' do
|
||||
new_class = -> {
|
||||
Class.new do
|
||||
class_eval('include Sidekiq::Worker')
|
||||
|
||||
define_method(:foo) { get_sidekiq_options }
|
||||
end
|
||||
}
|
||||
|
||||
it 'does not explode when new initializing classes from multiple threads' do
|
||||
10000.times do
|
||||
klass = new_class.call
|
||||
|
||||
t1 = Thread.new { klass.sidekiq_options({}) }
|
||||
t2 = Thread.new { klass.sidekiq_options({}) }
|
||||
t1.join
|
||||
t2.join
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue