2 class SleepyPenguin::Epoll
5 # SleepyPenguin::Epoll.new([flags]) -> Epoll object
7 # Creates a new Epoll object with an optional +flags+ argument.
8 # +flags+ may currently be +:CLOEXEC+ or +0+ (or +nil+).
9 def initialize(create_flags = nil)
10 @io = SleepyPenguin::Epoll::IO.new(create_flags)
15 @create_flags = create_flags
16 @copies = { @io => self }
19 def __ep_reinit # :nodoc:
22 @io = SleepyPenguin::Epoll::IO.new(@create_flags)
25 # auto-reinitialize the Epoll object after forking
26 def __ep_check # :nodoc:
29 objects = @copies.values
30 @copies.each_key(&:close).clear
39 # Epoll objects may be watched by IO.select and similar methods
47 # Calls epoll_wait(2) and yields Integer +events+ and +IO+ objects watched
48 # for. +maxevents+ is the maximum number of events to process at once,
49 # lower numbers may prevent starvation when used by epoll_wait in multiple
50 # threads. Larger +maxevents+ reduces syscall overhead for
51 # single-threaded applications. +maxevents+ defaults to 64 events.
52 # +timeout+ is specified in milliseconds, +nil+
53 # (the default) meaning it will block and wait indefinitely.
55 # As of sleepy_penguin 3.5.0+, it is possible to nest
56 # #wait calls within the same thread.
57 def wait(maxevents = 64, timeout = nil)
58 # snapshot the marks so we do can sit this thread on epoll_wait while other
59 # threads may call epoll_ctl. People say RCU is a poor man's GC, but our
60 # (ab)use of GC here is inspired by RCU...
61 snapshot = @mtx.synchronize do
66 # we keep a snapshot of @marks around in case another thread closes
67 # the io while it is being transferred to userspace. We release mtx
68 # so another thread may add events to us while we're sleeping.
69 @io.epoll_wait(maxevents, timeout) { |events, io| yield(events, io) }
71 # hopefully Ruby does not optimize this array away...
75 # Starts watching a given +io+ object with +events+ which may be an Integer
76 # bitmask or Array representing arrays to watch for.
79 events = __event_flags(events)
82 @io.epoll_ctl(CTL_ADD, io, events)
92 # Disables an +IO+ object from being watched.
97 @io.epoll_ctl(CTL_DEL, io, 0)
98 @events[fd] = @marks[fd] = nil
104 # ep.delete(io) -> io or nil
106 # This method is deprecated and will be removed in sleepy_penguin 4.x
108 # Stops an +io+ object from being monitored. This is like Epoll#del
109 # but returns +nil+ on ENOENT instead of raising an error. This is
110 # useful for apps that do not care to track the status of an
111 # epoll object itself.
113 # This method is deprecated and will be removed in sleepy_penguin 4.x
119 return if nil == cur_io || cur_io.to_io.closed?
120 @io.epoll_ctl(CTL_DEL, io, 0)
121 @events[fd] = @marks[fd] = nil
124 rescue Errno::ENOENT, Errno::EBADF
128 # epoll.mod(io, flags) -> 0
130 # Changes the watch for an existing +IO+ object based on +events+.
131 # Returns zero on success, will raise SystemError on failure.
133 events = __event_flags(events)
137 @io.epoll_ctl(CTL_MOD, io, events)
138 @marks[fd] = io # may be a different object with same fd/file
144 # ep.set(io, flags) -> 0
146 # This method is deprecated and will be removed in sleepy_penguin 4.x
148 # Used to avoid exceptions when your app is too lazy to check
149 # what state a descriptor is in, this sets the epoll descriptor
150 # to watch an +io+ with the given +events+
152 # +events+ may be an array of symbols or an unsigned Integer bit mask:
154 # - events = [ :IN, :ET ]
155 # - events = SleepyPenguin::Epoll::IN | SleepyPenguin::Epoll::ET
157 # See constants in Epoll for more information.
159 # This method is deprecated and will be removed in sleepy_penguin 4.x
166 cur_events = @events[fd]
167 return 0 if (cur_events & ONESHOT) == 0 && cur_events == events
169 @io.epoll_ctl(CTL_MOD, io, events)
171 warn "epoll event cache failed (mod -> add)\n"
172 @io.epoll_ctl(CTL_ADD, io, events)
177 @io.epoll_ctl(CTL_ADD, io, events)
179 warn "epoll event cache failed (add -> mod)\n"
180 @io.epoll_ctl(CTL_MOD, io, events)
192 # Closes an existing Epoll object and returns memory back to the kernel.
193 # Raises IOError if object is already closed.
202 # ep.closed? -> true or false
204 # Returns whether or not an Epoll object is closed.
211 # we still support integer FDs for some debug functions
212 def __fileno(io) # :nodoc:
213 Integer === io ? io : io.to_io.fileno
217 # ep.io_for(io) -> object
219 # Returns the given +IO+ object currently being watched for. Different
220 # +IO+ objects may internally refer to the same process file descriptor.
221 # Mostly used for debugging.
231 # epoll.events_for(io) -> Integer
233 # Returns the events currently watched for in current Epoll object.
234 # Mostly used for debugging.
243 # backwards compatibility, to be removed in 4.x
244 alias flags_for events_for
247 # epoll.include?(io) -> true or false
249 # Returns whether or not a given +IO+ is watched and prevented from being
250 # garbage-collected by the current Epoll object. This may include
251 # closed +IO+ objects.
256 @marks[fd] ? true : false
260 def initialize_copy(src) # :nodoc: