# IO-Management # Fires if IO is ready a given block. # # Select.open do |sel| # sock = TCPSocket.new 'localhost', 8090 # buffer = '' # # sel.on_read serv do |sock| # buffer << sock.sysread # end # # sel.on_write STDOUT do |sock| # return if buffer.empty? # written = sock.syswrite buffer # buffer = buffer[written..-1] # end # # sel.on_error sock do |sock| # sel.close # end # end class Select READ, WRITE, ERROR = 1, 2, 3 attr_reader :read, :write, :error attr_accessor :exit, :exit_on_empty # There are no events to wait? def empty? @read.empty? && @write.empty? && @error.empty? end class < Time.now e.call @times.shift end end class Entry < Time attr_reader :do def do &e @do = e end def call *p @do.call *p end def self.new *a, &e x = self.at *a x.do &e x end end def at a, &e a = Entry.new a, &e if e @times << a @times.sort! end end class Select::Buffer " end def remove x self.slice! 0...x end alias remove! remove def each! x return Enumerator.new( self, :each!, x) unless block_given? s = nil yield s while s = self.slice!( x) self end def + s self.class.new super(s) end end class Select::Socket attr_reader :select, :sock, :bufsize, :parent def initialize opts self.init opts @select.read_set @sock, &method( :event_read) @select.error_set @sock, &method( :event_error) end def init sock: , delimiter: nil, select: nil, bufsize: nil, parent: nil @sock = sock @select = select || Select.new @bufsize = bufsize || 4096 @parent = parent || nil self.delimiter = opts[ :delimiter] || $/ @linebuf, @writebuf = Select::Buffer.new(""), Select::Buffer.new("") end def delimiter= delimiter @delimiter = case delimiter when Regexp then Regexp.new "^.*?"+delimiter.source when Fixnum, Bignum then /^.{#{delimiter}}/ else Regexp.new "^.*?"+Regexp.quote( delimiter.to_s) end end def write str e = @writebuf.empty? @writebuf += str @select.write_set @sock, &method( :event_write) if e end alias :print :write alias :<< :write def puts str self.write "#{str}\n" end def close @select.del @sock @sock.close @parent.event_client_closed self if @parent.respond_to? :event_client_closed rescue IOError end def event_line line end def event_read sock = @sock, event = :read @linebuf += sock.readpartial @bufsize @linebuf.each! @delimiter, &self.method( :event_line) rescue EOFError self.event_eof sock rescue Errno::EPIPE => e self.event_errno e, sock, event rescue IOError self.event_ioerror sock, event rescue Errno::ECONNRESET => e self.event_errno e, sock, event end def event_write sock = @sock, event = :write @writebuf.remove! sock.syswrite( @writebuf) @select.write_del sock if @writebuf.empty? @writebuf rescue IOError @select.del @sock @writebuf end def event_eof sock = @sock self.close end def event_errno errno, sock = @sock, event = :error self.close end def event_error sock = @sock, event = :error self.close end def event_ioerror sock = @sock, event = :error end def closed? @sock.closed? end end class Select::Server attr_reader :select, :clientclass def initialize opts raise "You can't use this class directly. Create a subclass" if self.class.superclass == Object init opts select.read_set @sock, &self.method( :event_conn) end def init sock: , select: nil, clientclass: nil @sock = sock @select = select || Select.new @clientclass = clientclass || Select::Socket @clients = [] end def run @select.run end def delete sock @select.del sock end def event_conn sock = @sock, event = :read a = sock.accept c = event_new_client a if c.kind_of? Hash cc = c[ :clientclass] || @clientclass h = { :sock => a, :select => @select, :parent => self }.update c c = cc.new h end @clients.push c end def event_error sock = @sock, event = :error end def event_new_client sock Hash.new end def event_client_closed client @clients.delete client end def close @select.del @sock @sock.close rescue IOError end def clients_close @clients.each {|c| c.close } end end