raindrops-maintained 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.document +7 -0
  3. data/.gitattributes +4 -0
  4. data/.gitignore +16 -0
  5. data/.manifest +62 -0
  6. data/.olddoc.yml +16 -0
  7. data/COPYING +165 -0
  8. data/GIT-VERSION-FILE +1 -0
  9. data/GIT-VERSION-GEN +40 -0
  10. data/GNUmakefile +4 -0
  11. data/LATEST +9 -0
  12. data/LICENSE +16 -0
  13. data/NEWS +384 -0
  14. data/README +101 -0
  15. data/TODO +3 -0
  16. data/archive/.gitignore +3 -0
  17. data/archive/slrnpull.conf +4 -0
  18. data/examples/linux-listener-stats.rb +122 -0
  19. data/examples/middleware.ru +5 -0
  20. data/examples/watcher.ru +4 -0
  21. data/examples/watcher_demo.ru +13 -0
  22. data/examples/yahns.conf.rb +30 -0
  23. data/examples/zbatery.conf.rb +16 -0
  24. data/ext/raindrops/extconf.rb +163 -0
  25. data/ext/raindrops/linux_inet_diag.c +713 -0
  26. data/ext/raindrops/my_fileno.h +16 -0
  27. data/ext/raindrops/raindrops.c +487 -0
  28. data/ext/raindrops/raindrops_atomic.h +23 -0
  29. data/ext/raindrops/tcp_info.c +245 -0
  30. data/lib/raindrops/aggregate/last_data_recv.rb +94 -0
  31. data/lib/raindrops/aggregate/pmq.rb +245 -0
  32. data/lib/raindrops/aggregate.rb +8 -0
  33. data/lib/raindrops/last_data_recv.rb +102 -0
  34. data/lib/raindrops/linux.rb +77 -0
  35. data/lib/raindrops/middleware/proxy.rb +40 -0
  36. data/lib/raindrops/middleware.rb +153 -0
  37. data/lib/raindrops/struct.rb +62 -0
  38. data/lib/raindrops/watcher.rb +428 -0
  39. data/lib/raindrops.rb +72 -0
  40. data/pkg.mk +151 -0
  41. data/raindrops-maintained.gemspec +1 -0
  42. data/raindrops.gemspec +26 -0
  43. data/setup.rb +1586 -0
  44. data/test/ipv6_enabled.rb +9 -0
  45. data/test/rack_unicorn.rb +11 -0
  46. data/test/test_aggregate_pmq.rb +65 -0
  47. data/test/test_inet_diag_socket.rb +16 -0
  48. data/test/test_last_data_recv.rb +57 -0
  49. data/test/test_last_data_recv_unicorn.rb +69 -0
  50. data/test/test_linux.rb +281 -0
  51. data/test/test_linux_all_tcp_listen_stats.rb +66 -0
  52. data/test/test_linux_all_tcp_listen_stats_leak.rb +43 -0
  53. data/test/test_linux_ipv6.rb +166 -0
  54. data/test/test_linux_middleware.rb +64 -0
  55. data/test/test_linux_reuseport_tcp_listen_stats.rb +51 -0
  56. data/test/test_middleware.rb +128 -0
  57. data/test/test_middleware_unicorn.rb +37 -0
  58. data/test/test_middleware_unicorn_ipv6.rb +37 -0
  59. data/test/test_raindrops.rb +207 -0
  60. data/test/test_raindrops_gc.rb +38 -0
  61. data/test/test_struct.rb +54 -0
  62. data/test/test_tcp_info.rb +88 -0
  63. data/test/test_watcher.rb +186 -0
  64. metadata +193 -0
@@ -0,0 +1,9 @@
1
+ def ipv6_enabled?
2
+ tmp = TCPServer.new(ENV["TEST_HOST6"] || '::1', 0)
3
+ tmp.close
4
+ true
5
+ rescue => e
6
+ warn "skipping IPv6 tests, host does not seem to be IPv6 enabled:"
7
+ warn " #{e.class}: #{e}"
8
+ false
9
+ end
@@ -0,0 +1,11 @@
1
+ # -*- encoding: binary -*-
2
+ require "test/unit"
3
+ require "raindrops"
4
+ require "open-uri"
5
+ begin
6
+ require "unicorn"
7
+ require "rack"
8
+ require "rack/lobster"
9
+ rescue LoadError => e
10
+ warn "W: #{e} skipping test since Rack or Unicorn was not found"
11
+ end
@@ -0,0 +1,65 @@
1
+ require "test/unit"
2
+ require "raindrops"
3
+ pmq = begin
4
+ Raindrops::Aggregate::PMQ
5
+ rescue LoadError => e
6
+ warn "W: #{e} skipping #{__FILE__}"
7
+ false
8
+ end
9
+ if RUBY_VERSION.to_f < 1.9
10
+ pmq = false
11
+ warn "W: skipping #{__FILE__}, only Ruby 1.9 supported for now"
12
+ end
13
+
14
+ Thread.abort_on_exception = true
15
+
16
+ class TestAggregatePMQ < Test::Unit::TestCase
17
+
18
+ def setup
19
+ @queue = "/test.#{rand}"
20
+ end
21
+
22
+ def teardown
23
+ POSIX_MQ.unlink @queue
24
+ end
25
+
26
+ def test_run
27
+ pmq = Raindrops::Aggregate::PMQ.new :queue => @queue
28
+ thr = Thread.new { pmq.master_loop }
29
+ agg = Aggregate.new
30
+ (1..10).each { |i| pmq << i; agg << i }
31
+ pmq.stop_master_loop
32
+ assert thr.join
33
+ assert_equal agg.count, pmq.count
34
+ assert_equal agg.mean, pmq.mean
35
+ assert_equal agg.std_dev, pmq.std_dev
36
+ assert_equal agg.min, pmq.min
37
+ assert_equal agg.max, pmq.max
38
+ assert_equal agg.to_s, pmq.to_s
39
+ end
40
+
41
+ def test_multi_process
42
+ nr_workers = 4
43
+ nr = 100
44
+ pmq = Raindrops::Aggregate::PMQ.new :queue => @queue
45
+ pid = fork { pmq.master_loop }
46
+ workers = (1..nr_workers).map {
47
+ fork {
48
+ (1..nr).each { |i| pmq << i }
49
+ pmq.flush
50
+ }
51
+ }
52
+ workers.each { |wpid| assert Process.waitpid2(wpid).last.success? }
53
+ pmq.stop_master_loop
54
+ assert Process.waitpid2(pid).last.success?
55
+ assert_equal 400, pmq.count
56
+ agg = Aggregate.new
57
+ (1..nr_workers).map { (1..nr).each { |i| agg << i } }
58
+ assert_equal agg.to_s, pmq.to_s
59
+ assert_equal agg.mean, pmq.mean
60
+ assert_equal agg.std_dev, pmq.std_dev
61
+ assert_equal agg.min, pmq.min
62
+ assert_equal agg.max, pmq.max
63
+ assert_equal agg.to_s, pmq.to_s
64
+ end
65
+ end if pmq
@@ -0,0 +1,16 @@
1
+ # -*- encoding: binary -*-
2
+ require 'test/unit'
3
+ require 'raindrops'
4
+ require 'fcntl'
5
+ $stderr.sync = $stdout.sync = true
6
+
7
+ class TestInetDiagSocket < Test::Unit::TestCase
8
+ def test_new
9
+ sock = Raindrops::InetDiagSocket.new
10
+ assert_kind_of Socket, sock
11
+ assert_kind_of Integer, sock.fileno
12
+ flags = sock.fcntl(Fcntl::F_GETFD)
13
+ assert_equal Fcntl::FD_CLOEXEC, flags & Fcntl::FD_CLOEXEC
14
+ assert_nil sock.close
15
+ end
16
+ end if RUBY_PLATFORM =~ /linux/
@@ -0,0 +1,57 @@
1
+ begin
2
+ require 'aggregate'
3
+ have_aggregate = true
4
+ rescue LoadError => e
5
+ warn "W: #{e} skipping #{__FILE__}"
6
+ end
7
+ require 'test/unit'
8
+ require 'raindrops'
9
+ require 'io/wait'
10
+
11
+ class TestLastDataRecv < Test::Unit::TestCase
12
+ def setup
13
+ Raindrops::Aggregate::LastDataRecv.default_aggregate = []
14
+ end
15
+
16
+ def teardown
17
+ Raindrops::Aggregate::LastDataRecv.default_aggregate = nil
18
+ end
19
+
20
+ def test_accept_nonblock_agg
21
+ s = Socket.new(:INET, :STREAM, 0)
22
+ s.listen(128)
23
+ addr = s.connect_address
24
+ s.extend(Raindrops::Aggregate::LastDataRecv)
25
+ s.raindrops_aggregate = []
26
+ c = Socket.new(:INET, :STREAM, 0)
27
+ c.connect(addr)
28
+ c.write '.' # for TCP_DEFER_ACCEPT
29
+ client, ai = s.accept_nonblock(exception: false)
30
+ assert client.kind_of?(Socket)
31
+ assert ai.kind_of?(Addrinfo)
32
+ assert_equal 1, s.raindrops_aggregate.size
33
+ assert s.raindrops_aggregate[0].instance_of?(Integer)
34
+ client, ai = s.accept_nonblock(exception: false)
35
+ assert_equal :wait_readable, client
36
+ assert_nil ai
37
+ assert_equal 1, s.raindrops_aggregate.size
38
+ assert_raise(IO::WaitReadable) { s.accept_nonblock }
39
+ end
40
+
41
+ def test_accept_nonblock_one
42
+ s = TCPServer.new('127.0.0.1', 0)
43
+ s.extend(Raindrops::Aggregate::LastDataRecv)
44
+ s.raindrops_aggregate = []
45
+ addr = s.addr
46
+ c = TCPSocket.new(addr[3], addr[1])
47
+ c.write '.' # for TCP_DEFER_ACCEPT
48
+ client = s.accept_nonblock(exception: false)
49
+ assert client.kind_of?(TCPSocket)
50
+ assert_equal 1, s.raindrops_aggregate.size
51
+ assert s.raindrops_aggregate[0].instance_of?(Integer)
52
+ client = s.accept_nonblock(exception: false)
53
+ assert_equal :wait_readable, client
54
+ assert_equal 1, s.raindrops_aggregate.size
55
+ assert_raise(IO::WaitReadable) { s.accept_nonblock }
56
+ end
57
+ end if RUBY_PLATFORM =~ /linux/ && have_aggregate
@@ -0,0 +1,69 @@
1
+ # -*- encoding: binary -*-
2
+ require "./test/rack_unicorn"
3
+ require "tempfile"
4
+ require "net/http"
5
+
6
+ $stderr.sync = $stdout.sync = true
7
+ pmq = begin
8
+ Raindrops::Aggregate::PMQ
9
+ rescue LoadError => e
10
+ warn "W: #{e} skipping #{__FILE__}"
11
+ false
12
+ end
13
+ if RUBY_VERSION.to_f < 1.9
14
+ pmq = false
15
+ warn "W: skipping test=#{__FILE__}, only Ruby 1.9 supported for now"
16
+ end
17
+
18
+ class TestLastDataRecvUnicorn < Test::Unit::TestCase
19
+ def setup
20
+ @queue = "/test.#{rand}"
21
+ @host = ENV["UNICORN_TEST_ADDR"] || "127.0.0.1"
22
+ @sock = TCPServer.new @host, 0
23
+ @port = @sock.addr[1]
24
+ ENV["UNICORN_FD"] = @sock.fileno.to_s
25
+ @host_with_port = "#@host:#@port"
26
+ @cfg = Tempfile.new 'unicorn_config_file'
27
+ @cfg.puts "require 'raindrops'"
28
+ @cfg.puts "preload_app true"
29
+ ENV['RAINDROPS_MQUEUE'] = @queue
30
+ # @cfg.puts "worker_processes 4"
31
+ @opts = { :listeners => [ @host_with_port ], :config_file => @cfg.path }
32
+ end
33
+
34
+ def test_auto_listener
35
+ @srv = fork {
36
+ Thread.abort_on_exception = true
37
+ app = %q!Rack::Builder.new do
38
+ map("/ldr") { run Raindrops::LastDataRecv.new }
39
+ map("/") { run Rack::Lobster.new }
40
+ end.to_app!
41
+ def app.arity; 0; end
42
+ def app.call; eval self; end
43
+ Unicorn::HttpServer.new(app, @opts).start.join
44
+ }
45
+ 400.times { assert_kind_of Net::HTTPSuccess, get("/") }
46
+ resp = get("/ldr")
47
+ # # p(resp.methods - Object.methods)
48
+ # resp.each_header { |k,v| p [k, "=" , v] }
49
+ assert resp.header["x-count"]
50
+ assert resp.header["x-min"]
51
+ assert resp.header["x-max"]
52
+ assert resp.header["x-mean"]
53
+ assert resp.header["x-std-dev"]
54
+ assert resp.header["x-outliers-low"]
55
+ assert resp.header["x-outliers-high"]
56
+ assert resp.body.size > 0
57
+ end
58
+
59
+ def get(path)
60
+ Net::HTTP.start(@host, @port) { |http| http.get path }
61
+ end
62
+
63
+ def teardown
64
+ Process.kill :QUIT, @srv
65
+ _, status = Process.waitpid2 @srv
66
+ assert status.success?
67
+ POSIX_MQ.unlink @queue
68
+ end
69
+ end if defined?(Unicorn) && RUBY_PLATFORM =~ /linux/ && pmq
@@ -0,0 +1,281 @@
1
+ # -*- encoding: binary -*-
2
+ require 'test/unit'
3
+ require 'tempfile'
4
+ require 'raindrops'
5
+ require 'socket'
6
+ require 'pp'
7
+ $stderr.sync = $stdout.sync = true
8
+
9
+ class TestLinux < Test::Unit::TestCase
10
+ include Raindrops::Linux
11
+
12
+ TEST_ADDR = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
13
+
14
+ def setup
15
+ @to_close = []
16
+ end
17
+
18
+ def teardown
19
+ @to_close.each { |io| io.close unless io.closed? }
20
+ end
21
+
22
+ def test_unix
23
+ tmp = Tempfile.new("\xde\xad\xbe\xef") # valid path, really :)
24
+ File.unlink(tmp.path)
25
+ us = UNIXServer.new(tmp.path)
26
+ stats = unix_listener_stats([tmp.path])
27
+ assert_equal 1, stats.size
28
+ assert_equal 0, stats[tmp.path].active
29
+ assert_equal 0, stats[tmp.path].queued
30
+
31
+ @to_close << UNIXSocket.new(tmp.path)
32
+ stats = unix_listener_stats([tmp.path])
33
+ assert_equal 1, stats.size
34
+ assert_equal 0, stats[tmp.path].active
35
+ assert_equal 1, stats[tmp.path].queued
36
+
37
+ @to_close << UNIXSocket.new(tmp.path)
38
+ stats = unix_listener_stats([tmp.path])
39
+ assert_equal 1, stats.size
40
+ assert_equal 0, stats[tmp.path].active
41
+ assert_equal 2, stats[tmp.path].queued
42
+
43
+ @to_close << us.accept
44
+ stats = unix_listener_stats([tmp.path])
45
+ assert_equal 1, stats.size
46
+ assert_equal 1, stats[tmp.path].active
47
+ assert_equal 1, stats[tmp.path].queued
48
+ end
49
+
50
+ def test_unix_all
51
+ tmp = Tempfile.new("\xde\xad\xbe\xef") # valid path, really :)
52
+ File.unlink(tmp.path)
53
+ us = UNIXServer.new(tmp.path)
54
+ @to_close << UNIXSocket.new(tmp.path)
55
+ stats = unix_listener_stats
56
+ assert_equal 0, stats[tmp.path].active
57
+ assert_equal 1, stats[tmp.path].queued
58
+
59
+ @to_close << UNIXSocket.new(tmp.path)
60
+ stats = unix_listener_stats
61
+ assert_equal 0, stats[tmp.path].active
62
+ assert_equal 2, stats[tmp.path].queued
63
+
64
+ @to_close << us.accept
65
+ stats = unix_listener_stats
66
+ assert_equal 1, stats[tmp.path].active
67
+ assert_equal 1, stats[tmp.path].queued
68
+ end
69
+
70
+ def test_unix_all_unused
71
+ tmp = Tempfile.new("\xde\xad\xbe\xef") # valid path, really :)
72
+ File.unlink(tmp.path)
73
+ us = UNIXServer.new(tmp.path)
74
+ stats = unix_listener_stats
75
+ assert stats.keys.include?(tmp.path), stats.inspect
76
+
77
+ assert_equal 0, stats[tmp.path].active
78
+ assert_equal 0, stats[tmp.path].queued
79
+ us.close
80
+ end
81
+
82
+ def test_unix_resolves_symlinks
83
+ tmp = Tempfile.new("\xde\xad\xbe\xef") # valid path, really :)
84
+ File.unlink(tmp.path)
85
+ us = UNIXServer.new(tmp.path)
86
+
87
+ # Create a symlink
88
+ link = Tempfile.new("somethingelse")
89
+ File.unlink(link.path) # We need an available name, not an actual file
90
+ File.symlink(tmp.path, link.path)
91
+
92
+ @to_close << UNIXSocket.new(tmp.path)
93
+ stats = unix_listener_stats
94
+ assert_equal 0, stats[tmp.path].active
95
+ assert_equal 1, stats[tmp.path].queued
96
+
97
+ @to_close << UNIXSocket.new(link.path)
98
+ stats = unix_listener_stats([link.path])
99
+ assert_equal 0, stats[link.path].active
100
+ assert_equal 2, stats[link.path].queued
101
+
102
+ assert_equal stats[link.path].object_id, stats[tmp.path].object_id
103
+
104
+ @to_close << us.accept
105
+ stats = unix_listener_stats
106
+ assert_equal 1, stats[tmp.path].active
107
+ assert_equal 1, stats[tmp.path].queued
108
+ end
109
+
110
+ def test_tcp
111
+ s = TCPServer.new(TEST_ADDR, 0)
112
+ port = s.addr[1]
113
+ addr = "#{TEST_ADDR}:#{port}"
114
+ addrs = [ addr ]
115
+ stats = tcp_listener_stats(addrs)
116
+ assert_equal 1, stats.size
117
+ assert_equal 0, stats[addr].queued
118
+ assert_equal 0, stats[addr].active
119
+
120
+ @to_close << TCPSocket.new(TEST_ADDR, port)
121
+ stats = tcp_listener_stats(addrs)
122
+ assert_equal 1, stats.size
123
+ assert_equal 1, stats[addr].queued
124
+ assert_equal 0, stats[addr].active
125
+
126
+ @to_close << s.accept
127
+ stats = tcp_listener_stats(addrs)
128
+ assert_equal 1, stats.size
129
+ assert_equal 0, stats[addr].queued
130
+ assert_equal 1, stats[addr].active
131
+ end
132
+
133
+ def test_tcp_reuse_sock
134
+ nlsock = Raindrops::InetDiagSocket.new
135
+ s = TCPServer.new(TEST_ADDR, 0)
136
+ port = s.addr[1]
137
+ addr = "#{TEST_ADDR}:#{port}"
138
+ addrs = [ addr ]
139
+ stats = tcp_listener_stats(addrs, nlsock)
140
+ assert_equal 1, stats.size
141
+ assert_equal 0, stats[addr].queued
142
+ assert_equal 0, stats[addr].active
143
+
144
+ @to_close << TCPSocket.new(TEST_ADDR, port)
145
+ stats = tcp_listener_stats(addrs, nlsock)
146
+ assert_equal 1, stats.size
147
+ assert_equal 1, stats[addr].queued
148
+ assert_equal 0, stats[addr].active
149
+
150
+ @to_close << s.accept
151
+ stats = tcp_listener_stats(addrs, nlsock)
152
+ assert_equal 1, stats.size
153
+ assert_equal 0, stats[addr].queued
154
+ assert_equal 1, stats[addr].active
155
+ ensure
156
+ nlsock.close
157
+ end
158
+
159
+ def test_tcp_multi
160
+ s1 = TCPServer.new(TEST_ADDR, 0)
161
+ s2 = TCPServer.new(TEST_ADDR, 0)
162
+ port1, port2 = s1.addr[1], s2.addr[1]
163
+ addr1, addr2 = "#{TEST_ADDR}:#{port1}", "#{TEST_ADDR}:#{port2}"
164
+ addrs = [ addr1, addr2 ]
165
+ stats = tcp_listener_stats(addrs)
166
+ assert_equal 2, stats.size
167
+ assert_equal 0, stats[addr1].queued
168
+ assert_equal 0, stats[addr1].active
169
+ assert_equal 0, stats[addr2].queued
170
+ assert_equal 0, stats[addr2].active
171
+
172
+ @to_close << TCPSocket.new(TEST_ADDR, port1)
173
+ stats = tcp_listener_stats(addrs)
174
+ assert_equal 2, stats.size
175
+ assert_equal 1, stats[addr1].queued
176
+ assert_equal 0, stats[addr1].active
177
+ assert_equal 0, stats[addr2].queued
178
+ assert_equal 0, stats[addr2].active
179
+
180
+ sc1 = s1.accept
181
+ stats = tcp_listener_stats(addrs)
182
+ assert_equal 2, stats.size
183
+ assert_equal 0, stats[addr1].queued
184
+ assert_equal 1, stats[addr1].active
185
+ assert_equal 0, stats[addr2].queued
186
+ assert_equal 0, stats[addr2].active
187
+
188
+ @to_close << TCPSocket.new(TEST_ADDR, port2)
189
+ stats = tcp_listener_stats(addrs)
190
+ assert_equal 2, stats.size
191
+ assert_equal 0, stats[addr1].queued
192
+ assert_equal 1, stats[addr1].active
193
+ assert_equal 1, stats[addr2].queued
194
+ assert_equal 0, stats[addr2].active
195
+
196
+ @to_close << TCPSocket.new(TEST_ADDR, port2)
197
+ stats = tcp_listener_stats(addrs)
198
+ assert_equal 2, stats.size
199
+ assert_equal 0, stats[addr1].queued
200
+ assert_equal 1, stats[addr1].active
201
+ assert_equal 2, stats[addr2].queued
202
+ assert_equal 0, stats[addr2].active
203
+
204
+ @to_close << s2.accept
205
+ stats = tcp_listener_stats(addrs)
206
+ assert_equal 2, stats.size
207
+ assert_equal 0, stats[addr1].queued
208
+ assert_equal 1, stats[addr1].active
209
+ assert_equal 1, stats[addr2].queued
210
+ assert_equal 1, stats[addr2].active
211
+
212
+ sc1.close
213
+ stats = tcp_listener_stats(addrs)
214
+ assert_equal 0, stats[addr1].queued
215
+ assert_equal 0, stats[addr1].active
216
+ assert_equal 1, stats[addr2].queued
217
+ assert_equal 1, stats[addr2].active
218
+
219
+ # make sure we don't leave "true" placeholders in results if a
220
+ # listener becomes invalid (even momentarily).
221
+ s2.close
222
+ stats = tcp_listener_stats(addrs)
223
+ assert stats.values.all? { |x| x.instance_of?(Raindrops::ListenStats) },
224
+ "placeholders left: #{stats.inspect}"
225
+ end
226
+
227
+ # tries to overflow buffers
228
+ def test_tcp_stress_test
229
+ nr_proc = 32
230
+ nr_sock = 500
231
+ s = TCPServer.new(TEST_ADDR, 0)
232
+ port = s.addr[1]
233
+ addr = "#{TEST_ADDR}:#{port}"
234
+ addrs = [ addr ]
235
+ rda, wra = IO.pipe
236
+ rdb, wrb = IO.pipe
237
+
238
+ nr_proc.times do
239
+ fork do
240
+ rda.close
241
+ wrb.close
242
+ @to_close.concat((1..nr_sock).map { s.accept })
243
+ wra.syswrite('.')
244
+ wra.close
245
+ rdb.sysread(1) # wait for parent to nuke us
246
+ end
247
+ end
248
+
249
+ nr_proc.times do
250
+ fork do
251
+ rda.close
252
+ wrb.close
253
+ @to_close.concat((1..nr_sock).map { TCPSocket.new(TEST_ADDR, port) })
254
+ wra.syswrite('.')
255
+ wra.close
256
+ rdb.sysread(1) # wait for parent to nuke us
257
+ end
258
+ end
259
+
260
+ assert_equal('.' * (nr_proc * 2), rda.read(nr_proc * 2))
261
+
262
+ rda.close
263
+ stats = tcp_listener_stats(addrs)
264
+ expect = { addr => Raindrops::ListenStats[nr_sock * nr_proc, 0] }
265
+ assert_equal expect, stats
266
+
267
+ @to_close << TCPSocket.new(TEST_ADDR, port)
268
+ stats = tcp_listener_stats(addrs)
269
+ expect = { addr => Raindrops::ListenStats[nr_sock * nr_proc, 1] }
270
+ assert_equal expect, stats
271
+
272
+ if ENV["BENCHMARK"].to_i != 0
273
+ require 'benchmark'
274
+ puts(Benchmark.measure{1000.times { tcp_listener_stats(addrs) }})
275
+ end
276
+
277
+ wrb.syswrite('.' * (nr_proc * 2)) # broadcast a wakeup
278
+ statuses = Process.waitall
279
+ statuses.each { |(_,status)| assert status.success?, status.inspect }
280
+ end if ENV["STRESS"].to_i != 0
281
+ end if RUBY_PLATFORM =~ /linux/
@@ -0,0 +1,66 @@
1
+ # -*- encoding: binary -*-
2
+ require 'test/unit'
3
+ require 'socket'
4
+ require 'raindrops'
5
+ require 'pp'
6
+ $stderr.sync = $stdout.sync = true
7
+
8
+ class TestLinuxAllTcpListenStats < Test::Unit::TestCase
9
+ include Raindrops::Linux
10
+ TEST_ADDR = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
11
+
12
+ def test_print_all
13
+ puts "EVERYTHING"
14
+ pp Raindrops::Linux.tcp_listener_stats
15
+ puts("-" * 72)
16
+ end if $stdout.tty?
17
+
18
+ def setup
19
+ @socks = []
20
+ end
21
+
22
+ def teardown
23
+ @socks.each { |io| io.closed? or io.close }
24
+ end
25
+
26
+ def new_server
27
+ s = TCPServer.new TEST_ADDR, 0
28
+ @socks << s
29
+ [ s, s.addr[1] ]
30
+ end
31
+
32
+ def new_client(port)
33
+ s = TCPSocket.new("127.0.0.1", port)
34
+ @socks << s
35
+ s
36
+ end
37
+
38
+ def new_accept(srv)
39
+ c = srv.accept
40
+ @socks << c
41
+ c
42
+ end
43
+
44
+ def test_all_ports
45
+ srv, port = new_server
46
+ addr = "#{TEST_ADDR}:#{port}"
47
+ all = Raindrops::Linux.tcp_listener_stats
48
+ assert_equal [0,0], all[addr].to_a
49
+
50
+ new_client(port)
51
+ all = Raindrops::Linux.tcp_listener_stats
52
+ assert_equal [0,1], all[addr].to_a
53
+
54
+ new_client(port)
55
+ all = Raindrops::Linux.tcp_listener_stats
56
+ assert_equal [0,2], all[addr].to_a
57
+
58
+ new_accept(srv)
59
+ all = Raindrops::Linux.tcp_listener_stats
60
+ assert_equal [1,1], all[addr].to_a
61
+
62
+ new_accept(srv)
63
+ all = Raindrops::Linux.tcp_listener_stats
64
+ assert_equal [2,0], all[addr].to_a
65
+ end
66
+ end if RUBY_PLATFORM =~ /linux/
@@ -0,0 +1,43 @@
1
+ # -*- encoding: binary -*-
2
+ require 'test/unit'
3
+ require 'raindrops'
4
+ require 'socket'
5
+ require 'benchmark'
6
+ $stderr.sync = $stdout.sync = true
7
+
8
+ class TestLinuxAllTcpListenStatsLeak < Test::Unit::TestCase
9
+
10
+ TEST_ADDR = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
11
+
12
+
13
+ def rss_kb
14
+ File.readlines("/proc/#$$/status").grep(/VmRSS:/)[0].split(/\s+/)[1].to_i
15
+ end
16
+ def test_leak
17
+ s = TCPServer.new(TEST_ADDR, 0)
18
+ start_kb = rss_kb
19
+ p [ :start_kb, start_kb ]
20
+ assert_nothing_raised do
21
+ p(Benchmark.measure {
22
+ 1000.times { Raindrops::Linux.all_tcp_listener_stats }
23
+ })
24
+ end
25
+ cur_kb = rss_kb
26
+ p [ :cur_kb, cur_kb ]
27
+ now = Time.now.to_i
28
+ fin = now + 60
29
+ assert_nothing_raised do
30
+ 1000000000.times { |i|
31
+ if (i % 1024) == 0
32
+ now = Time.now.to_i
33
+ break if now > fin
34
+ end
35
+ Raindrops::Linux.all_tcp_listener_stats
36
+ }
37
+ end
38
+ cur_kb = rss_kb
39
+ p [ :cur_kb, cur_kb ]
40
+ ensure
41
+ s.close
42
+ end
43
+ end if ENV["STRESS"].to_i != 0