raindrops-maintained 0.21.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.
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