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 { |epio| epio.close }
40 # Epoll objects may be watched by IO.select and similar methods
48 # Calls epoll_wait(2) and yields Integer +events+ and +IO+ objects watched
49 # for. +maxevents+ is the maximum number of events to process at once,
50 # lower numbers may prevent starvation when used by epoll_wait in multiple
51 # threads. Larger +maxevents+ reduces syscall overhead for
52 # single-threaded applications. +maxevents+ defaults to 64 events.
53 # +timeout+ is specified in milliseconds, +nil+
54 # (the default) meaning it will block and wait indefinitely.
55 def wait(maxevents = 64, timeout = nil)
56 # snapshot the marks so we do can sit this thread on epoll_wait while other
57 # threads may call epoll_ctl. People say RCU is a poor man's GC, but our
58 # (ab)use of GC here is inspired by RCU...
59 snapshot = @mtx.synchronize do
64 # we keep a snapshot of @marks around in case another thread closes
65 # the io while it is being transferred to userspace. We release mtx
66 # so another thread may add events to us while we're sleeping.
67 @io.epoll_wait(maxevents, timeout) { |events, io| yield(events, io) }
69 # hopefully Ruby does not optimize this array away...
73 # Starts watching a given +io+ object with +events+ which may be an Integer
74 # bitmask or Array representing arrays to watch for.
77 events = __event_flags(events)
80 @io.epoll_ctl(CTL_ADD, io, events)
90 # Disables an +IO+ object from being watched.
95 @io.epoll_ctl(CTL_DEL, io, 0)
96 @events[fd] = @marks[fd] = nil
102 # ep.delete(io) -> io or nil
104 # This method is deprecated and will be removed in sleepy_penguin 4.x
106 # Stops an +io+ object from being monitored. This is like Epoll#del
107 # but returns +nil+ on ENOENT instead of raising an error. This is
108 # useful for apps that do not care to track the status of an
109 # epoll object itself.
111 # This method is deprecated and will be removed in sleepy_penguin 4.x
117 return if nil == cur_io || cur_io.to_io.closed?
118 @io.epoll_ctl(CTL_DEL, io, 0)
119 @events[fd] = @marks[fd] = nil
122 rescue Errno::ENOENT, Errno::EBADF
126 # epoll.mod(io, flags) -> 0
128 # Changes the watch for an existing +IO+ object based on +events+.
129 # Returns zero on success, will raise SystemError on failure.
131 events = __event_flags(events)
135 @io.epoll_ctl(CTL_MOD, io, events)
136 @marks[fd] = io # may be a different object with same fd/file
142 # ep.set(io, flags) -> 0
144 # This method is deprecated and will be removed in sleepy_penguin 4.x
146 # Used to avoid exceptions when your app is too lazy to check
147 # what state a descriptor is in, this sets the epoll descriptor
148 # to watch an +io+ with the given +events+
150 # +events+ may be an array of symbols or an unsigned Integer bit mask:
152 # - events = [ :IN, :ET ]
153 # - events = SleepyPenguin::Epoll::IN | SleepyPenguin::Epoll::ET
155 # See constants in Epoll for more information.
157 # This method is deprecated and will be removed in sleepy_penguin 4.x
164 cur_events = @events[fd]
165 return 0 if (cur_events & ONESHOT) == 0 && cur_events == events
167 @io.epoll_ctl(CTL_MOD, io, events)
169 warn "epoll event cache failed (mod -> add)\n"
170 @io.epoll_ctl(CTL_ADD, io, events)
175 @io.epoll_ctl(CTL_ADD, io, events)
177 warn "epoll event cache failed (add -> mod)\n"
178 @io.epoll_ctl(CTL_MOD, io, events)
190 # Closes an existing Epoll object and returns memory back to the kernel.
191 # Raises IOError if object is already closed.
200 # ep.closed? -> true or false
202 # Returns whether or not an Epoll object is closed.
209 # we still support integer FDs for some debug functions
210 def __fileno(io) # :nodoc:
211 Integer === io ? io : io.to_io.fileno
215 # ep.io_for(io) -> object
217 # Returns the given +IO+ object currently being watched for. Different
218 # +IO+ objects may internally refer to the same process file descriptor.
219 # Mostly used for debugging.
229 # epoll.events_for(io) -> Integer
231 # Returns the events currently watched for in current Epoll object.
232 # Mostly used for debugging.
241 # backwards compatibility, to be removed in 4.x
242 alias flags_for events_for
245 # epoll.include?(io) -> true or false
247 # Returns whether or not a given +IO+ is watched and prevented from being
248 # garbage-collected by the current Epoll object. This may include
249 # closed +IO+ objects.
254 @marks[fd] ? true : false
258 def initialize_copy(src) # :nodoc: