@@ -68,20 +68,20 @@ def write(str)
6868 if @timeout . nil? or @timeout == 0
6969 @handle . write ( str )
7070 else
71+ deadline = Process . clock_gettime ( Process ::CLOCK_MONOTONIC ) + @timeout
7172 len = 0
72- start = Time . now
73- while Time . now - start < @timeout
74- rd , wr , = IO . select ( nil , [ @handle ] , nil , @timeout )
75- if wr and not wr . empty?
73+
74+ while len < str . length
75+ begin
7676 len += @handle . write_nonblock ( str [ len ..-1 ] )
77- break if len >= str . length
77+ rescue IO ::WaitWritable
78+ wait_for ( :write , deadline , str . length )
79+ rescue IO ::WaitReadable
80+ wait_for ( :read , deadline , str . length )
7881 end
7982 end
80- if len < str . length
81- raise TransportException . new ( TransportException ::TIMED_OUT , "Socket: Timed out writing #{ str . length } bytes to #{ @desc } " )
82- else
83- len
84- end
83+
84+ len
8585 end
8686 rescue TransportException => e
8787 # pass this on
@@ -100,19 +100,16 @@ def read(sz)
100100 if @timeout . nil? or @timeout == 0
101101 data = @handle . readpartial ( sz )
102102 else
103- # it's possible to interrupt select for something other than the timeout
104- # so we need to ensure we've waited long enough, but not too long
105- start = Time . now
106- timespent = 0
107- rd = loop do
108- rd , = IO . select ( [ @handle ] , nil , nil , @timeout - timespent )
109- timespent = Time . now - start
110- break rd if ( rd and not rd . empty? ) or timespent >= @timeout
111- end
112- if rd . nil? or rd . empty?
113- raise TransportException . new ( TransportException ::TIMED_OUT , "Socket: Timed out reading #{ sz } bytes from #{ @desc } " )
114- else
115- data = @handle . readpartial ( sz )
103+ deadline = Process . clock_gettime ( Process ::CLOCK_MONOTONIC ) + @timeout
104+
105+ data = loop do
106+ begin
107+ break @handle . read_nonblock ( sz )
108+ rescue IO ::WaitReadable
109+ wait_for ( :read , deadline , sz )
110+ rescue IO ::WaitWritable
111+ wait_for ( :write , deadline , sz )
112+ end
116113 end
117114 end
118115 rescue TransportException => e
@@ -141,5 +138,33 @@ def to_io
141138 def to_s
142139 "socket(#{ @host } :#{ @port } )"
143140 end
141+
142+ private
143+
144+ def wait_for ( operation , deadline , sz )
145+ rd_ary , wr_ary = case operation
146+ when :read
147+ [ [ @handle ] , nil ]
148+ when :write
149+ [ nil , [ @handle ] ]
150+ else
151+ raise ArgumentError , "Unknown IO wait operation: #{ operation . inspect } "
152+ end
153+
154+ loop do
155+ remaining = deadline - Process . clock_gettime ( Process ::CLOCK_MONOTONIC )
156+ if remaining <= 0
157+ case operation
158+ when :read
159+ raise TransportException . new ( TransportException ::TIMED_OUT , "Socket: Timed out reading #{ sz } bytes from #{ @desc } " )
160+ when :write
161+ raise TransportException . new ( TransportException ::TIMED_OUT , "Socket: Timed out writing #{ sz } bytes to #{ @desc } " )
162+ end
163+ end
164+
165+ rd , wr , = IO . select ( rd_ary , wr_ary , nil , remaining )
166+ return if ( rd && !rd . empty? ) || ( wr && !wr . empty? )
167+ end
168+ end
144169 end
145170end
0 commit comments