Project

General

Profile

Bug #13085 ยป 0001-basicsocket-Linux-workaround-for-excess-garbage-on-w.patch

normalperson (Eric Wong), 01/17/2017 12:08 AM

View differences:

ext/socket/basicsocket.c
662 662
    return rsock_do_not_reverse_lookup?Qtrue:Qfalse;
663 663
}
664 664

  
665

  
666
/*
667
 * Linux-only workaround for [ruby-core:78898] [Bug #13085]
668
 * We use send(... MSG_DONTWAIT) instead of write_nonblock because
669
 * it has no side effects with F_SETFL, so it is limited to Linux
670
 * for now.
671
 */
672
#if MSG_DONTWAIT_RELIABLE != 0
673
static VALUE bsock_write(VALUE sock, VALUE buf)
674
{
675
    long off = 0;
676
    long len;
677
    const char *ptr;
678
    rb_io_t *fptr;
679
    long wlen;
680
    ssize_t n;
681
    ssize_t ret = 0;
682

  
683
    buf = rb_obj_as_string(buf);
684
    RSTRING_GETMEM(buf, ptr, len);
685
    sock = rb_io_get_write_io(sock);
686
    GetOpenFile(sock, fptr);
687

  
688
    for (;;) {
689
	if (fptr->mode & FMODE_SYNC && fptr->wbuf.len == 0) {
690
	    wlen = len - off;
691

  
692
	    /* string may be modified during rb_io_wait_writable, or empty */
693
	    if (wlen <= 0)
694
		break;
695

  
696
	    rb_io_check_writable(fptr);
697
	    n = send(fptr->fd, ptr + off, wlen, MSG_DONTWAIT);
698
	    if (n >= 0) {
699
		ret += n;
700
		wlen -= n;
701
		if (wlen == 0)
702
		    break;
703
		off += n;
704
	    }
705
	    else if (rb_io_wait_writable(fptr->fd)) {
706
		GetOpenFile(sock, fptr);
707
		RSTRING_GETMEM(buf, ptr, len);
708
	    }
709
	    else {
710
		rb_sys_fail("send(2)");
711
	    }
712
	}
713
	else {
714
	    if (off != 0) {
715
		buf = rb_str_substr(buf, off, len - off);
716
	    }
717

  
718
	    return rb_call_super(1, &buf);
719
	}
720
    }
721

  
722
    return SSIZET2NUM(ret);
723
}
724
#endif /* MSG_DONTWAIT_RELIABLE */
725

  
665 726
/*
666 727
 * call-seq:
667 728
 *   BasicSocket.do_not_reverse_lookup = bool
......
715 776
    rb_define_method(rb_cBasicSocket, "send", rsock_bsock_send, -1);
716 777
    rb_define_method(rb_cBasicSocket, "recv", bsock_recv, -1);
717 778

  
779
#if MSG_DONTWAIT_RELIABLE != 0
780
    rb_define_method(rb_cBasicSocket, "write", bsock_write, 1);
781
    rb_define_method(rb_cBasicSocket, "syswrite", bsock_write, 1);
782
#endif
783

  
718 784
    rb_define_method(rb_cBasicSocket, "do_not_reverse_lookup", bsock_do_not_reverse_lookup, 0);
719 785
    rb_define_method(rb_cBasicSocket, "do_not_reverse_lookup=", bsock_do_not_reverse_lookup_set, 1);
720 786

  
test/socket/test_basicsocket.rb
2 2

  
3 3
begin
4 4
  require "socket"
5
  require "io/nonblock"
5 6
  require "test/unit"
6 7
rescue LoadError
7 8
end
......
152 153
      sock.close
153 154
    end
154 155
  end
156

  
157
  # Linux-only workaround for [ruby-core:78898] [Bug #13085]
158
  def test_write_workaround_linux
159
    socks do |sserv, ssock, csock|
160
      x = ssock.method(:write)
161
      buf = $$.to_s
162
      len = buf.bytesize
163
      assert_not_predicate buf, :frozen?
164
      res = {}
165
      before = GC.stat(res)[:total_allocated_objects]
166
      n = ssock.write(buf)
167
      after = GC.stat(res)[:total_allocated_objects]
168
      assert_equal before, after, 'no allocation during write'
169
      assert_not_predicate buf, :frozen?, 'no inadvertant freeze'
170
      assert_equal len, n, 'wrote expected size'
171
      assert_equal $$.to_s, buf, 'no inadvertant modification to write buf'
172
      assert_equal $$.to_s, csock.read(len), 'drained expected data'
173
      assert_not_predicate ssock, :nonblock?, 'no side effect after read'
174

  
175
      ssock.sync = false
176
      ssock.write(buf)
177
      assert_equal :wait_readable, csock.read_nonblock(len, exception: false),
178
                  'userspace buffering still works if requested'
179

  
180
      assert_warning(/syswrite for buffered IO/, 'syswrite fallback works') do
181
        old, $VERBOSE = $VERBOSE, true
182
        ssock.syswrite("\n")
183
        $VERBOSE = old
184
      end
185
      ssock.flush
186
      assert_equal "\n#$$", csock.read(len + 1), 'drained expected data'
187
      ssock.sync = true
188

  
189
      # big write
190
      buf = IO.read(__FILE__)
191
      len = buf.bytesize
192
      n = 0
193
      th = Thread.new { loop { n += ssock.write(buf) } }
194
      Thread.pass until th.stop?
195
      nwrite = n
196
      exp = buf * (nwrite / len) + buf.byteslice(0, nwrite % len)
197
      res = csock.read(nwrite)
198
      assert_equal(exp, res)
199
      th.exit
200
      th.join
201
    end
202
  end if RUBY_PLATFORM =~ /linux/ && Socket.const_defined?(:MSG_DONTWAIT)
155 203
end if defined?(BasicSocket)
156
-