inotify: disable GVL-releasing close for 1.9.3dev
[sleepy_penguin.git] / test / test_epoll.rb
blob69e4f4c1d629be8ea59ed4ffa9c83f52c8d452e2
1 require 'test/unit'
2 require 'fcntl'
3 require 'socket'
4 $-w = true
6 require 'sleepy_penguin'
8 class TestEpoll < Test::Unit::TestCase
9   include SleepyPenguin
10   RBX = defined?(RUBY_ENGINE) && (RUBY_ENGINE == 'rbx')
12   def setup
13     @rd, @wr = IO.pipe
14     @ep = Epoll.new
15   end
17   def test_constants
18     Epoll.constants.each do |const|
19       next if const.to_sym == :IO
20       nr = Epoll.const_get(const)
21       assert nr <= 0xffffffff, "#{const}=#{nr}"
22     end
23   end
25   def test_cross_thread
26     tmp = []
27     Thread.new { sleep 0.100; @ep.add(@wr, Epoll::OUT) }
28     t0 = Time.now
29     @ep.wait { |flags,obj| tmp << [ flags, obj ] }
30     elapsed = Time.now - t0
31     assert elapsed >= 0.100
32     assert_equal [[Epoll::OUT, @wr]], tmp
33   end
35   def test_fork_safe
36     tmp = []
37     @ep.add @rd, Epoll::IN
38     pid = fork do
39       @ep.wait(nil, 100) { |flags,obj| tmp << [ flags, obj ] }
40       exit!(tmp.empty?)
41     end
42     @wr.syswrite "HI"
43     _, status = Process.waitpid2(pid)
44     assert status.success?
45     @ep.wait(nil, 0) { |flags,obj| tmp << [ flags, obj ] }
46     assert_equal [[Epoll::IN, @rd]], tmp
47   end
49   def test_after_fork_usability
50     fork { @ep.add(@rd, Epoll::IN); exit!(0) }
51     fork { @ep.set(@rd, Epoll::IN); exit!(0) }
52     fork { @ep.to_io; exit!(0) }
53     fork { @ep.dup; exit!(0) }
54     fork { @ep.clone; exit!(0) }
55     fork { @ep.close; exit!(0) }
56     fork { @ep.closed?; exit!(0) }
57     fork {
58       begin
59         @ep.del(@rd)
60       rescue Errno::ENOENT
61         exit!(0)
62       end
63       exit!(1)
64     }
65     res = Process.waitall
66     res.each { |(pid,status)| assert status.success? }
67   end
69   def test_tcp_connect_nonblock_edge
70     epflags = Epoll::OUT | Epoll::ET
71     host = '127.0.0.1'
72     srv = TCPServer.new(host, 0)
73     port = srv.addr[1]
74     addr = Socket.pack_sockaddr_in(port, host)
75     sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
76     assert_raises(Errno::EINPROGRESS) { sock.connect_nonblock(addr) }
77     IO.select(nil, [ sock ], [sock ])
78     @ep.add(sock, epflags)
79     tmp = []
80     @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
81     assert_equal [ [Epoll::OUT,  sock] ], tmp
82   end
84   def test_tcp_connect_edge
85     epflags = Epoll::OUT | Epoll::ET
86     host = '127.0.0.1'
87     srv = TCPServer.new(host, 0)
88     port = srv.addr[1]
89     sock = TCPSocket.new(host, port)
90     @ep.add(sock, epflags)
91     tmp = []
92     @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
93     assert_equal [ [Epoll::OUT,  sock] ], tmp
94   end
96   def teardown
97     assert_nothing_raised do
98       @rd.close unless @rd.closed?
99       @wr.close unless @wr.closed?
100       @ep.close unless @ep.closed?
101     end
102   end
104   def test_max_events_big
105     @ep.add @rd, Epoll::IN
106     tmp = []
107     thr = Thread.new { @ep.wait(1024) { |flags, obj| tmp << [ flags, obj ] } }
108     Thread.pass
109     assert tmp.empty?
110     @wr.write '.'
111     thr.join
112     assert_equal([[Epoll::IN, @rd]], tmp)
113     tmp.clear
114     thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
115     thr.join
116     assert_equal([[Epoll::IN, @rd]], tmp)
117   end
119   def test_max_events_small
120     @ep.add @rd, Epoll::IN | Epoll::ET
121     @ep.add @wr, Epoll::OUT | Epoll::ET
122     @wr.write '.'
123     tmp = []
124     @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
125     assert_equal 1, tmp.size
126     @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
127     assert_equal 2, tmp.size
128   end
130   def test_signal_safe
131     time = {}
132     trap(:USR1) { time[:USR1] = Time.now; sleep 0.1; @wr.write '.' }
133     @ep.add @rd, Epoll::IN
134     tmp = []
135     pid = fork do
136       sleep 0.1 # slightly racy :<
137       Process.kill(:USR1, Process.ppid)
138     end
139     time[:START_WAIT] = Time.now
140     begin
141       @ep.wait { |flags, obj| tmp << [ flags, obj ]; time[:EP] = Time.now }
142     rescue Errno::EINTR
143       retry
144     end
145     assert_equal([[Epoll::IN, @rd]], tmp)
146     _, status = Process.waitpid2(pid)
147     assert status.success?
148     assert((time[:USR1] - time[:START_WAIT]) >= 0.1)
149     assert((time[:USR1] - time[:START_WAIT]) < 0.15)
150     assert((time[:EP] - time[:USR1]) >= 0.1)
151     assert((time[:EP] - time[:USR1]) < 0.15)
152     ensure
153       trap(:USR1, 'DEFAULT')
154   end unless RBX
156   def test_close
157     @ep.add @rd, Epoll::IN
158     tmp = []
159     thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
160     @rd.close
161     @wr.close
162     assert_nil thr.join(0.01)
163     assert thr.alive?
164     thr.kill
165     assert tmp.empty?
166   end
168   def test_rdhup
169     defined?(Epoll::RDHUP) or
170       return warn "skipping test, EPOLLRDHUP not available"
171     rd, wr = UNIXSocket.pair
172     @ep.add rd, Epoll::RDHUP
173     tmp = []
174     thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
175     wr.shutdown Socket::SHUT_WR
176     thr.join
177     assert_equal([[ Epoll::RDHUP, rd ]], tmp)
178   end
180   def test_hup
181     @ep.add @rd, Epoll::IN
182     tmp = []
183     thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
184     @wr.close
185     thr.join
186     assert_equal([[ Epoll::HUP, @rd ]], tmp)
187   end
189   def test_multiple
190     r, w = IO.pipe
191     assert_nothing_raised do
192       @ep.add r, Epoll::IN
193       @ep.add @rd, Epoll::IN
194       @ep.add w, Epoll::OUT
195       @ep.add @wr, Epoll::OUT
196     end
197     tmp = []
198     @ep.wait { |flags, obj| tmp << [ flags, obj ] }
199     assert_equal 2, tmp.size
200     assert_equal [ Epoll::OUT ], tmp.map { |flags, obj| flags }.uniq
201     ios = tmp.map { |flags, obj| obj }
202     assert ios.include?(@wr)
203     assert ios.include?(w)
204   end
206   def test_gc
207     assert_nothing_raised { 4096.times { Epoll.new } }
208     assert ! @ep.closed?
209   end unless RBX
211   def test_gc_to_io
212     assert_nothing_raised do
213       4096.times do
214         ep = Epoll.new
215         io = ep.to_io
216       end
217     end
218     assert ! @ep.closed?
219   end unless RBX
221   def test_clone
222     tmp = []
223     clone = @ep.clone
224     assert @ep.to_io.fileno != clone.to_io.fileno
225     clone.add @wr, Epoll::OUT
226     @ep.wait(nil, 0) { |flags, obj| tmp << [ flags, obj ] }
227     assert_equal([[Epoll::OUT, @wr]], tmp)
228     assert_nothing_raised { clone.close }
229   end
231   def test_dup
232     tmp = []
233     clone = @ep.dup
234     assert @ep.to_io.fileno != clone.to_io.fileno
235     clone.add @wr, Epoll::OUT
236     @ep.wait(nil, 0) { |flags, obj| tmp << [ flags, obj ] }
237     assert_equal([[Epoll::OUT, @wr]], tmp)
238     assert_nothing_raised { clone.close }
239   end
241   def test_set_idempotency
242     assert_nothing_raised do
243       @ep.set @rd, Epoll::IN
244       @ep.set @rd, Epoll::IN
245       @ep.set @wr, Epoll::OUT
246       @ep.set @wr, Epoll::OUT
247     end
248   end
250   def test_wait_timeout
251     t0 = Time.now
252     assert_equal 0, @ep.wait(nil, 100) { |flags,obj| assert false }
253     diff = Time.now - t0
254     assert(diff >= 0.075, "#{diff} < 0.100s")
255   end
257   def test_del
258     assert_raises(Errno::ENOENT) { @ep.del(@rd) }
259     assert_nothing_raised do
260       @ep.add(@rd, Epoll::IN)
261       @ep.del(@rd)
262     end
263   end
265   def test_wait_read
266     @ep.add(@rd, Epoll::IN)
267     assert_equal 0, @ep.wait(nil, 0) { |flags,obj| assert false }
268     @wr.syswrite '.'
269     i = 0
270     nr = @ep.wait(nil, 0) do |flags,obj|
271       assert_equal Epoll::IN, flags
272       assert_equal obj, @rd
273       i += 1
274     end
275     assert_equal 1, i
276     assert_equal 1, nr
277   end
279   def test_wait_write
280     @ep.add(@wr, Epoll::OUT | Epoll::IN)
281     i = 0
282     nr = @ep.wait(nil, 0) do |flags, obj|
283       assert_equal Epoll::OUT, flags
284       assert_equal obj, @wr
285       i += 1
286     end
287     assert_equal 1, nr
288     assert_equal 1, i
289   end
291   def test_wait_write_blocked
292     begin
293       @wr.write_nonblock('.' * 65536)
294     rescue Errno::EAGAIN
295       break
296     end while true
297     @ep.add(@wr, Epoll::OUT | Epoll::IN)
298     i = 0
299     assert_equal 0, @ep.wait(nil, 0) { |flags,event| assert false }
300   end
302   def test_selectable
303     tmp = nil
304     @ep.add @rd, Epoll::IN
305     thr = Thread.new { tmp = IO.select([ @ep ]) }
306     thr.join 0.01
307     assert_nil tmp
308     @wr.write '.'
309     thr.join
310     assert_equal([[@ep],[],[]], tmp)
311   end
313   def test_new_no_cloexec
314     @ep.close
315     io = Epoll.new(0).to_io
316     assert((io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC) == 0)
317   end
319   def test_new_cloexec
320     @ep.close
321     io = Epoll.new(Epoll::CLOEXEC).to_io
322     assert((io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC) == Fcntl::FD_CLOEXEC)
323   end
325   def test_new
326     @ep.close
327     io = Epoll.new.to_io
328     assert_equal 0, io.fcntl(Fcntl::F_GETFD)
329   end
331   def test_delete
332     assert_nil @ep.delete(@rd)
333     assert_nil @ep.delete(@wr)
334     assert_nothing_raised { @ep.add @rd, Epoll::IN }
335     assert_equal @rd, @ep.delete(@rd)
336     assert_nil @ep.delete(@rd)
337   end
339   def test_io_for
340     @ep.add @rd, Epoll::IN
341     assert_equal @rd, @ep.io_for(@rd.fileno)
342     assert_equal @rd, @ep.io_for(@rd)
343     @ep.del @rd
344     assert_nil @ep.io_for(@rd.fileno)
345     assert_nil @ep.io_for(@rd)
346   end
348   def test_flags_for
349     @ep.add @rd, Epoll::IN
350     assert_equal Epoll::IN, @ep.flags_for(@rd.fileno)
351     assert_equal Epoll::IN, @ep.flags_for(@rd)
353     @ep.del @rd
354     assert_nil @ep.flags_for(@rd.fileno)
355     assert_nil @ep.flags_for(@rd)
356   end
358   def test_flags_for_sym
359     @ep.add @rd, :IN
360     assert_equal Epoll::IN, @ep.flags_for(@rd.fileno)
361     assert_equal Epoll::IN, @ep.flags_for(@rd)
363     @ep.del @rd
364     assert_nil @ep.flags_for(@rd.fileno)
365     assert_nil @ep.flags_for(@rd)
366   end
368   def test_flags_for_sym_ary
369     @ep.add @rd, [:IN, :ET]
370     expect = Epoll::IN | Epoll::ET
371     assert_equal expect, @ep.flags_for(@rd.fileno)
372     assert_equal expect, @ep.flags_for(@rd)
374     @ep.del @rd
375     assert_nil @ep.flags_for(@rd.fileno)
376     assert_nil @ep.flags_for(@rd)
377   end
379   def test_include?
380     assert ! @ep.include?(@rd)
381     @ep.add @rd, Epoll::IN
382     assert @ep.include?(@rd)
383     assert @ep.include?(@rd.fileno)
384     assert ! @ep.include?(@wr)
385     assert ! @ep.include?(@wr.fileno)
386   end