[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]