[Feature #20244] Show the conflicting another chdir block

This commit is contained in:
Nobuyoshi Nakada 2024-02-07 19:51:30 +09:00
parent 8fe86feecd
commit 4bb8f8582f
2 changed files with 27 additions and 11 deletions

13
dir.c
View File

@ -1043,14 +1043,20 @@ dir_chdir0(VALUE path)
static struct { static struct {
VALUE thread; VALUE thread;
VALUE path;
int line;
int blocking; int blocking;
} chdir_lock = { } chdir_lock = {
.blocking = 0, .thread = Qnil, .blocking = 0, .thread = Qnil,
.path = Qnil, .line = 0,
}; };
static void static void
chdir_enter(void) chdir_enter(void)
{ {
if (chdir_lock.blocking == 0) {
chdir_lock.path = rb_source_location(&chdir_lock.line);
}
chdir_lock.blocking++; chdir_lock.blocking++;
if (NIL_P(chdir_lock.thread)) { if (NIL_P(chdir_lock.thread)) {
chdir_lock.thread = rb_thread_current(); chdir_lock.thread = rb_thread_current();
@ -1063,6 +1069,8 @@ chdir_leave(void)
chdir_lock.blocking--; chdir_lock.blocking--;
if (chdir_lock.blocking == 0) { if (chdir_lock.blocking == 0) {
chdir_lock.thread = Qnil; chdir_lock.thread = Qnil;
chdir_lock.path = Qnil;
chdir_lock.line = 0;
} }
} }
@ -1075,6 +1083,9 @@ chdir_alone_block_p(void)
rb_raise(rb_eRuntimeError, "conflicting chdir during another chdir block"); rb_raise(rb_eRuntimeError, "conflicting chdir during another chdir block");
if (!block_given) { if (!block_given) {
rb_warn("conflicting chdir during another chdir block"); rb_warn("conflicting chdir during another chdir block");
if (!NIL_P(chdir_lock.path)) {
rb_compile_warn(RSTRING_PTR(chdir_lock.path), chdir_lock.line, "here");
}
} }
} }
return block_given; return block_given;
@ -3640,6 +3651,8 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname)
void void
Init_Dir(void) Init_Dir(void)
{ {
rb_gc_register_address(&chdir_lock.path);
rb_cDir = rb_define_class("Dir", rb_cObject); rb_cDir = rb_define_class("Dir", rb_cObject);
rb_include_module(rb_cDir, rb_mEnumerable); rb_include_module(rb_cDir, rb_mEnumerable);

View File

@ -104,22 +104,23 @@ class TestDir < Test::Unit::TestCase
assert_raise(ArgumentError) { Dir.chdir } assert_raise(ArgumentError) { Dir.chdir }
ENV["HOME"] = pwd ENV["HOME"] = pwd
Dir.chdir do Dir.chdir do
assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) } conflicting = /conflicting chdir during another chdir block\n^#{Regexp.quote(__FILE__)}:#{__LINE__-1}:/
assert_warning(conflicting) { Dir.chdir(pwd) }
assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(@root) } assert_warning(conflicting) { Dir.chdir(@root) }
assert_equal(@root, Dir.pwd) assert_equal(@root, Dir.pwd)
assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) } assert_warning(conflicting) { Dir.chdir(pwd) }
assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; Dir.chdir(@root) }.join } assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; Dir.chdir(@root) }.join }
assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; Dir.chdir(@root) { } }.join } assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; Dir.chdir(@root) { } }.join }
assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) } assert_warning(conflicting) { Dir.chdir(pwd) }
assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(@root) } assert_warning(conflicting) { Dir.chdir(@root) }
assert_equal(@root, Dir.pwd) assert_equal(@root, Dir.pwd)
assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) } assert_warning(conflicting) { Dir.chdir(pwd) }
Dir.chdir(@root) do Dir.chdir(@root) do
assert_equal(@root, Dir.pwd) assert_equal(@root, Dir.pwd)
end end
@ -142,23 +143,25 @@ class TestDir < Test::Unit::TestCase
ENV["HOME"] = pwd ENV["HOME"] = pwd
ret = root_dir.chdir do |*a| ret = root_dir.chdir do |*a|
conflicting = /conflicting chdir during another chdir block\n^#{Regexp.quote(__FILE__)}:#{__LINE__-1}:/
assert_empty(a) assert_empty(a)
assert_warning(/conflicting chdir during another chdir block/) { dir.chdir } assert_warning(conflicting) { dir.chdir }
assert_warning(/conflicting chdir during another chdir block/) { root_dir.chdir } assert_warning(conflicting) { root_dir.chdir }
assert_equal(@root, Dir.pwd) assert_equal(@root, Dir.pwd)
assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; dir.chdir }.join } assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; dir.chdir }.join }
assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; dir.chdir{} }.join } assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; dir.chdir{} }.join }
assert_warning(/conflicting chdir during another chdir block/) { dir.chdir } assert_warning(conflicting) { dir.chdir }
assert_equal(pwd, Dir.pwd) assert_equal(pwd, Dir.pwd)
assert_warning(/conflicting chdir during another chdir block/) { root_dir.chdir } assert_warning(conflicting) { root_dir.chdir }
assert_equal(@root, Dir.pwd) assert_equal(@root, Dir.pwd)
assert_warning(/conflicting chdir during another chdir block/) { dir.chdir } assert_warning(conflicting) { dir.chdir }
root_dir.chdir do root_dir.chdir do
assert_equal(@root, Dir.pwd) assert_equal(@root, Dir.pwd)