ruby-changes:45399
From: normal <ko1@a...>
Date: Tue, 31 Jan 2017 07:13:23 +0900 (JST)
Subject: [ruby-changes:45399] normal:r57472 (trunk): io.c (rb_io_syswrite): avoid leaving garbage after write
normal 2017-01-31 07:03:57 +0900 (Tue, 31 Jan 2017) New Revision: 57472 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=57472 Log: io.c (rb_io_syswrite): avoid leaving garbage after write As with IO#write, IO#syswrite also generates garbage which can be harmful in hand-coded read-write loops. * io.c (swrite_arg, swrite_do, swrite_end): new (rb_io_syswrite): use new functions to cleanup garbage [ruby-core:78898] [Bug #13085] Modified files: trunk/io.c trunk/test/ruby/test_io.rb Index: test/ruby/test_io.rb =================================================================== --- test/ruby/test_io.rb (revision 57471) +++ test/ruby/test_io.rb (revision 57472) @@ -3512,11 +3512,13 @@ __END__ https://github.com/ruby/ruby/blob/trunk/test/ruby/test_io.rb#L3512 with_pipe do |r, w| before = ObjectSpace.count_objects(res)[:T_STRING] n = w.write(buf) + s = w.syswrite(buf) after = ObjectSpace.count_objects(res)[:T_STRING] assert_equal before, after, 'no strings left over after write [ruby-core:78898] [Bug #13085]' assert_not_predicate buf, :frozen?, 'no inadvertant freeze' - assert_equal buf.bytesize, n, 'wrote expected size' + assert_equal buf.bytesize, n, 'write wrote expected size' + assert_equal s, n, 'syswrite wrote expected size' end end end Index: io.c =================================================================== --- io.c (revision 57471) +++ io.c (revision 57472) @@ -4745,6 +4745,34 @@ rb_io_sysseek(int argc, VALUE *argv, VAL https://github.com/ruby/ruby/blob/trunk/io.c#L4745 return OFFT2NUM(pos); } +struct swrite_arg { + VALUE orig; + VALUE tmp; + rb_io_t *fptr; +}; + +static VALUE +swrite_do(VALUE arg) +{ + struct swrite_arg *sa = (struct swrite_arg *)arg; + const char *ptr; + long len; + + RSTRING_GETMEM(sa->tmp, ptr, len); + + return (VALUE)rb_write_internal(sa->fptr->fd, ptr, len); +} + +static VALUE +swrite_end(VALUE arg) +{ + struct swrite_arg *sa = (struct swrite_arg *)arg; + + rb_str_tmp_frozen_release(sa->orig, sa->tmp); + + return Qfalse; +} + /* * call-seq: * ios.syswrite(string) -> integer @@ -4761,26 +4789,25 @@ rb_io_sysseek(int argc, VALUE *argv, VAL https://github.com/ruby/ruby/blob/trunk/io.c#L4789 static VALUE rb_io_syswrite(VALUE io, VALUE str) { - rb_io_t *fptr; + struct swrite_arg sa; long n; if (!RB_TYPE_P(str, T_STRING)) str = rb_obj_as_string(str); io = GetWriteIO(io); - GetOpenFile(io, fptr); - rb_io_check_writable(fptr); - - str = rb_str_new_frozen(str); + GetOpenFile(io, sa.fptr); + rb_io_check_writable(sa.fptr); - if (fptr->wbuf.len) { + if (sa.fptr->wbuf.len) { rb_warn("syswrite for buffered IO"); } - n = rb_write_internal(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str)); - RB_GC_GUARD(str); + sa.orig = str; + sa.tmp = rb_str_tmp_frozen_acquire(str); + n = (long)rb_ensure(swrite_do, (VALUE)&sa, swrite_end, (VALUE)&sa); - if (n == -1) rb_sys_fail_path(fptr->pathv); + if (n == -1) rb_sys_fail_path(sa.fptr->pathv); return LONG2FIX(n); } -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/