From ea20ed92c36f90537ab694324ed7290527977579 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 17 Jun 2020 18:28:40 +0200 Subject: [PATCH] Use the exception: false API of read/write/connect nonblock --- .rubocop.yml | 3 ++ lib/redis/connection/ruby.rb | 95 +++++++++++++++++++----------------- 2 files changed, 53 insertions(+), 45 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index fb20d92e9..96fb535ce 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -57,6 +57,9 @@ Style/ParallelAssignment: Style/NumericPredicate: Enabled: false +Style/IfUnlessModifier: + Enabled: false + Style/SignalException: Exclude: - 'lib/redis/connection/synchrony.rb' diff --git a/lib/redis/connection/ruby.rb b/lib/redis/connection/ruby.rb index a9aca333d..7984e3248 100644 --- a/lib/redis/connection/ruby.rb +++ b/lib/redis/connection/ruby.rb @@ -49,19 +49,18 @@ def gets end def _read_from_socket(nbytes) - begin - read_nonblock(nbytes) - rescue IO::WaitReadable - if IO.select([self], nil, nil, @timeout) - retry - else - raise Redis::TimeoutError - end - rescue IO::WaitWritable - if IO.select(nil, [self], nil, @timeout) - retry - else - raise Redis::TimeoutError + loop do + case chunk = read_nonblock(nbytes, exception: false) + when :wait_readable + unless IO.select([self], nil, nil, @timeout) + raise Redis::TimeoutError + end + when :wait_writable + unless IO.select(nil, [self], nil, @timeout) + raise Redis::TimeoutError + end + when String + return chunk end end rescue EOFError @@ -69,19 +68,24 @@ def _read_from_socket(nbytes) end def _write_to_socket(data) - begin - write_nonblock(data) - rescue IO::WaitWritable - if IO.select(nil, [self], nil, @write_timeout) - retry - else - raise Redis::TimeoutError - end - rescue IO::WaitReadable - if IO.select([self], nil, nil, @write_timeout) - retry - else - raise Redis::TimeoutError + total_bytes_written = 0 + loop do + case bytes_written = write_nonblock(data, exception: false) + when :wait_readable + unless IO.select([self], nil, nil, @timeout) + raise Redis::TimeoutError + end + when :wait_writable + unless IO.select(nil, [self], nil, @timeout) + raise Redis::TimeoutError + end + when Integer + total_bytes_written += bytes_written + if bytes_written < data.bytesize + data.slice!(0, bytes_written) + else + return total_bytes_written + end end end rescue EOFError @@ -245,25 +249,26 @@ def self.connect(host, port, timeout, ssl_params) ssl_sock = new(tcp_sock, ctx) ssl_sock.hostname = host - begin - # Initiate the socket connection in the background. If it doesn't fail - # immediately it will raise an IO::WaitWritable (Errno::EINPROGRESS) - # indicating the connection is in progress. - # Unlike waiting for a tcp socket to connect, you can't time out ssl socket - # connections during the connect phase properly, because IO.select only partially works. - # Instead, you have to retry. - ssl_sock.connect_nonblock - rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable - if IO.select([ssl_sock], nil, nil, timeout) - retry - else - raise TimeoutError - end - rescue IO::WaitWritable - if IO.select(nil, [ssl_sock], nil, timeout) - retry - else - raise TimeoutError + loop do + begin + # Initiate the socket connection in the background. If it doesn't fail + # immediately it will return :wait_writable (Errno::EINPROGRESS) + # indicating the connection is in progress. + # Unlike waiting for a tcp socket to connect, you can't time out ssl socket + # connections during the connect phase properly, because IO.select only partially works. + # Instead, you have to retry. + case result = ssl_sock.connect_nonblock(exception: false) + when :wait_readable + unless IO.select([ssl_sock], nil, nil, timeout) + raise TimeoutError + end + when :wait_writable + unless IO.select(nil, [ssl_sock], nil, timeout) + raise TimeoutError + end + else + break + end end end