sleepy_penguin 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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