* io.c (rb_io_advise): New API. IO#advise() allows to tell the

ruby runtime how it expects to use a file handle. This feature
          can be improved a performance some situations.
          Note: This feature is mainly developed by Run Paint Run Run.
          Thank you! [ruby-core:33110] [Ruby 1.9-Feature#4038]

        * io.c (do_io_advise): Helper function.
        * io.c (io_advise_sym_to_const): ditto.



git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@30229 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
kosaki 2010-12-16 15:01:55 +00:00
parent e46e5d2438
commit cc9b886691
4 changed files with 207 additions and 1 deletions

View File

@ -1,3 +1,14 @@
Fri Dec 17 00:05:40 2010 KOSAKI Motohiro <kosaki.motohiro@gmail.com>
* io.c (rb_io_advise): New API. IO#advise() allows to tell the
ruby runtime how it expects to use a file handle. This feature
can be improved a performance some situations.
Note: This feature is mainly developed by Run Paint Run Run.
Thank you! [ruby-core:33110] [Ruby 1.9-Feature#4038]
* io.c (do_io_advise): Helper function.
* io.c (io_advise_sym_to_const): ditto.
Thu Dec 16 23:29:20 2010 Nobuyoshi Nakada <nobu@ruby-lang.org> Thu Dec 16 23:29:20 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
* tool/rbinstall.rb (bin-comm): use transformed name. * tool/rbinstall.rb (bin-comm): use transformed name.

View File

@ -1291,7 +1291,7 @@ else
fi fi
AC_CHECK_FUNCS(fmod killpg wait4 waitpid fork spawnv syscall chroot getcwd eaccess\ AC_CHECK_FUNCS(fmod killpg wait4 waitpid fork spawnv syscall chroot getcwd eaccess\
truncate ftruncate chsize times utimes utimensat fcntl lockf lstat\ truncate ftruncate chsize times utimes utimensat fcntl lockf lstat\
link symlink readlink readdir_r fsync fdatasync fchown\ link symlink readlink readdir_r fsync fdatasync fchown posix_fadvise\
setitimer setruid seteuid setreuid setresuid setproctitle socketpair\ setitimer setruid seteuid setreuid setresuid setproctitle socketpair\
setrgid setegid setregid setresgid issetugid pause lchown lchmod\ setrgid setegid setregid setresgid issetugid pause lchown lchmod\
getpgrp setpgrp getpgid setpgid initgroups getgroups setgroups\ getpgrp setpgrp getpgid setpgid initgroups getgroups setgroups\

161
io.c
View File

@ -1,3 +1,4 @@
/********************************************************************** /**********************************************************************
io.c - io.c -
@ -7389,6 +7390,157 @@ select_end(VALUE arg)
} }
#endif #endif
#ifdef HAVE_POSIX_FADVISE
static VALUE sym_normal, sym_sequential, sym_random,
sym_willneed, sym_dontneed, sym_noreuse;
struct io_advise_struct {
int fd;
off_t offset;
off_t len;
int advice;
};
static VALUE
io_advise_internal(void *arg)
{
struct io_advise_struct *ptr = arg;
return posix_fadvise(ptr->fd, ptr->offset, ptr->len, ptr->advice);
}
static VALUE io_advise_sym_to_const(VALUE sym)
{
#ifdef POSIX_FADV_NORMAL
if (sym == sym_normal)
return INT2NUM(POSIX_FADV_NORMAL);
#endif
#ifdef POSIX_FADV_RANDOM
if (sym == sym_random)
return INT2NUM(POSIX_FADV_RANDOM);
#endif
#ifdef POSIX_FADV_SEQUENTIAL
if (sym == sym_sequential)
return INT2NUM(POSIX_FADV_SEQUENTIAL);
#endif
#ifdef POSIX_FADV_WILLNEED
if (sym == sym_willneed)
return INT2NUM(POSIX_FADV_WILLNEED);
#endif
#ifdef POSIX_FADV_DONTNEED
if (sym == sym_dontneed)
return INT2NUM(POSIX_FADV_DONTNEED);
#endif
#ifdef POSIX_FADV_NOREUSE
if (sym == sym_noreuse)
return INT2NUM(POSIX_FADV_NOREUSE);
#endif
return Qnil;
}
static VALUE
do_io_advise(rb_io_t *fptr, VALUE advice, off_t offset, off_t len)
{
int rv;
struct io_advise_struct ias;
VALUE num_adv;
num_adv = io_advise_sym_to_const(advice);
/*
* The platform doesn't support this hint. We don't raise exception, instead
* silently ignore it. Because IO::advise is only hint.
*/
if (num_adv == Qnil)
return Qnil;
ias.fd = fptr->fd;
ias.advice = NUM2INT(num_adv);
ias.offset = offset;
ias.len = len;
if (rv = (int)rb_thread_blocking_region(io_advise_internal, &ias, RUBY_UBF_IO, 0))
/* posix_fadvise(2) doesn't set errno. On success it returns 0; otherwise
it returns the error code. */
rb_syserr_fail(rv, RSTRING_PTR(fptr->pathv));
return Qnil;
}
#endif /* HAVE_POSIX_FADVISE */
/*
* call-seq:
* ios.advise(advice, offset=0, len=0) -> nil
*
* Announce an intention to access data from the current file in a
* specific pattern. On platforms that do not support the
* <em>posix_fadvise(2)</em> system call, this method is a no-op.
*
* _advice_ is one of the following symbols:
*
* * :normal - No advice to give; the default assumption for an open file.
* * :sequential - The data will be accessed sequentially:
* with lower offsets read before higher ones.
* * :random - The data will be accessed in random order.
* * :willneed - The data will be accessed in the near future.
* * :dontneed - The data will not be accessed in the near future.
* * :noreuse - The data will only be accessed once.
*
* The semantics of a piece of advice are platform-dependent. See
* <em>man 2 posix_fadvise</em> for details.
*
* "data" means the region of the current file that begins at
* _offset_ and extends for _len_ bytes. If _len_ is 0, the region
* ends at the last byte of the file. By default, both _offset_ and
* _len_ are 0, meaning that the advice applies to the entire file.
*
* If an error occurs, one of the following exceptions will be raised:
*
* * <code>IOError</code> - The <code>IO</code> stream is closed.
* * <code>Errno::EBADF</code> - The file descriptor of the current file is
invalid.
* * <code>Errno::EINVAL</code> - An invalid value for _advice_ was given.
* * <code>Errno::ESPIPE</code> - The file descriptor of the current
* * file refers to a FIFO or pipe. (Linux raises <code>Errno::EINVAL</code>
* * in this case).
* * <code>TypeError</code> - Either _advice_ was not a Symbol, or one of the
other arguments was not an <code>Integer</code>.
* * <code>RangeError</code> - One of the arguments given was too big/small.
*
* This list is not exhaustive; other Errno:: exceptions are also possible.
*/
static VALUE
rb_io_advise(int argc, VALUE *argv, VALUE io)
{
int rv;
VALUE advice, offset, len;
off_t off, l;
rb_io_t *fptr;
rb_scan_args(argc, argv, "12", &advice, &offset, &len);
if (TYPE(advice) != T_SYMBOL)
rb_raise(rb_eTypeError, "advice must be a Symbol");
io = GetWriteIO(io);
GetOpenFile(io, fptr);
off = NIL_P(offset) ? 0 : NUM2OFFT(offset);
l = NIL_P(len) ? 0 : NUM2OFFT(len);
#ifdef HAVE_POSIX_FADVISE
return do_io_advise(fptr, advice, off, l);
#else
/* Ignore all hint */
return Qnil;
#endif
}
/* /*
* call-seq: * call-seq:
* IO.select(read_array * IO.select(read_array
@ -10191,6 +10343,7 @@ Init_IO(void)
rb_define_method(rb_cIO, "binmode", rb_io_binmode_m, 0); rb_define_method(rb_cIO, "binmode", rb_io_binmode_m, 0);
rb_define_method(rb_cIO, "binmode?", rb_io_binmode_p, 0); rb_define_method(rb_cIO, "binmode?", rb_io_binmode_p, 0);
rb_define_method(rb_cIO, "sysseek", rb_io_sysseek, -1); rb_define_method(rb_cIO, "sysseek", rb_io_sysseek, -1);
rb_define_method(rb_cIO, "advise", rb_io_advise, -1);
rb_define_method(rb_cIO, "ioctl", rb_io_ioctl, -1); rb_define_method(rb_cIO, "ioctl", rb_io_ioctl, -1);
rb_define_method(rb_cIO, "fcntl", rb_io_fcntl, -1); rb_define_method(rb_cIO, "fcntl", rb_io_fcntl, -1);
@ -10367,4 +10520,12 @@ Init_IO(void)
sym_textmode = ID2SYM(rb_intern("textmode")); sym_textmode = ID2SYM(rb_intern("textmode"));
sym_binmode = ID2SYM(rb_intern("binmode")); sym_binmode = ID2SYM(rb_intern("binmode"));
sym_autoclose = ID2SYM(rb_intern("autoclose")); sym_autoclose = ID2SYM(rb_intern("autoclose"));
#ifdef HAVE_POSIX_FADVISE
sym_normal = ID2SYM(rb_intern("normal"));
sym_sequential = ID2SYM(rb_intern("sequential"));
sym_random = ID2SYM(rb_intern("random"));
sym_willneed = ID2SYM(rb_intern("willneed"));
sym_dontneed = ID2SYM(rb_intern("dontneed"));
sym_noreuse = ID2SYM(rb_intern("noreuse"));
#endif
} }

View File

@ -1737,4 +1737,38 @@ End
end end
end end
end end
def test_advise
t = make_tempfile
assert_raise(ArgumentError, "no arguments") { t.advise }
%w{normal random sequential willneed dontneed noreuse}.map(&:to_sym).each do |adv|
[[0,0], [0, 20], [400, 2]].each do |offset, len|
open(make_tempfile.path) do |t|
assert_equal(t.advise(adv, offset, len), nil)
assert_raise(ArgumentError, "superfluous arguments") do
t.advise(adv, offset, len, offset)
end
assert_raise(TypeError, "wrong type for first argument") do
t.advise(adv.to_s, offset, len)
end
assert_raise(TypeError, "wrong type for last argument") do
t.advise(adv, offset, Array(len))
end
assert_raise(RangeError, "last argument too big") do
t.advise(adv, offset, 9999e99)
end
end
assert_raise(IOError, "closed file") do
make_tempfile.advise(adv.to_sym, offset, len)
end
end
end
%w{Normal rand glark will_need zzzzzzzzzzzz \u2609}.map(&:to_sym).each do |adv|
[[0,0], [0, 20], [400, 2]].each do |offset, len|
open(make_tempfile.path) do |t|
assert_equal(t.advise(adv, offset, len), nil)
end
end
end
end
end end