Bug #13085 » 0001-basicsocket-Linux-workaround-for-excess-garbage-on-w.patch
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)
|
||
-
|