lib/lightio/library/io.rb in lightio-0.2.2 vs lib/lightio/library/io.rb in lightio-0.3.0
- old
+ new
@@ -2,49 +2,124 @@
module LightIO::Library
class IO
include LightIO::Wrap::IOWrapper
wrap ::IO
- extend Forwardable
- def_delegators :@io_watcher, :wait, :wait_readable, :wait_writable
+ wrap_blocking_methods :read, :write
- wrap_blocking_methods :read, :write, exception_symbol: false
-
alias_method :<<, :write
def read(length=nil, outbuf=nil)
raise ArgumentError, "negative length #{length} given" if length && length < 0
(outbuf ||= "").clear
loop do
readlen = length.nil? ? 4096 : length - outbuf.size
- begin
- outbuf << wait_nonblock(:read_nonblock, readlen, exception_symbol: false)
+ if (data = wait_nonblock(:read_nonblock, readlen))
+ outbuf << data
if length == outbuf.size
return outbuf
end
- rescue EOFError
- return outbuf
+ else
+ return length.nil? ? '' : nil
end
end
end
def readpartial(maxlen, outbuf=nil)
(outbuf ||= "").clear
- outbuf << wait_nonblock(:read_nonblock, maxlen, exception_symbol: false)
+ if (data = wait_nonblock(:read_nonblock, maxlen))
+ outbuf << data
+ else
+ raise EOFError, 'end of file reached'
+ end
outbuf
end
+ def getbyte
+ read(1)
+ end
+
+ def getc
+ wait_readable
+ @io.getc
+ end
+
+ def readline(*args)
+ line = gets(*args)
+ raise EOFError, 'end of file reached' if line.nil?
+ line
+ end
+
+ def readlines(*args)
+ result = []
+ until eof?
+ result << readline(*args)
+ end
+ result
+ end
+
+ def readchar
+ c = getc
+ raise EOFError, 'end of file reached' if c.nil?
+ c
+ end
+
+ def readbyte
+ b = getbyte
+ raise EOFError, 'end of file reached' if b.nil?
+ b
+ end
+
+ def eof
+ wait_readable
+ @io.eof?
+ end
+
+ alias eof? eof
+
+ def gets(*args)
+ raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..2)" if args.size > 2
+ return nil if eof?
+ sep = $/
+ if args[0].is_a?(Numeric)
+ limit = args.shift
+ else
+ sep = args.shift if args.size > 0
+ limit = args.shift if args.first.is_a?(Numeric)
+ end
+ s = ''
+ while (c = getc)
+ s << c
+ break if limit && s.size == limit
+ break if c == sep
+ end
+ $_ = s
+ end
+
def close(*args)
# close watcher before io closed
@io_watcher.close
@io.close(*args)
end
def to_io
self
end
+ private
+ def wait_readable
+ # if IO is already readable, continue wait_readable may block it forever
+ # so use getbyte detect this situation
+ # Maybe move getc and gets to thread pool is a good idea
+ b = getbyte
+ if b
+ ungetbyte(b)
+ return
+ end
+ @io_watcher.wait_readable
+ end
+
class << self
def open(*args)
io = self.new(*args)
yield io
ensure
@@ -63,21 +138,19 @@
end
[IO._wrap(r), IO._wrap(w)]
end
def select(read_fds, write_fds=nil, _except_fds=nil, timeout=nil)
- timer = 0
+ timer = timeout && Time.now
# run once ioloop
LightIO.sleep 0
loop do
r_fds = (read_fds || []).select {|fd| fd.closed? ? raise(IOError, 'closed stream') : fd.instance_variable_get(:@io_watcher).readable?}
w_fds = (write_fds || []).select {|fd| fd.closed? ? raise(IOError, 'closed stream') : fd.instance_variable_get(:@io_watcher).writable?}
e_fds = []
if r_fds.empty? && w_fds.empty?
- interval = 0.1
- LightIO.sleep interval
- timer += interval
- if timeout && timer > timeout
+ LightIO.sleep 0
+ if timeout && Time.now - timer > timeout
return nil
end
else
return [r_fds, w_fds, e_fds]
end