[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);
}
static int chdir_blocking = 0;
static VALUE chdir_thread = Qnil;
static struct {
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 {
VALUE old_path, new_path;
@ -1056,9 +1092,7 @@ chdir_yield(VALUE v)
struct chdir_data *args = (void *)v;
dir_chdir0(args->new_path);
args->done = TRUE;
chdir_blocking++;
if (NIL_P(chdir_thread))
chdir_thread = rb_thread_current();
chdir_enter();
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;
if (args->done) {
chdir_blocking--;
if (chdir_blocking == 0)
chdir_thread = Qnil;
chdir_leave();
dir_chdir0(args->old_path);
}
return Qnil;
@ -1078,14 +1110,7 @@ chdir_restore(VALUE v)
static VALUE
chdir_path(VALUE path, bool yield_path)
{
if (chdir_blocking > 0) {
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()) {
if (chdir_alone_block_p()) {
struct chdir_data args;
args.old_path = rb_str_encode_ospath(rb_dir_getwd());
@ -1215,9 +1240,7 @@ fchdir_yield(VALUE v)
struct fchdir_data *args = (void *)v;
dir_fchdir(args->fd);
args->done = TRUE;
chdir_blocking++;
if (NIL_P(chdir_thread))
chdir_thread = rb_thread_current();
chdir_enter();
return rb_yield_values(0);
}
@ -1226,9 +1249,7 @@ fchdir_restore(VALUE v)
{
struct fchdir_data *args = (void *)v;
if (args->done) {
chdir_blocking--;
if (chdir_blocking == 0)
chdir_thread = Qnil;
chdir_leave();
dir_fchdir(RB_NUM2INT(dir_fileno(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);
if (chdir_blocking > 0) {
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()) {
if (chdir_alone_block_p()) {
struct fchdir_data args;
args.old_dir = dir_s_alloc(klass);
dir_initialize(NULL, args.old_dir, rb_fstring_cstr("."), Qnil);