Class: Kernel::Epoll

Inherits:
Object
  • Object
show all
Extended by:
FFI::Library
Defined in:
lib/ktools/epoll.rb

Defined Under Namespace

Classes: Epoll_data, Epoll_event

Constant Summary collapse

EP_FLAGS =
{
  :read => EPOLLIN,
  :write => EPOLLOUT,
  :hangup => EPOLLHUP,
  :priority => EPOLLPRI,
  :edge => EPOLLET,
  :oneshot => EPOLLONESHOT,
  :error => EPOLLERR
}

Instance Method Summary collapse

Constructor Details

#initialize(size = 1024) ⇒ Epoll

Creates a new epoll event queue. Takes an optional size parameter (default 1024) that is a hint to the kernel about how many descriptors it will be handling. Read man epoll_create for details on this. Raises an error if the operation fails.

Raises:

  • (SystemCallError)


65
66
67
68
69
# File 'lib/ktools/epoll.rb', line 65

def initialize(size=1024)
  @fds = {}
  @epfd = epoll_create(size)
  raise SystemCallError.new("Error creating epoll descriptor", get_errno) unless @epfd > 0
end

Instance Method Details

#add(type, target, options = {}) ⇒ Object

Generic method for adding events. This simply calls the proper add_foo method specified by the type symbol. Example:

ep.add(:socket, sock, :events => [:read])
calls -> ep.add_socket(sock, events => [:read])

Note: even though epoll only supports :socket style descriptors, we keep this for consistency with other APIs.



77
78
79
80
81
82
83
84
# File 'lib/ktools/epoll.rb', line 77

def add(type, target, options={})
  case type
  when :socket
    add_socket(target, options)
  else
    raise ArgumentError.new("Epoll only supports socket style descriptors")
  end
end

#add_socket(target, options = {}) ⇒ Object

Add events to a socket-style descriptor (socket or pipe). Your target can be either an IO object (socket, pipe), or a file descriptor number.

Supported :events are:

  • :read - The descriptor has become readable.

  • :write - The descriptor has become writeable.

  • :priority - There is urgent data available for read operations.

  • :error - Error condition happened on the associated file descriptor. (Always active)

  • :hangup - Hang up happened on the associated file descriptor. (Always active)

  • :remote_hangup - Stream socket peer closed the connection, or shut down writing half of connection. (Missing from some kernel verions)

Supported :flags are:

  • :edge - Sets the Edge Triggered behavior for the associated file descriptor. (see manpage)

  • :oneshot - Sets the one-shot behaviour for the associated file descriptor. (Event only fires once)

Example:

irb(main):001:0> require 'ktools'
=> true
irb(main):002:0> r, w = IO.pipe
=> [#<IO:0x89be38c>, #<IO:0x89be378>]
irb(main):003:0> ep = Epoll.new
=> #<Kernel::Epoll:0x89bca3c @fds={}, @epfd=5>
irb(main):004:0> ep.add(:socket, r, :events => [:read])
=> true
irb(main):005:0> ep.poll
=> []
irb(main):006:0> w.write 'foo'
=> 3
irb(main):007:0> ep.poll
=> [{:target=>#<IO:0x89be38c>, :event=>:read, :type=>:socket}]
irb(main):008:0> [r, w, ep].each{|x| x.close }


120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/ktools/epoll.rb', line 120

def add_socket(target, options={})
  fdnum = target.respond_to?(:fileno) ? target.fileno : target
  events = (options[:events] + (options[:flags] || [])).inject(0){|m,i| m | EP_FLAGS[i]}

  ev = Epoll_event.new
  ev[:events] = events
  ev[:data] = Epoll_data.new
  ev[:data][:fd] = fdnum

  if epoll_ctl(@epfd, EPOLL_CTL_ADD, fdnum, ev) == -1
    return false
  else
    @fds[fdnum] = {:target => target, :event => ev}
    return true
  end
end

#closeObject



198
199
200
# File 'lib/ktools/epoll.rb', line 198

def close
  IO.for_fd(@epfd).close
end

#delete(type, target) ⇒ Object

Stop generating events for the given type and event target, ie:

ep.delete(:socket, sock)

Note: even though epoll only supports :socket style descriptors, we keep this for consistency with other APIs.



190
191
192
193
194
195
196
# File 'lib/ktools/epoll.rb', line 190

def delete(type, target)
  ident = target.respond_to?(:fileno) ? target.fileno : target
  h = @fds[ident]
  return false if h.nil?
  epoll_ctl(@epfd, EPOLL_CTL_DEL, ident, h[:event])
  return true
end

#poll(timeout = 0.0) ⇒ Object

Poll for an event. Pass an optional timeout float as number of seconds to wait for an event. Default is 0.0 (do not wait).

Using a timeout will block for the duration of the timeout. Under Ruby 1.9.1, we use rb_thread_blocking_region() under the hood to allow other threads to run during this call. Prior to 1.9 though, we do not have native threads and hence this call will block the whole interpreter (all threads) until it returns.

This call returns an array of hashes, similar to the following:

=> [{:type=>:socket, :target=>#<IO:0x4fa90c>, :event=>:read}]
  • :type - will be the type of event target, i.e. an event set with #add_socket will have :type => :socket

  • :target - the ‘target’ or ‘subject’ of the event. This can be a File, IO, process or signal number.

  • :event - the event that occurred on the target. This is one of the symbols you passed as :events => [:foo] when adding the event.

Note: even though epoll only supports :socket style descriptors, we keep :type for consistency with other APIs.



151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/ktools/epoll.rb', line 151

def poll(timeout=0.0)
  timeout = (timeout * 1000).to_i
  ev = Epoll_event.new
  case epoll_wait(@epfd, ev, 1, timeout)
  when -1
    [errno]
  when 0
    []
  else
    [process_event(ev)]
  end
end

#process_event(ev) ⇒ Object

:nodoc:



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/ktools/epoll.rb', line 164

def process_event(ev) #:nodoc:
  h = @fds[ev[:data][:fd]]
  return nil if h.nil?

  event = if ev[:events] & EPOLLIN == EPOLLIN
    :read
  elsif ev[:events] & EPOLLOUT == EPOLLOUT
    :write
  elsif ev[:events] & ERPOLLPRI == EPOLLPRI
    :priority
  elsif ev[:events] & EPOLLERR == EPOLLERR
    :error
  elsif ev[:events] & EPOLLHUP == EPOLLHUP
    :hangup
  elsif Epoll.const_defined?("EPOLLRDHUP") and ev[:events] & EPOLLRDHUP == EPOLLRDHUP
    :remote_hangup
  end

  delete(:socket, h[:target]) if ev[:events] & EPOLLONESHOT == EPOLLONESHOT
  {:target => h[:target], :event => event, :type => :socket}
end