ruby-changes:48715
From: normal <ko1@a...>
Date: Sat, 18 Nov 2017 11:01:54 +0900 (JST)
Subject: [ruby-changes:48715] normal:r60831 (trunk): dir.c: openat calls release GVL, too
normal 2017-11-18 11:01:49 +0900 (Sat, 18 Nov 2017) New Revision: 60831 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=60831 Log: 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 Modified files: trunk/dir.c Index: dir.c =================================================================== --- dir.c (revision 60830) +++ dir.c (revision 60831) @@ -1404,18 +1404,80 @@ do_lstat(int fd, const char *path, struc https://github.com/ruby/ruby/blob/trunk/dir.c#L1404 #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 https://github.com/ruby/ruby/blob/trunk/dir.c#L1487 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) https://github.com/ruby/ruby/blob/trunk/dir.c#L3084 } 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) https://github.com/ruby/ruby/blob/trunk/dir.c#L3093 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; -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/