[Feature #20244] Extract chdir_lock and its stuffs

This commit is contained in:
Nobuyoshi Nakada 2024-02-07 19:43:57 +09:00
parent 1ad366134d
commit 8fe86feecd

74
dir.c
View File

@ -1041,8 +1041,44 @@ dir_chdir0(VALUE path)
rb_sys_fail_path(path); rb_sys_fail_path(path);
} }
static int chdir_blocking = 0; static struct {
static VALUE chdir_thread = Qnil; VALUE thread;
int blocking;
} chdir_lock = {
.blocking = 0, .thread = Qnil,
};
static void
chdir_enter(void)
{
chdir_lock.blocking++;
if (NIL_P(chdir_lock.thread)) {
chdir_lock.thread = rb_thread_current();
}
}
static void
chdir_leave(void)
{
chdir_lock.blocking--;
if (chdir_lock.blocking == 0) {
chdir_lock.thread = Qnil;
}
}
static int
chdir_alone_block_p(void)
{
int block_given = rb_block_given_p();
if (chdir_lock.blocking > 0) {
if (rb_thread_current() != chdir_lock.thread)
rb_raise(rb_eRuntimeError, "conflicting chdir during another chdir block");
if (!block_given) {
rb_warn("conflicting chdir during another chdir block");
}
}
return block_given;
}
struct chdir_data { struct chdir_data {
VALUE old_path, new_path; VALUE old_path, new_path;
@ -1056,9 +1092,7 @@ chdir_yield(VALUE v)
struct chdir_data *args = (void *)v; struct chdir_data *args = (void *)v;
dir_chdir0(args->new_path); dir_chdir0(args->new_path);
args->done = TRUE; args->done = TRUE;
chdir_blocking++; chdir_enter();
if (NIL_P(chdir_thread))
chdir_thread = rb_thread_current();
return args->yield_path ? rb_yield(args->new_path) : rb_yield_values2(0, NULL); return args->yield_path ? rb_yield(args->new_path) : rb_yield_values2(0, NULL);
} }
@ -1067,9 +1101,7 @@ chdir_restore(VALUE v)
{ {
struct chdir_data *args = (void *)v; struct chdir_data *args = (void *)v;
if (args->done) { if (args->done) {
chdir_blocking--; chdir_leave();
if (chdir_blocking == 0)
chdir_thread = Qnil;
dir_chdir0(args->old_path); dir_chdir0(args->old_path);
} }
return Qnil; return Qnil;
@ -1078,14 +1110,7 @@ chdir_restore(VALUE v)
static VALUE static VALUE
chdir_path(VALUE path, bool yield_path) chdir_path(VALUE path, bool yield_path)
{ {
if (chdir_blocking > 0) { if (chdir_alone_block_p()) {
if (rb_thread_current() != chdir_thread)
rb_raise(rb_eRuntimeError, "conflicting chdir during another chdir block");
if (!rb_block_given_p())
rb_warn("conflicting chdir during another chdir block");
}
if (rb_block_given_p()) {
struct chdir_data args; struct chdir_data args;
args.old_path = rb_str_encode_ospath(rb_dir_getwd()); args.old_path = rb_str_encode_ospath(rb_dir_getwd());
@ -1215,9 +1240,7 @@ fchdir_yield(VALUE v)
struct fchdir_data *args = (void *)v; struct fchdir_data *args = (void *)v;
dir_fchdir(args->fd); dir_fchdir(args->fd);
args->done = TRUE; args->done = TRUE;
chdir_blocking++; chdir_enter();
if (NIL_P(chdir_thread))
chdir_thread = rb_thread_current();
return rb_yield_values(0); return rb_yield_values(0);
} }
@ -1226,9 +1249,7 @@ fchdir_restore(VALUE v)
{ {
struct fchdir_data *args = (void *)v; struct fchdir_data *args = (void *)v;
if (args->done) { if (args->done) {
chdir_blocking--; chdir_leave();
if (chdir_blocking == 0)
chdir_thread = Qnil;
dir_fchdir(RB_NUM2INT(dir_fileno(args->old_dir))); dir_fchdir(RB_NUM2INT(dir_fileno(args->old_dir)));
} }
dir_close(args->old_dir); dir_close(args->old_dir);
@ -1292,14 +1313,7 @@ dir_s_fchdir(VALUE klass, VALUE fd_value)
{ {
int fd = RB_NUM2INT(fd_value); int fd = RB_NUM2INT(fd_value);
if (chdir_blocking > 0) { if (chdir_alone_block_p()) {
if (rb_thread_current() != chdir_thread)
rb_raise(rb_eRuntimeError, "conflicting chdir during another chdir block");
if (!rb_block_given_p())
rb_warn("conflicting chdir during another chdir block");
}
if (rb_block_given_p()) {
struct fchdir_data args; struct fchdir_data args;
args.old_dir = dir_s_alloc(klass); args.old_dir = dir_s_alloc(klass);
dir_initialize(NULL, args.old_dir, rb_fstring_cstr("."), Qnil); dir_initialize(NULL, args.old_dir, rb_fstring_cstr("."), Qnil);