class LightIO::Watchers::IO

LightIO::Watchers::IO provide a NIO::Monitor wrap to manage 'raw' socket / io

@Example:

#- wait_read for server socket
io_watcher = LightIO::Watchers::IO.new(server_socket, :r)
loop do
  io_watcher.wait_read
  client_socket = server_socket.accept
  # do something
end
io_watcher.close

Public Class Methods

finalizer(monitor) click to toggle source
# File lib/lightio/watchers/io.rb, line 41
def finalizer(monitor)
  proc {monitor.close if monitor && !monitor.close?}
end
new(io, interests=:rw) click to toggle source

Create a io watcher @param [Socket] io An IO-able object @param [Symbol] interests :r, :w, :rw - Is io readable? writeable? or both @return [LightIO::Watchers::IO]

# File lib/lightio/watchers/io.rb, line 20
def initialize(io, interests=:rw)
  @io = io
  @ioloop = LightIO::Core::IOloop.current
  @waiting = false
  @error = nil
  # maintain socket status, see https://github.com/socketry/lightio/issues/1
  @readiness = nil
  @monitor = nil
end

Public Instance Methods

clear_status() click to toggle source
# File lib/lightio/watchers/io.rb, line 71
def clear_status
  @readiness = nil
end
close() click to toggle source
# File lib/lightio/watchers/io.rb, line 101
def close
  set_close_error
  return if closed?
  monitor.close
  callback_on_waiting
end
closed?() click to toggle source
# File lib/lightio/watchers/io.rb, line 49
def closed?
  # check @monitor exists, avoid unnecessary monitor created
  return true unless @monitor
  monitor.closed?
end
monitor(interests=:rw) click to toggle source

NIO::Monitor

# File lib/lightio/watchers/io.rb, line 31
def monitor(interests=:rw)
  @monitor ||= begin
    raise @error if @error
    monitor = @ioloop.add_io_wait(@io, interests) {callback_on_waiting}
    ObjectSpace.define_finalizer(self, self.class.finalizer(monitor))
    monitor
  end
end
readable?() click to toggle source

this method return previous IO.select status should avoid to directly use

# File lib/lightio/watchers/io.rb, line 57
def readable?
  check_monitor_read
  @readiness == :r || @readiness == :rw
end
set_callback(&blk) click to toggle source
# File lib/lightio/watchers/io.rb, line 114
def set_callback(&blk)
  @callback = blk
end
start(ioloop) click to toggle source

just implement IOloop#wait watcher interface

# File lib/lightio/watchers/io.rb, line 110
def start(ioloop)
  # do nothing
end
wait(timeout=nil, mode=:read) click to toggle source
# File lib/lightio/watchers/io.rb, line 89
def wait(timeout=nil, mode=:read)
  LightIO::Timeout.timeout(timeout) do
    check_monitor(mode)
    in_waiting(mode) do
      wait_in_ioloop
    end
    self
  end
rescue Timeout::Error
  nil
end
wait_readable(timeout=nil) click to toggle source

Blocking until io is readable @param [Numeric] timeout return nil after timeout seconds, otherwise return self @return [LightIO::Watchers::IO, nil]

# File lib/lightio/watchers/io.rb, line 78
def wait_readable(timeout=nil)
  wait timeout, :read
end
wait_writable(timeout=nil) click to toggle source

Blocking until io is writable @param [Numeric] timeout return nil after timeout seconds, otherwise return self @return [LightIO::Watchers::IO, nil]

# File lib/lightio/watchers/io.rb, line 85
def wait_writable(timeout=nil)
  wait timeout, :write
end
writable?() click to toggle source

this method return previous IO.select status should avoid to directly use

# File lib/lightio/watchers/io.rb, line 64
def writable?
  check_monitor_write
  @readiness == :w || @readiness == :rw
end
Also aliased as: writeable?
writeable?()
Alias for: writable?

Private Instance Methods

callback_on_waiting() click to toggle source
# File lib/lightio/watchers/io.rb, line 167
def callback_on_waiting
  # update readiness on callback
  @readiness = monitor.readiness
  # only call callback on waiting
  return unless io_is_ready?
  if @error
    # if error occurred in io waiting, send it to callback, see IOloop#wait
    callback&.call(LightIO::Core::Beam::BeamError.new(@error))
  else
    callback&.call
  end
end
check_monitor(mode) click to toggle source
# File lib/lightio/watchers/io.rb, line 123
def check_monitor(mode)
  case mode
    when :read
      check_monitor_read
    when :write
      check_monitor_write
    when :read_write
      check_monitor_read_write
    else
      raise ArgumentError, "get unknown value #{mode}"
  end
end
check_monitor_read() click to toggle source
# File lib/lightio/watchers/io.rb, line 136
def check_monitor_read
  if monitor(:r).interests == :w
    monitor.interests = :rw
  end
end
check_monitor_read_write() click to toggle source
# File lib/lightio/watchers/io.rb, line 148
def check_monitor_read_write
  if monitor(:rw).interests != :rw
    monitor.interests = :rw
  end
end
check_monitor_write() click to toggle source
# File lib/lightio/watchers/io.rb, line 142
def check_monitor_write
  if monitor(:w).interests == :r
    monitor.interests = :rw
  end
end
in_waiting(mode) { || ... } click to toggle source
# File lib/lightio/watchers/io.rb, line 161
def in_waiting(mode)
  @waiting = mode
  yield
  @waiting = false
end
io_is_ready?() click to toggle source
# File lib/lightio/watchers/io.rb, line 180
def io_is_ready?
  return false unless @waiting
  return true if closed?
  if @waiting == :r
    readable?
  elsif @waiting == :w
    writable?
  else
    readable? || writable?
  end
end
set_close_error() click to toggle source
# File lib/lightio/watchers/io.rb, line 119
def set_close_error
  @error ||= IOError.new('closed stream')
end
wait_in_ioloop() click to toggle source

Blocking until io interests is satisfied

# File lib/lightio/watchers/io.rb, line 155
def wait_in_ioloop
  raise LightIO::Error, "Watchers::IO can't cross threads" if @ioloop != LightIO::Core::IOloop.current
  raise EOFError, "can't wait closed IO watcher" if @monitor.closed?
  @ioloop.wait(self)
end