From 8109114b18f76d8427f701c3705186632609be01 Mon Sep 17 00:00:00 2001 From: nobu Date: Mon, 3 Apr 2017 00:10:50 +0000 Subject: 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 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58240 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- io.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) (limited to 'io.c') diff --git a/io.c b/io.c index 085d7df2c9..3ac6b97a0e 100644 --- a/io.c +++ b/io.c @@ -4832,6 +4832,148 @@ rb_io_sysread(int argc, VALUE *argv, VALUE io) 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 maxlen bytes from ios 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 outbuf argument is present, it must + * reference a String, which will receive the data. + * Raises SystemCallError on error, EOFError + * at end of file and NotImplementedError 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 ios at offset 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 SystemCallError on error and NotImplementedError + * 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) 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); -- cgit v1.2.3