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