dir.c: release GVL around remaining readdir calls

Release GVL around all remaining readdir calls from the Dir
class to prevent pathological stalls on slow filesystems in
multi-threaded applications.

opendir, rewinddir, closedir calls are not affected yet, but
will be changed in future commits.

In the future, further work may be done consolidate multiple GVL
releasing calls to reduce overhead, similar to how changes to
Dir.empty? were made in r60111

* dir.c (nogvl_readdir): new function
  (readdir_without_gvl): ditto
  (dir_read): s/READDIR/readdir_without_gvl/
  (dir_each_entry): ditto
  (glob_helper): ditto

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@60769 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
normal 2017-11-14 23:20:31 +00:00
Родитель c00265bbcc
Коммит ea3211720d
1 изменённых файлов: 27 добавлений и 11 удалений

38
dir.c
Просмотреть файл

@ -743,6 +743,20 @@ to_be_skipped(const struct dirent *dp)
return FALSE;
}
static void *
nogvl_readdir(void *ptr)
{
struct dir_data *dirp = ptr;
return READDIR(dirp->dir, dirp->enc);
}
static struct dirent *
readdir_without_gvl(struct dir_data *dirp)
{
return rb_thread_call_without_gvl(nogvl_readdir, dirp, RUBY_UBF_IO, 0);
}
/*
* call-seq:
* dir.read -> string or nil
@ -763,7 +777,7 @@ dir_read(VALUE dir)
GetDIR(dir, dirp);
errno = 0;
if ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
if ((dp = readdir_without_gvl(dirp))) {
return rb_external_str_new_with_enc(dp->d_name, NAMLEN(dp), dirp->enc);
}
else {
@ -818,7 +832,7 @@ dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE), VALUE arg, int children_o
GetDIR(dir, dirp);
rewinddir(dirp->dir);
IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp->dir, RSTRING_PTR(dirp->path)));
while ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
while ((dp = readdir_without_gvl(dirp)) != NULL) {
const char *name = dp->d_name;
size_t namlen = NAMLEN(dp);
VALUE path;
@ -2006,25 +2020,27 @@ glob_helper(
if (pathtype == path_noent) return 0;
if (magical || recursive) {
struct dir_data dd;
struct dirent *dp;
DIR *dirp;
# if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
char *plainname = 0;
# endif
IF_NORMALIZE_UTF8PATH(int norm_p);
dd.enc = enc;
# if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
if (cur + 1 == end && (*cur)->type <= ALPHA) {
plainname = join_path(path, pathlen, dirsep, (*cur)->str, strlen((*cur)->str));
if (!plainname) return -1;
dirp = do_opendir(fd, plainname, flags, enc, funcs->error, arg, &status);
dd.dir = do_opendir(fd, plainname, flags, enc, funcs->error, arg, &status);
GLOB_FREE(plainname);
}
else
# else
;
# endif
dirp = do_opendir(fd, *base ? base : ".", flags, enc, funcs->error, arg, &status);
if (dirp == NULL) {
dd.dir = do_opendir(fd, *base ? base : ".", flags, enc, funcs->error, arg, &status);
if (dd.dir == NULL) {
# if FNM_SYSCASE || NORMALIZE_UTF8PATH
if ((magical < 2) && !recursive && (errno == EACCES)) {
/* no read permission, fallback */
@ -2033,19 +2049,19 @@ glob_helper(
# endif
return status;
}
IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp, *base ? base : "."));
IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dd.dir, *base ? base : "."));
# if NORMALIZE_UTF8PATH
if (!(norm_p || magical || recursive)) {
closedir(dirp);
closedir(dd.dir);
goto literally;
}
# endif
# ifdef HAVE_GETATTRLIST
if (is_case_sensitive(dirp, path) == 0)
if (is_case_sensitive(dd.dir, path) == 0)
flags |= FNM_CASEFOLD;
# endif
while ((dp = READDIR(dirp, enc)) != NULL) {
while ((dp = readdir_without_gvl(&dd)) != NULL) {
char *buf;
rb_pathtype_t new_pathtype = path_unknown;
const char *name;
@ -2140,7 +2156,7 @@ glob_helper(
if (status) break;
}
closedir(dirp);
closedir(dd.dir);
}
else if (plain) {
struct glob_pattern **copy_beg, **copy_end, **cur2;