Skip to content

Commit

Permalink
Make global stubs thread-safe
Browse files Browse the repository at this point in the history
This fixes an issue where testing multi-threaded code could sometimes result in incorrect responses being returned. Specifically in the case of VCR, since it seems to mock a nil response first before stubbing the real value, a nil response could sometimes be returned. This was because the hash being used by global stubs is not thread-safe.

I tried to write a test for this, but given the multi-threaded nature, and the fact it doesn't happen on MRI, it was difficult to find anything that failed. The existing tests do at least regression test it though.

closes #907
  • Loading branch information
Adam Harwood committed Oct 12, 2020
1 parent 7b5023d commit f7a1beb
Showing 1 changed file with 12 additions and 2 deletions.
14 changes: 12 additions & 2 deletions lib/webmock/stub_registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,20 @@ def register_global_stub(&block)
# That way, there's no race condition in case #to_return
# doesn't run immediately after stub.with.
responses = {}
response_lock = Mutex.new

stub = ::WebMock::RequestStub.new(:any, ->(uri) { true }).with { |request|
responses[request.object_id] = yield(request)
}.to_return(lambda { |request| responses.delete(request.object_id) })
update_response = -> { responses[request.object_id] = yield(request) }

# The block can recurse, so only lock if we don't already own it
if response_lock.owned?
update_response.call
else
response_lock.synchronize(&update_response)
end
}.to_return(lambda { |request|
response_lock.synchronize { responses.delete(request.object_id) }
})

global_stubs.push stub
end
Expand Down

0 comments on commit f7a1beb

Please sign in to comment.