Feature #4038 » io-advise-5.patch
| configure.in | ||
|---|---|---|
| 
     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\ 
   | 
||
| io.c | ||
|---|---|---|
| 
     /********************************************************************** 
   | 
||
| 
       io.c - 
   | 
||
| ... | ... | |
| 
     #define rb_io_fdatasync rb_f_notimplement 
   | 
||
| 
     #endif 
   | 
||
| 
     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 sym_normal,   sym_sequential, sym_random, 
   | 
||
| 
                  sym_willneed, sym_dontneed, sym_noreuse; 
   | 
||
| 
     /* 
   | 
||
| 
      *  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 initadvice, initoffset, initlen; 
   | 
||
| 
       rb_io_t *fptr; 
   | 
||
| 
       struct io_advise_struct ias; 
   | 
||
| 
       rb_scan_args(argc, argv, "12", &initadvice, &initoffset, &initlen); 
   | 
||
| 
       if (TYPE(initadvice) != T_SYMBOL) 
   | 
||
| 
           rb_raise(rb_eTypeError, "advice must be a Symbol"); 
   | 
||
| 
       ias.offset = NIL_P(initoffset) ? 0 : NUM2OFFT(initoffset); 
   | 
||
| 
       ias.len    = NIL_P(initlen)    ? 0 : NUM2OFFT(initlen); 
   | 
||
| 
       if      (initadvice == sym_normal)     { 
   | 
||
| 
     #ifdef POSIX_FADV_NORMAL 
   | 
||
| 
           ias.advice = POSIX_FADV_NORMAL; 
   | 
||
| 
     #endif 
   | 
||
| 
       } 
   | 
||
| 
       else if (initadvice == sym_random)     { 
   | 
||
| 
     #ifdef POSIX_FADV_RANDOM 
   | 
||
| 
           ias.advice = POSIX_FADV_RANDOM; 
   | 
||
| 
     #endif 
   | 
||
| 
       } 
   | 
||
| 
       else if (initadvice == sym_sequential) { 
   | 
||
| 
     #ifdef POSIX_FADV_SEQUENTIAL 
   | 
||
| 
           ias.advice = POSIX_FADV_SEQUENTIAL; 
   | 
||
| 
     #endif 
   | 
||
| 
       } 
   | 
||
| 
       else if (initadvice == sym_willneed)   { 
   | 
||
| 
     #ifdef POSIX_FADV_WILLNEED 
   | 
||
| 
           ias.advice = POSIX_FADV_WILLNEED; 
   | 
||
| 
     #endif 
   | 
||
| 
       } 
   | 
||
| 
       else if (initadvice == sym_dontneed)   { 
   | 
||
| 
     #ifdef POSIX_FADV_DONTNEED 
   | 
||
| 
           ias.advice = POSIX_FADV_DONTNEED; 
   | 
||
| 
     #endif 
   | 
||
| 
       } 
   | 
||
| 
       else if (initadvice == sym_noreuse)    { 
   | 
||
| 
     #ifdef POSIX_FADV_NOREUSE 
   | 
||
| 
           ias.advice = POSIX_FADV_NOREUSE; 
   | 
||
| 
     #endif 
   | 
||
| 
       } 
   | 
||
| 
       else 
   | 
||
| 
           rb_raise(rb_eArgError, "Invalid advice: :%s", 
   | 
||
| 
     	       RSTRING_PTR(rb_id2str(SYM2ID(initadvice)))); 
   | 
||
| 
     #ifdef HAVE_POSIX_FADVISE 
   | 
||
| 
       io = GetWriteIO(io); 
   | 
||
| 
       GetOpenFile(io, fptr); 
   | 
||
| 
       ias.fd = fptr->fd; 
   | 
||
| 
       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)); 
   | 
||
| 
     #endif 
   | 
||
| 
       return Qnil; 
   | 
||
| 
     } 
   | 
||
| 
     /* 
   | 
||
| 
      *  call-seq: 
   | 
||
| 
      *     ios.fileno    -> fixnum 
   | 
||
| ... | ... | |
| 
         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); 
   | 
||
| ... | ... | |
| 
         sym_textmode = ID2SYM(rb_intern("textmode")); 
   | 
||
| 
         sym_binmode = ID2SYM(rb_intern("binmode")); 
   | 
||
| 
         sym_autoclose = ID2SYM(rb_intern("autoclose")); 
   | 
||
| 
         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")); 
   | 
||
| 
     } 
   | 
||
| test/ruby/test_io.rb | ||
|---|---|---|
| 
           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 |a| 
   | 
||
| 
           [[0,0], [0, 20], [400, 2]].each do |l, o| 
   | 
||
| 
             open(make_tempfile.path) do |t| 
   | 
||
| 
               assert_equal(t.advise(a, l, o), nil) 
   | 
||
| 
               assert_raise(ArgumentError, "superfluous arguments") do 
   | 
||
| 
                 t.advise(a, l, o, l) 
   | 
||
| 
               end 
   | 
||
| 
               assert_raise(TypeError, "wrong type for first argument") do 
   | 
||
| 
                 t.advise(a.to_s, l, o) 
   | 
||
| 
               end 
   | 
||
| 
               assert_raise(TypeError, "wrong type for last argument") do 
   | 
||
| 
                 t.advise(a, l, Array(o)) 
   | 
||
| 
               end 
   | 
||
| 
               assert_raise(RangeError, "last argument too big") do 
   | 
||
| 
                 t.advise(a, l, 9999e99) 
   | 
||
| 
               end 
   | 
||
| 
             end 
   | 
||
| 
             assert_raise(IOError, "closed file") do  
   | 
||
| 
               make_tempfile.advise(a.to_sym, l, o) 
   | 
||
| 
             end 
   | 
||
| 
           end 
   | 
||
| 
         end 
   | 
||
| 
         %w{Normal rand glark will_need zzzzzzzzzzzz \u2609}.map(&:to_sym).each do |a| 
   | 
||
| 
           [[0,0], [0, 20], [400, 2]].each do |l, o| 
   | 
||
| 
             open(make_tempfile.path) do |t| 
   | 
||
| 
               assert_raise(ArgumentError, "invalid advice") { t.advise(a, l, o) } 
   | 
||
| 
             end 
   | 
||
| 
           end 
   | 
||
| 
         end 
   | 
||
| 
       end 
   | 
||
| 
     end 
   | 
||