-
Notifications
You must be signed in to change notification settings - Fork 533
Isolate Dir.chdir to a new process, or mutex #372
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
3d789f7
3ad8851
ba1df97
f3f7518
f1cd6de
df31b57
22ae609
5449c01
ae1baa3
e57f46f
0d32576
a100a36
1cdc5fd
cdcc6a5
0fb9798
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,12 @@ | |
module Git | ||
|
||
class Base | ||
# Adding a mutex to the class because each repo should be sharing the same mutex | ||
# in case we need to Dir.chdir and we don't have fork() support to isolate that | ||
class << self | ||
attr_accessor :chdir_semaphore | ||
end | ||
Git::Base.chdir_semaphore = Mutex.new | ||
|
||
include Git::Base::Factory | ||
|
||
|
@@ -92,18 +98,37 @@ def initialize(options = {}) | |
@index = options[:index] ? Git::Index.new(options[:index], false) : nil | ||
end | ||
|
||
# changes current working directory for a block | ||
# to the git working directory | ||
# changes current working directory for a block to the git working directory. | ||
# | ||
# Note: If we can fork() or spawn(), Dir.chdir will happen in a new process | ||
# otherwise, we will use a mutex to prevent threading errors | ||
# See https://github.com/ruby-git/ruby-git/issues/355 for more info | ||
# | ||
# example | ||
# @git.chdir do | ||
# # write files | ||
# @git.add | ||
# @git.commit('message') | ||
# end | ||
def chdir # :yields: the Git::Path | ||
Dir.chdir(dir.path) do | ||
yield dir.path | ||
def chdir(&block) # :yields: the Git::Path | ||
chdir_block = Proc.new do | ||
Dir.chdir(dir.path) do | ||
block.call(dir.path) | ||
end | ||
end | ||
|
||
if Process.respond_to?(:fork) | ||
# Forking this process so that we can be threadsafe | ||
pid = Process.fork do | ||
chdir_block.call | ||
end | ||
Process.wait(pid) | ||
else | ||
# Windows and NetBSD 4 don't support fork() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting anecdote, I wasn't aware of NetBSD 4 here. But OTOH, it's past EOL already. Maybe we can drop that reference and instead mention JRuby (which is actively used by many), since it also has problems with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True dat |
||
# let's use a mutex to prevent race conditions with threads | ||
Git::Base.chdir_semaphore.synchronize do | ||
chdir_block.call | ||
end | ||
end | ||
end | ||
|
||
|
@@ -144,9 +169,7 @@ def repo | |
|
||
# returns the repository size in bytes | ||
def repo_size | ||
Dir.chdir(repo.path) do | ||
return `du -s`.chomp.split.first.to_i | ||
end | ||
return `du -s #{repo.path}`.chomp.split.first.to_i | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very good fix, absolutely no reason to |
||
end | ||
|
||
def set_index(index_file, check = true) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -310,7 +310,7 @@ def branches_all | |
def list_files(ref_dir) | ||
dir = File.join(@git_dir, 'refs', ref_dir) | ||
files = [] | ||
Dir.chdir(dir) { files = Dir.glob('**/*').select { |f| File.file?(f) } } rescue nil | ||
files = Dir.glob(File.join(dir, '**/*')).select { |f| File.file?(f) } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I was thinking about that too. I wasn't convinced it was needed, but it's probably safer since passing |
||
files | ||
end | ||
|
||
|
@@ -442,11 +442,7 @@ def config_get(name) | |
command('config', ['--get', name]) | ||
end | ||
|
||
if @git_dir | ||
Dir.chdir(@git_dir, &do_get) | ||
else | ||
do_get.call | ||
end | ||
do_get.call(@git_dir) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not 💯 this will work, but tests pass. Seems like we might need to pass the |
||
end | ||
|
||
def global_config_get(name) | ||
|
@@ -458,11 +454,7 @@ def config_list | |
parse_config_list command_lines('config', ['--list']) | ||
end | ||
|
||
if @git_dir | ||
Dir.chdir(@git_dir, &build_list) | ||
else | ||
build_list.call | ||
end | ||
build_list.call(@git_dir) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here, I think we need to utilize the |
||
end | ||
|
||
def global_config_list | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works perfectly fine, but maybe the approach below would be even better. It shouldn't really be an accessor but more like a reader since it's an immutable data structure that shouldn't be tampered with elsewhere, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you're right, but are you sure that's threadsafe? If we have 2 threads that call
chdir_semaphore
before it is set, is it possible they both end up callingMutex.new
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. So maybe something like this then:
(if I remember the context right - the `@chdir_semaphore call in normal class context will assign it to the class-level variable. Please try this out before trusting me blindly on this.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@perlun I tested with your suggestion, it works 👍