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
return rsock_do_not_reverse_lookup?Qtrue:Qfalse;
}
/*
* Linux-only workaround for [ruby-core:78898] [Bug #13085]
* We use send(... MSG_DONTWAIT) instead of write_nonblock because
* it has no side effects with F_SETFL, so it is limited to Linux
* for now.
*/
#if MSG_DONTWAIT_RELIABLE != 0
static VALUE bsock_write(VALUE sock, VALUE buf)
{
long off = 0;
long len;
const char *ptr;
rb_io_t *fptr;
long wlen;
ssize_t n;
ssize_t ret = 0;
buf = rb_obj_as_string(buf);
RSTRING_GETMEM(buf, ptr, len);
sock = rb_io_get_write_io(sock);
GetOpenFile(sock, fptr);
for (;;) {
if (fptr->mode & FMODE_SYNC && fptr->wbuf.len == 0) {
wlen = len - off;
/* string may be modified during rb_io_wait_writable, or empty */
if (wlen <= 0)
break;
rb_io_check_writable(fptr);
n = send(fptr->fd, ptr + off, wlen, MSG_DONTWAIT);
if (n >= 0) {
ret += n;
wlen -= n;
if (wlen == 0)
break;
off += n;
}
else if (rb_io_wait_writable(fptr->fd)) {
GetOpenFile(sock, fptr);
RSTRING_GETMEM(buf, ptr, len);
}
else {
rb_sys_fail("send(2)");
}
}
else {
if (off != 0) {
buf = rb_str_substr(buf, off, len - off);
}
return rb_call_super(1, &buf);
}
}
return SSIZET2NUM(ret);
}
#endif /* MSG_DONTWAIT_RELIABLE */
/*
* call-seq:
* BasicSocket.do_not_reverse_lookup = bool
......
rb_define_method(rb_cBasicSocket, "send", rsock_bsock_send, -1);
rb_define_method(rb_cBasicSocket, "recv", bsock_recv, -1);
#if MSG_DONTWAIT_RELIABLE != 0
rb_define_method(rb_cBasicSocket, "write", bsock_write, 1);
rb_define_method(rb_cBasicSocket, "syswrite", bsock_write, 1);
#endif
rb_define_method(rb_cBasicSocket, "do_not_reverse_lookup", bsock_do_not_reverse_lookup, 0);
rb_define_method(rb_cBasicSocket, "do_not_reverse_lookup=", bsock_do_not_reverse_lookup_set, 1);
test/socket/test_basicsocket.rb
begin
require "socket"
require "io/nonblock"
require "test/unit"
rescue LoadError
end
......
sock.close
end
end
# Linux-only workaround for [ruby-core:78898] [Bug #13085]
def test_write_workaround_linux
socks do |sserv, ssock, csock|
x = ssock.method(:write)
buf = $$.to_s
len = buf.bytesize
assert_not_predicate buf, :frozen?
res = {}
before = GC.stat(res)[:total_allocated_objects]
n = ssock.write(buf)
after = GC.stat(res)[:total_allocated_objects]
assert_equal before, after, 'no allocation during write'
assert_not_predicate buf, :frozen?, 'no inadvertant freeze'
assert_equal len, n, 'wrote expected size'
assert_equal $$.to_s, buf, 'no inadvertant modification to write buf'
assert_equal $$.to_s, csock.read(len), 'drained expected data'
assert_not_predicate ssock, :nonblock?, 'no side effect after read'
ssock.sync = false
ssock.write(buf)
assert_equal :wait_readable, csock.read_nonblock(len, exception: false),
'userspace buffering still works if requested'
assert_warning(/syswrite for buffered IO/, 'syswrite fallback works') do
old, $VERBOSE = $VERBOSE, true
ssock.syswrite("\n")
$VERBOSE = old
end
ssock.flush
assert_equal "\n#$$", csock.read(len + 1), 'drained expected data'
ssock.sync = true
# big write
buf = IO.read(__FILE__)
len = buf.bytesize
n = 0
th = Thread.new { loop { n += ssock.write(buf) } }
Thread.pass until th.stop?
nwrite = n
exp = buf * (nwrite / len) + buf.byteslice(0, nwrite % len)
res = csock.read(nwrite)
assert_equal(exp, res)
th.exit
th.join
end
end if RUBY_PLATFORM =~ /linux/ && Socket.const_defined?(:MSG_DONTWAIT)
end if defined?(BasicSocket)
-
(3-3/4)