From cc9b886691bc4d2f9b81edc5583c929fda7ea769 Mon Sep 17 00:00:00 2001 From: kosaki Date: Thu, 16 Dec 2010 15:01:55 +0000 Subject: [PATCH] * 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 --- ChangeLog | 11 +++ configure.in | 2 +- io.c | 161 +++++++++++++++++++++++++++++++++++++++++++ test/ruby/test_io.rb | 34 +++++++++ 4 files changed, 207 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index b98df5ae13..7e787c7a34 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +Fri Dec 17 00:05:40 2010 KOSAKI Motohiro + + * 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 * tool/rbinstall.rb (bin-comm): use transformed name. diff --git a/configure.in b/configure.in index 9dacccd369..63a88bc3f7 100644 --- a/configure.in +++ b/configure.in @@ -1291,7 +1291,7 @@ else fi AC_CHECK_FUNCS(fmod killpg wait4 waitpid fork spawnv syscall chroot getcwd eaccess\ 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\ setrgid setegid setregid setresgid issetugid pause lchown lchmod\ getpgrp setpgrp getpgid setpgid initgroups getgroups setgroups\ diff --git a/io.c b/io.c index fed5a4be24..9825dc01ec 100644 --- a/io.c +++ b/io.c @@ -1,3 +1,4 @@ + /********************************************************************** io.c - @@ -7389,6 +7390,157 @@ select_end(VALUE arg) } #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 + * posix_fadvise(2) 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 + * man 2 posix_fadvise 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: + * + * * IOError - The IO stream is closed. + * * Errno::EBADF - The file descriptor of the current file is + invalid. + * * Errno::EINVAL - An invalid value for _advice_ was given. + * * Errno::ESPIPE - The file descriptor of the current + * * file refers to a FIFO or pipe. (Linux raises Errno::EINVAL + * * in this case). + * * TypeError - Either _advice_ was not a Symbol, or one of the + other arguments was not an Integer. + * * RangeError - 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: * 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_p, 0); 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, "fcntl", rb_io_fcntl, -1); @@ -10367,4 +10520,12 @@ Init_IO(void) sym_textmode = ID2SYM(rb_intern("textmode")); sym_binmode = ID2SYM(rb_intern("binmode")); 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 } diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 75263e9cc5..b3d543fc2f 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -1737,4 +1737,38 @@ 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