Project

General

Profile

Feature #4038 ยป io-advise-5.patch

runpaint (Run Paint Run Run), 11/13/2010 09:07 AM

View differences:

configure.in
1291 1291
fi
1292 1292
AC_CHECK_FUNCS(fmod killpg wait4 waitpid fork spawnv syscall chroot getcwd eaccess\
1293 1293
	      truncate ftruncate chsize times utimes utimensat fcntl lockf lstat\
1294
	      link symlink readlink readdir_r fsync fdatasync fchown\
1294
	      link symlink readlink readdir_r fsync fdatasync fchown posix_fadvise\
1295 1295
	      setitimer setruid seteuid setreuid setresuid setproctitle socketpair\
1296 1296
	      setrgid setegid setregid setresgid issetugid pause lchown lchmod\
1297 1297
	      getpgrp setpgrp getpgid setpgid initgroups getgroups setgroups\
io.c
1

  
1 2
/**********************************************************************
2 3

  
3 4
  io.c -
......
1429 1430
#define rb_io_fdatasync rb_f_notimplement
1430 1431
#endif
1431 1432

  
1433
struct io_advise_struct {
1434
    int fd;
1435
    off_t offset;
1436
    off_t len;
1437
    int advice;
1438
};
1439

  
1440
static VALUE
1441
io_advise_internal(void *arg)
1442
{
1443
    struct io_advise_struct *ptr = arg;
1444
    return posix_fadvise(ptr->fd, ptr->offset, ptr->len, ptr->advice);
1445
}
1446

  
1447
static VALUE sym_normal,   sym_sequential, sym_random,
1448
             sym_willneed, sym_dontneed, sym_noreuse;
1449
/*
1450
 *  call-seq:
1451
 *     ios.advise(advice, offset=0, len=0) -> nil
1452
 *
1453
 *  Announce an intention to access data from the current file in a
1454
 *  specific pattern. On platforms that do not support the
1455
 *  <em>posix_fadvise(2)</em> system call, this method is a no-op.
1456
 *
1457
 * _advice_ is one of the following symbols:
1458
 *
1459
 *  * :normal - No advice to give; the default assumption for an open file.
1460
 *  * :sequential - The data will be accessed sequentially:
1461
 *     with lower offsets read before higher ones.
1462
 *  * :random - The data will be accessed in random order.
1463
 *  * :willneed - The data will be accessed in the near future.
1464
 *  * :dontneed - The data will not be accessed in the near future.
1465
 *  * :noreuse - The data will only be accessed once.
1466
 *
1467
 * The semantics of a piece of advice are platform-dependent. See
1468
 * <em>man 2 posix_fadvise</em> for details.
1469
 *
1470
 *  "data" means the region of the current file that begins at
1471
 *  _offset_ and extends for _len_ bytes. If _len_ is 0, the region
1472
 *  ends at the last byte of the file. By default, both _offset_ and
1473
 *  _len_ are 0, meaning that the advice applies to the entire file.
1474
 *
1475
 *  If an error occurs, one of the following exceptions will be raised:
1476
 *
1477
 *  * <code>IOError</code> - The <code>IO</code> stream is closed.
1478
 *  * <code>Errno::EBADF</code> - The file descriptor of the current file is
1479
      invalid.
1480
 *  * <code>Errno::EINVAL</code> - An invalid value for _advice_ was given.
1481
 *  * <code>Errno::ESPIPE</code> - The file descriptor of the current
1482
 *  * file refers to a FIFO or pipe. (Linux raises <code>Errno::EINVAL</code>
1483
 *  * in this case).
1484
 *  * <code>TypeError</code> - Either _advice_ was not a Symbol, or one of the
1485
      other arguments was not an <code>Integer</code>.
1486
 *  * <code>RangeError</code> - One of the arguments given was too big/small.
1487
 *
1488
 * This list is not exhaustive; other Errno:: exceptions are also possible.
1489
 */
1490

  
1491
static VALUE
1492
rb_io_advise(int argc, VALUE *argv, VALUE io)
1493
{
1494
  int rv;
1495
  VALUE initadvice, initoffset, initlen;
1496
  rb_io_t *fptr;
1497
  struct io_advise_struct ias;
1498

  
1499
  rb_scan_args(argc, argv, "12", &initadvice, &initoffset, &initlen);
1500
  if (TYPE(initadvice) != T_SYMBOL)
1501
      rb_raise(rb_eTypeError, "advice must be a Symbol");
1502

  
1503
  ias.offset = NIL_P(initoffset) ? 0 : NUM2OFFT(initoffset);
1504
  ias.len    = NIL_P(initlen)    ? 0 : NUM2OFFT(initlen);
1505

  
1506
  if      (initadvice == sym_normal)     {
1507
#ifdef POSIX_FADV_NORMAL
1508
      ias.advice = POSIX_FADV_NORMAL;
1509
#endif
1510
  }
1511
  else if (initadvice == sym_random)     {
1512
#ifdef POSIX_FADV_RANDOM
1513
      ias.advice = POSIX_FADV_RANDOM;
1514
#endif
1515
  }
1516
  else if (initadvice == sym_sequential) {
1517
#ifdef POSIX_FADV_SEQUENTIAL
1518
      ias.advice = POSIX_FADV_SEQUENTIAL;
1519
#endif
1520
  }
1521
  else if (initadvice == sym_willneed)   {
1522
#ifdef POSIX_FADV_WILLNEED
1523
      ias.advice = POSIX_FADV_WILLNEED;
1524
#endif
1525
  }
1526
  else if (initadvice == sym_dontneed)   {
1527
#ifdef POSIX_FADV_DONTNEED
1528
      ias.advice = POSIX_FADV_DONTNEED;
1529
#endif
1530
  }
1531
  else if (initadvice == sym_noreuse)    {
1532
#ifdef POSIX_FADV_NOREUSE
1533
      ias.advice = POSIX_FADV_NOREUSE;
1534
#endif
1535
  }
1536
  else
1537
      rb_raise(rb_eArgError, "Invalid advice: :%s",
1538
	       RSTRING_PTR(rb_id2str(SYM2ID(initadvice))));
1539

  
1540
#ifdef HAVE_POSIX_FADVISE
1541
  io = GetWriteIO(io);
1542
  GetOpenFile(io, fptr);
1543
  ias.fd = fptr->fd;
1544

  
1545
  if (rv = (int)rb_thread_blocking_region(io_advise_internal, &ias, RUBY_UBF_IO, 0))
1546
      /* posix_fadvise(2) doesn't set errno. On success it returns 0; otherwise
1547
	 it returns the error code. */
1548
      rb_syserr_fail(rv, RSTRING_PTR(fptr->pathv));
1549
#endif
1550
  return Qnil;
1551
}
1552

  
1432 1553
/*
1433 1554
 *  call-seq:
1434 1555
 *     ios.fileno    -> fixnum
......
10126 10247
    rb_define_method(rb_cIO, "binmode",  rb_io_binmode_m, 0);
10127 10248
    rb_define_method(rb_cIO, "binmode?", rb_io_binmode_p, 0);
10128 10249
    rb_define_method(rb_cIO, "sysseek", rb_io_sysseek, -1);
10250
    rb_define_method(rb_cIO, "advise", rb_io_advise, -1);
10129 10251

  
10130 10252
    rb_define_method(rb_cIO, "ioctl", rb_io_ioctl, -1);
10131 10253
    rb_define_method(rb_cIO, "fcntl", rb_io_fcntl, -1);
......
10302 10424
    sym_textmode = ID2SYM(rb_intern("textmode"));
10303 10425
    sym_binmode = ID2SYM(rb_intern("binmode"));
10304 10426
    sym_autoclose = ID2SYM(rb_intern("autoclose"));
10427
    sym_normal = ID2SYM(rb_intern("normal"));
10428
    sym_sequential = ID2SYM(rb_intern("sequential"));
10429
    sym_random = ID2SYM(rb_intern("random"));
10430
    sym_willneed = ID2SYM(rb_intern("willneed"));
10431
    sym_dontneed = ID2SYM(rb_intern("dontneed"));
10432
    sym_noreuse = ID2SYM(rb_intern("noreuse"));
10305 10433
}
test/ruby/test_io.rb
1722 1722
      end
1723 1723
    end
1724 1724
  end
1725

  
1726
  def test_advise
1727
    t = make_tempfile
1728
    assert_raise(ArgumentError, "no arguments") { t.advise }
1729
    %w{normal random sequential willneed dontneed noreuse}.map(&:to_sym).each do |a|
1730
      [[0,0], [0, 20], [400, 2]].each do |l, o|
1731
        open(make_tempfile.path) do |t|
1732
          assert_equal(t.advise(a, l, o), nil)
1733
          assert_raise(ArgumentError, "superfluous arguments") do
1734
            t.advise(a, l, o, l)
1735
          end
1736
          assert_raise(TypeError, "wrong type for first argument") do
1737
            t.advise(a.to_s, l, o)
1738
          end
1739
          assert_raise(TypeError, "wrong type for last argument") do
1740
            t.advise(a, l, Array(o))
1741
          end
1742
          assert_raise(RangeError, "last argument too big") do
1743
            t.advise(a, l, 9999e99)
1744
          end
1745
        end
1746
        assert_raise(IOError, "closed file") do 
1747
          make_tempfile.advise(a.to_sym, l, o)
1748
        end
1749
      end
1750
    end
1751
    %w{Normal rand glark will_need zzzzzzzzzzzz \u2609}.map(&:to_sym).each do |a|
1752
      [[0,0], [0, 20], [400, 2]].each do |l, o|
1753
        open(make_tempfile.path) do |t|
1754
          assert_raise(ArgumentError, "invalid advice") { t.advise(a, l, o) }
1755
        end
1756
      end
1757
    end
1758
  end
1725 1759
end