From 8840b033fbf5f33923adbb7a1c38177f56bbe0b2 Mon Sep 17 00:00:00 2001 From: normal Date: Sat, 18 Nov 2017 02:01:49 +0000 Subject: dir.c: openat calls release GVL, too openat(2) also performs a path lookup, so it is also subject to pathological slowdowns like opendir(3) and open(2) syscalls. * dir.c (struct opendir_at_arg): new struct for callback (with_gvl_gc_for_fd): new callback for rb_thread_call_with_gvl (gc_for_fd_with_gvl): moved up (nogvl_opendir_at): extracted from do_opendir (opendir_at): new wrapper to release GVL for opendir_at (do_opendir): use new wrappers to release GVL (nogvl_dir_empty_p): adjust for gc_for_fd_with_gvl git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@60831 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- dir.c | 119 +++++++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 77 insertions(+), 42 deletions(-) diff --git a/dir.c b/dir.c index e044228a5b..04f8d99efe 100644 --- a/dir.c +++ b/dir.c @@ -1404,18 +1404,80 @@ do_lstat(int fd, const char *path, struct stat *pst, int flags, rb_encoding *enc #define do_lstat do_stat #endif -static DIR * -do_opendir(const int basefd, const char *path, int flags, rb_encoding *enc, - ruby_glob_errfunc *errfunc, VALUE arg, int *status) +struct opendir_at_arg { + int basefd; + const char *path; +}; + +static void * +with_gvl_gc_for_fd(void *ptr) { + int *e = ptr; + + return (void *)(rb_gc_for_fd(*e) ? Qtrue : Qfalse); +} + +static int +gc_for_fd_with_gvl(int e) +{ + return (int)(VALUE)rb_thread_call_with_gvl(with_gvl_gc_for_fd, &e); +} + +static void * +nogvl_opendir_at(void *ptr) +{ + const struct opendir_at_arg *oaa = ptr; + DIR *dirp; + #if USE_OPENDIR_AT const int opendir_flags = (O_RDONLY|O_CLOEXEC| -#ifdef O_DIRECTORY +# ifdef O_DIRECTORY O_DIRECTORY| -#endif +# endif /* O_DIRECTORY */ 0); - int fd; -#endif + int fd = openat(oaa->basefd, oaa->path, 0, opendir_flags); + + dirp = fd >= 0 ? fdopendir(fd) : 0; + if (!dirp) { + int e = errno; + + switch (gc_for_fd_with_gvl(e)) { + default: + if (fd < 0) fd = openat(oaa->basefd, oaa->path, 0, opendir_flags); + if (fd >= 0) dirp = fdopendir(fd); + if (dirp) return dirp; + + e = errno; + /* fallthrough*/ + case 0: + if (fd >= 0) close(fd); + errno = e; + } + } +#else /* !USE_OPENDIR_AT */ + dirp = opendir(oaa->path); + if (!dirp && gc_for_fd_with_gvl(errno)) + dirp = opendir(oaa->path); +#endif /* !USE_OPENDIR_AT */ + + return dirp; +} + +static DIR * +opendir_at(int basefd, const char *path) +{ + struct opendir_at_arg oaa; + + oaa.basefd = basefd; + oaa.path = path; + + return rb_thread_call_without_gvl(nogvl_opendir_at, &oaa, RUBY_UBF_IO, 0); +} + +static DIR * +do_opendir(const int basefd, const char *path, int flags, rb_encoding *enc, + ruby_glob_errfunc *errfunc, VALUE arg, int *status) +{ DIR *dirp; #ifdef _WIN32 VALUE tmp = 0; @@ -1425,37 +1487,18 @@ do_opendir(const int basefd, const char *path, int flags, rb_encoding *enc, path = RSTRING_PTR(tmp); } #endif -#if USE_OPENDIR_AT - fd = openat(basefd, path, 0, opendir_flags); - dirp = (fd < 0) ? NULL : fdopendir(fd); -#else - dirp = opendir_without_gvl(path); -#endif + dirp = opendir_at(basefd, path); if (!dirp) { int e = errno; - switch (rb_gc_for_fd(e)) { - default: -#if USE_OPENDIR_AT - if ((fd >= 0) || (fd = openat(basefd, path, 0, opendir_flags)) >= 0) { - dirp = fdopendir(fd); - } -#else - dirp = opendir_without_gvl(path); -#endif - if (dirp) break; - e = errno; - /* fallback */ - case 0: -#if USE_OPENDIR_AT - if (fd >= 0) close(fd); -#endif - *status = 0; - if (to_be_ignored(e)) break; + + *status = 0; + if (!to_be_ignored(e)) { if (errfunc) { *status = (*errfunc)(path, arg, enc, e); - break; } - sys_warning(path, enc); + else { + sys_warning(path, enc); + } } } #ifdef _WIN32 @@ -3040,14 +3083,6 @@ rb_dir_exists_p(VALUE obj, VALUE fname) return rb_file_directory_p(obj, fname); } -static void * -gc_for_fd_with_gvl(void *ptr) -{ - int *e = ptr; - - return (void *)(rb_gc_for_fd(*e) ? Qtrue : Qfalse); -} - static void * nogvl_dir_empty_p(void *ptr) { @@ -3058,7 +3093,7 @@ nogvl_dir_empty_p(void *ptr) if (!dir) { int e = errno; - switch ((int)(VALUE)rb_thread_call_with_gvl(gc_for_fd_with_gvl, &e)) { + switch (gc_for_fd_with_gvl(e)) { default: dir = opendir(path); if (dir) break; -- cgit v1.2.3