From 56c4eb8a98957da88ef8efd42c6e39eaf0a1b69d Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 5 Jan 2017 00:49:02 +0000 Subject: copy_file_range: move wrapper to Ruby for keyword arg handling Keyword args allows for a smaller interface for common use, while retaining the capability to use offsets for both input and output. The current (2.4) Ruby C API for keyword args is slow and creates too many garbage objects. As with our splice wrapper, use a pure Ruby wrapper around an internal C function. --- ext/sleepy_penguin/cfr.c | 39 +++++++++++++++++++-------------------- lib/sleepy_penguin.rb | 5 +++-- lib/sleepy_penguin/cfr.rb | 6 ++++++ test/test_cfr.rb | 10 +++++++++- 4 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 lib/sleepy_penguin/cfr.rb diff --git a/ext/sleepy_penguin/cfr.c b/ext/sleepy_penguin/cfr.c index e90d959..acece4f 100644 --- a/ext/sleepy_penguin/cfr.c +++ b/ext/sleepy_penguin/cfr.c @@ -34,38 +34,37 @@ static void *nogvl_cfr(void *ptr) a->fd_out, a->off_out, a->len, a->flags); } -static VALUE rb_cfr(int argc, VALUE *argv, VALUE mod) +/* :nodoc: */ +static VALUE rb_sp_cfr(VALUE mod, VALUE io_in, VALUE off_in, + VALUE io_out, VALUE off_out, + VALUE len, VALUE flags) { - off_t i, o; - VALUE io_in, off_in, io_out, off_out, len, flags; - ssize_t bytes; + off_t i = 0, o = 0; struct copy_args a; - - rb_scan_args(argc, argv, "51", - &io_in, &off_in, &io_out, &off_out, &len, &flags); + ssize_t bytes; a.off_in = NIL_P(off_in) ? NULL : (i = NUM2OFFT(off_in), &i); a.off_out = NIL_P(off_out) ? NULL : (o = NUM2OFFT(off_out), &o); a.len = NUM2SIZET(len); - a.flags = NIL_P(flags) ? 0 : NUM2UINT(flags); + a.flags = NUM2UINT(flags); -again: - a.fd_in = rb_sp_fileno(io_in); - a.fd_out = rb_sp_fileno(io_out); - bytes = (ssize_t)IO_RUN(nogvl_cfr, &a); - if (bytes < 0) { - if (errno == EINTR) - goto again; - rb_sys_fail("copy_file_range"); - } else if (bytes == 0) { - rb_eof_error(); + for (;;) { + a.fd_in = rb_sp_fileno(io_in); + a.fd_out = rb_sp_fileno(io_out); + bytes = (ssize_t)IO_RUN(nogvl_cfr, &a); + if (bytes < 0) { + switch (errno) { + case EINTR: continue; + default: rb_sys_fail("copy_file_range"); + } + } + return SSIZET2NUM(bytes); } - return SSIZET2NUM(bytes); } void sleepy_penguin_init_cfr(void) { VALUE mod = rb_define_module("SleepyPenguin"); - rb_define_singleton_method(mod, "copy_file_range", rb_cfr, -1); + rb_define_singleton_method(mod, "__cfr", rb_sp_cfr, 6); } diff --git a/lib/sleepy_penguin.rb b/lib/sleepy_penguin.rb index 63f293d..07c431b 100644 --- a/lib/sleepy_penguin.rb +++ b/lib/sleepy_penguin.rb @@ -16,6 +16,7 @@ if defined?(SleepyPenguin::Inotify) && end end -if SleepyPenguin.respond_to?(:__splice) || SleepyPenguin.respond_to?(:__tee) - require_relative 'sleepy_penguin/splice' +module SleepyPenguin + require_relative 'sleepy_penguin/splice' if respond_to?(:__splice) + require_relative 'sleepy_penguin/cfr' if respond_to?(:__cfr) end diff --git a/lib/sleepy_penguin/cfr.rb b/lib/sleepy_penguin/cfr.rb new file mode 100644 index 0000000..af94499 --- /dev/null +++ b/lib/sleepy_penguin/cfr.rb @@ -0,0 +1,6 @@ +module SleepyPenguin + def self.copy_file_range(io_in, io_out, len, flags = 0, + off_in: nil, off_out: nil) + __cfr(io_in, off_in, io_out, off_out, len, flags) + end +end diff --git a/test/test_cfr.rb b/test/test_cfr.rb index 3483c5a..0775c0f 100644 --- a/test/test_cfr.rb +++ b/test/test_cfr.rb @@ -13,7 +13,7 @@ class TestCfr < Test::Unit::TestCase assert_equal 5, src.syswrite(str) src.sysseek(0) begin - nr = SleepyPenguin.copy_file_range(src, nil, dst, nil, size, 0) + nr = SleepyPenguin.copy_file_range(src, dst, size) rescue Errno::EINVAL warn 'copy_file_range not supported (requires Linux 4.5+)' warn "We have: #{`uname -a`}" @@ -22,6 +22,14 @@ class TestCfr < Test::Unit::TestCase assert_equal nr, 5 dst.sysseek(0) assert_equal str, dst.sysread(5) + + nr = SleepyPenguin.copy_file_range(src, dst, size, off_in: 1, off_out: 0) + assert_equal 4, nr + dst.sysseek(0) + assert_equal 'bcde', dst.sysread(4) + + nr = SleepyPenguin.copy_file_range(src, dst, size, off_in: 9) + assert_equal 0, nr, 'no EOFError' ensure dst.close! src.close! -- cgit v1.2.3-24-ge0c7