Class: SleepyPenguin::Epoll
- Inherits:
-
Object
- Object
- SleepyPenguin::Epoll
- Defined in:
- ext/sleepy_penguin/epoll.c,
lib/sleepy_penguin/epoll.rb,
ext/sleepy_penguin/epoll.c
Overview
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.
Defined Under Namespace
Classes: IO
Constant Summary collapse
- CTL_ADD =
registers a target
IO
object via epoll_ctl INT2NUM(EPOLL_CTL_ADD)
- CTL_DEL =
unregisters a target
IO
object via epoll_ctl INT2NUM(EPOLL_CTL_DEL)
- CTL_MOD =
modifies the registration of a target
IO
object via epoll_ctl INT2NUM(EPOLL_CTL_MOD)
- CLOEXEC =
specifies whether close-on-exec flag is set for Epoll.new
INT2NUM(EPOLL_CLOEXEC)
- IN =
watch for read/recv operations
UINT2NUM(EPOLLIN)
- OUT =
watch for write/send operations
UINT2NUM(EPOLLOUT)
- RDHUP =
Watch a specified io for shutdown(SHUT_WR) on the remote-end. Available since Linux 2.6.17.
UINT2NUM(EPOLLRDHUP)
- 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
UINT2NUM(EPOLLWAKEUP)
- 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 targetIO
is shared among multiple epoll objects. Available since Linux 4.5 UINT2NUM(EPOLLEXCLUSIVE)
- PRI =
watch for urgent read(2) data
UINT2NUM(EPOLLPRI)
- ERR =
watch for errors, there is no need to specify this, it is always monitored when an
IO
is watched UINT2NUM(EPOLLERR)
- HUP =
watch for hangups, there is no need to specify this, it is always monitored when an
IO
is watched UINT2NUM(EPOLLHUP)
- ET =
notifications are only Edge Triggered, see epoll(7)
UINT2NUM((uint32_t)EPOLLET)
- ONESHOT =
unwatch the descriptor once any event has fired
UINT2NUM(EPOLLONESHOT)
Instance Method Summary collapse
-
#__ep_check ⇒ Object
auto-reinitialize the Epoll object after forking.
-
#__ep_reinit ⇒ Object
:nodoc:.
-
#__event_flags(flags) ⇒ Object
:nodoc:.
-
#__fileno(io) ⇒ Object
we still support integer FDs for some debug functions.
-
#add(io, events) ⇒ Object
Starts watching a given
io
object withevents
which may be an Integer bitmask or Array representing arrays to watch for. -
#close ⇒ Object
call-seq: ep.close -> nil.
-
#closed? ⇒ Boolean
call-seq: ep.closed? -> true or false.
-
#del(io) ⇒ Object
call-seq: ep.del(io) -> 0.
-
#delete(io) ⇒ Object
call-seq: ep.delete(io) -> io or nil.
-
#events_for(io) ⇒ Object
(also: #flags_for)
call-seq: epoll.events_for(io) -> Integer.
-
#include?(io) ⇒ Boolean
call-seq: epoll.include?(io) -> true or false.
-
#initialize(create_flags = nil) ⇒ Epoll
constructor
call-seq: SleepyPenguin::Epoll.new() -> Epoll object.
-
#initialize_copy(src) ⇒ Object
:nodoc:.
-
#io_for(io) ⇒ Object
call-seq: ep.io_for(io) -> object.
-
#mod(io, events) ⇒ Object
call-seq: epoll.mod(io, flags) -> 0.
-
#set(io, events) ⇒ Object
call-seq: ep.set(io, flags) -> 0.
-
#to_io ⇒ Object
Epoll objects may be watched by IO.select and similar methods.
-
#wait(maxevents = 64, timeout = nil) ⇒ Object
Calls epoll_wait(2) and yields Integer
events
andIO
objects watched for.
Constructor Details
#initialize(create_flags = nil) ⇒ Epoll
call-seq:
SleepyPenguin::Epoll.new([flags]) -> Epoll object
Creates a new Epoll object with an optional flags
argument. flags
may currently be :CLOEXEC
or 0
(or nil
).
9 10 11 12 13 14 15 16 17 |
# 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 |
Instance Method Details
#__ep_check ⇒ Object
auto-reinitialize the Epoll object after forking
26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/sleepy_penguin/epoll.rb', line 26 def __ep_check # :nodoc: return if @pid == $$ return if @io.closed? objects = @copies.values @copies.each_key(&:close).clear __ep_reinit objects.each do |obj| io_dup = @io.dup @copies[io_dup] = obj end @pid = $$ end |
#__ep_reinit ⇒ Object
:nodoc:
19 20 21 22 23 |
# File 'lib/sleepy_penguin/epoll.rb', line 19 def __ep_reinit # :nodoc: @events.clear @marks.clear @io = SleepyPenguin::Epoll::IO.new(@create_flags) end |
#__event_flags(flags) ⇒ Object
:nodoc:
217 218 219 220 |
# File 'ext/sleepy_penguin/epoll.c', line 217
static VALUE event_flags(VALUE self, VALUE flags)
{
return UINT2NUM(rb_sp_get_uflags(self, flags));
}
|
#__fileno(io) ⇒ Object
we still support integer FDs for some debug functions
212 213 214 |
# File 'lib/sleepy_penguin/epoll.rb', line 212 def __fileno(io) # :nodoc: Integer === io ? io : io.to_io.fileno end |
#add(io, events) ⇒ Object
Starts watching a given io
object with events
which may be an Integer bitmask or Array representing arrays to watch for.
77 78 79 80 81 82 83 84 85 86 87 |
# 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 ⇒ Object
call-seq:
ep.close -> nil
Closes an existing Epoll object and returns memory back to the kernel. Raises IOError if object is already closed.
194 195 196 197 198 199 |
# File 'lib/sleepy_penguin/epoll.rb', line 194 def close @mtx.synchronize do @copies.delete(@io) @io.close end end |
#closed? ⇒ Boolean
call-seq:
ep.closed? -> true or false
Returns whether or not an Epoll object is closed.
205 206 207 208 209 |
# File 'lib/sleepy_penguin/epoll.rb', line 205 def closed? @mtx.synchronize do @io.closed? end end |
#del(io) ⇒ Object
call-seq:
ep.del(io) -> 0
Disables an IO
object from being watched.
93 94 95 96 97 98 99 100 101 |
# 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) ⇒ Object
call-seq:
ep.delete(io) -> io or nil
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
114 115 116 117 118 119 120 121 122 123 124 125 |
# 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) ⇒ Object Also known as: flags_for
call-seq:
epoll.events_for(io) -> Integer
Returns the events currently watched for in current Epoll object. Mostly used for debugging.
235 236 237 238 239 240 241 |
# File 'lib/sleepy_penguin/epoll.rb', line 235 def events_for(io) fd = __fileno(io) @mtx.synchronize do __ep_check @events[fd] end end |
#include?(io) ⇒ Boolean
call-seq:
epoll.include?(io) -> true or false
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.
252 253 254 255 256 257 258 |
# 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 |
#initialize_copy(src) ⇒ Object
:nodoc:
260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/sleepy_penguin/epoll.rb', line 260 def initialize_copy(src) # :nodoc: @mtx.synchronize do __ep_check rv = super unless @io.closed? @io = @io.dup @copies[@io] = self end rv end end |
#io_for(io) ⇒ Object
call-seq:
ep.io_for(io) -> object
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.
222 223 224 225 226 227 228 |
# 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, events) ⇒ Object
call-seq:
epoll.mod(io, flags) -> 0
Changes the watch for an existing IO
object based on events
. Returns zero on success, will raise SystemError on failure.
132 133 134 135 136 137 138 139 140 141 |
# 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, events) ⇒ Object
call-seq:
ep.set(io, flags) -> 0
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:
-
events = [ :IN, :ET ]
-
events = SleepyPenguin::Epoll::IN | SleepyPenguin::Epoll::ET
See constants in Epoll for more information.
This method is deprecated and will be removed in sleepy_penguin 4.x
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# 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 ⇒ Object
Epoll objects may be watched by IO.select and similar methods
40 41 42 43 44 45 |
# File 'lib/sleepy_penguin/epoll.rb', line 40 def to_io @mtx.synchronize do __ep_check @io end end |
#wait(maxevents = 64, timeout = nil) ⇒ Object
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.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# 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 |