aboutsummaryrefslogtreecommitdiffstats
path: root/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'dir.c')
-rw-r--r--dir.c72
1 files changed, 48 insertions, 24 deletions
diff --git a/dir.c b/dir.c
index b7afaec4e0..d7e5d82e9f 100644
--- a/dir.c
+++ b/dir.c
@@ -13,6 +13,7 @@
#include "internal.h"
#include "encindex.h"
+#include "ruby/thread.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -722,6 +723,8 @@ fundamental_encoding_p(rb_encoding *enc)
#else
# define READDIR(dir, enc) readdir((dir))
#endif
+
+/* safe to use without GVL */
static int
to_be_skipped(const struct dirent *dp)
{
@@ -2982,6 +2985,46 @@ 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)
+{
+ const char *path = ptr;
+ DIR *dir = opendir(path);
+ struct dirent *dp;
+ VALUE result = Qtrue;
+
+ if (!dir) {
+ int e = errno;
+ switch ((int)(VALUE)rb_thread_call_with_gvl(gc_for_fd_with_gvl, &e)) {
+ default:
+ dir = opendir(path);
+ if (dir) break;
+ e = errno;
+ /* fall through */
+ case 0:
+ if (e == ENOTDIR) return (void *)Qfalse;
+ errno = e; /* for rb_sys_fail_path */
+ return (void *)Qundef;
+ }
+ }
+ while ((dp = READDIR(dir, NULL)) != NULL) {
+ if (!to_be_skipped(dp)) {
+ result = Qfalse;
+ break;
+ }
+ }
+ closedir(dir);
+ return (void *)result;
+}
+
/*
* call-seq:
* Dir.empty?(path_name) -> true or false
@@ -2992,9 +3035,7 @@ rb_dir_exists_p(VALUE obj, VALUE fname)
static VALUE
rb_dir_s_empty_p(VALUE obj, VALUE dirname)
{
- DIR *dir;
- struct dirent *dp;
- VALUE result = Qtrue, orig;
+ VALUE result, orig;
const char *path;
enum {false_on_notdir = 1};
@@ -3023,28 +3064,11 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname)
}
#endif
- dir = opendir(path);
- if (!dir) {
- int e = errno;
- switch (rb_gc_for_fd(e)) {
- default:
- dir = opendir(path);
- if (dir) break;
- e = errno;
- /* fall through */
- case 0:
- if (false_on_notdir && e == ENOTDIR) return Qfalse;
- rb_syserr_fail_path(e, orig);
- }
- }
- errno = 0;
- while ((dp = READDIR(dir, NULL)) != NULL) {
- if (!to_be_skipped(dp)) {
- result = Qfalse;
- break;
- }
+ result = (VALUE)rb_thread_call_without_gvl(nogvl_dir_empty_p, (void *)path,
+ RUBY_UBF_IO, 0);
+ if (result == Qundef) {
+ rb_sys_fail_path(orig);
}
- closedir(dir);
return result;
}