From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS47066 71.19.144.0/20 X-Spam-Status: No, score=-1.9 required=3.0 tests=AWL,BAYES_00, MSGID_FROM_MTA_HEADER shortcircuit=no autolearn=unavailable version=3.3.2 Path: news.gmane.org!not-for-mail From: Eric Wong Newsgroups: gmane.comp.lang.ruby.kgio.general Subject: [PATCH] add kgio_syssend method to wrap send(2) Date: Tue, 4 Feb 2014 01:42:43 +0000 Message-ID: <20140204014243.GA31488@dcvr.yhbt.net> References: <20140204014243.GA31488@dcvr.yhbt.net> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-Trace: ger.gmane.org 1391478172 11209 80.91.229.3 (4 Feb 2014 01:42:52 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Tue, 4 Feb 2014 01:42:52 +0000 (UTC) To: kgio@librelist.org Original-X-From: kgio@librelist.org Tue Feb 04 02:43:01 2014 Return-path: Envelope-to: gclrkg-kgio@m.gmane.org In-Reply-To: <20140204014243.GA31488@dcvr.yhbt.net> List-Archive: List-Help: List-Id: List-Post: List-Subscribe: List-Unsubscribe: Precedence: list Original-Sender: kgio@librelist.org Xref: news.gmane.org gmane.comp.lang.ruby.kgio.general:257 Archived-At: Received: from zedshaw2.xen.prgmr.com ([71.19.156.177]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1WAV2S-0003Id-Ad for gclrkg-kgio@m.gmane.org; Tue, 04 Feb 2014 02:43:00 +0100 Received: from zedshaw2.xen.prgmr.com (unknown [IPv6:::1]) by zedshaw2.xen.prgmr.com (Postfix) with ESMTP id 2C22D73DBF for ; Tue, 4 Feb 2014 01:51:07 +0000 (UTC) This behaves like kgio_trywrite on GNU/Linux, but allows extra flags to be specified. The main purpose of this is to support use of the MSG_MORE flag on GNU/Linux. --- ext/kgio/write.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test/test_syssend.rb | 43 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 test/test_syssend.rb diff --git a/ext/kgio/write.c b/ext/kgio/write.c index 53b5b54..d118fd0 100644 --- a/ext/kgio/write.c +++ b/ext/kgio/write.c @@ -15,6 +15,7 @@ struct wr_args { const char *ptr; long len; int fd; + int flags; }; static void prepare_write(struct wr_args *a, VALUE io, VALUE str) @@ -159,6 +160,56 @@ static VALUE kgio_trysend(VALUE io, VALUE str) # define kgio_trysend kgio_trywrite #endif /* ! USE_MSG_DONTWAIT */ +#ifdef HAVE_RB_THREAD_IO_BLOCKING_REGION +# include "blocking_io_region.h" +#ifdef MSG_DONTWAIT /* Linux only */ +# define MY_MSG_DONTWAIT (MSG_DONTWAIT) +#else +# define MY_MSG_DONTWAIT (0) +#endif + +static VALUE nogvl_send(void *ptr) +{ + struct wr_args *a = ptr; + + return (VALUE)send(a->fd, a->ptr, a->len, a->flags); +} +/* + * call-seq: + * + * io.kgio_syssend(str, flags) -> nil, String or :wait_writable + * + * Returns nil if the write was completed in full. + * + * Returns a String containing the unwritten portion if EAGAIN + * was encountered, but some portion was successfully written. + * + * Returns :wait_writable if EAGAIN is encountered and nothing + * was written. + * + * This method is only available on Ruby 1.9.3 or later. + */ +static VALUE kgio_syssend(VALUE io, VALUE str, VALUE flags) +{ + struct wr_args a; + long n; + + a.flags = NUM2INT(flags); + prepare_write(&a, io, str); + if (a.flags & MY_MSG_DONTWAIT) { + do { + n = (long)send(a.fd, a.ptr, a.len, a.flags); + } while (write_check(&a, n, "send", 0) != 0); + } else { + do { + n = (long)rb_thread_io_blocking_region( + nogvl_send, &a, a.fd); + } while (write_check(&a, n, "send", 0) != 0); + } + return a.buf; +} +#endif /* HAVE_RB_THREAD_IO_BLOCKING_REGION */ + /* * call-seq: * @@ -209,4 +260,8 @@ void init_kgio_write(void) mSocketMethods = rb_define_module_under(mKgio, "SocketMethods"); rb_define_method(mSocketMethods, "kgio_write", kgio_send, 1); rb_define_method(mSocketMethods, "kgio_trywrite", kgio_trysend, 1); + +#ifdef USE_MSG_DONTWAIT + rb_define_method(mSocketMethods, "kgio_syssend", kgio_syssend, 2); +#endif } diff --git a/test/test_syssend.rb b/test/test_syssend.rb new file mode 100644 index 0000000..5089ce3 --- /dev/null +++ b/test/test_syssend.rb @@ -0,0 +1,43 @@ +require 'test/unit' +require 'kgio' + +class TestKgioSyssend < Test::Unit::TestCase + def setup + @host = '127.0.0.1' || ENV["TEST_HOST"] + end + + def test_syssend + srv = Kgio::TCPServer.new(@host, 0) + port = srv.addr[1] + client = TCPSocket.new(@host, port) + acc = srv.kgio_accept + th = Thread.new { client.readpartial(4) } + sleep(0.05) + assert_nil acc.kgio_syssend("HI", Socket::MSG_DONTWAIT | Socket::MSG_MORE) + assert_nil acc.kgio_syssend("HI", Socket::MSG_DONTWAIT) + assert_equal "HIHI", th.value + + buf = "*" * 123 + res = [] + case rv = acc.kgio_syssend(buf, Socket::MSG_DONTWAIT) + when nil + when String + res << rv + when Symbol + res << rv + break + end while true + assert_equal :wait_writable, res.last + if res.size > 1 + assert_kind_of String, res[-2] + else + warn "res too small" + end + + # blocking + th = Thread.new { loop { acc.kgio_syssend("ZZZZ", 0) } } + assert_nil th.join(0.1) + ensure + [ srv, acc, client ].each { |io| io.close if io } + end +end if RUBY_PLATFORM =~ /linux/ -- Eric Wong