ruby-concurrency / concurrent-ruby

Modern concurrency tools including agents, futures, promises, thread pools, supervisors, and more. Inspired by Erlang, Clojure, Scala, Go, Java, JavaScript, and classic concurrency patterns.
https://ruby-concurrency.github.io/concurrent-ruby/
Other
5.71k stars 420 forks source link

Concurrent::Promises.future deadlock in RSpec test #1055

Closed chernenkov-ayu-sbmt closed 4 months ago

chernenkov-ayu-sbmt commented 4 months ago

Hello! I found deadlock behaviour when I was writing RSpec test for my custom Scientist::Experiment implementation (see scientist gem) which use Concurrent::Promises.future for running observations in parallel. I'm not sure why it happens and how to resolve it. I prepared a small example code which reproduces the deadlock. Please, help!

It is interesting, that when I change let(:experiment_run) to:

Scientist.run "test_experiment" do |e|
  e.use { "expected_result" }
  e.try { "expected_result" }
end

then test finishes successfully.

It is also interesting, that in my original code (for the Rails service) doesn't raise Deadlock error but hangs forever instead.

Context:

* Operating system:                mac
* Ruby implementation:             Ruby 3.3.0
* `concurrent-ruby` version:       1.3.3
* `concurrent-ruby-ext` installed: no
* `concurrent-ruby-edge` used:     no
bensheldon commented 4 months ago

Thanks for opening the issue, and very much for the nice reproduction. I don't think the source of this problem is Concurrent Ruby, nor Scientist

I checked out your example. I think the cause is from RSpec let blocks that have a Mutex and are lazy evaluated. The simplest change to your code would be to use let! so they are eager-evaluated.

I don't know enough about RSpec to say whether deadlock on let is expected, undefined, or a bug, but you might search RSpec issues to see if this has been reported and if not, do so. I found it interesting.

chernenkov-ayu-sbmt commented 4 months ago

Thank you!