fiber/*: more efficient keepalive_timeout expiry
[rainbows.git] / lib / rainbows / fiber / io.rb
blob94996eeda24236fbdbc79d99f0de40cff610320d
1 # -*- encoding: binary -*-
3 # A Fiber-aware IO class, gives users the illusion of a synchronous
4 # interface that yields away from the current Fiber whenever
5 # the underlying descriptor is blocked on reads or write
7 # This is a stable, legacy interface and should be preserved for all
8 # future versions of Rainbows!  However, new apps should use
9 # Rainbows::Fiber::IO::Socket or Rainbows::Fiber::IO::Pipe instead.
11 class Rainbows::Fiber::IO
12   attr_accessor :to_io
14   # :stopdoc:
15   # see Rainbows::Fiber::IO::Compat for initialize implementation
16   class << self
17     alias :[] :new
18   end
19   # :startdoc:
21   # needed to write errors with
22   def write_nonblock(buf)
23     @to_io.write_nonblock(buf)
24   end
26   def kgio_addr
27     @to_io.kgio_addr
28   end
30   # for wrapping output response bodies
31   def each(&block)
32     buf = readpartial(16384)
33     yield buf
34     yield buf while readpartial(16384, buf)
35     rescue EOFError
36       self
37   end
39   def closed?
40     @to_io.closed?
41   end
43   def fileno
44     @to_io.fileno
45   end
47   def write(buf)
48     if @to_io.respond_to?(:kgio_trywrite)
49       begin
50         case rv = @to_io.kgio_trywrite(buf)
51         when nil
52           return
53         when String
54           buf = rv
55         when :wait_writable
56           kgio_wait_writable
57         end
58       end while true
59     else
60       begin
61         (rv = @to_io.write_nonblock(buf)) == buf.bytesize and return
62         buf = byte_slice(buf, rv..-1)
63       rescue Errno::EAGAIN
64         kgio_wait_writable
65       end while true
66     end
67   end
69   def byte_slice(buf, range) # :nodoc:
70     if buf.encoding != Encoding::BINARY
71       buf.dup.force_encoding(Encoding::BINARY)[range]
72     else
73       buf[range]
74     end
75   end
77   # used for reading headers (respecting keepalive_timeout)
78   def timed_read(buf)
79     expire = nil
80     if @to_io.respond_to?(:kgio_tryread)
81       begin
82         case rv = @to_io.kgio_tryread(16384, buf)
83         when :wait_readable
84           return if expire && expire < Time.now
85           expire ||= read_expire
86           kgio_wait_readable
87         else
88           return rv
89         end
90       end while true
91     else
92       begin
93         return @to_io.read_nonblock(16384, buf)
94       rescue Errno::EAGAIN
95         return if expire && expire < Time.now
96         expire ||= read_expire
97         kgio_wait_readable
98       end while true
99     end
100   end
102   def readpartial(length, buf = "")
103     if @to_io.respond_to?(:kgio_tryread)
104       begin
105         rv = @to_io.kgio_tryread(length, buf)
106         case rv
107         when nil
108           raise EOFError, "end of file reached", []
109         when :wait_readable
110           kgio_wait_readable
111         else
112           return rv
113         end
114       end while true
115     else
116       begin
117         return @to_io.read_nonblock(length, buf)
118       rescue Errno::EAGAIN
119         kgio_wait_readable
120       end while true
121     end
122   end
124   def kgio_read(*args)
125     @to_io.kgio_read(*args)
126   end
128   def kgio_read!(*args)
129     @to_io.kgio_read!(*args)
130   end
132   def kgio_trywrite(*args)
133     @to_io.kgio_trywrite(*args)
134   end
136   autoload :Socket, 'rainbows/fiber/io/socket'
137   autoload :Pipe, 'rainbows/fiber/io/pipe'
140 # :stopdoc:
141 require 'rainbows/fiber/io/methods'
142 require 'rainbows/fiber/io/compat'
143 Rainbows::Client.__send__(:include, Rainbows::Fiber::IO::Methods)
144 class Rainbows::Fiber::IO
145   include Rainbows::Fiber::IO::Compat
146   include Rainbows::Fiber::IO::Methods
147   alias_method :wait_readable, :kgio_wait_readable
148   alias_method :wait_writable, :kgio_wait_writable