Forum: Ruby-core Feature Proposal: Dir.chdir Thread Safety

Db953d125f5cc49756edb6149f1b813e?d=identicon&s=25 richard s. (richard_s)
on 2014-05-01 17:34
(Received via mailing list)
Issue #9785 has been updated by Richard Schneeman.


I think maybe the openat and family of *at calls is close to my original
proposal but does not help for executing a script inside of a chdir
block:
https://github.com/heroku/hatchet/commit/f882d8920....

As my original proposal violates the basic assumptions of threads and
CWD, I think this specific proposal can be closed. Maybe when support
becomes better for those functions or if someone has a better idea of
how to utilize them we can open up a new issue. Here is a related
discussion from 2008 that I found:
https://www.ruby-forum.com/topic/165079

----------------------------------------
Feature #9785: Feature Proposal: Dir.chdir Thread Safety
https://bugs.ruby-lang.org/issues/9785#change-46430

* Author: Richard Schneeman
* Status: Open
* Priority: Normal
* Assignee:
* Category: core
* Target version: current: 2.2.0
----------------------------------------
I am proposing that `Dir.chdir` with a block be local to the current
thread and any threads that are created inside of that block.
`FileUtils.cd` and `FileUtils.chdir` should also behave the same way.

Currently `Dir.chdir` will change the directory for the entire process.
This makes writing a program that modifies different directories in
threads very difficult. Here is some ruby code that demonstrates the
problem:

```ruby
# /tmp/code.rb

require 'fileutils'

FileUtils.mkdir_p("/tmp/foo")
FileUtils.mkdir_p("/tmp/bar")


threads = []
threads << Thread.new do
  Dir.chdir("/tmp/foo") do
    puts "Thread in Dir.chdir('/tmp/foo') pwd: #{`pwd`}"
  end
end


threads << Thread.new do
  puts "Thread without Dir.chdir        pwd: #{`pwd`}"
end

threads.map(&:join)
```

When you run it you get different results:

```
$ ruby /tmp/code.rb
Thread without Dir.chdir        pwd: /tmp
Thread in Dir.chdir('/tmp/foo') pwd: /private/tmp/foo

$ ruby /tmp/code.rb
Thread in Dir.chdir('/tmp/foo') pwd: /private/tmp/foo
Thread without Dir.chdir        pwd: /private/tmp/foo
```

This is because Dir.chdir is not limited to the scope of the block but
rather changes the working directory globally for the entire process
including different threads.

Threads in MRI are very good for reading and writing to the disk,
however many times a programmer wishes to read or write to disk they
will want to use `Dir.chdir`. The current behavior of `Dir.chdir`
prevents a programmer from changing directory inside of threads and can
be very confusing for anyone who does not know this behavior.

For a better programming experience either we can make `Dir.chdir`
thread aware, or introduce a new way to change the directory inside of a
new thread such as `Dir.threadsafe_chdir`, I believe the first option is
the best.
This topic is locked and can not be replied to.