Skip to content

Futures, Promises, and shared state in local vars #223

Closed
@jrochkind

Description

@jrochkind

I wrote up this issue more fully in my blog, at https://bibwild.wordpress.com/2015/01/15/ruby-threads-gotcha-with-local-vars-and-shared-state/

But consider this little demo showing the issue:

value = 100

future = Concurrent::Future.execute do
  sleep 1
  # DANGER will robinson!
  value + 1
end

value = 200

puts future.value # you get 201, not 101!

That 'value' local var is the same one as the outer scope, changes to it in the scope where the thread/future was created effect the thread too.

With stdlib Thread.new, you can get around this with the args to Thread.new:

value = "original"
t = Thread.new(value) do |t_value|
   sleep 1
   puts value
end
value = "new"
t.join

You still get "original", not "new", because of the use of the arg to Thread.new to pass the value to the block argument.

A raw Concurrent::ThreadPoolExecutor lets you do this too, eg pool.post(work) do |t_work|

But Future (and Promise?) don't seem to have an API to do this, to pass an arg whose value gets passed to the block arg, to break the 'connection' with the local var in lexical scope, so you can actually fix it's value in the task you are creating and not worry about the outer scope changing it from under you.

Am I missing something? Or do the expected ways one might use Future/Promise mean that this probably won't come up? Or should Future/Promise have an API similar to Thread/ThreadPoolExecutor, to allow parameters to be passed in and fixed?

Metadata

Metadata

Assignees

Labels

enhancementAdding features, adding tests, improving documentation.questionAn user question, does not change the library.

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions