class SleepyPenguin::Epoll

The Epoll class provides high-level access to epoll(7) functionality in the Linux 2.6 and later kernels. It provides fork and GC-safety for Ruby objects stored within the IO object and may be passed as an argument to IO.select.

Constants

CLOEXEC

specifies whether close-on-exec flag is set for Epoll.new

CTL_ADD

registers a target IO object via epoll_ctl

CTL_DEL

unregisters a target IO object via epoll_ctl

CTL_MOD

modifies the registration of a target IO object via epoll_ctl

ERR

watch for errors, there is no need to specify this, it is always monitored when an IO is watched

ET

notifications are only Edge Triggered, see epoll(7)

EXCLUSIVE

Sets an exclusive wakeup mode for the epoll object that is being attached to the target IO. This avoids thundering herd scenarios when the same target IO is shared among multiple epoll objects. Available since Linux 4.5

HUP

watch for hangups, there is no need to specify this, it is always monitored when an IO is watched

IN

watch for read/recv operations

ONESHOT

unwatch the descriptor once any event has fired

OUT

watch for write/send operations

PRI

watch for urgent read(2) data

RDHUP

Watch a specified io for shutdown(SHUT_WR) on the remote-end. Available since Linux 2.6.17.

WAKEUP

This prevents system suspend while event is ready. This requires the caller to have the CAP_BLOCK_SUSPEND capability Available since Linux 3.5

Public Class Methods

SleepyPenguin::Epoll.new([flags]) → Epoll object click to toggle source

Creates a new Epoll object with an optional flags argument. flags may currently be :CLOEXEC or 0 (or nil).

# File lib/sleepy_penguin/epoll.rb, line 9
def initialize(create_flags = nil)
  @io = SleepyPenguin::Epoll::IO.new(create_flags)
  @mtx = Mutex.new
  @events = []
  @marks = []
  @pid = $$
  @create_flags = create_flags
  @copies = { @io => self }
end

Public Instance Methods

add(io, events) click to toggle source

Starts watching a given io object with events which may be an Integer bitmask or Array representing arrays to watch for.

# File lib/sleepy_penguin/epoll.rb, line 77
def add(io, events)
  fd = io.to_io.fileno
  events = __event_flags(events)
  @mtx.synchronize do
    __ep_check
    @io.epoll_ctl(CTL_ADD, io, events)
    @events[fd] = events
    @marks[fd] = io
  end
  0
end
close → nil click to toggle source

Closes an existing Epoll object and returns memory back to the kernel. Raises IOError if object is already closed.

# File lib/sleepy_penguin/epoll.rb, line 194
def close
  @mtx.synchronize do
    @copies.delete(@io)
    @io.close
  end
end
closed? → true or false click to toggle source

Returns whether or not an Epoll object is closed.

# File lib/sleepy_penguin/epoll.rb, line 205
def closed?
  @mtx.synchronize do
    @io.closed?
  end
end
del(io) → 0 click to toggle source

Disables an IO object from being watched.

# File lib/sleepy_penguin/epoll.rb, line 93
def del(io)
  fd = io.to_io.fileno
  @mtx.synchronize do
    __ep_check
    @io.epoll_ctl(CTL_DEL, io, 0)
    @events[fd] = @marks[fd] = nil
  end
  0
end
delete(io) → io or nil click to toggle source

This method is deprecated and will be removed in sleepy_penguin 4.x

Stops an io object from being monitored. This is like Epoll#del but returns nil on ENOENT instead of raising an error. This is useful for apps that do not care to track the status of an epoll object itself.

This method is deprecated and will be removed in sleepy_penguin 4.x

# File lib/sleepy_penguin/epoll.rb, line 114
def delete(io)
  fd = io.to_io.fileno
  @mtx.synchronize do
    __ep_check
    cur_io = @marks[fd]
    return if nil == cur_io || cur_io.to_io.closed?
    @io.epoll_ctl(CTL_DEL, io, 0)
    @events[fd] = @marks[fd] = nil
  end
  io
rescue Errno::ENOENT, Errno::EBADF
end
events_for(io) → Integer click to toggle source

Returns the events currently watched for in current Epoll object. Mostly used for debugging.

# File lib/sleepy_penguin/epoll.rb, line 235
def events_for(io)
  fd = __fileno(io)
  @mtx.synchronize do
    __ep_check
    @events[fd]
  end
end
Also aliased as: flags_for

backwards compatibility, to be removed in 4.x

Alias for: events_for
include?(io) → true or false click to toggle source

Returns whether or not a given IO is watched and prevented from being garbage-collected by the current Epoll object. This may include closed IO objects.

# File lib/sleepy_penguin/epoll.rb, line 252
def include?(io)
  fd = __fileno(io)
  @mtx.synchronize do
    __ep_check
    @marks[fd] ? true : false
  end
end
io_for(io) → object click to toggle source

Returns the given IO object currently being watched for. Different IO objects may internally refer to the same process file descriptor. Mostly used for debugging.

# File lib/sleepy_penguin/epoll.rb, line 222
def io_for(io)
  fd = __fileno(io)
  @mtx.synchronize do
    __ep_check
    @marks[fd]
  end
end
mod(io, flags) → 0 click to toggle source

Changes the watch for an existing IO object based on events. Returns zero on success, will raise SystemError on failure.

# File lib/sleepy_penguin/epoll.rb, line 132
def mod(io, events)
  events = __event_flags(events)
  fd = io.to_io.fileno
  @mtx.synchronize do
    __ep_check
    @io.epoll_ctl(CTL_MOD, io, events)
    @marks[fd] = io # may be a different object with same fd/file
    @events[fd] = events
  end
end
set(io, flags) → 0 click to toggle source

This method is deprecated and will be removed in sleepy_penguin 4.x

Used to avoid exceptions when your app is too lazy to check what state a descriptor is in, this sets the epoll descriptor to watch an io with the given events

events may be an array of symbols or an unsigned Integer bit mask:

See constants in Epoll for more information.

This method is deprecated and will be removed in sleepy_penguin 4.x

# File lib/sleepy_penguin/epoll.rb, line 160
def set(io, events)
  fd = io.to_io.fileno
  @mtx.synchronize do
    __ep_check
    cur_io = @marks[fd]
    if cur_io == io
      cur_events = @events[fd]
      return 0 if (cur_events & ONESHOT) == 0 && cur_events == events
      begin
        @io.epoll_ctl(CTL_MOD, io, events)
      rescue Errno::ENOENT
        warn "epoll event cache failed (mod -> add)\n"
        @io.epoll_ctl(CTL_ADD, io, events)
        @marks[fd] = io
      end
    else
      begin
        @io.epoll_ctl(CTL_ADD, io, events)
      rescue Errno::EEXIST
        warn "epoll event cache failed (add -> mod)\n"
        @io.epoll_ctl(CTL_MOD, io, events)
      end
      @marks[fd] = io
    end
    @events[fd] = events
  end
  0
end
to_io() click to toggle source

Epoll objects may be watched by IO.select and similar methods

# File lib/sleepy_penguin/epoll.rb, line 40
def to_io
  @mtx.synchronize do
    __ep_check
    @io
  end
end
wait(maxevents = 64, timeout = nil) { |events, io| ... } click to toggle source

Calls epoll_wait(2) and yields Integer events and IO objects watched for. maxevents is the maximum number of events to process at once, lower numbers may prevent starvation when used by epoll_wait in multiple threads. Larger maxevents reduces syscall overhead for single-threaded applications. maxevents defaults to 64 events. timeout is specified in milliseconds, nil (the default) meaning it will block and wait indefinitely.

As of sleepy_penguin 3.5.0+, it is possible to nest wait calls within the same thread.

# File lib/sleepy_penguin/epoll.rb, line 57
def wait(maxevents = 64, timeout = nil)
  # snapshot the marks so we do can sit this thread on epoll_wait while other
  # threads may call epoll_ctl.  People say RCU is a poor man's GC, but our
  # (ab)use of GC here is inspired by RCU...
  snapshot = @mtx.synchronize do
    __ep_check
    @marks.dup
  end

  # we keep a snapshot of @marks around in case another thread closes
  # the io while it is being transferred to userspace.  We release mtx
  # so another thread may add events to us while we're sleeping.
  @io.epoll_wait(maxevents, timeout) { |events, io| yield(events, io) }
ensure
  # hopefully Ruby does not optimize this array away...
  snapshot.clear
end