2009-01-17 07:11:27 +03:00
|
|
|
/************************************************
|
|
|
|
|
|
|
|
init.c -
|
|
|
|
|
|
|
|
created at: Thu Mar 31 12:21:29 JST 1994
|
|
|
|
|
|
|
|
Copyright (C) 1993-2007 Yukihiro Matsumoto
|
|
|
|
|
|
|
|
************************************************/
|
|
|
|
|
|
|
|
#include "rubysocket.h"
|
|
|
|
|
2018-01-23 18:31:22 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
|
|
|
|
#endif
|
|
|
|
|
2009-01-17 07:11:27 +03:00
|
|
|
VALUE rb_cBasicSocket;
|
|
|
|
VALUE rb_cIPSocket;
|
|
|
|
VALUE rb_cTCPSocket;
|
|
|
|
VALUE rb_cTCPServer;
|
|
|
|
VALUE rb_cUDPSocket;
|
|
|
|
#ifdef AF_UNIX
|
|
|
|
VALUE rb_cUNIXSocket;
|
|
|
|
VALUE rb_cUNIXServer;
|
|
|
|
#endif
|
|
|
|
VALUE rb_cSocket;
|
2009-02-05 14:01:43 +03:00
|
|
|
VALUE rb_cAddrinfo;
|
2009-01-17 07:11:27 +03:00
|
|
|
|
|
|
|
VALUE rb_eSocket;
|
|
|
|
|
|
|
|
#ifdef SOCKS
|
|
|
|
VALUE rb_cSOCKSSocket;
|
|
|
|
#endif
|
|
|
|
|
2010-02-02 12:15:19 +03:00
|
|
|
int rsock_do_not_reverse_lookup = 1;
|
socket: avoid arg parsing in rsock_s_accept_nonblock
* ext/socket/init.c (rsock_s_accept_nonblock): avoid parsing args
[ruby-core:71439] [Feature #11339]
* ext/socket/rubysocket.h: adjust prototype
* ext/socket/socket.c (sock_accept_nonblock): make private
* ext/socket/tcpserver.c (tcp_accept_nonblock): ditto
* ext/socket/unixserver.c (unix_accept_nonblock): ditto
* ext/socket/lib/socket.rb (Socket#accept_nonblock):
implement as wrapper, move RDoc
(TCPServer#accept_nonblock): ditto
(UNIXServer#accept_nonblock): ditto
target 0: a (ruby 2.3.0dev (2015-11-12 trunk 52550) [x86_64-linux])
target 1: b (ruby 2.3.0dev (2015-11-12 avoid-kwarg-capi 52550) [x86_64-linux]
-----------------------------------------------------------
accept_nonblock
require 'tempfile'
require 'socket'
require 'io/wait'
nr = 500000
Tempfile.create(%w(accept_nonblock .sock)) do |tmp|
path = tmp.path
File.unlink(path)
s = UNIXServer.new(path)
addr = Socket.sockaddr_un(path).freeze
nr.times do
s.accept_nonblock(exception: false)
c = UNIXSocket.new(path)
s.wait_readable
s.accept_nonblock(exception: false).close
c.close
end
end
-----------------------------------------------------------
raw data:
[["accept_nonblock",
[[4.807877402752638,
4.930681671947241,
4.738454818725586,
4.69268161803484,
4.684675686061382],
[4.253904823213816,
4.255124930292368,
4.295955188572407,
4.248479191213846,
4.213303029537201]]]]
Elapsed time: 45.123040065 (sec)
-----------------------------------------------------------
benchmark results:
minimum results in each 5 measurements.
Execution time (sec)
name a b
accept_nonblock 4.685 4.213
Speedup ratio: compare with the result of `a' (greater is better)
name b
accept_nonblock 1.112
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52601 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-11-17 02:40:15 +03:00
|
|
|
static VALUE sym_wait_readable;
|
2009-01-17 07:11:27 +03:00
|
|
|
|
|
|
|
void
|
2009-03-01 09:30:41 +03:00
|
|
|
rsock_raise_socket_error(const char *reason, int error)
|
2009-01-17 07:11:27 +03:00
|
|
|
{
|
|
|
|
#ifdef EAI_SYSTEM
|
2015-10-19 04:33:49 +03:00
|
|
|
int e;
|
|
|
|
if (error == EAI_SYSTEM && (e = errno) != 0)
|
|
|
|
rb_syserr_fail(e, reason);
|
2009-01-17 07:11:27 +03:00
|
|
|
#endif
|
2018-01-23 18:31:22 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
rb_encoding *enc = rb_default_internal_encoding();
|
|
|
|
VALUE msg = rb_sprintf("%s: ", reason);
|
|
|
|
if (!enc) enc = rb_default_internal_encoding();
|
|
|
|
rb_str_concat(msg, rb_w32_conv_from_wchar(gai_strerrorW(error), enc));
|
|
|
|
rb_exc_raise(rb_exc_new_str(rb_eSocket, msg));
|
|
|
|
#else
|
2009-01-17 07:11:27 +03:00
|
|
|
rb_raise(rb_eSocket, "%s: %s", reason, gai_strerror(error));
|
2018-01-23 18:31:22 +03:00
|
|
|
#endif
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
|
|
|
|
2018-04-30 05:17:03 +03:00
|
|
|
#if defined __APPLE__
|
|
|
|
# define do_write_retry(code) do {ret = code;} while (ret == -1 && errno == EPROTOTYPE)
|
|
|
|
#else
|
|
|
|
# define do_write_retry(code) ret = code
|
|
|
|
#endif
|
|
|
|
|
2015-11-17 05:15:55 +03:00
|
|
|
VALUE
|
|
|
|
rsock_init_sock(VALUE sock, int fd)
|
|
|
|
{
|
|
|
|
rb_io_t *fp;
|
|
|
|
|
2015-12-21 21:57:50 +03:00
|
|
|
rb_update_max_fd(fd);
|
2009-01-17 07:11:27 +03:00
|
|
|
MakeOpenFile(sock, fp);
|
|
|
|
fp->fd = fd;
|
|
|
|
fp->mode = FMODE_READWRITE|FMODE_DUPLEX;
|
|
|
|
rb_io_ascii8bit_binmode(sock);
|
2009-03-01 09:30:41 +03:00
|
|
|
if (rsock_do_not_reverse_lookup) {
|
2009-01-17 07:11:27 +03:00
|
|
|
fp->mode |= FMODE_NOREVLOOKUP;
|
|
|
|
}
|
|
|
|
rb_io_synchronized(fp);
|
|
|
|
|
|
|
|
return sock;
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
2009-03-01 09:30:41 +03:00
|
|
|
rsock_sendto_blocking(void *data)
|
2009-01-17 07:11:27 +03:00
|
|
|
{
|
2009-03-01 09:30:41 +03:00
|
|
|
struct rsock_send_arg *arg = data;
|
2009-01-17 07:11:27 +03:00
|
|
|
VALUE mesg = arg->mesg;
|
2018-04-30 05:17:03 +03:00
|
|
|
ssize_t ret;
|
|
|
|
do_write_retry(sendto(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg),
|
|
|
|
arg->flags, arg->to, arg->tolen));
|
|
|
|
return (VALUE)ret;
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
2009-03-01 09:30:41 +03:00
|
|
|
rsock_send_blocking(void *data)
|
2009-01-17 07:11:27 +03:00
|
|
|
{
|
2009-03-01 09:30:41 +03:00
|
|
|
struct rsock_send_arg *arg = data;
|
2009-01-17 07:11:27 +03:00
|
|
|
VALUE mesg = arg->mesg;
|
2018-04-30 05:17:03 +03:00
|
|
|
ssize_t ret;
|
|
|
|
do_write_retry(send(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg),
|
|
|
|
arg->flags));
|
|
|
|
return (VALUE)ret;
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
struct recvfrom_arg {
|
|
|
|
int fd, flags;
|
|
|
|
VALUE str;
|
2020-03-31 14:18:21 +03:00
|
|
|
size_t length;
|
2009-01-17 07:11:27 +03:00
|
|
|
socklen_t alen;
|
2013-02-24 21:51:17 +04:00
|
|
|
union_sockaddr buf;
|
2009-01-17 07:11:27 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
recvfrom_blocking(void *data)
|
|
|
|
{
|
|
|
|
struct recvfrom_arg *arg = data;
|
2013-02-16 15:44:42 +04:00
|
|
|
socklen_t len0 = arg->alen;
|
|
|
|
ssize_t ret;
|
2020-03-31 14:18:21 +03:00
|
|
|
ret = recvfrom(arg->fd, RSTRING_PTR(arg->str), arg->length,
|
2013-02-24 21:51:17 +04:00
|
|
|
arg->flags, &arg->buf.addr, &arg->alen);
|
2013-02-16 15:44:42 +04:00
|
|
|
if (ret != -1 && len0 < arg->alen)
|
|
|
|
arg->alen = len0;
|
2020-03-31 14:18:21 +03:00
|
|
|
|
2013-02-16 15:44:42 +04:00
|
|
|
return (VALUE)ret;
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
|
|
|
|
2015-06-15 23:02:43 +03:00
|
|
|
static VALUE
|
|
|
|
rsock_strbuf(VALUE str, long buflen)
|
|
|
|
{
|
|
|
|
long len;
|
|
|
|
|
2019-09-25 06:59:12 +03:00
|
|
|
if (NIL_P(str)) return rb_str_new(0, buflen);
|
2015-06-15 23:02:43 +03:00
|
|
|
|
|
|
|
StringValue(str);
|
|
|
|
len = RSTRING_LEN(str);
|
|
|
|
if (len >= buflen) {
|
|
|
|
rb_str_modify(str);
|
|
|
|
} else {
|
|
|
|
rb_str_modify_expand(str, buflen - len);
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
recvfrom_locktmp(VALUE v)
|
|
|
|
{
|
|
|
|
struct recvfrom_arg *arg = (struct recvfrom_arg *)v;
|
|
|
|
|
|
|
|
return rb_thread_io_blocking_region(recvfrom_blocking, arg, arg->fd);
|
|
|
|
}
|
|
|
|
|
2009-01-17 07:11:27 +03:00
|
|
|
VALUE
|
2021-05-08 15:13:47 +03:00
|
|
|
rsock_s_recvfrom(VALUE socket, int argc, VALUE *argv, enum sock_recv_type from)
|
2009-01-17 07:11:27 +03:00
|
|
|
{
|
|
|
|
rb_io_t *fptr;
|
2015-06-15 23:02:43 +03:00
|
|
|
VALUE str;
|
2009-01-17 07:11:27 +03:00
|
|
|
struct recvfrom_arg arg;
|
|
|
|
VALUE len, flg;
|
|
|
|
long buflen;
|
|
|
|
long slen;
|
|
|
|
|
2015-06-15 23:02:43 +03:00
|
|
|
rb_scan_args(argc, argv, "12", &len, &flg, &str);
|
2009-01-17 07:11:27 +03:00
|
|
|
|
2021-05-08 15:13:47 +03:00
|
|
|
if (flg == Qnil)
|
|
|
|
arg.flags = 0;
|
|
|
|
else
|
|
|
|
arg.flags = NUM2INT(flg);
|
|
|
|
|
2009-01-17 07:11:27 +03:00
|
|
|
buflen = NUM2INT(len);
|
2015-06-15 23:02:43 +03:00
|
|
|
str = rsock_strbuf(str, buflen);
|
2009-01-17 07:11:27 +03:00
|
|
|
|
2021-05-08 15:13:47 +03:00
|
|
|
RB_IO_POINTER(socket, fptr);
|
|
|
|
|
2009-01-17 07:11:27 +03:00
|
|
|
if (rb_io_read_pending(fptr)) {
|
2021-05-08 15:13:47 +03:00
|
|
|
rb_raise(rb_eIOError, "recv for buffered IO");
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
2021-05-08 15:13:47 +03:00
|
|
|
|
2009-01-17 07:11:27 +03:00
|
|
|
arg.fd = fptr->fd;
|
2010-04-28 12:14:13 +04:00
|
|
|
arg.alen = (socklen_t)sizeof(arg.buf);
|
2015-06-15 23:02:43 +03:00
|
|
|
arg.str = str;
|
2020-03-31 14:18:21 +03:00
|
|
|
arg.length = buflen;
|
2009-01-17 07:11:27 +03:00
|
|
|
|
2021-05-08 15:13:47 +03:00
|
|
|
while (true) {
|
|
|
|
rb_io_check_closed(fptr);
|
2021-06-19 04:47:16 +03:00
|
|
|
|
|
|
|
#ifdef RSOCK_WAIT_BEFORE_BLOCKING
|
|
|
|
rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), Qnil);
|
|
|
|
#endif
|
2021-05-08 15:13:47 +03:00
|
|
|
|
|
|
|
slen = (long)rb_str_locktmp_ensure(str, recvfrom_locktmp, (VALUE)&arg);
|
|
|
|
|
|
|
|
if (slen >= 0) break;
|
|
|
|
|
|
|
|
if (!rb_io_maybe_wait_readable(errno, socket, Qnil))
|
2009-02-22 13:28:38 +03:00
|
|
|
rb_sys_fail("recvfrom(2)");
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
|
|
|
|
2020-03-31 14:18:21 +03:00
|
|
|
/* Resize the string to the amount of data received */
|
|
|
|
rb_str_set_len(str, slen);
|
2009-01-17 07:11:27 +03:00
|
|
|
switch (from) {
|
|
|
|
case RECV_RECV:
|
|
|
|
return str;
|
|
|
|
case RECV_IP:
|
|
|
|
#if 0
|
|
|
|
if (arg.alen != sizeof(struct sockaddr_in)) {
|
|
|
|
rb_raise(rb_eTypeError, "sockaddr size differs - should not happen");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (arg.alen && arg.alen != sizeof(arg.buf)) /* OSX doesn't return a from result for connection-oriented sockets */
|
2013-02-24 21:51:17 +04:00
|
|
|
return rb_assoc_new(str, rsock_ipaddr(&arg.buf.addr, arg.alen, fptr->mode & FMODE_NOREVLOOKUP));
|
2009-01-17 07:11:27 +03:00
|
|
|
else
|
|
|
|
return rb_assoc_new(str, Qnil);
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_UN_H
|
|
|
|
case RECV_UNIX:
|
2013-02-24 21:51:17 +04:00
|
|
|
return rb_assoc_new(str, rsock_unixaddr(&arg.buf.un, arg.alen));
|
2009-01-17 07:11:27 +03:00
|
|
|
#endif
|
|
|
|
case RECV_SOCKET:
|
2021-05-08 15:13:47 +03:00
|
|
|
return rb_assoc_new(str, rsock_io_socket_addrinfo(socket, &arg.buf.addr, arg.alen));
|
2009-01-17 07:11:27 +03:00
|
|
|
default:
|
2009-03-01 09:30:41 +03:00
|
|
|
rb_bug("rsock_s_recvfrom called with bad value");
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
socket: avoid arg parsing in rsock_s_recvfrom_nonblock
* ext/socket/init.c (rsock_s_recvfrom_nonblock):
avoid arg parsing with C API
[ruby-core:71439] [Feature #11339]
* ext/socket/basicsocket.c (bsock_recv_nonblock):
adjust for above change, make private
* ext/socket/socket.c (sock_recvfrom_nonblock): ditto
* ext/socket/udpsocket.c (udp_recvfrom_nonblock): ditto
* ext/socket/lib/socket.rb (BasicSocket#recv_nonblock):
new wrapper for private method, move RDoc
(Socket#recvfrom_nonblock): ditto
(UDPSocket#recvfrom_nonblock): ditto
Note, not adding bm_recv_nonblock.rb to benchmark/ directory
since it is non-portable. It is only in this commit message.
Benchmark results + code
target 0: a (ruby 2.3.0dev (2015-11-12 trunk 52540) [x86_64-linux])
target 1: b (ruby 2.3.0dev (2015-11-12 avoid-kwarg-capi 52540) [x86_64-linux]
-----------------------------------------------------------
recv_nonblock
require 'socket'
nr = 1000000
msg = 'hello world'
buf = ''
size = msg.bytesize
UNIXSocket.pair(:SEQPACKET) do |a, b|
nr.times do
a.sendmsg(msg)
b.recv_nonblock(size, 0, buf, exception: false)
end
end
-----------------------------------------------------------
raw data:
[["recv_nonblock",
[[1.83511221408844,
1.8703329525887966,
1.8448856547474861,
1.859263762831688,
1.8331583738327026],
[1.5637447573244572,
1.4062932096421719,
1.4247371144592762,
1.4108827747404575,
1.4802536629140377]]]]
Elapsed time: 16.530452496 (sec)
-----------------------------------------------------------
benchmark results:
minimum results in each 5 measurements.
Execution time (sec)
name a b
recv_nonblock 1.833 1.406
Speedup ratio: compare with the result of `a' (greater is better)
name b
recv_nonblock 1.304
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52598 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-11-17 02:25:03 +03:00
|
|
|
rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str,
|
|
|
|
VALUE ex, enum sock_recv_type from)
|
2009-01-17 07:11:27 +03:00
|
|
|
{
|
|
|
|
rb_io_t *fptr;
|
2013-02-24 21:51:17 +04:00
|
|
|
union_sockaddr buf;
|
2010-04-28 12:14:13 +04:00
|
|
|
socklen_t alen = (socklen_t)sizeof buf;
|
2009-01-17 07:11:27 +03:00
|
|
|
long buflen;
|
|
|
|
long slen;
|
|
|
|
int fd, flags;
|
|
|
|
VALUE addr = Qnil;
|
2013-02-16 15:44:42 +04:00
|
|
|
socklen_t len0;
|
2009-01-17 07:11:27 +03:00
|
|
|
|
socket: avoid arg parsing in rsock_s_recvfrom_nonblock
* ext/socket/init.c (rsock_s_recvfrom_nonblock):
avoid arg parsing with C API
[ruby-core:71439] [Feature #11339]
* ext/socket/basicsocket.c (bsock_recv_nonblock):
adjust for above change, make private
* ext/socket/socket.c (sock_recvfrom_nonblock): ditto
* ext/socket/udpsocket.c (udp_recvfrom_nonblock): ditto
* ext/socket/lib/socket.rb (BasicSocket#recv_nonblock):
new wrapper for private method, move RDoc
(Socket#recvfrom_nonblock): ditto
(UDPSocket#recvfrom_nonblock): ditto
Note, not adding bm_recv_nonblock.rb to benchmark/ directory
since it is non-portable. It is only in this commit message.
Benchmark results + code
target 0: a (ruby 2.3.0dev (2015-11-12 trunk 52540) [x86_64-linux])
target 1: b (ruby 2.3.0dev (2015-11-12 avoid-kwarg-capi 52540) [x86_64-linux]
-----------------------------------------------------------
recv_nonblock
require 'socket'
nr = 1000000
msg = 'hello world'
buf = ''
size = msg.bytesize
UNIXSocket.pair(:SEQPACKET) do |a, b|
nr.times do
a.sendmsg(msg)
b.recv_nonblock(size, 0, buf, exception: false)
end
end
-----------------------------------------------------------
raw data:
[["recv_nonblock",
[[1.83511221408844,
1.8703329525887966,
1.8448856547474861,
1.859263762831688,
1.8331583738327026],
[1.5637447573244572,
1.4062932096421719,
1.4247371144592762,
1.4108827747404575,
1.4802536629140377]]]]
Elapsed time: 16.530452496 (sec)
-----------------------------------------------------------
benchmark results:
minimum results in each 5 measurements.
Execution time (sec)
name a b
recv_nonblock 1.833 1.406
Speedup ratio: compare with the result of `a' (greater is better)
name b
recv_nonblock 1.304
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52598 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-11-17 02:25:03 +03:00
|
|
|
flags = NUM2INT(flg);
|
2009-01-17 07:11:27 +03:00
|
|
|
buflen = NUM2INT(len);
|
2015-06-15 23:02:43 +03:00
|
|
|
str = rsock_strbuf(str, buflen);
|
2009-01-17 07:11:27 +03:00
|
|
|
|
|
|
|
#ifdef MSG_DONTWAIT
|
|
|
|
/* MSG_DONTWAIT avoids the race condition between fcntl and recvfrom.
|
|
|
|
It is not portable, though. */
|
|
|
|
flags |= MSG_DONTWAIT;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
GetOpenFile(sock, fptr);
|
|
|
|
if (rb_io_read_pending(fptr)) {
|
|
|
|
rb_raise(rb_eIOError, "recvfrom for buffered IO");
|
|
|
|
}
|
|
|
|
fd = fptr->fd;
|
|
|
|
|
|
|
|
rb_io_check_closed(fptr);
|
2015-05-29 05:24:18 +03:00
|
|
|
|
|
|
|
if (!MSG_DONTWAIT_RELIABLE)
|
|
|
|
rb_io_set_nonblock(fptr);
|
|
|
|
|
2013-02-16 15:44:42 +04:00
|
|
|
len0 = alen;
|
2013-02-24 21:51:17 +04:00
|
|
|
slen = recvfrom(fd, RSTRING_PTR(str), buflen, flags, &buf.addr, &alen);
|
2013-02-16 15:44:42 +04:00
|
|
|
if (slen != -1 && len0 < alen)
|
|
|
|
alen = len0;
|
2009-01-17 07:11:27 +03:00
|
|
|
|
|
|
|
if (slen < 0) {
|
2015-12-23 11:57:48 +03:00
|
|
|
int e = errno;
|
|
|
|
switch (e) {
|
2009-02-22 07:38:46 +03:00
|
|
|
case EAGAIN:
|
|
|
|
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
|
|
|
|
case EWOULDBLOCK:
|
|
|
|
#endif
|
socket: avoid arg parsing in rsock_s_recvfrom_nonblock
* ext/socket/init.c (rsock_s_recvfrom_nonblock):
avoid arg parsing with C API
[ruby-core:71439] [Feature #11339]
* ext/socket/basicsocket.c (bsock_recv_nonblock):
adjust for above change, make private
* ext/socket/socket.c (sock_recvfrom_nonblock): ditto
* ext/socket/udpsocket.c (udp_recvfrom_nonblock): ditto
* ext/socket/lib/socket.rb (BasicSocket#recv_nonblock):
new wrapper for private method, move RDoc
(Socket#recvfrom_nonblock): ditto
(UDPSocket#recvfrom_nonblock): ditto
Note, not adding bm_recv_nonblock.rb to benchmark/ directory
since it is non-portable. It is only in this commit message.
Benchmark results + code
target 0: a (ruby 2.3.0dev (2015-11-12 trunk 52540) [x86_64-linux])
target 1: b (ruby 2.3.0dev (2015-11-12 avoid-kwarg-capi 52540) [x86_64-linux]
-----------------------------------------------------------
recv_nonblock
require 'socket'
nr = 1000000
msg = 'hello world'
buf = ''
size = msg.bytesize
UNIXSocket.pair(:SEQPACKET) do |a, b|
nr.times do
a.sendmsg(msg)
b.recv_nonblock(size, 0, buf, exception: false)
end
end
-----------------------------------------------------------
raw data:
[["recv_nonblock",
[[1.83511221408844,
1.8703329525887966,
1.8448856547474861,
1.859263762831688,
1.8331583738327026],
[1.5637447573244572,
1.4062932096421719,
1.4247371144592762,
1.4108827747404575,
1.4802536629140377]]]]
Elapsed time: 16.530452496 (sec)
-----------------------------------------------------------
benchmark results:
minimum results in each 5 measurements.
Execution time (sec)
name a b
recv_nonblock 1.833 1.406
Speedup ratio: compare with the result of `a' (greater is better)
name b
recv_nonblock 1.304
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52598 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-11-17 02:25:03 +03:00
|
|
|
if (ex == Qfalse)
|
2015-06-15 22:38:49 +03:00
|
|
|
return sym_wait_readable;
|
2015-12-23 17:58:47 +03:00
|
|
|
rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE, e, "recvfrom(2) would block");
|
2009-02-22 07:38:46 +03:00
|
|
|
}
|
2015-12-23 11:57:48 +03:00
|
|
|
rb_syserr_fail(e, "recvfrom(2)");
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
2015-06-15 23:02:43 +03:00
|
|
|
if (slen != RSTRING_LEN(str)) {
|
2009-01-17 07:11:27 +03:00
|
|
|
rb_str_set_len(str, slen);
|
|
|
|
}
|
|
|
|
switch (from) {
|
|
|
|
case RECV_RECV:
|
|
|
|
return str;
|
|
|
|
|
|
|
|
case RECV_IP:
|
|
|
|
if (alen && alen != sizeof(buf)) /* connection-oriented socket may not return a from result */
|
2013-02-24 21:51:17 +04:00
|
|
|
addr = rsock_ipaddr(&buf.addr, alen, fptr->mode & FMODE_NOREVLOOKUP);
|
2009-01-17 07:11:27 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RECV_SOCKET:
|
2013-02-24 21:51:17 +04:00
|
|
|
addr = rsock_io_socket_addrinfo(sock, &buf.addr, alen);
|
2009-01-17 07:11:27 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2009-03-01 09:30:41 +03:00
|
|
|
rb_bug("rsock_s_recvfrom_nonblock called with bad value");
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
|
|
|
return rb_assoc_new(str, addr);
|
|
|
|
}
|
|
|
|
|
2017-10-28 02:26:48 +03:00
|
|
|
#if MSG_DONTWAIT_RELIABLE
|
|
|
|
static VALUE sym_wait_writable;
|
|
|
|
|
|
|
|
/* copied from io.c :< */
|
|
|
|
static long
|
|
|
|
read_buffered_data(char *ptr, long len, rb_io_t *fptr)
|
|
|
|
{
|
|
|
|
int n = fptr->rbuf.len;
|
|
|
|
|
|
|
|
if (n <= 0) return 0;
|
|
|
|
if (n > len) n = (int)len;
|
|
|
|
MEMMOVE(ptr, fptr->rbuf.ptr+fptr->rbuf.off, char, n);
|
|
|
|
fptr->rbuf.off += n;
|
|
|
|
fptr->rbuf.len -= n;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* :nodoc: */
|
|
|
|
VALUE
|
|
|
|
rsock_read_nonblock(VALUE sock, VALUE length, VALUE buf, VALUE ex)
|
|
|
|
{
|
|
|
|
rb_io_t *fptr;
|
|
|
|
long n;
|
|
|
|
long len = NUM2LONG(length);
|
|
|
|
VALUE str = rsock_strbuf(buf, len);
|
|
|
|
char *ptr;
|
|
|
|
|
|
|
|
GetOpenFile(sock, fptr);
|
|
|
|
|
|
|
|
if (len == 0) {
|
2020-03-31 14:18:21 +03:00
|
|
|
rb_str_set_len(str, 0);
|
2017-10-28 02:26:48 +03:00
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr = RSTRING_PTR(str);
|
|
|
|
n = read_buffered_data(ptr, len, fptr);
|
|
|
|
if (n <= 0) {
|
|
|
|
n = (long)recv(fptr->fd, ptr, len, MSG_DONTWAIT);
|
|
|
|
if (n < 0) {
|
|
|
|
int e = errno;
|
|
|
|
if ((e == EWOULDBLOCK || e == EAGAIN)) {
|
|
|
|
if (ex == Qfalse) return sym_wait_readable;
|
|
|
|
rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE,
|
|
|
|
e, "read would block");
|
|
|
|
}
|
|
|
|
rb_syserr_fail_path(e, fptr->pathv);
|
|
|
|
}
|
|
|
|
}
|
2020-03-31 14:18:21 +03:00
|
|
|
if (n != RSTRING_LEN(str)) {
|
2017-10-28 02:26:48 +03:00
|
|
|
rb_str_modify(str);
|
|
|
|
rb_str_set_len(str, n);
|
|
|
|
}
|
|
|
|
if (n == 0) {
|
|
|
|
if (ex == Qfalse) return Qnil;
|
|
|
|
rb_eof_error();
|
|
|
|
}
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* :nodoc: */
|
|
|
|
VALUE
|
|
|
|
rsock_write_nonblock(VALUE sock, VALUE str, VALUE ex)
|
|
|
|
{
|
|
|
|
rb_io_t *fptr;
|
|
|
|
long n;
|
|
|
|
|
|
|
|
if (!RB_TYPE_P(str, T_STRING))
|
|
|
|
str = rb_obj_as_string(str);
|
|
|
|
|
|
|
|
sock = rb_io_get_write_io(sock);
|
|
|
|
GetOpenFile(sock, fptr);
|
|
|
|
rb_io_check_writable(fptr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As with IO#write_nonblock, we may block if somebody is relying on
|
|
|
|
* buffered I/O; but nobody actually hits this because pipes and sockets
|
|
|
|
* are not userspace-buffered in Ruby by default.
|
|
|
|
*/
|
|
|
|
if (fptr->wbuf.len > 0) {
|
|
|
|
rb_io_flush(sock);
|
|
|
|
}
|
|
|
|
|
2018-04-30 05:17:03 +03:00
|
|
|
#ifdef __APPLE__
|
|
|
|
again:
|
|
|
|
#endif
|
2017-10-28 02:26:48 +03:00
|
|
|
n = (long)send(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str), MSG_DONTWAIT);
|
|
|
|
if (n < 0) {
|
|
|
|
int e = errno;
|
|
|
|
|
2018-04-30 05:17:03 +03:00
|
|
|
#ifdef __APPLE__
|
|
|
|
if (e == EPROTOTYPE) {
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
#endif
|
2017-10-28 02:26:48 +03:00
|
|
|
if (e == EWOULDBLOCK || e == EAGAIN) {
|
|
|
|
if (ex == Qfalse) return sym_wait_writable;
|
|
|
|
rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e,
|
|
|
|
"write would block");
|
|
|
|
}
|
|
|
|
rb_syserr_fail_path(e, fptr->pathv);
|
|
|
|
}
|
|
|
|
|
|
|
|
return LONG2FIX(n);
|
|
|
|
}
|
|
|
|
#endif /* MSG_DONTWAIT_RELIABLE */
|
|
|
|
|
2020-05-14 13:10:55 +03:00
|
|
|
static int
|
|
|
|
rsock_socket0(int domain, int type, int proto)
|
2014-01-28 18:37:34 +04:00
|
|
|
{
|
|
|
|
#ifdef SOCK_CLOEXEC
|
2020-05-14 13:10:55 +03:00
|
|
|
type |= SOCK_CLOEXEC;
|
|
|
|
#endif
|
2014-01-28 18:37:34 +04:00
|
|
|
|
2020-05-14 13:10:55 +03:00
|
|
|
#ifdef SOCK_NONBLOCK
|
|
|
|
type |= SOCK_NONBLOCK;
|
2014-01-28 18:37:34 +04:00
|
|
|
#endif
|
|
|
|
|
2020-05-14 13:10:55 +03:00
|
|
|
int result = socket(domain, type, proto);
|
|
|
|
|
|
|
|
if (result == -1)
|
2011-11-05 10:46:02 +04:00
|
|
|
return -1;
|
|
|
|
|
2020-05-14 13:10:55 +03:00
|
|
|
rb_fd_fix_cloexec(result);
|
2014-01-31 12:06:19 +04:00
|
|
|
|
2020-05-14 13:10:55 +03:00
|
|
|
#ifndef SOCK_NONBLOCK
|
|
|
|
rsock_make_fd_nonblock(result);
|
|
|
|
#endif
|
2014-01-31 12:06:19 +04:00
|
|
|
|
2020-05-14 13:10:55 +03:00
|
|
|
return result;
|
2014-01-31 12:06:19 +04:00
|
|
|
}
|
2011-11-05 10:46:02 +04:00
|
|
|
|
|
|
|
int
|
|
|
|
rsock_socket(int domain, int type, int proto)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = rsock_socket0(domain, type, proto);
|
|
|
|
if (fd < 0) {
|
2015-11-24 01:50:53 +03:00
|
|
|
if (rb_gc_for_fd(errno)) {
|
2011-11-05 10:46:02 +04:00
|
|
|
fd = rsock_socket0(domain, type, proto);
|
|
|
|
}
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
2011-11-03 17:14:48 +04:00
|
|
|
if (0 <= fd)
|
2011-11-05 10:46:02 +04:00
|
|
|
rb_update_max_fd(fd);
|
2009-01-17 07:11:27 +03:00
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2014-09-18 01:20:58 +04:00
|
|
|
/* emulate blocking connect behavior on EINTR or non-blocking socket */
|
2009-01-17 07:11:27 +03:00
|
|
|
static int
|
2020-09-25 10:20:18 +03:00
|
|
|
wait_connectable(int fd, struct timeval *timeout)
|
2009-01-17 07:11:27 +03:00
|
|
|
{
|
2014-09-18 01:20:58 +04:00
|
|
|
int sockerr, revents;
|
2009-01-17 07:11:27 +03:00
|
|
|
socklen_t sockerrlen;
|
|
|
|
|
2014-09-18 01:20:58 +04:00
|
|
|
sockerrlen = (socklen_t)sizeof(sockerr);
|
|
|
|
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen) < 0)
|
|
|
|
return -1;
|
2011-05-04 06:44:28 +04:00
|
|
|
|
2018-11-08 06:27:16 +03:00
|
|
|
/* necessary for non-blocking sockets (at least ECONNREFUSED) */
|
|
|
|
switch (sockerr) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
#ifdef EALREADY
|
|
|
|
case EALREADY:
|
|
|
|
#endif
|
|
|
|
#ifdef EISCONN
|
|
|
|
case EISCONN:
|
|
|
|
#endif
|
|
|
|
#ifdef ECONNREFUSED
|
|
|
|
case ECONNREFUSED:
|
|
|
|
#endif
|
|
|
|
#ifdef EHOSTUNREACH
|
|
|
|
case EHOSTUNREACH:
|
|
|
|
#endif
|
|
|
|
errno = sockerr;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-09-18 01:20:58 +04:00
|
|
|
/*
|
|
|
|
* Stevens book says, successful finish turn on RB_WAITFD_OUT and
|
|
|
|
* failure finish turn on both RB_WAITFD_IN and RB_WAITFD_OUT.
|
|
|
|
* So it's enough to wait only RB_WAITFD_OUT and check the pending error
|
|
|
|
* by getsockopt().
|
|
|
|
*
|
|
|
|
* Note: rb_wait_for_single_fd already retries on EINTR/ERESTART
|
|
|
|
*/
|
2020-09-25 10:20:18 +03:00
|
|
|
revents = rb_wait_for_single_fd(fd, RB_WAITFD_IN|RB_WAITFD_OUT, timeout);
|
2014-09-18 01:20:58 +04:00
|
|
|
|
|
|
|
if (revents < 0)
|
|
|
|
return -1;
|
2011-05-04 06:44:28 +04:00
|
|
|
|
2014-09-18 01:20:58 +04:00
|
|
|
sockerrlen = (socklen_t)sizeof(sockerr);
|
|
|
|
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen) < 0)
|
|
|
|
return -1;
|
2009-01-17 07:11:27 +03:00
|
|
|
|
2014-09-18 01:20:58 +04:00
|
|
|
switch (sockerr) {
|
|
|
|
case 0:
|
|
|
|
/*
|
|
|
|
* be defensive in case some platforms set SO_ERROR on the original,
|
|
|
|
* interrupted connect()
|
|
|
|
*/
|
2020-09-25 10:20:18 +03:00
|
|
|
|
|
|
|
/* when the connection timed out, no errno is set and revents is 0. */
|
|
|
|
if (timeout && revents == 0) {
|
|
|
|
errno = ETIMEDOUT;
|
|
|
|
return -1;
|
|
|
|
}
|
2014-09-18 01:20:58 +04:00
|
|
|
case EINTR:
|
|
|
|
#ifdef ERESTART
|
|
|
|
case ERESTART:
|
2009-01-17 07:11:27 +03:00
|
|
|
#endif
|
2014-09-18 01:20:58 +04:00
|
|
|
case EAGAIN:
|
|
|
|
#ifdef EINPROGRESS
|
|
|
|
case EINPROGRESS:
|
2009-01-17 07:11:27 +03:00
|
|
|
#endif
|
2014-09-18 01:20:58 +04:00
|
|
|
#ifdef EALREADY
|
|
|
|
case EALREADY:
|
2009-01-17 07:11:27 +03:00
|
|
|
#endif
|
2014-09-18 01:20:58 +04:00
|
|
|
#ifdef EISCONN
|
|
|
|
case EISCONN:
|
2009-01-17 07:11:27 +03:00
|
|
|
#endif
|
2014-09-18 01:20:58 +04:00
|
|
|
return 0; /* success */
|
|
|
|
default:
|
|
|
|
/* likely (but not limited to): ECONNREFUSED, ETIMEDOUT, EHOSTUNREACH */
|
|
|
|
errno = sockerr;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2009-01-17 07:11:27 +03:00
|
|
|
|
|
|
|
struct connect_arg {
|
|
|
|
int fd;
|
|
|
|
socklen_t len;
|
2014-09-22 04:46:25 +04:00
|
|
|
const struct sockaddr *sockaddr;
|
2009-01-17 07:11:27 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
connect_blocking(void *data)
|
|
|
|
{
|
|
|
|
struct connect_arg *arg = data;
|
|
|
|
return (VALUE)connect(arg->fd, arg->sockaddr, arg->len);
|
|
|
|
}
|
|
|
|
|
2009-01-27 14:07:41 +03:00
|
|
|
#if defined(SOCKS) && !defined(SOCKS5)
|
|
|
|
static VALUE
|
|
|
|
socks_connect_blocking(void *data)
|
|
|
|
{
|
|
|
|
struct connect_arg *arg = data;
|
|
|
|
return (VALUE)Rconnect(arg->fd, arg->sockaddr, arg->len);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-01-17 07:11:27 +03:00
|
|
|
int
|
2020-09-25 10:20:18 +03:00
|
|
|
rsock_connect(int fd, const struct sockaddr *sockaddr, int len, int socks, struct timeval *timeout)
|
2009-01-17 07:11:27 +03:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
rb_blocking_function_t *func = connect_blocking;
|
|
|
|
struct connect_arg arg;
|
|
|
|
|
|
|
|
arg.fd = fd;
|
|
|
|
arg.sockaddr = sockaddr;
|
|
|
|
arg.len = len;
|
|
|
|
#if defined(SOCKS) && !defined(SOCKS5)
|
|
|
|
if (socks) func = socks_connect_blocking;
|
|
|
|
#endif
|
2014-09-18 01:20:58 +04:00
|
|
|
status = (int)BLOCKING_REGION_FD(func, &arg);
|
2011-05-01 19:38:53 +04:00
|
|
|
|
2014-09-18 01:20:58 +04:00
|
|
|
if (status < 0) {
|
|
|
|
switch (errno) {
|
|
|
|
case EINTR:
|
|
|
|
#ifdef ERESTART
|
|
|
|
case ERESTART:
|
2009-01-17 07:11:27 +03:00
|
|
|
#endif
|
2014-09-18 01:20:58 +04:00
|
|
|
case EAGAIN:
|
|
|
|
#ifdef EINPROGRESS
|
|
|
|
case EINPROGRESS:
|
2009-01-17 07:11:27 +03:00
|
|
|
#endif
|
2020-09-25 10:20:18 +03:00
|
|
|
return wait_connectable(fd, timeout);
|
2014-09-18 01:20:58 +04:00
|
|
|
}
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
2014-09-18 01:20:58 +04:00
|
|
|
return status;
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
|
|
|
|
2018-11-22 11:46:51 +03:00
|
|
|
void
|
|
|
|
rsock_make_fd_nonblock(int fd)
|
2009-01-17 07:11:27 +03:00
|
|
|
{
|
2020-05-14 13:10:55 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
2009-01-17 07:11:27 +03:00
|
|
|
int flags;
|
|
|
|
#ifdef F_GETFL
|
|
|
|
flags = fcntl(fd, F_GETFL);
|
|
|
|
if (flags == -1) {
|
2013-04-06 06:39:44 +04:00
|
|
|
rb_sys_fail("fnctl(2)");
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
flags = 0;
|
|
|
|
#endif
|
|
|
|
flags |= O_NONBLOCK;
|
|
|
|
if (fcntl(fd, F_SETFL, flags) == -1) {
|
2013-04-06 06:39:44 +04:00
|
|
|
rb_sys_fail("fnctl(2)");
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-01 03:26:19 +04:00
|
|
|
static int
|
2020-05-14 13:10:55 +03:00
|
|
|
cloexec_accept(int socket, struct sockaddr *address, socklen_t *address_len)
|
2011-11-01 03:26:19 +04:00
|
|
|
{
|
2013-01-30 16:01:51 +04:00
|
|
|
socklen_t len0 = 0;
|
2013-02-04 10:19:40 +04:00
|
|
|
if (address_len) len0 = *address_len;
|
2020-05-14 13:10:55 +03:00
|
|
|
|
2013-02-04 10:19:40 +04:00
|
|
|
#ifdef HAVE_ACCEPT4
|
2020-05-14 13:10:55 +03:00
|
|
|
int flags = SOCK_CLOEXEC;
|
|
|
|
|
2015-05-17 08:56:07 +03:00
|
|
|
#ifdef SOCK_NONBLOCK
|
2020-05-14 13:10:55 +03:00
|
|
|
flags |= SOCK_NONBLOCK;
|
2013-04-05 20:40:36 +04:00
|
|
|
#endif
|
2020-05-14 13:10:55 +03:00
|
|
|
|
|
|
|
int result = accept4(socket, address, address_len, flags);
|
|
|
|
if (result == -1) return -1;
|
|
|
|
|
2015-05-17 08:56:07 +03:00
|
|
|
#ifndef SOCK_NONBLOCK
|
2020-05-14 13:10:55 +03:00
|
|
|
rsock_make_fd_nonblock(result);
|
2015-05-17 08:56:07 +03:00
|
|
|
#endif
|
2020-05-14 13:10:55 +03:00
|
|
|
#else
|
|
|
|
int result = accept(socket, address, address_len);
|
|
|
|
if (result == -1) return -1;
|
|
|
|
|
|
|
|
rb_maygvl_fd_fix_cloexec(result);
|
|
|
|
rsock_make_fd_nonblock(result);
|
2011-11-01 03:26:19 +04:00
|
|
|
#endif
|
2020-05-14 13:10:55 +03:00
|
|
|
|
2013-01-30 16:01:51 +04:00
|
|
|
if (address_len && len0 < *address_len) *address_len = len0;
|
2020-05-14 13:10:55 +03:00
|
|
|
return result;
|
2011-11-01 03:26:19 +04:00
|
|
|
}
|
|
|
|
|
2009-01-17 07:11:27 +03:00
|
|
|
VALUE
|
socket: avoid arg parsing in rsock_s_accept_nonblock
* ext/socket/init.c (rsock_s_accept_nonblock): avoid parsing args
[ruby-core:71439] [Feature #11339]
* ext/socket/rubysocket.h: adjust prototype
* ext/socket/socket.c (sock_accept_nonblock): make private
* ext/socket/tcpserver.c (tcp_accept_nonblock): ditto
* ext/socket/unixserver.c (unix_accept_nonblock): ditto
* ext/socket/lib/socket.rb (Socket#accept_nonblock):
implement as wrapper, move RDoc
(TCPServer#accept_nonblock): ditto
(UNIXServer#accept_nonblock): ditto
target 0: a (ruby 2.3.0dev (2015-11-12 trunk 52550) [x86_64-linux])
target 1: b (ruby 2.3.0dev (2015-11-12 avoid-kwarg-capi 52550) [x86_64-linux]
-----------------------------------------------------------
accept_nonblock
require 'tempfile'
require 'socket'
require 'io/wait'
nr = 500000
Tempfile.create(%w(accept_nonblock .sock)) do |tmp|
path = tmp.path
File.unlink(path)
s = UNIXServer.new(path)
addr = Socket.sockaddr_un(path).freeze
nr.times do
s.accept_nonblock(exception: false)
c = UNIXSocket.new(path)
s.wait_readable
s.accept_nonblock(exception: false).close
c.close
end
end
-----------------------------------------------------------
raw data:
[["accept_nonblock",
[[4.807877402752638,
4.930681671947241,
4.738454818725586,
4.69268161803484,
4.684675686061382],
[4.253904823213816,
4.255124930292368,
4.295955188572407,
4.248479191213846,
4.213303029537201]]]]
Elapsed time: 45.123040065 (sec)
-----------------------------------------------------------
benchmark results:
minimum results in each 5 measurements.
Execution time (sec)
name a b
accept_nonblock 4.685 4.213
Speedup ratio: compare with the result of `a' (greater is better)
name b
accept_nonblock 1.112
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52601 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-11-17 02:40:15 +03:00
|
|
|
rsock_s_accept_nonblock(VALUE klass, VALUE ex, rb_io_t *fptr,
|
2015-03-12 06:03:04 +03:00
|
|
|
struct sockaddr *sockaddr, socklen_t *len)
|
2009-01-17 07:11:27 +03:00
|
|
|
{
|
|
|
|
int fd2;
|
2015-03-12 06:03:04 +03:00
|
|
|
|
2009-01-17 07:11:27 +03:00
|
|
|
rb_io_set_nonblock(fptr);
|
2020-05-14 13:10:55 +03:00
|
|
|
fd2 = cloexec_accept(fptr->fd, (struct sockaddr*)sockaddr, len);
|
2009-01-17 07:11:27 +03:00
|
|
|
if (fd2 < 0) {
|
2015-12-23 11:57:48 +03:00
|
|
|
int e = errno;
|
|
|
|
switch (e) {
|
2009-02-22 07:38:46 +03:00
|
|
|
case EAGAIN:
|
|
|
|
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
|
|
|
|
case EWOULDBLOCK:
|
|
|
|
#endif
|
|
|
|
case ECONNABORTED:
|
|
|
|
#if defined EPROTO
|
|
|
|
case EPROTO:
|
|
|
|
#endif
|
socket: avoid arg parsing in rsock_s_accept_nonblock
* ext/socket/init.c (rsock_s_accept_nonblock): avoid parsing args
[ruby-core:71439] [Feature #11339]
* ext/socket/rubysocket.h: adjust prototype
* ext/socket/socket.c (sock_accept_nonblock): make private
* ext/socket/tcpserver.c (tcp_accept_nonblock): ditto
* ext/socket/unixserver.c (unix_accept_nonblock): ditto
* ext/socket/lib/socket.rb (Socket#accept_nonblock):
implement as wrapper, move RDoc
(TCPServer#accept_nonblock): ditto
(UNIXServer#accept_nonblock): ditto
target 0: a (ruby 2.3.0dev (2015-11-12 trunk 52550) [x86_64-linux])
target 1: b (ruby 2.3.0dev (2015-11-12 avoid-kwarg-capi 52550) [x86_64-linux]
-----------------------------------------------------------
accept_nonblock
require 'tempfile'
require 'socket'
require 'io/wait'
nr = 500000
Tempfile.create(%w(accept_nonblock .sock)) do |tmp|
path = tmp.path
File.unlink(path)
s = UNIXServer.new(path)
addr = Socket.sockaddr_un(path).freeze
nr.times do
s.accept_nonblock(exception: false)
c = UNIXSocket.new(path)
s.wait_readable
s.accept_nonblock(exception: false).close
c.close
end
end
-----------------------------------------------------------
raw data:
[["accept_nonblock",
[[4.807877402752638,
4.930681671947241,
4.738454818725586,
4.69268161803484,
4.684675686061382],
[4.253904823213816,
4.255124930292368,
4.295955188572407,
4.248479191213846,
4.213303029537201]]]]
Elapsed time: 45.123040065 (sec)
-----------------------------------------------------------
benchmark results:
minimum results in each 5 measurements.
Execution time (sec)
name a b
accept_nonblock 4.685 4.213
Speedup ratio: compare with the result of `a' (greater is better)
name b
accept_nonblock 1.112
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52601 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2015-11-17 02:40:15 +03:00
|
|
|
if (ex == Qfalse)
|
2015-03-12 06:03:04 +03:00
|
|
|
return sym_wait_readable;
|
2015-12-23 17:58:47 +03:00
|
|
|
rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE, e, "accept(2) would block");
|
2009-02-22 07:38:46 +03:00
|
|
|
}
|
2015-12-23 11:57:48 +03:00
|
|
|
rb_syserr_fail(e, "accept(2)");
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
2011-11-01 07:04:03 +04:00
|
|
|
rb_update_max_fd(fd2);
|
2009-03-01 09:30:41 +03:00
|
|
|
return rsock_init_sock(rb_obj_alloc(klass), fd2);
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
struct accept_arg {
|
|
|
|
int fd;
|
|
|
|
struct sockaddr *sockaddr;
|
|
|
|
socklen_t *len;
|
|
|
|
};
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
accept_blocking(void *data)
|
|
|
|
{
|
|
|
|
struct accept_arg *arg = data;
|
2020-05-14 13:10:55 +03:00
|
|
|
return (VALUE)cloexec_accept(arg->fd, arg->sockaddr, arg->len);
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
2021-05-08 15:13:47 +03:00
|
|
|
rsock_s_accept(VALUE klass, VALUE io, struct sockaddr *sockaddr, socklen_t *len)
|
2009-01-17 07:11:27 +03:00
|
|
|
{
|
2021-05-08 15:13:47 +03:00
|
|
|
rb_io_t *fptr = NULL;
|
|
|
|
RB_IO_POINTER(io, fptr);
|
|
|
|
|
|
|
|
struct accept_arg accept_arg = {
|
|
|
|
.fd = fptr->fd,
|
|
|
|
.sockaddr = sockaddr,
|
|
|
|
.len = len
|
|
|
|
};
|
|
|
|
|
2021-06-19 04:47:16 +03:00
|
|
|
int retry = 0, peer;
|
2009-01-17 07:11:27 +03:00
|
|
|
|
|
|
|
retry:
|
2021-06-19 04:47:16 +03:00
|
|
|
#ifdef RSOCK_WAIT_BEFORE_BLOCKING
|
|
|
|
rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), Qnil);
|
|
|
|
#endif
|
|
|
|
peer = (int)BLOCKING_REGION_FD(accept_blocking, &accept_arg);
|
2021-05-08 15:13:47 +03:00
|
|
|
if (peer < 0) {
|
|
|
|
int error = errno;
|
|
|
|
|
|
|
|
switch (error) {
|
|
|
|
case EMFILE:
|
|
|
|
case ENFILE:
|
|
|
|
case ENOMEM:
|
|
|
|
if (retry) break;
|
|
|
|
rb_gc();
|
|
|
|
retry = 1;
|
|
|
|
goto retry;
|
|
|
|
default:
|
|
|
|
if (!rb_io_maybe_wait_readable(error, io, Qnil)) break;
|
|
|
|
retry = 0;
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
rb_syserr_fail(error, "accept(2)");
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
2021-05-08 15:13:47 +03:00
|
|
|
|
|
|
|
rb_update_max_fd(peer);
|
|
|
|
|
|
|
|
if (!klass) return INT2NUM(peer);
|
|
|
|
|
|
|
|
return rsock_init_sock(rb_obj_alloc(klass), peer);
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|
|
|
|
|
2009-03-01 14:58:13 +03:00
|
|
|
int
|
2015-07-02 04:58:14 +03:00
|
|
|
rsock_getfamily(rb_io_t *fptr)
|
2009-02-09 16:56:43 +03:00
|
|
|
{
|
2013-02-24 21:51:17 +04:00
|
|
|
union_sockaddr ss;
|
2010-04-28 12:14:13 +04:00
|
|
|
socklen_t sslen = (socklen_t)sizeof(ss);
|
2015-07-02 04:58:14 +03:00
|
|
|
int cached = fptr->mode & FMODE_SOCK;
|
|
|
|
|
|
|
|
if (cached) {
|
|
|
|
switch (cached) {
|
|
|
|
#ifdef AF_UNIX
|
|
|
|
case FMODE_UNIX: return AF_UNIX;
|
|
|
|
#endif
|
|
|
|
case FMODE_INET: return AF_INET;
|
|
|
|
case FMODE_INET6: return AF_INET6;
|
|
|
|
}
|
|
|
|
}
|
2009-02-09 16:56:43 +03:00
|
|
|
|
2013-02-24 21:51:17 +04:00
|
|
|
ss.addr.sa_family = AF_UNSPEC;
|
2015-07-02 04:58:14 +03:00
|
|
|
if (getsockname(fptr->fd, &ss.addr, &sslen) < 0)
|
2009-07-17 08:38:27 +04:00
|
|
|
return AF_UNSPEC;
|
2009-02-09 16:56:43 +03:00
|
|
|
|
2015-07-02 04:58:14 +03:00
|
|
|
switch (ss.addr.sa_family) {
|
|
|
|
#ifdef AF_UNIX
|
|
|
|
case AF_UNIX: fptr->mode |= FMODE_UNIX; break;
|
|
|
|
#endif
|
|
|
|
case AF_INET: fptr->mode |= FMODE_INET; break;
|
|
|
|
case AF_INET6: fptr->mode |= FMODE_INET6; break;
|
|
|
|
}
|
|
|
|
|
2013-02-24 21:51:17 +04:00
|
|
|
return ss.addr.sa_family;
|
2009-02-09 16:56:43 +03:00
|
|
|
}
|
|
|
|
|
2009-01-17 07:11:27 +03:00
|
|
|
void
|
2014-09-30 09:25:32 +04:00
|
|
|
rsock_init_socket_init(void)
|
2009-01-17 07:11:27 +03:00
|
|
|
{
|
2011-08-16 03:08:39 +04:00
|
|
|
/*
|
|
|
|
* SocketError is the error class for socket.
|
|
|
|
*/
|
2009-01-17 07:11:27 +03:00
|
|
|
rb_eSocket = rb_define_class("SocketError", rb_eStandardError);
|
2010-03-22 19:15:21 +03:00
|
|
|
rsock_init_ipsocket();
|
|
|
|
rsock_init_tcpsocket();
|
|
|
|
rsock_init_tcpserver();
|
|
|
|
rsock_init_sockssocket();
|
|
|
|
rsock_init_udpsocket();
|
|
|
|
rsock_init_unixsocket();
|
|
|
|
rsock_init_unixserver();
|
|
|
|
rsock_init_sockopt();
|
|
|
|
rsock_init_ancdata();
|
|
|
|
rsock_init_addrinfo();
|
2013-05-11 12:32:26 +04:00
|
|
|
rsock_init_sockifaddr();
|
2010-03-22 19:15:21 +03:00
|
|
|
rsock_init_socket_constants();
|
2015-03-12 06:03:04 +03:00
|
|
|
|
|
|
|
#undef rb_intern
|
|
|
|
sym_wait_readable = ID2SYM(rb_intern("wait_readable"));
|
2017-10-28 02:26:48 +03:00
|
|
|
|
|
|
|
#if MSG_DONTWAIT_RELIABLE
|
|
|
|
sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
|
|
|
|
#endif
|
2009-01-17 07:11:27 +03:00
|
|
|
}
|