Skip to content

Commit

Permalink
Merge 009f4a5 into fe3fdd2
Browse files Browse the repository at this point in the history
  • Loading branch information
grddev committed May 10, 2016
2 parents fe3fdd2 + 009f4a5 commit 959f88d
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 16 deletions.
25 changes: 19 additions & 6 deletions lib/ione/byte_buffer.rb
Expand Up @@ -253,22 +253,35 @@ def update(location, bytes)
# in situations where a loop wants to offer some bytes but can't be sure
# how many will be accepted — for example when writing to a socket.
#
# By providing the readonly argument, the peek is even cheaper in that it
# only considers the read buffer. In this mode, the method returns nil
# to signify that the read buffer is empty. With just a single reader,
# like the IO reactor, where reads only happen in one thread, this means
# that `cheap_peek(true)` can be called without holding a lock to protect
# against concurrent writes
#
# @example feeding bytes to a socket
# while true
# _, writables, _ = IO.select(nil, sockets)
# if writables
# writables.each do |io|
# n = io.write_nonblock(buffer.cheap_peek)
# bytes = buffer.cheap_peek(true)
# unless bytes
# bytes = buffer_lock.synchronize { buffer.cheap_peak }
# end
# n = io.write_nonblock(bytes)
# buffer.discard(n)
# end
# end
#
# @return [String] some bytes from the start of the buffer
def cheap_peek
if @offset >= @read_buffer.bytesize
swap_buffers
# @param [Boolean] readonly to specify to only look at the read buffer
# @return [String, nil] some bytes from the start of the buffer, or nil when read buffer empty in readonly mode
def cheap_peek(readonly = false)
has_read_buffer = @offset < @read_buffer.bytesize
if has_read_buffer || !readonly
swap_buffers unless has_read_buffer
@read_buffer[@offset, @read_buffer.bytesize - @offset]
end
@read_buffer[@offset, @read_buffer.bytesize - @offset]
end

def eql?(other)
Expand Down
31 changes: 21 additions & 10 deletions lib/ione/io/base_connection.rb
Expand Up @@ -163,18 +163,29 @@ def write(bytes=nil)
def flush
should_close = false
if @state == CONNECTED_STATE || @state == DRAINING_STATE
@lock.lock
begin
if @writable
bytes_written = @io.write_nonblock(@write_buffer.cheap_peek)
@write_buffer.discard(bytes_written)
bytes = @write_buffer.cheap_peek(true)
unless bytes
@lock.lock
begin
if @writable
bytes = @write_buffer.cheap_peek
end
ensure
@lock.unlock
end
@writable = !@write_buffer.empty?
if @state == DRAINING_STATE && !@writable
should_close = true
end
if bytes
bytes_written = @io.write_nonblock(bytes)
@lock.lock
begin
@write_buffer.discard(bytes_written)
@writable = !@write_buffer.empty?
if @state == DRAINING_STATE && !@writable
should_close = true
end
ensure
@lock.unlock
end
ensure
@lock.unlock
end
close if should_close
end
Expand Down
22 changes: 22 additions & 0 deletions spec/ione/byte_buffer_spec.rb
Expand Up @@ -330,6 +330,28 @@ module Ione
x.bytesize.should be <= buffer.bytesize
buffer.to_str.should start_with(x)
end

it 'considers contents in the write when read buffer consumed' do
buffer.append('foo')
buffer.append('bar')
buffer.read_byte
buffer.discard(5)
buffer.append('hello')
x = buffer.cheap_peek
x.bytesize.should be > 0
x.bytesize.should be <= buffer.bytesize
buffer.to_str.should start_with(x)
end

it 'returns nil in readonly mode when read buffer is consumed' do
buffer.append('foo')
buffer.append('bar')
buffer.read_byte
buffer.discard(5)
buffer.append('hello')
x = buffer.cheap_peek(true)
x.should be_nil
end
end

describe '#getbyte' do
Expand Down

0 comments on commit 959f88d

Please sign in to comment.