mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Dir.empty? releases GVL
This converts all slow syscalls in the Dir.empty? implementation to release GVL. We avoid unnecessarily GVL release and reacquire for each slow call (opendir, readdir, closedir) and instead only release and acquire the GVL once in the common case. Benchmark results show a small degradation in single-threaded performance: Execution time (sec) name trunk built dir_empty_p 0.689 0.758 Speedup ratio: compare with the result of `trunk' (greater is better) name built dir_empty_p 0.909 * dir.c (rb_gc_for_fd_with_gvl): new function (nogvl_dir_empty_p): ditto (dir_s_empty_p): use new functions to release GVL * benchmark/bm_dir_empty_p.rb: new benchmark [ruby-core:83071] [Feature #13958] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@60111 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
fbb3432736
commit
1e14126b4f
2 changed files with 53 additions and 24 deletions
5
benchmark/bm_dir_empty_p.rb
Normal file
5
benchmark/bm_dir_empty_p.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
require 'tmpdir'
|
||||||
|
max = 100_000
|
||||||
|
Dir.mktmpdir('bm_dir_empty_p') do |dir|
|
||||||
|
max.times { Dir.empty?(dir) }
|
||||||
|
end
|
72
dir.c
72
dir.c
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "encindex.h"
|
#include "encindex.h"
|
||||||
|
#include "ruby/thread.h"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
@ -722,6 +723,8 @@ fundamental_encoding_p(rb_encoding *enc)
|
||||||
#else
|
#else
|
||||||
# define READDIR(dir, enc) readdir((dir))
|
# define READDIR(dir, enc) readdir((dir))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* safe to use without GVL */
|
||||||
static int
|
static int
|
||||||
to_be_skipped(const struct dirent *dp)
|
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);
|
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:
|
* call-seq:
|
||||||
* Dir.empty?(path_name) -> true or false
|
* Dir.empty?(path_name) -> true or false
|
||||||
|
@ -2992,9 +3035,7 @@ rb_dir_exists_p(VALUE obj, VALUE fname)
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_dir_s_empty_p(VALUE obj, VALUE dirname)
|
rb_dir_s_empty_p(VALUE obj, VALUE dirname)
|
||||||
{
|
{
|
||||||
DIR *dir;
|
VALUE result, orig;
|
||||||
struct dirent *dp;
|
|
||||||
VALUE result = Qtrue, orig;
|
|
||||||
const char *path;
|
const char *path;
|
||||||
enum {false_on_notdir = 1};
|
enum {false_on_notdir = 1};
|
||||||
|
|
||||||
|
@ -3023,28 +3064,11 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dir = opendir(path);
|
result = (VALUE)rb_thread_call_without_gvl(nogvl_dir_empty_p, (void *)path,
|
||||||
if (!dir) {
|
RUBY_UBF_IO, 0);
|
||||||
int e = errno;
|
if (result == Qundef) {
|
||||||
switch (rb_gc_for_fd(e)) {
|
rb_sys_fail_path(orig);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closedir(dir);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue