ruby-changes:46122
From: nobu <ko1@a...>
Date: Mon, 3 Apr 2017 09:10:55 +0900 (JST)
Subject: [ruby-changes:46122] nobu:r58240 (trunk): Add IO#pread and IO#pwrite methods
nobu 2017-04-03 09:10:50 +0900 (Mon, 03 Apr 2017) New Revision: 58240 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=58240 Log: Add IO#pread and IO#pwrite methods These methods are useful for safe/concurrent file I/O in multi-thread/process environments and also fairly standard nowadays especially in systems supporting pthreads. Based on patches by Avseyev <sergey.avseyev@g...> at [ruby-core:79290]. [Feature #4532] * configure.in: check for pwrite(2). pread() is already used internally for IO.copy_stream. * io.c: implement wrappers for pread(2) and pwrite(2) and expose them in IO. Modified files: trunk/NEWS trunk/configure.in trunk/doc/contributors.rdoc trunk/io.c trunk/test/ruby/test_io.rb Index: configure.in =================================================================== --- configure.in (revision 58239) +++ configure.in (revision 58240) @@ -2427,6 +2427,7 @@ AC_CHECK_FUNCS(posix_fadvise) https://github.com/ruby/ruby/blob/trunk/configure.in#L2427 AC_CHECK_FUNCS(posix_memalign) AC_CHECK_FUNCS(ppoll) AC_CHECK_FUNCS(pread) +AC_CHECK_FUNCS(pwrite) AC_CHECK_FUNCS(qsort_r) AC_CHECK_FUNCS(qsort_s) AC_CHECK_FUNCS(readlink) Index: io.c =================================================================== --- io.c (revision 58239) +++ io.c (revision 58240) @@ -4832,6 +4832,148 @@ rb_io_sysread(int argc, VALUE *argv, VAL https://github.com/ruby/ruby/blob/trunk/io.c#L4832 return str; } +#if defined(HAVE_PREAD) || defined(HAVE_PWRITE) +struct prdwr_internal_arg { + int fd; + void *buf; + size_t count; + off_t offset; +}; +#endif /* HAVE_PREAD || HAVE_PWRITE */ + +#if defined(HAVE_PREAD) +static VALUE +internal_pread_func(void *arg) +{ + struct prdwr_internal_arg *p = arg; + return (VALUE)pread(p->fd, p->buf, p->count, p->offset); +} + +static VALUE +pread_internal_call(VALUE arg) +{ + struct prdwr_internal_arg *p = (struct prdwr_internal_arg *)arg; + return rb_thread_io_blocking_region(internal_pread_func, p, p->fd); +} + +/* + * call-seq: + * ios.pread(maxlen, offset[, outbuf]) -> string + * + * Reads <i>maxlen</i> bytes from <em>ios</em> using the pread system call + * and returns them as a string without modifying the underlying + * descriptor offset. This is advantageous compared to combining IO#seek + * and IO#read in that it is atomic, allowing multiple threads/process to + * share the same IO object for reading the file at various locations. + * This bypasses any userspace buffering of the IO layer. + * If the optional <i>outbuf</i> argument is present, it must + * reference a String, which will receive the data. + * Raises <code>SystemCallError</code> on error, <code>EOFError</code> + * at end of file and <code>NotImplementedError</code> if platform does not + * implement the system call. + * + * f = File.new("testfile") + * f.read #=> "This is line one\nThis is line two\n" + * f.pread(12, 0) #=> "This is line" + * f.pread(9, 8) #=> "line one\n" + */ +static VALUE +rb_io_pread(int argc, VALUE *argv, VALUE io) +{ + VALUE len, offset, str; + rb_io_t *fptr; + ssize_t n; + struct prdwr_internal_arg arg; + + rb_scan_args(argc, argv, "21", &len, &offset, &str); + arg.count = NUM2SIZET(len); + arg.offset = NUM2OFFT(offset); + + io_setstrbuf(&str, (long)arg.count); + if (arg.count == 0) return str; + arg.buf = RSTRING_PTR(str); + + GetOpenFile(io, fptr); + rb_io_check_byte_readable(fptr); + + arg.fd = fptr->fd; + rb_io_check_closed(fptr); + + rb_str_locktmp(str); + n = (ssize_t)rb_ensure(pread_internal_call, (VALUE)&arg, rb_str_unlocktmp, str); + + if (n == -1) { + rb_sys_fail_path(fptr->pathv); + } + io_set_read_length(str, n); + if (n == 0 && arg.count > 0) { + rb_eof_error(); + } + OBJ_TAINT(str); + + return str; +} +#else +# define rb_io_pread rb_f_notimplement +#endif /* HAVE_PREAD */ + +#if defined(HAVE_PWRITE) +static VALUE +internal_pwrite_func(void *ptr) +{ + struct prdwr_internal_arg *arg = ptr; + + return (VALUE)pwrite(arg->fd, arg->buf, arg->count, arg->offset); +} + +/* + * call-seq: + * ios.pwrite(string, offset) -> integer + * + * Writes the given string to <em>ios</em> at <i>offset</i> using pwrite() + * system call. This is advantageous to combining IO#seek and IO#write + * in that it is atomic, allowing multiple threads/process to share the + * same IO object for reading the file at various locations. + * This bypasses any userspace buffering of the IO layer. + * Returns the number of bytes written. + * Raises <code>SystemCallError</code> on error and <code>NotImplementedError</code> + * if platform does not implement the system call. + * + * f = File.new("out", "w") + * f.pwrite("ABCDEF", 3) #=> 6 + * + * File.read("out") #=> "\u0000\u0000\u0000ABCDEF" + */ +static VALUE +rb_io_pwrite(VALUE io, VALUE offset, VALUE str) +{ + rb_io_t *fptr; + ssize_t n; + struct prdwr_internal_arg arg; + + if (!RB_TYPE_P(str, T_STRING)) + str = rb_obj_as_string(str); + + arg.buf = RSTRING_PTR(str); + arg.count = (size_t)RSTRING_LEN(str); + arg.offset = NUM2OFFT(offset); + + io = GetWriteIO(io); + GetOpenFile(io, fptr); + rb_io_check_writable(fptr); + arg.fd = fptr->fd; + + n = (ssize_t)rb_thread_io_blocking_region(internal_pwrite_func, &arg, fptr->fd); + RB_GC_GUARD(str); + + if (n == -1) rb_sys_fail_path(fptr->pathv); + + return SSIZET2NUM(n); +} +#else +# define rb_io_pwrite rb_f_notimplement +#endif /* HAVE_PWRITE */ + VALUE rb_io_binmode(VALUE io) { @@ -12464,6 +12606,9 @@ Init_IO(void) https://github.com/ruby/ruby/blob/trunk/io.c#L12606 rb_define_method(rb_cIO, "syswrite", rb_io_syswrite, 1); rb_define_method(rb_cIO, "sysread", rb_io_sysread, -1); + rb_define_method(rb_cIO, "pread", rb_io_pread, -1); + rb_define_method(rb_cIO, "pwrite", rb_io_pwrite, 2); + rb_define_method(rb_cIO, "fileno", rb_io_fileno, 0); rb_define_alias(rb_cIO, "to_i", "fileno"); rb_define_method(rb_cIO, "to_io", rb_io_to_io, 0); Index: NEWS =================================================================== --- NEWS (revision 58239) +++ NEWS (revision 58240) @@ -24,6 +24,11 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L24 * Integer.sqrt [Feature #13219] +* IO + + * IO#pread [Feature #4532] + * IO#pwrite [Feature #4532] + * Regexp * Update Onigmo 6.1.1. * Support absent operator https://github.com/k-takata/Onigmo/issues/82 Index: doc/contributors.rdoc =================================================================== --- doc/contributors.rdoc (revision 58239) +++ doc/contributors.rdoc (revision 58240) @@ -37,6 +37,9 @@ arton https://github.com/ruby/ruby/blob/trunk/doc/contributors.rdoc#L37 * He is the distributor of ActiveScriptRuby and experimental 1.9.0-x installers for win32. * Wrote patches for win32ole, gc.c, tmpdir.rb +Sergey Avseyev +* Added IO#pread and IO#pwrite. + == B Daniel Berger Index: test/ruby/test_io.rb =================================================================== --- test/ruby/test_io.rb (revision 58239) +++ test/ruby/test_io.rb (revision 58240) @@ -3525,5 +3525,26 @@ __END__ https://github.com/ruby/ruby/blob/trunk/test/ruby/test_io.rb#L3525 end end end + + def test_pread + make_tempfile { |t| + open(t.path) do |f| + assert_equal("bar", f.pread(3, 4)) + buf = "asdf" + assert_equal("bar", f.pread(3, 4, buf)) + assert_equal("bar", buf) + assert_raise(EOFError) { f.pread(1, f.size) } + end + } + end if IO.method_defined?(:pread) + + def test_pwrite + make_tempfile { |t| + open(t.path, IO::RDWR) do |f| + assert_equal(3, f.pwrite(4, "ooo")) + assert_equal("ooo", f.pread(3, 4)) + end + } + end if IO.method_defined?(:pread) and IO.method_defined?(:pwrite) end end -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/