sleepy_penguin 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,36 @@
1
+ ENV["VERSION"] or abort "VERSION= must be specified"
2
+ manifest = File.readlines('.manifest').map! { |x| x.chomp! }
3
+ summary = File.readlines("README")[0].gsub(/\A=\s+\S+[^\w]+/, '').strip
4
+ description = File.read("README").split(/\n\n/)[1].strip
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{sleepy_penguin}
8
+ s.version = ENV["VERSION"]
9
+
10
+ s.homepage = 'http://bogomips.org/sleepy_penguin/'
11
+ s.authors = ["sleepy_penguin hackers"]
12
+ s.date = Time.now.utc.strftime('%Y-%m-%d')
13
+ s.description = description
14
+ s.email = %q{sleepy.penguin@librelist.com}
15
+
16
+ s.extra_rdoc_files = File.readlines('.document').map! do |x|
17
+ x.chomp!
18
+ if File.directory?(x)
19
+ manifest.grep(%r{\A#{x}/})
20
+ elsif File.file?(x)
21
+ x
22
+ else
23
+ nil
24
+ end
25
+ end.flatten.compact
26
+
27
+ s.files = manifest
28
+ s.rdoc_options = [ "-t", summary ]
29
+ s.require_paths = %w(lib ext)
30
+ s.rubyforge_project = %q{rainbows}
31
+ s.summary = summary
32
+ s.test_files = Dir['test/test_*.rb']
33
+ s.extensions = %w(ext/sleepy_penguin/extconf.rb)
34
+
35
+ # s.license = %w(LGPL) # disabled for compatibility with older RubyGems
36
+ end
@@ -0,0 +1,320 @@
1
+ require 'test/unit'
2
+ require 'fcntl'
3
+ require 'socket'
4
+ $-w = true
5
+
6
+ require 'sleepy_penguin'
7
+
8
+ class TestEpoll < Test::Unit::TestCase
9
+ include SleepyPenguin
10
+ RBX = defined?(RUBY_ENGINE) && (RUBY_ENGINE == 'rbx')
11
+
12
+ def setup
13
+ @rd, @wr = IO.pipe
14
+ @ep = Epoll.new
15
+ end
16
+
17
+ def teardown
18
+ [ @rd, @wr, @ep ].each { |io| io.close unless io.closed? }
19
+ end
20
+
21
+ def test_cross_thread
22
+ tmp = []
23
+ Thread.new { sleep 0.100; @ep.add(@wr, Epoll::OUT) }
24
+ t0 = Time.now
25
+ @ep.wait { |flags,obj| tmp << [ flags, obj ] }
26
+ elapsed = Time.now - t0
27
+ assert elapsed >= 0.100
28
+ assert_equal [[Epoll::OUT, @wr]], tmp
29
+ end
30
+
31
+ def test_fork_safe
32
+ tmp = []
33
+ @ep.add @rd, Epoll::IN
34
+ pid = fork do
35
+ @ep.wait(nil, 100) { |flags,obj| tmp << [ flags, obj ] }
36
+ exit!(tmp.empty?)
37
+ end
38
+ @wr.syswrite "HI"
39
+ _, status = Process.waitpid2(pid)
40
+ assert status.success?
41
+ @ep.wait(nil, 0) { |flags,obj| tmp << [ flags, obj ] }
42
+ assert_equal [[Epoll::IN, @rd]], tmp
43
+ end
44
+
45
+ def test_after_fork_usability
46
+ fork { @ep.add(@rd, Epoll::IN); exit!(0) }
47
+ fork { @ep.set(@rd, Epoll::IN); exit!(0) }
48
+ fork { @ep.to_io; exit!(0) }
49
+ fork { @ep.dup; exit!(0) }
50
+ fork { @ep.clone; exit!(0) }
51
+ fork { @ep.close; exit!(0) }
52
+ fork { @ep.closed?; exit!(0) }
53
+ fork {
54
+ begin
55
+ @ep.del(@rd)
56
+ rescue Errno::ENOENT
57
+ exit!(0)
58
+ end
59
+ exit!(1)
60
+ }
61
+ res = Process.waitall
62
+ res.each { |(pid,status)| assert status.success? }
63
+ end
64
+
65
+ def test_tcp_connect_nonblock_edge
66
+ epflags = Epoll::OUT | Epoll::ET
67
+ host = '127.0.0.1'
68
+ srv = TCPServer.new(host, 0)
69
+ port = srv.addr[1]
70
+ addr = Socket.pack_sockaddr_in(port, host)
71
+ sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
72
+ assert_raises(Errno::EINPROGRESS) { sock.connect_nonblock(addr) }
73
+ IO.select(nil, [ sock ], [sock ])
74
+ @ep.add(sock, epflags)
75
+ tmp = []
76
+ @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
77
+ assert_equal [ [Epoll::OUT, sock] ], tmp
78
+ end
79
+
80
+ def test_tcp_connect_edge
81
+ epflags = Epoll::OUT | Epoll::ET
82
+ host = '127.0.0.1'
83
+ srv = TCPServer.new(host, 0)
84
+ port = srv.addr[1]
85
+ sock = TCPSocket.new(host, port)
86
+ @ep.add(sock, epflags)
87
+ tmp = []
88
+ @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
89
+ assert_equal [ [Epoll::OUT, sock] ], tmp
90
+ end
91
+
92
+ def teardown
93
+ assert_nothing_raised do
94
+ @rd.close unless @rd.closed?
95
+ @wr.close unless @wr.closed?
96
+ @ep.close unless @ep.closed?
97
+ end
98
+ end
99
+
100
+ def test_max_events_big
101
+ @ep.add @rd, Epoll::IN
102
+ tmp = []
103
+ thr = Thread.new { @ep.wait(1024) { |flags, obj| tmp << [ flags, obj ] } }
104
+ Thread.pass
105
+ assert tmp.empty?
106
+ @wr.write '.'
107
+ thr.join
108
+ assert_equal([[Epoll::IN, @rd]], tmp)
109
+ tmp.clear
110
+ thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
111
+ thr.join
112
+ assert_equal([[Epoll::IN, @rd]], tmp)
113
+ end
114
+
115
+ def test_max_events_small
116
+ @ep.add @rd, Epoll::IN | Epoll::ET
117
+ @ep.add @wr, Epoll::OUT | Epoll::ET
118
+ @wr.write '.'
119
+ tmp = []
120
+ @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
121
+ assert_equal 1, tmp.size
122
+ @ep.wait(1) { |flags, obj| tmp << [ flags, obj ] }
123
+ assert_equal 2, tmp.size
124
+ end
125
+
126
+ def test_signal_safe
127
+ time = {}
128
+ trap(:USR1) { time[:USR1] = Time.now; sleep 0.1; @wr.write '.' }
129
+ @ep.add @rd, Epoll::IN
130
+ tmp = []
131
+ pid = fork do
132
+ sleep 0.1 # slightly racy :<
133
+ Process.kill(:USR1, Process.ppid)
134
+ end
135
+ time[:START_WAIT] = Time.now
136
+ @ep.wait { |flags, obj| tmp << [ flags, obj ]; time[:EP] = Time.now }
137
+ assert_equal([[Epoll::IN, @rd]], tmp)
138
+ _, status = Process.waitpid2(pid)
139
+ assert status.success?
140
+ assert((time[:USR1] - time[:START_WAIT]) >= 0.1)
141
+ assert((time[:USR1] - time[:START_WAIT]) < 0.15)
142
+ assert((time[:EP] - time[:USR1]) >= 0.1)
143
+ assert((time[:EP] - time[:USR1]) < 0.15)
144
+ ensure
145
+ trap(:USR1, 'DEFAULT')
146
+ end unless RBX
147
+
148
+ def test_close
149
+ @ep.add @rd, Epoll::IN
150
+ tmp = []
151
+ thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
152
+ @rd.close
153
+ @wr.close
154
+ assert_nil thr.join(0.01)
155
+ assert thr.alive?
156
+ thr.kill
157
+ assert tmp.empty?
158
+ end
159
+
160
+ def test_rdhup
161
+ rd, wr = UNIXSocket.pair
162
+ @ep.add rd, Epoll::RDHUP
163
+ tmp = []
164
+ thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
165
+ wr.shutdown Socket::SHUT_WR
166
+ thr.join
167
+ assert_equal([[ Epoll::RDHUP, rd ]], tmp)
168
+ end
169
+
170
+ def test_hup
171
+ @ep.add @rd, Epoll::IN
172
+ tmp = []
173
+ thr = Thread.new { @ep.wait { |flags, obj| tmp << [ flags, obj ] } }
174
+ @wr.close
175
+ thr.join
176
+ assert_equal([[ Epoll::HUP, @rd ]], tmp)
177
+ end
178
+
179
+ def test_multiple
180
+ r, w = IO.pipe
181
+ assert_nothing_raised do
182
+ @ep.add r, Epoll::IN
183
+ @ep.add @rd, Epoll::IN
184
+ @ep.add w, Epoll::OUT
185
+ @ep.add @wr, Epoll::OUT
186
+ end
187
+ tmp = []
188
+ @ep.wait { |flags, obj| tmp << [ flags, obj ] }
189
+ assert_equal 2, tmp.size
190
+ assert_equal [ Epoll::OUT ], tmp.map { |flags, obj| flags }.uniq
191
+ ios = tmp.map { |flags, obj| obj }
192
+ assert ios.include?(@wr)
193
+ assert ios.include?(w)
194
+ end
195
+
196
+ def test_gc
197
+ assert_nothing_raised { 4096.times { Epoll.new } }
198
+ assert ! @ep.closed?
199
+ end unless RBX
200
+
201
+ def test_gc_to_io
202
+ assert_nothing_raised do
203
+ 4096.times do
204
+ ep = Epoll.new
205
+ io = ep.to_io
206
+ end
207
+ end
208
+ assert ! @ep.closed?
209
+ end unless RBX
210
+
211
+ def test_clone
212
+ tmp = []
213
+ clone = @ep.clone
214
+ assert @ep.to_io.fileno != clone.to_io.fileno
215
+ clone.add @wr, Epoll::OUT
216
+ @ep.wait(nil, 0) { |flags, obj| tmp << [ flags, obj ] }
217
+ assert_equal([[Epoll::OUT, @wr]], tmp)
218
+ assert_nothing_raised { clone.close }
219
+ end
220
+
221
+ def test_dup
222
+ tmp = []
223
+ clone = @ep.dup
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
230
+
231
+ def test_set_idempotency
232
+ assert_nothing_raised do
233
+ @ep.set @rd, Epoll::IN
234
+ @ep.set @rd, Epoll::IN
235
+ @ep.set @wr, Epoll::OUT
236
+ @ep.set @wr, Epoll::OUT
237
+ end
238
+ end
239
+
240
+ def test_wait_timeout
241
+ t0 = Time.now
242
+ assert_equal 0, @ep.wait(nil, 100) { |flags,obj| assert false }
243
+ diff = Time.now - t0
244
+ assert(diff >= 0.075, "#{diff} < 0.100s")
245
+ end
246
+
247
+ def test_del
248
+ assert_raises(Errno::ENOENT) { @ep.del(@rd) }
249
+ assert_nothing_raised do
250
+ @ep.add(@rd, Epoll::IN)
251
+ @ep.del(@rd)
252
+ end
253
+ end
254
+
255
+ def test_wait_read
256
+ @ep.add(@rd, Epoll::IN)
257
+ assert_equal 0, @ep.wait(nil, 0) { |flags,obj| assert false }
258
+ @wr.syswrite '.'
259
+ i = 0
260
+ nr = @ep.wait(nil, 0) do |flags,obj|
261
+ assert_equal Epoll::IN, flags
262
+ assert_equal obj, @rd
263
+ i += 1
264
+ end
265
+ assert_equal 1, i
266
+ assert_equal 1, nr
267
+ end
268
+
269
+ def test_wait_write
270
+ @ep.add(@wr, Epoll::OUT | Epoll::IN)
271
+ i = 0
272
+ nr = @ep.wait(nil, 0) do |flags, obj|
273
+ assert_equal Epoll::OUT, flags
274
+ assert_equal obj, @wr
275
+ i += 1
276
+ end
277
+ assert_equal 1, nr
278
+ assert_equal 1, i
279
+ end
280
+
281
+ def test_wait_write_blocked
282
+ begin
283
+ @wr.write_nonblock('.' * 65536)
284
+ rescue Errno::EAGAIN
285
+ break
286
+ end while true
287
+ @ep.add(@wr, Epoll::OUT | Epoll::IN)
288
+ i = 0
289
+ assert_equal 0, @ep.wait(nil, 0) { |flags,event| assert false }
290
+ end
291
+
292
+ def test_selectable
293
+ tmp = nil
294
+ @ep.add @rd, Epoll::IN
295
+ thr = Thread.new { tmp = IO.select([ @ep ]) }
296
+ thr.join 0.01
297
+ assert_nil tmp
298
+ @wr.write '.'
299
+ thr.join
300
+ assert_equal([[@ep],[],[]], tmp)
301
+ end
302
+
303
+ def test_new_no_cloexec
304
+ @ep.close
305
+ io = Epoll.new(0).to_io
306
+ assert((io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC) == 0)
307
+ end
308
+
309
+ def test_new_cloexec
310
+ @ep.close
311
+ io = Epoll.new(Epoll::CLOEXEC).to_io
312
+ assert((io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC) == Fcntl::FD_CLOEXEC)
313
+ end
314
+
315
+ def test_new
316
+ @ep.close
317
+ io = Epoll.new.to_io
318
+ assert((io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC) == Fcntl::FD_CLOEXEC)
319
+ end
320
+ end
@@ -0,0 +1,48 @@
1
+ require 'test/unit'
2
+ require 'fcntl'
3
+ $-w = true
4
+
5
+ require 'sleepy_penguin'
6
+
7
+ class TestEventFD < Test::Unit::TestCase
8
+ include SleepyPenguin
9
+
10
+ def test_constants
11
+ defined?(EventFD::NONBLOCK) and
12
+ assert_kind_of Integer, EventFD::NONBLOCK
13
+ defined?(EventFD::CLOEXEC) and
14
+ assert_kind_of Integer, EventFD::CLOEXEC
15
+ defined?(EventFD::SEMAPHORE) and
16
+ assert_kind_of Integer, EventFD::SEMAPHORE
17
+ end
18
+
19
+ def test_new
20
+ efd = EventFD.new 0
21
+ assert_kind_of(IO, efd)
22
+ end
23
+
24
+ def test_new_nonblock
25
+ efd = EventFD.new(0, EventFD::NONBLOCK)
26
+ flags = efd.fcntl(Fcntl::F_GETFL) & Fcntl::O_NONBLOCK
27
+ assert_equal(Fcntl::O_NONBLOCK, flags)
28
+ end if defined?(EventFD::NONBLOCK)
29
+
30
+ def test_new_cloexec
31
+ efd = EventFD.new(0, EventFD::CLOEXEC)
32
+ flags = efd.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC
33
+ assert_equal(Fcntl::FD_CLOEXEC, flags)
34
+ end if defined?(EventFD::CLOEXEC)
35
+
36
+ def test_incr_value
37
+ efd = EventFD.new(0)
38
+ assert_nil efd.incr(1)
39
+ assert_equal 1, efd.value
40
+
41
+ assert_raises(Errno::EAGAIN) { efd.value_nonblock }
42
+ assert_nil efd.incr(9)
43
+ assert_equal 9, efd.value_nonblock
44
+
45
+ assert_nil efd.incr(0xfffffffffffffffe)
46
+ assert_raises(Errno::EAGAIN) { efd.incr_nonblock 1 }
47
+ end
48
+ end if defined?(SleepyPenguin::EventFD)
@@ -0,0 +1,38 @@
1
+ require 'test/unit'
2
+ require 'fcntl'
3
+ $-w = true
4
+
5
+ require 'sleepy_penguin'
6
+
7
+ class TestTimerFD < Test::Unit::TestCase
8
+ include SleepyPenguin
9
+
10
+ def test_constants
11
+ assert_kind_of Integer, TimerFD::REALTIME
12
+ assert_kind_of Integer, TimerFD::MONOTONIC
13
+ end
14
+
15
+ def test_create
16
+ tfd = TimerFD.create
17
+ assert_kind_of(IO, tfd)
18
+ end
19
+
20
+ def test_create_nonblock
21
+ tfd = TimerFD.create(TimerFD::REALTIME, TimerFD::NONBLOCK)
22
+ flags = tfd.fcntl(Fcntl::F_GETFL) & Fcntl::O_NONBLOCK
23
+ assert_equal(Fcntl::O_NONBLOCK, flags)
24
+ end if defined?(TimerFD::NONBLOCK)
25
+
26
+ def test_create_cloexec
27
+ tfd = TimerFD.create(TimerFD::REALTIME, TimerFD::CLOEXEC)
28
+ flags = tfd.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC
29
+ assert_equal(Fcntl::FD_CLOEXEC, flags)
30
+ end if defined?(TimerFD::CLOEXEC)
31
+
32
+ def test_settime
33
+ tfd = TimerFD.create(TimerFD::REALTIME)
34
+ assert_equal([0, 0], tfd.settime(TimerFD::ABSTIME, 0, 0.01))
35
+ sleep 0.01
36
+ assert_equal 1, tfd.expirations
37
+ end
38
+ end if defined?(SleepyPenguin::TimerFD)
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sleepy_penguin
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - sleepy_penguin hackers
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-09-26 00:00:00 +00:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: |-
23
+ sleepy_penguin provides access to newer, Linux-only system calls to wait
24
+ on events from traditionally non-I/O sources. Bindings to the eventfd,
25
+ timerfd, and epoll interfaces are provided.
26
+ email: sleepy.penguin@librelist.com
27
+ executables: []
28
+
29
+ extensions:
30
+ - ext/sleepy_penguin/extconf.rb
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README
34
+ - TODO
35
+ - NEWS
36
+ - ChangeLog
37
+ - lib/sleepy_penguin.rb
38
+ - ext/sleepy_penguin/init.c
39
+ - ext/sleepy_penguin/eventfd.c
40
+ - ext/sleepy_penguin/timerfd.c
41
+ files:
42
+ - .document
43
+ - .gitignore
44
+ - .manifest
45
+ - COPYING
46
+ - ChangeLog
47
+ - GIT-VERSION-FILE
48
+ - GIT-VERSION-GEN
49
+ - GNUmakefile
50
+ - LICENSE
51
+ - NEWS
52
+ - README
53
+ - Rakefile
54
+ - TODO
55
+ - ext/sleepy_penguin/epoll.c
56
+ - ext/sleepy_penguin/eventfd.c
57
+ - ext/sleepy_penguin/extconf.rb
58
+ - ext/sleepy_penguin/init.c
59
+ - ext/sleepy_penguin/nonblock.h
60
+ - ext/sleepy_penguin/sleepy_penguin.h
61
+ - ext/sleepy_penguin/timerfd.c
62
+ - ext/sleepy_penguin/value2timespec.h
63
+ - lib/sleepy_penguin.rb
64
+ - setup.rb
65
+ - sleepy_penguin.gemspec
66
+ - test/test_epoll.rb
67
+ - test/test_eventfd.rb
68
+ - test/test_timerfd.rb
69
+ has_rdoc: true
70
+ homepage: http://bogomips.org/sleepy_penguin/
71
+ licenses: []
72
+
73
+ post_install_message:
74
+ rdoc_options:
75
+ - -t
76
+ - Ruby I/O events for Linux
77
+ require_paths:
78
+ - lib
79
+ - ext
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ hash: 3
95
+ segments:
96
+ - 0
97
+ version: "0"
98
+ requirements: []
99
+
100
+ rubyforge_project: rainbows
101
+ rubygems_version: 1.3.7
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: Ruby I/O events for Linux
105
+ test_files:
106
+ - test/test_epoll.rb
107
+ - test/test_eventfd.rb
108
+ - test/test_timerfd.rb