From 365557f111b453289a5e2ce0cdda0899ae248c71 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Fri, 8 Nov 2019 09:39:28 +0900 Subject: Define IO#read/write_nonblock with builtins. IO#read/write_nonblock methods are defined in prelude.rb with special private method __read/write_nonblock to reduce keyword parameters overhead. We can move them into io.rb with builtin functions. --- .document | 1 + common.mk | 7 +++- inits.c | 1 + io.c | 17 ++++++--- io.rb | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ prelude.rb | 123 ------------------------------------------------------------- 6 files changed, 142 insertions(+), 130 deletions(-) create mode 100644 io.rb diff --git a/.document b/.document index c0c9cded79..2a76ac0705 100644 --- a/.document +++ b/.document @@ -13,6 +13,7 @@ rbconfig.rb trace_point.rb ast.rb +io.rb # the lib/ directory (which has its own .document file) lib diff --git a/common.mk b/common.mk index 15561b6307..2b9fe1dbaa 100644 --- a/common.mk +++ b/common.mk @@ -1094,7 +1094,7 @@ preludes: {$(VPATH)}prelude.c preludes: {$(VPATH)}miniprelude.c preludes: {$(srcdir)}golf_prelude.c -BUILTIN_RB_SRCS = $(srcdir)/trace_point.rb $(srcdir)/ast.rb +BUILTIN_RB_SRCS = $(srcdir)/trace_point.rb $(srcdir)/ast.rb $(srcdir)/io.rb builtin_binary.inc: $(PREP) $(BUILTIN_RB_SRCS) $(srcdir)/tool/mk_builtin_binary.rb $(Q) $(MINIRUBY) $(srcdir)/tool/mk_builtin_binary.rb @@ -1105,6 +1105,9 @@ load_trace_point.inc: $(srcdir)/trace_point.rb $(srcdir)/tool/mk_builtin_loader. load_ast.inc: $(srcdir)/ast.rb $(srcdir)/tool/mk_builtin_loader.rb $(Q) $(BASERUBY) $(srcdir)/tool/mk_builtin_loader.rb $(srcdir)/ast.rb +load_io.inc: $(srcdir)/io.rb $(srcdir)/tool/mk_builtin_loader.rb + $(Q) $(BASERUBY) $(srcdir)/tool/mk_builtin_loader.rb $(srcdir)/io.rb + $(srcdir)/revision.h: $(Q)$(gnumake:yes=#) $(RM) $(@F) $(Q)$(gnumake:yes=#) exit > $@ || exit > $(@F) @@ -2217,6 +2220,7 @@ io.$(OBJEXT): $(CCAN_DIR)/str/str.h io.$(OBJEXT): $(hdrdir)/ruby.h io.$(OBJEXT): $(hdrdir)/ruby/ruby.h io.$(OBJEXT): {$(VPATH)}assert.h +io.$(OBJEXT): {$(VPATH)}builtin.h io.$(OBJEXT): {$(VPATH)}config.h io.$(OBJEXT): {$(VPATH)}defines.h io.$(OBJEXT): {$(VPATH)}dln.h @@ -2227,6 +2231,7 @@ io.$(OBJEXT): {$(VPATH)}intern.h io.$(OBJEXT): {$(VPATH)}internal.h io.$(OBJEXT): {$(VPATH)}io.c io.$(OBJEXT): {$(VPATH)}io.h +io.$(OBJEXT): {$(VPATH)}load_io.inc io.$(OBJEXT): {$(VPATH)}method.h io.$(OBJEXT): {$(VPATH)}missing.h io.$(OBJEXT): {$(VPATH)}node.h diff --git a/inits.c b/inits.c index 16569ad32a..3faa8b8c56 100644 --- a/inits.c +++ b/inits.c @@ -70,6 +70,7 @@ rb_call_inits(void) CALL(builtin); + CALL(IO_nonblock); CALL(ast); CALL(vm_trace); } diff --git a/io.c b/io.c index eccd429352..ada3300645 100644 --- a/io.c +++ b/io.c @@ -22,6 +22,7 @@ #include #include "ruby_atomic.h" #include "ccan/list/list.h" +#include "builtin.h" #undef free #define free(x) xfree(x) @@ -2941,7 +2942,7 @@ io_nonblock_eof(int no_exception) /* :nodoc: */ static VALUE -io_read_nonblock(VALUE io, VALUE length, VALUE str, VALUE ex) +io_read_nonblock(rb_execution_context_t *ec, VALUE io, VALUE length, VALUE str, VALUE ex) { rb_io_t *fptr; long n, len; @@ -2993,7 +2994,7 @@ io_read_nonblock(VALUE io, VALUE length, VALUE str, VALUE ex) /* :nodoc: */ static VALUE -io_write_nonblock(VALUE io, VALUE str, VALUE ex) +io_write_nonblock(rb_execution_context_t *ec, VALUE io, VALUE str, VALUE ex) { rb_io_t *fptr; long n; @@ -13312,10 +13313,6 @@ Init_IO(void) rb_define_method(rb_cIO, "readlines", rb_io_readlines, -1); - /* for prelude.rb use only: */ - rb_define_private_method(rb_cIO, "__read_nonblock", io_read_nonblock, 3); - rb_define_private_method(rb_cIO, "__write_nonblock", io_write_nonblock, 2); - rb_define_method(rb_cIO, "readpartial", io_readpartial, -1); rb_define_method(rb_cIO, "read", io_read, -1); rb_define_method(rb_cIO, "write", io_write_m, -1); @@ -13525,3 +13522,11 @@ Init_IO(void) sym_wait_readable = ID2SYM(rb_intern("wait_readable")); sym_wait_writable = ID2SYM(rb_intern("wait_writable")); } + +#include "load_io.inc" + +void +Init_IO_nonblock(void) +{ + load_io(); +} diff --git a/io.rb b/io.rb new file mode 100644 index 0000000000..1b6dddf9e5 --- /dev/null +++ b/io.rb @@ -0,0 +1,123 @@ +class IO + # other IO methods are defined in io.c + + # call-seq: + # ios.read_nonblock(maxlen [, options]) -> string + # ios.read_nonblock(maxlen, outbuf [, options]) -> outbuf + # + # Reads at most maxlen bytes from ios using + # the read(2) system call after O_NONBLOCK is set for + # the underlying file descriptor. + # + # If the optional outbuf argument is present, + # it must reference a String, which will receive the data. + # The outbuf will contain only the received data after the method call + # even if it is not empty at the beginning. + # + # read_nonblock just calls the read(2) system call. + # It causes all errors the read(2) system call causes: Errno::EWOULDBLOCK, Errno::EINTR, etc. + # The caller should care such errors. + # + # If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN, + # it is extended by IO::WaitReadable. + # So IO::WaitReadable can be used to rescue the exceptions for retrying + # read_nonblock. + # + # read_nonblock causes EOFError on EOF. + # + # On some platforms, such as Windows, non-blocking mode is not supported + # on IO objects other than sockets. In such cases, Errno::EBADF will + # be raised. + # + # If the read byte buffer is not empty, + # read_nonblock reads from the buffer like readpartial. + # In this case, the read(2) system call is not called. + # + # When read_nonblock raises an exception kind of IO::WaitReadable, + # read_nonblock should not be called + # until io is readable for avoiding busy loop. + # This can be done as follows. + # + # # emulates blocking read (readpartial). + # begin + # result = io.read_nonblock(maxlen) + # rescue IO::WaitReadable + # IO.select([io]) + # retry + # end + # + # Although IO#read_nonblock doesn't raise IO::WaitWritable. + # OpenSSL::Buffering#read_nonblock can raise IO::WaitWritable. + # If IO and SSL should be used polymorphically, + # IO::WaitWritable should be rescued too. + # See the document of OpenSSL::Buffering#read_nonblock for sample code. + # + # Note that this method is identical to readpartial + # except the non-blocking flag is set. + # + # By specifying a keyword argument _exception_ to +false+, you can indicate + # that read_nonblock should not raise an IO::WaitReadable exception, but + # return the symbol +:wait_readable+ instead. At EOF, it will return nil + # instead of raising EOFError. + def read_nonblock(len, buf = nil, exception: true) + __builtin_io_read_nonblock(len, buf, exception) + end + + # call-seq: + # ios.write_nonblock(string) -> integer + # ios.write_nonblock(string [, options]) -> integer + # + # Writes the given string to ios using + # the write(2) system call after O_NONBLOCK is set for + # the underlying file descriptor. + # + # It returns the number of bytes written. + # + # write_nonblock just calls the write(2) system call. + # It causes all errors the write(2) system call causes: Errno::EWOULDBLOCK, Errno::EINTR, etc. + # The result may also be smaller than string.length (partial write). + # The caller should care such errors and partial write. + # + # If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN, + # it is extended by IO::WaitWritable. + # So IO::WaitWritable can be used to rescue the exceptions for retrying write_nonblock. + # + # # Creates a pipe. + # r, w = IO.pipe + # + # # write_nonblock writes only 65536 bytes and return 65536. + # # (The pipe size is 65536 bytes on this environment.) + # s = "a" * 100000 + # p w.write_nonblock(s) #=> 65536 + # + # # write_nonblock cannot write a byte and raise EWOULDBLOCK (EAGAIN). + # p w.write_nonblock("b") # Resource temporarily unavailable (Errno::EAGAIN) + # + # If the write buffer is not empty, it is flushed at first. + # + # When write_nonblock raises an exception kind of IO::WaitWritable, + # write_nonblock should not be called + # until io is writable for avoiding busy loop. + # This can be done as follows. + # + # begin + # result = io.write_nonblock(string) + # rescue IO::WaitWritable, Errno::EINTR + # IO.select(nil, [io]) + # retry + # end + # + # Note that this doesn't guarantee to write all data in string. + # The length written is reported as result and it should be checked later. + # + # On some platforms such as Windows, write_nonblock is not supported + # according to the kind of the IO object. + # In such cases, write_nonblock raises Errno::EBADF. + # + # By specifying a keyword argument _exception_ to +false+, you can indicate + # that write_nonblock should not raise an IO::WaitWritable exception, but + # return the symbol +:wait_writable+ instead. + def write_nonblock(buf, exception: true) + __builtin_io_write_nonblock(buf, exception) + end +end diff --git a/prelude.rb b/prelude.rb index 7cc2d260b6..be249af751 100644 --- a/prelude.rb +++ b/prelude.rb @@ -13,129 +13,6 @@ class << Thread end end -class IO - - # call-seq: - # ios.read_nonblock(maxlen [, options]) -> string - # ios.read_nonblock(maxlen, outbuf [, options]) -> outbuf - # - # Reads at most maxlen bytes from ios using - # the read(2) system call after O_NONBLOCK is set for - # the underlying file descriptor. - # - # If the optional outbuf argument is present, - # it must reference a String, which will receive the data. - # The outbuf will contain only the received data after the method call - # even if it is not empty at the beginning. - # - # read_nonblock just calls the read(2) system call. - # It causes all errors the read(2) system call causes: Errno::EWOULDBLOCK, Errno::EINTR, etc. - # The caller should care such errors. - # - # If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN, - # it is extended by IO::WaitReadable. - # So IO::WaitReadable can be used to rescue the exceptions for retrying - # read_nonblock. - # - # read_nonblock causes EOFError on EOF. - # - # On some platforms, such as Windows, non-blocking mode is not supported - # on IO objects other than sockets. In such cases, Errno::EBADF will - # be raised. - # - # If the read byte buffer is not empty, - # read_nonblock reads from the buffer like readpartial. - # In this case, the read(2) system call is not called. - # - # When read_nonblock raises an exception kind of IO::WaitReadable, - # read_nonblock should not be called - # until io is readable for avoiding busy loop. - # This can be done as follows. - # - # # emulates blocking read (readpartial). - # begin - # result = io.read_nonblock(maxlen) - # rescue IO::WaitReadable - # IO.select([io]) - # retry - # end - # - # Although IO#read_nonblock doesn't raise IO::WaitWritable. - # OpenSSL::Buffering#read_nonblock can raise IO::WaitWritable. - # If IO and SSL should be used polymorphically, - # IO::WaitWritable should be rescued too. - # See the document of OpenSSL::Buffering#read_nonblock for sample code. - # - # Note that this method is identical to readpartial - # except the non-blocking flag is set. - # - # By specifying a keyword argument _exception_ to +false+, you can indicate - # that read_nonblock should not raise an IO::WaitReadable exception, but - # return the symbol +:wait_readable+ instead. At EOF, it will return nil - # instead of raising EOFError. - def read_nonblock(len, buf = nil, exception: true) - __read_nonblock(len, buf, exception) - end - - # call-seq: - # ios.write_nonblock(string) -> integer - # ios.write_nonblock(string [, options]) -> integer - # - # Writes the given string to ios using - # the write(2) system call after O_NONBLOCK is set for - # the underlying file descriptor. - # - # It returns the number of bytes written. - # - # write_nonblock just calls the write(2) system call. - # It causes all errors the write(2) system call causes: Errno::EWOULDBLOCK, Errno::EINTR, etc. - # The result may also be smaller than string.length (partial write). - # The caller should care such errors and partial write. - # - # If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN, - # it is extended by IO::WaitWritable. - # So IO::WaitWritable can be used to rescue the exceptions for retrying write_nonblock. - # - # # Creates a pipe. - # r, w = IO.pipe - # - # # write_nonblock writes only 65536 bytes and return 65536. - # # (The pipe size is 65536 bytes on this environment.) - # s = "a" * 100000 - # p w.write_nonblock(s) #=> 65536 - # - # # write_nonblock cannot write a byte and raise EWOULDBLOCK (EAGAIN). - # p w.write_nonblock("b") # Resource temporarily unavailable (Errno::EAGAIN) - # - # If the write buffer is not empty, it is flushed at first. - # - # When write_nonblock raises an exception kind of IO::WaitWritable, - # write_nonblock should not be called - # until io is writable for avoiding busy loop. - # This can be done as follows. - # - # begin - # result = io.write_nonblock(string) - # rescue IO::WaitWritable, Errno::EINTR - # IO.select(nil, [io]) - # retry - # end - # - # Note that this doesn't guarantee to write all data in string. - # The length written is reported as result and it should be checked later. - # - # On some platforms such as Windows, write_nonblock is not supported - # according to the kind of the IO object. - # In such cases, write_nonblock raises Errno::EBADF. - # - # By specifying a keyword argument _exception_ to +false+, you can indicate - # that write_nonblock should not raise an IO::WaitWritable exception, but - # return the symbol +:wait_writable+ instead. - def write_nonblock(buf, exception: true) - __write_nonblock(buf, exception) - end -end - class Binding # :nodoc: def irb -- cgit v1.2.3