diff options
author | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-01-30 22:03:57 +0000 |
---|---|---|
committer | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-01-30 22:03:57 +0000 |
commit | 345c459abfcab0d9f50468ad49a5bc3087b2381e (patch) | |
tree | 1f57203b2233d5b8ea6852d0cbbf9a417d7a47df | |
parent | 88bd76c7990e14f8ca6dc1055e1d17258620ba6b (diff) | |
download | ruby-345c459abfcab0d9f50468ad49a5bc3087b2381e.tar.gz |
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]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@57472 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | io.c | 45 | ||||
-rw-r--r-- | test/ruby/test_io.rb | 4 |
2 files changed, 39 insertions, 10 deletions
@@ -4745,6 +4745,34 @@ rb_io_sysseek(int argc, VALUE *argv, VALUE io) 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, VALUE io) 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); } diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index c501edd0ab..3cc410881c 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -3512,11 +3512,13 @@ __END__ 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 |