diff options
author | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-11-18 02:01:49 +0000 |
---|---|---|
committer | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-11-18 02:01:49 +0000 |
commit | 8840b033fbf5f33923adbb7a1c38177f56bbe0b2 (patch) | |
tree | d274f659a07be57ad6af316e1b00020a3dcc9f1d /dir.c | |
parent | 089eb0c5cedaa405efe024a4cf5e58faf2bc4a78 (diff) | |
download | ruby-8840b033fbf5f33923adbb7a1c38177f56bbe0b2.tar.gz |
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
Diffstat (limited to 'dir.c')
-rw-r--r-- | dir.c | 119 |
1 files changed, 77 insertions, 42 deletions
@@ -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 @@ -3041,14 +3084,6 @@ rb_dir_exists_p(VALUE obj, VALUE 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) { const char *path = 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; |