uringmachine 0.28.3 → 0.29.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -1
- data/TODO.md +29 -35
- data/benchmark/common.rb +6 -6
- data/benchmark/gets.rb +49 -0
- data/benchmark/gets_concurrent.rb +122 -0
- data/benchmark/{read_each.rb → output.rb} +27 -24
- data/docs/design/buffer_pool.md +35 -0
- data/docs/um_api.md +2 -0
- data/ext/um/extconf.rb +6 -5
- data/ext/um/um.c +50 -16
- data/ext/um/um.h +102 -32
- data/ext/um/um_buffer_pool.c +248 -0
- data/ext/um/um_class.c +28 -16
- data/ext/um/um_op.c +29 -13
- data/ext/um/um_ssl.c +24 -27
- data/ext/um/um_stream.c +382 -150
- data/ext/um/um_stream_class.c +119 -63
- data/ext/um/um_utils.c +6 -6
- data/grant-2025/tasks.md +13 -7
- data/lib/uringmachine/fiber_scheduler.rb +36 -10
- data/lib/uringmachine/version.rb +1 -1
- data/lib/uringmachine.rb +60 -19
- data/test/helper.rb +4 -0
- data/test/test_fiber.rb +93 -12
- data/test/test_fiber_scheduler.rb +8 -50
- data/test/test_stream.rb +466 -124
- data/test/test_um.rb +133 -49
- metadata +6 -4
- data/ext/um/um_buffer.c +0 -49
data/test/test_stream.rb
CHANGED
|
@@ -1,198 +1,328 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'helper'
|
|
4
|
+
require 'securerandom'
|
|
5
|
+
require 'openssl'
|
|
6
|
+
require 'localhost/authority'
|
|
4
7
|
|
|
5
8
|
class StreamBaseTest < UMBaseTest
|
|
9
|
+
attr_reader :stream
|
|
10
|
+
|
|
6
11
|
def setup
|
|
7
12
|
super
|
|
8
13
|
@rfd, @wfd = UM.pipe
|
|
9
14
|
@stream = UM::Stream.new(@machine, @rfd)
|
|
10
15
|
end
|
|
11
|
-
end
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
def teardown
|
|
18
|
+
@stream = nil
|
|
19
|
+
machine.close(@rfd) rescue nil
|
|
20
|
+
machine.close(@wfd) rescue nil
|
|
21
|
+
super
|
|
16
22
|
end
|
|
23
|
+
end
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
class StreamTest < StreamBaseTest
|
|
26
|
+
def buffer_metrics
|
|
27
|
+
machine.metrics.fetch_values(
|
|
28
|
+
:buffers_allocated,
|
|
29
|
+
:buffers_free,
|
|
30
|
+
:segments_free,
|
|
31
|
+
:buffer_space_allocated,
|
|
32
|
+
:buffer_space_commited,
|
|
33
|
+
)
|
|
20
34
|
end
|
|
21
35
|
|
|
22
|
-
def
|
|
23
|
-
|
|
36
|
+
def test_stream_basic_usage
|
|
37
|
+
assert_equal [0, 0, 0, 0, 0], buffer_metrics
|
|
38
|
+
machine.write(@wfd, "foobar")
|
|
24
39
|
machine.close(@wfd)
|
|
25
40
|
|
|
26
|
-
|
|
27
|
-
assert_equal '
|
|
28
|
-
|
|
41
|
+
buf = stream.get_string(3)
|
|
42
|
+
assert_equal 'foo', buf
|
|
43
|
+
|
|
44
|
+
buf = stream.get_string(-6)
|
|
45
|
+
assert_equal 'bar', buf
|
|
46
|
+
assert stream.eof?
|
|
47
|
+
|
|
48
|
+
stream.clear
|
|
49
|
+
|
|
50
|
+
# initial buffer size: 6BKV, initial buffers commited: 16 (256KB)
|
|
51
|
+
# (plus an additional buffer commited after first usage)
|
|
52
|
+
assert_equal [16, 0, 256, 16384 * 16, 16384 * 16 - 6], buffer_metrics
|
|
53
|
+
assert_equal 0, machine.metrics[:ops_pending]
|
|
29
54
|
end
|
|
30
55
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
56
|
+
def test_stream_clear
|
|
57
|
+
rfd, wfd = UM.pipe
|
|
58
|
+
stream = UM::Stream.new(machine, rfd)
|
|
34
59
|
|
|
35
|
-
|
|
36
|
-
|
|
60
|
+
assert_equal [0, 0, 0, 0, 0], buffer_metrics
|
|
61
|
+
machine.write(wfd, "foobar")
|
|
62
|
+
|
|
63
|
+
buf = stream.get_string(3)
|
|
37
64
|
assert_equal 'foo', buf
|
|
38
|
-
assert_equal ret, buf
|
|
39
65
|
|
|
40
|
-
|
|
41
|
-
assert_equal
|
|
42
|
-
|
|
66
|
+
assert_equal 1, machine.metrics[:ops_pending]
|
|
67
|
+
assert_equal 255, machine.metrics[:segments_free]
|
|
68
|
+
|
|
69
|
+
stream.clear
|
|
70
|
+
machine.snooze
|
|
71
|
+
assert_equal 0, machine.metrics[:ops_pending]
|
|
72
|
+
assert_equal 256, machine.metrics[:segments_free]
|
|
73
|
+
|
|
74
|
+
assert_equal [16, 0, 256, 16384 * 16, 16384 * 16 - 6], buffer_metrics
|
|
75
|
+
ensure
|
|
76
|
+
machine.close(rfd) rescue nil
|
|
77
|
+
machine.close(wfd) rescue nil
|
|
43
78
|
end
|
|
44
79
|
|
|
45
|
-
def
|
|
46
|
-
|
|
47
|
-
|
|
80
|
+
def test_stream_big_read
|
|
81
|
+
s1, s2 = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0)
|
|
82
|
+
stream = UM::Stream.new(machine, s2)
|
|
48
83
|
|
|
49
|
-
|
|
50
|
-
ret = @stream.get_line(buf, 3)
|
|
51
|
-
assert_nil ret
|
|
52
|
-
assert_equal '', buf
|
|
84
|
+
msg = '1234567' * 20000
|
|
53
85
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
86
|
+
f = machine.spin do
|
|
87
|
+
machine.sendv(s1, msg)
|
|
88
|
+
machine.snooze
|
|
89
|
+
machine.shutdown(s1, UM::SHUT_WR)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
buf = stream.get_string(msg.bytesize)
|
|
93
|
+
assert_equal msg, buf
|
|
94
|
+
ensure
|
|
95
|
+
machine.terminate(f)
|
|
96
|
+
machine.join(f)
|
|
58
97
|
end
|
|
59
98
|
|
|
60
|
-
def
|
|
61
|
-
|
|
99
|
+
def test_stream_buffer_reuse
|
|
100
|
+
s1, s2 = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0)
|
|
101
|
+
stream = UM::Stream.new(machine, s2)
|
|
102
|
+
|
|
103
|
+
msg = '1234567' * 20000
|
|
104
|
+
|
|
105
|
+
f = machine.spin do
|
|
106
|
+
machine.sendv(s1, msg, msg)
|
|
107
|
+
machine.sleep(0.05)
|
|
108
|
+
machine.sendv(s1, msg, msg)
|
|
109
|
+
machine.shutdown(s1, UM::SHUT_WR)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
buf = stream.get_string(msg.bytesize)
|
|
113
|
+
assert_equal msg, buf
|
|
114
|
+
|
|
115
|
+
buf = stream.get_string(msg.bytesize)
|
|
116
|
+
assert_equal msg, buf
|
|
117
|
+
|
|
118
|
+
buf = stream.get_string(msg.bytesize)
|
|
119
|
+
assert_equal msg, buf
|
|
120
|
+
|
|
121
|
+
buf = stream.get_string(msg.bytesize)
|
|
122
|
+
assert_equal msg, buf
|
|
123
|
+
|
|
124
|
+
stream.clear
|
|
125
|
+
# numbers may vary with different kernel versions
|
|
126
|
+
assert_in_range 24..32, machine.metrics[:buffers_allocated]
|
|
127
|
+
assert_in_range 10..18, machine.metrics[:buffers_free]
|
|
128
|
+
assert_equal 256, machine.metrics[:segments_free]
|
|
129
|
+
ensure
|
|
130
|
+
machine.terminate(f)
|
|
131
|
+
machine.join(f)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def test_stream_get_line
|
|
135
|
+
machine.write(@wfd, "foo\nbar\r\nbaz")
|
|
62
136
|
machine.close(@wfd)
|
|
63
137
|
|
|
64
|
-
|
|
65
|
-
ret = @stream.get_line(buf, -3)
|
|
66
|
-
assert_nil ret
|
|
67
|
-
assert_equal '', buf
|
|
138
|
+
assert_equal [0, 0, 0, 0, 0], buffer_metrics
|
|
68
139
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
assert_equal
|
|
72
|
-
assert_equal
|
|
140
|
+
assert_equal 'foo', stream.get_line(0)
|
|
141
|
+
|
|
142
|
+
assert_equal [16, 0, 255, 16384 * 16, 16384 * 16 - 12], buffer_metrics
|
|
143
|
+
assert_equal 'bar', stream.get_line(0)
|
|
144
|
+
assert_nil stream.get_line(0)
|
|
145
|
+
assert_equal "baz", stream.get_string(-6)
|
|
73
146
|
end
|
|
74
147
|
|
|
75
|
-
def
|
|
76
|
-
machine.write(@wfd, "
|
|
148
|
+
def test_stream_get_line_segmented
|
|
149
|
+
machine.write(@wfd, "foo\n")
|
|
150
|
+
assert_equal 'foo', stream.get_line(0)
|
|
151
|
+
|
|
152
|
+
machine.write(@wfd, "bar")
|
|
153
|
+
machine.write(@wfd, "\r\n")
|
|
154
|
+
machine.write(@wfd, "baz\n")
|
|
77
155
|
machine.close(@wfd)
|
|
78
156
|
|
|
79
|
-
|
|
80
|
-
assert_equal
|
|
81
|
-
assert_equal '
|
|
82
|
-
|
|
157
|
+
# three segments received
|
|
158
|
+
assert_equal [16, 0, 253, 16384 * 16, 16384 * 16 - 13], buffer_metrics
|
|
159
|
+
assert_equal 'bar', stream.get_line(0)
|
|
160
|
+
assert_equal [16, 0, 255, 16384 * 16, 16384 * 16 - 13], buffer_metrics
|
|
161
|
+
assert_equal 'baz', stream.get_line(0)
|
|
162
|
+
assert_equal [16, 0, 256, 16384 * 16, 16384 * 16 - 13], buffer_metrics
|
|
163
|
+
assert_nil stream.get_line(0)
|
|
83
164
|
end
|
|
84
165
|
|
|
85
|
-
def
|
|
86
|
-
machine.write(@wfd, "
|
|
166
|
+
def test_stream_get_line_maxlen
|
|
167
|
+
machine.write(@wfd, "foobar\r\n")
|
|
168
|
+
|
|
169
|
+
assert_nil stream.get_line(3)
|
|
170
|
+
# verify that stream pos has not changed
|
|
171
|
+
assert_equal 'foobar', stream.get_line(0)
|
|
172
|
+
|
|
173
|
+
machine.write(@wfd, "baz")
|
|
174
|
+
machine.write(@wfd, "\n")
|
|
175
|
+
machine.write(@wfd, "bizz")
|
|
176
|
+
machine.write(@wfd, "\n")
|
|
87
177
|
machine.close(@wfd)
|
|
88
178
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
assert_equal '
|
|
92
|
-
|
|
179
|
+
assert_nil stream.get_line(2)
|
|
180
|
+
assert_nil stream.get_line(3)
|
|
181
|
+
assert_equal 'baz', stream.get_line(4)
|
|
182
|
+
|
|
183
|
+
assert_nil stream.get_line(3)
|
|
184
|
+
assert_nil stream.get_line(4)
|
|
185
|
+
assert_equal 'bizz', stream.get_line(5)
|
|
93
186
|
|
|
94
|
-
|
|
95
|
-
assert_equal
|
|
96
|
-
assert_equal ret, buf
|
|
187
|
+
assert_nil stream.get_line(8)
|
|
188
|
+
assert_equal [16, 0, 256, 16384 * 16, 16384 * 16 - 17], buffer_metrics
|
|
97
189
|
end
|
|
98
190
|
|
|
99
|
-
def
|
|
100
|
-
machine.write(@wfd, "
|
|
191
|
+
def test_stream_get_string
|
|
192
|
+
machine.write(@wfd, "foobarbazblahzzz")
|
|
101
193
|
machine.close(@wfd)
|
|
102
194
|
|
|
103
|
-
|
|
104
|
-
assert_equal '
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
assert_nil ret
|
|
195
|
+
assert_equal 'foobar', stream.get_string(6)
|
|
196
|
+
assert_equal 'baz', stream.get_string(3)
|
|
197
|
+
assert_equal 'blah', stream.get_string(4)
|
|
198
|
+
assert_nil stream.get_string(4)
|
|
108
199
|
end
|
|
109
200
|
|
|
110
|
-
def
|
|
201
|
+
def test_stream_get_string_zero_len
|
|
111
202
|
machine.write(@wfd, "foobar")
|
|
203
|
+
|
|
204
|
+
assert_equal 'foobar', stream.get_string(0)
|
|
205
|
+
|
|
206
|
+
machine.write(@wfd, "bazblah")
|
|
112
207
|
machine.close(@wfd)
|
|
208
|
+
assert_equal 'bazblah', stream.get_string(0)
|
|
209
|
+
assert_nil stream.get_string(0)
|
|
210
|
+
end
|
|
113
211
|
|
|
114
|
-
|
|
115
|
-
|
|
212
|
+
def test_stream_get_string_negative_len
|
|
213
|
+
machine.write(@wfd, "foobar")
|
|
116
214
|
|
|
117
|
-
|
|
118
|
-
assert_equal
|
|
215
|
+
assert_equal 'foo', stream.get_string(-3)
|
|
216
|
+
assert_equal 'bar', stream.get_string(-6)
|
|
119
217
|
|
|
120
|
-
|
|
121
|
-
|
|
218
|
+
machine.write(@wfd, "bazblah")
|
|
219
|
+
machine.close(@wfd)
|
|
220
|
+
assert_equal 'bazblah', stream.get_string(-12)
|
|
221
|
+
assert_nil stream.get_string(-3)
|
|
222
|
+
end
|
|
122
223
|
|
|
123
|
-
|
|
124
|
-
|
|
224
|
+
def test_stream_big_data
|
|
225
|
+
data = SecureRandom.random_bytes(300_000)
|
|
226
|
+
fiber = machine.spin {
|
|
227
|
+
machine.writev(@wfd, data)
|
|
228
|
+
machine.close(@wfd)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
received = []
|
|
232
|
+
loop {
|
|
233
|
+
msg = stream.get_string(-60_000)
|
|
234
|
+
break if !msg
|
|
235
|
+
|
|
236
|
+
received << msg
|
|
237
|
+
}
|
|
238
|
+
machine.join(fiber)
|
|
239
|
+
# since a pipe is limited to 64KB, we're going to receive 4 pairs of 60000B
|
|
240
|
+
# and 5536B, then the remainder
|
|
241
|
+
assert_equal 9, received.size
|
|
242
|
+
assert_equal data, received.join
|
|
125
243
|
end
|
|
126
244
|
end
|
|
127
245
|
|
|
128
246
|
class StreamRespTest < StreamBaseTest
|
|
129
|
-
def
|
|
247
|
+
def test_stream_resp_decode
|
|
130
248
|
machine.write(@wfd, "+foo bar\r\n")
|
|
131
|
-
assert_equal "foo bar",
|
|
249
|
+
assert_equal "foo bar", stream.resp_decode
|
|
132
250
|
|
|
133
251
|
machine.write(@wfd, "+baz\r\n")
|
|
134
|
-
assert_equal "baz",
|
|
252
|
+
assert_equal "baz", stream.resp_decode
|
|
135
253
|
|
|
136
254
|
machine.write(@wfd, "-foobar\r\n")
|
|
137
|
-
o =
|
|
255
|
+
o = stream.resp_decode
|
|
138
256
|
assert_kind_of UM::Stream::RESPError, o
|
|
139
257
|
assert_equal "foobar", o.message
|
|
140
258
|
|
|
141
259
|
machine.write(@wfd, "!3\r\nbaz\r\n")
|
|
142
|
-
o =
|
|
260
|
+
o = stream.resp_decode
|
|
143
261
|
assert_kind_of UM::Stream::RESPError, o
|
|
144
262
|
assert_equal "baz", o.message
|
|
145
263
|
|
|
146
264
|
machine.write(@wfd, ":123\r\n")
|
|
147
|
-
assert_equal 123,
|
|
265
|
+
assert_equal 123, stream.resp_decode
|
|
148
266
|
|
|
149
267
|
machine.write(@wfd, ":-123\r\n")
|
|
150
|
-
assert_equal(-123,
|
|
268
|
+
assert_equal(-123, stream.resp_decode)
|
|
151
269
|
|
|
152
270
|
machine.write(@wfd, ",123.321\r\n")
|
|
153
|
-
assert_equal 123.321,
|
|
271
|
+
assert_equal 123.321, stream.resp_decode
|
|
154
272
|
|
|
155
273
|
machine.write(@wfd, "_\r\n")
|
|
156
|
-
assert_nil
|
|
274
|
+
assert_nil stream.resp_decode
|
|
157
275
|
|
|
158
276
|
machine.write(@wfd, "#t\r\n")
|
|
159
|
-
assert_equal true,
|
|
277
|
+
assert_equal true, stream.resp_decode
|
|
160
278
|
|
|
161
279
|
machine.write(@wfd, "#f\r\n")
|
|
162
|
-
assert_equal false,
|
|
280
|
+
assert_equal false, stream.resp_decode
|
|
163
281
|
|
|
164
282
|
machine.write(@wfd, "$6\r\nfoobar\r\n")
|
|
165
|
-
assert_equal "foobar",
|
|
283
|
+
assert_equal "foobar", stream.resp_decode
|
|
166
284
|
|
|
167
285
|
machine.write(@wfd, "$3\r\nbaz\r\n")
|
|
168
|
-
assert_equal "baz",
|
|
286
|
+
assert_equal "baz", stream.resp_decode
|
|
169
287
|
|
|
170
288
|
machine.write(@wfd, "=10\r\ntxt:foobar\r\n")
|
|
171
|
-
assert_equal "foobar",
|
|
289
|
+
assert_equal "foobar", stream.resp_decode
|
|
172
290
|
|
|
173
291
|
machine.write(@wfd, "*3\r\n+foo\r\n:42\r\n$3\r\nbar\r\n")
|
|
174
|
-
assert_equal ['foo', 42, 'bar'],
|
|
292
|
+
assert_equal ['foo', 42, 'bar'], stream.resp_decode
|
|
175
293
|
|
|
176
294
|
machine.write(@wfd, "~3\r\n+foo\r\n:42\r\n$3\r\nbar\r\n")
|
|
177
|
-
assert_equal ['foo', 42, 'bar'],
|
|
295
|
+
assert_equal ['foo', 42, 'bar'], stream.resp_decode
|
|
178
296
|
|
|
179
297
|
machine.write(@wfd, ">3\r\n+foo\r\n:42\r\n$3\r\nbar\r\n")
|
|
180
|
-
assert_equal ['foo', 42, 'bar'],
|
|
298
|
+
assert_equal ['foo', 42, 'bar'], stream.resp_decode
|
|
181
299
|
|
|
182
300
|
machine.write(@wfd, "%2\r\n+a\r\n:42\r\n+b\r\n:43\r\n")
|
|
183
|
-
assert_equal({ 'a' => 42, 'b' => 43 },
|
|
301
|
+
assert_equal({ 'a' => 42, 'b' => 43 }, stream.resp_decode)
|
|
184
302
|
|
|
185
303
|
machine.write(@wfd, "|2\r\n+a\r\n:42\r\n+b\r\n:43\r\n")
|
|
186
|
-
assert_equal({ 'a' => 42, 'b' => 43 },
|
|
304
|
+
assert_equal({ 'a' => 42, 'b' => 43 }, stream.resp_decode)
|
|
187
305
|
|
|
188
306
|
machine.write(@wfd, "%2\r\n+a\r\n:42\r\n+b\r\n*3\r\n+foo\r\n+bar\r\n+baz\r\n")
|
|
189
|
-
assert_equal({ 'a' => 42, 'b' => ['foo', 'bar', 'baz'] },
|
|
307
|
+
assert_equal({ 'a' => 42, 'b' => ['foo', 'bar', 'baz'] }, stream.resp_decode)
|
|
308
|
+
end
|
|
190
309
|
|
|
191
|
-
|
|
192
|
-
|
|
310
|
+
def test_stream_resp_decode_segmented
|
|
311
|
+
machine.write(@wfd, "\n")
|
|
312
|
+
assert_equal "", stream.get_line(0)
|
|
313
|
+
|
|
314
|
+
machine.write(@wfd, "+foo")
|
|
315
|
+
machine.write(@wfd, " ")
|
|
316
|
+
machine.write(@wfd, "bar\r")
|
|
317
|
+
machine.write(@wfd, "\n")
|
|
318
|
+
assert_equal "foo bar", stream.resp_decode
|
|
319
|
+
machine.write(@wfd, "$6\r")
|
|
320
|
+
machine.write(@wfd, "\nbazbug")
|
|
321
|
+
machine.write(@wfd, "\r\n")
|
|
322
|
+
assert_equal "bazbug", stream.resp_decode
|
|
193
323
|
end
|
|
194
324
|
|
|
195
|
-
def
|
|
325
|
+
def test_stream_resp_encode
|
|
196
326
|
s = UM::Stream
|
|
197
327
|
assert_equal "_\r\n", s.resp_encode(+'', nil)
|
|
198
328
|
assert_equal "#t\r\n", s.resp_encode(+'', true)
|
|
@@ -200,54 +330,266 @@ class StreamRespTest < StreamBaseTest
|
|
|
200
330
|
assert_equal ":42\r\n", s.resp_encode(+'', 42)
|
|
201
331
|
assert_equal ",42.1\r\n", s.resp_encode(+'', 42.1)
|
|
202
332
|
assert_equal "$6\r\nfoobar\r\n", s.resp_encode(+'', 'foobar')
|
|
203
|
-
assert_equal "$10\r\nפובאר\r\n",
|
|
204
|
-
|
|
205
|
-
assert_equal "*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",
|
|
206
|
-
s.resp_encode(+'', ['foo', 'bar'])
|
|
333
|
+
assert_equal "$10\r\nפובאר\r\n", s.resp_encode(+'', 'פובאר')
|
|
207
334
|
|
|
208
335
|
assert_equal "*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",
|
|
209
336
|
s.resp_encode(+'', ['foo', 'bar'])
|
|
210
337
|
|
|
211
|
-
assert_equal "*2\r\n$3\r\nfoo\r\n$4\r\nxbar\r\n",
|
|
212
|
-
s.resp_encode(+'', ['foo', 'xbar'])
|
|
213
|
-
|
|
214
338
|
assert_equal "%2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$3\r\nbaz\r\n:42\r\n",
|
|
215
339
|
s.resp_encode(+'', { 'foo' => 'bar', 'baz' => 42 })
|
|
216
340
|
end
|
|
341
|
+
end
|
|
217
342
|
|
|
218
|
-
|
|
219
|
-
|
|
343
|
+
class StreamStressTest < UMBaseTest
|
|
344
|
+
def setup
|
|
345
|
+
super
|
|
220
346
|
|
|
221
|
-
|
|
222
|
-
|
|
347
|
+
@port = assign_port
|
|
348
|
+
@listen_fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
349
|
+
machine.setsockopt(@listen_fd, UM::SOL_SOCKET, UM::SO_REUSEADDR, true)
|
|
350
|
+
machine.setsockopt(@listen_fd, UM::SOL_SOCKET, UM::SO_REUSEPORT, true)
|
|
351
|
+
machine.bind(@listen_fd, '127.0.0.1', @port)
|
|
352
|
+
machine.listen(@listen_fd, 128)
|
|
223
353
|
|
|
224
|
-
|
|
225
|
-
|
|
354
|
+
@received = []
|
|
355
|
+
@response_body = "Hello, world!"
|
|
356
|
+
@response_headers = "HTTP/1.1 200\r\nContent-Length: #{@response_body.bytesize}\r\n\r\n"
|
|
357
|
+
end
|
|
226
358
|
|
|
227
|
-
|
|
228
|
-
|
|
359
|
+
def start_connection_fiber(fd)
|
|
360
|
+
machine.spin do
|
|
361
|
+
stream = UM::Stream.new(machine, fd)
|
|
362
|
+
while (msg = stream.get_line(0))
|
|
363
|
+
@received << msg
|
|
364
|
+
end
|
|
365
|
+
machine.sendv(fd, @response_headers, @response_body)
|
|
366
|
+
machine.close(fd)
|
|
367
|
+
rescue => e
|
|
368
|
+
p e
|
|
369
|
+
p e.backtrace
|
|
370
|
+
end
|
|
371
|
+
end
|
|
229
372
|
|
|
230
|
-
|
|
231
|
-
|
|
373
|
+
def test_stream_server_big_lines
|
|
374
|
+
server_fibers = []
|
|
375
|
+
server_fibers << machine.spin do
|
|
376
|
+
machine.accept_each(@listen_fd) { |fd|
|
|
377
|
+
server_fibers << start_connection_fiber(fd)
|
|
378
|
+
}
|
|
379
|
+
rescue Errno::EINVAL
|
|
380
|
+
ignore
|
|
381
|
+
rescue => e
|
|
382
|
+
p e
|
|
383
|
+
p e.backtrace
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
client_count = 1000
|
|
387
|
+
msg_count = 100
|
|
388
|
+
length = 100
|
|
389
|
+
|
|
390
|
+
total_msgs = client_count * msg_count
|
|
391
|
+
msg = "#{SecureRandom.hex(length / 2)}\n" * msg_count
|
|
392
|
+
client_fibers = client_count.times.map {
|
|
393
|
+
machine.snooze
|
|
394
|
+
machine.spin do
|
|
395
|
+
fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
396
|
+
machine.connect(fd, '127.0.0.1', @port)
|
|
397
|
+
machine.sleep(0.05)
|
|
398
|
+
machine.send(fd, msg, msg.bytesize, UM::MSG_WAITALL)
|
|
399
|
+
machine.sleep(0.05)
|
|
400
|
+
machine.close(fd)
|
|
401
|
+
rescue => e
|
|
402
|
+
p e
|
|
403
|
+
p e.backtrace
|
|
404
|
+
end
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
machine.await(client_fibers)
|
|
408
|
+
machine.shutdown(@listen_fd, UM::SHUT_RD)
|
|
409
|
+
machine.await(server_fibers)
|
|
410
|
+
machine.snooze
|
|
411
|
+
|
|
412
|
+
assert_equal total_msgs, @received.size
|
|
413
|
+
assert_equal msg * client_count, @received.map { it + "\n" }.join
|
|
414
|
+
end
|
|
232
415
|
|
|
233
|
-
|
|
234
|
-
|
|
416
|
+
def test_stream_server_http
|
|
417
|
+
server_fibers = []
|
|
418
|
+
server_fibers << machine.spin do
|
|
419
|
+
machine.accept_each(@listen_fd) { |fd|
|
|
420
|
+
server_fibers << start_connection_fiber(fd)
|
|
421
|
+
}
|
|
422
|
+
rescue Errno::EINVAL
|
|
423
|
+
# ignore
|
|
424
|
+
rescue => e
|
|
425
|
+
p e
|
|
426
|
+
p e.backtrace
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
client_count = 1000
|
|
430
|
+
msg_count = 16
|
|
431
|
+
msg = "GET http://127.0.0.1:1234/ HTTP/1.1\r\nhost: 127.0.0.1\r\n\r\n"
|
|
432
|
+
client_fibers = client_count.times.map {
|
|
433
|
+
machine.snooze
|
|
434
|
+
machine.spin do
|
|
435
|
+
fd = machine.socket(UM::AF_INET, UM::SOCK_STREAM, 0, 0)
|
|
436
|
+
machine.connect(fd, '127.0.0.1', @port)
|
|
437
|
+
msg_count.times {
|
|
438
|
+
machine.send(fd, msg, msg.bytesize, UM::MSG_WAITALL)
|
|
439
|
+
}
|
|
440
|
+
machine.close(fd)
|
|
441
|
+
rescue => e
|
|
442
|
+
p e
|
|
443
|
+
p e.backtrace
|
|
444
|
+
end
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
machine.await(client_fibers)
|
|
448
|
+
machine.shutdown(@listen_fd, UM::SHUT_RD)
|
|
449
|
+
machine.await(server_fibers)
|
|
450
|
+
machine.snooze
|
|
451
|
+
# assert_equal total_msgs, @received.size
|
|
452
|
+
assert_equal msg * msg_count * client_count, @received.map { it + "\r\n" }.join
|
|
235
453
|
end
|
|
236
454
|
end
|
|
237
455
|
|
|
238
|
-
class
|
|
239
|
-
def
|
|
240
|
-
machine.
|
|
241
|
-
|
|
456
|
+
class StreamDevRandomTest < UMBaseTest
|
|
457
|
+
def test_stream_dev_random_get_line
|
|
458
|
+
fd = machine.open('/dev/random', UM::O_RDONLY)
|
|
459
|
+
stream = UM::Stream.new(machine, fd)
|
|
460
|
+
|
|
461
|
+
n = 100000
|
|
462
|
+
lines = []
|
|
463
|
+
n.times {
|
|
464
|
+
lines << stream.get_line(0)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
assert_equal n, lines.size
|
|
468
|
+
ensure
|
|
469
|
+
stream.clear rescue nil
|
|
470
|
+
machine.close(fd) rescue nil
|
|
471
|
+
end
|
|
242
472
|
|
|
243
|
-
|
|
244
|
-
|
|
473
|
+
def get_line_do(n, acc)
|
|
474
|
+
fd = @machine.open('/dev/random', UM::O_RDONLY)
|
|
475
|
+
stream = UM::Stream.new(@machine, fd)
|
|
476
|
+
n.times { acc << stream.get_line(0) }
|
|
477
|
+
end
|
|
245
478
|
|
|
246
|
-
|
|
247
|
-
|
|
479
|
+
def test_stream_dev_random_get_line_concurrent
|
|
480
|
+
acc = []
|
|
481
|
+
c = 1
|
|
482
|
+
n = 100000
|
|
483
|
+
ff = c.times.map {
|
|
484
|
+
machine.spin { get_line_do(n, acc) }
|
|
485
|
+
}
|
|
486
|
+
machine.await(ff)
|
|
487
|
+
assert_equal c * n, acc.size
|
|
488
|
+
end
|
|
248
489
|
|
|
249
|
-
|
|
250
|
-
|
|
490
|
+
def test_stream_dev_random_get_string
|
|
491
|
+
fd = machine.open('/dev/random', UM::O_RDONLY)
|
|
492
|
+
stream = UM::Stream.new(machine, fd)
|
|
493
|
+
|
|
494
|
+
n = 256
|
|
495
|
+
size = 65536 * 8
|
|
496
|
+
count = 0
|
|
497
|
+
# lines = []
|
|
498
|
+
n.times {
|
|
499
|
+
l = stream.get_string(size)
|
|
500
|
+
refute_nil l
|
|
501
|
+
assert_equal size, l.bytesize
|
|
502
|
+
|
|
503
|
+
count += 1
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
assert_equal n, count
|
|
507
|
+
ensure
|
|
508
|
+
stream.clear rescue nil
|
|
251
509
|
end
|
|
252
510
|
end
|
|
253
511
|
|
|
512
|
+
class StreamModeTest < UMBaseTest
|
|
513
|
+
def test_stream_default_mode
|
|
514
|
+
r, w = UM.pipe
|
|
515
|
+
stream = UM::Stream.new(machine, r)
|
|
516
|
+
assert_equal :bp_read, stream.mode
|
|
517
|
+
ensure
|
|
518
|
+
machine.close(r) rescue nil
|
|
519
|
+
machine.close(w) rescue nil
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
def test_stream_recv_mode_non_socket
|
|
523
|
+
r, w = UM.pipe
|
|
524
|
+
machine.write(w, 'foobar')
|
|
525
|
+
machine.close(w)
|
|
526
|
+
|
|
527
|
+
stream = UM::Stream.new(machine, r, :bp_recv)
|
|
528
|
+
assert_equal :bp_recv, stream.mode
|
|
529
|
+
# assert :bp_recv, stream.mode
|
|
530
|
+
assert_raises(Errno::ENOTSOCK) { stream.get_string(0) }
|
|
531
|
+
ensure
|
|
532
|
+
machine.close(r) rescue nil
|
|
533
|
+
machine.close(w) rescue nil
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
def test_stream_recv_mode_socket
|
|
537
|
+
r, w = UM.socketpair(UM::AF_UNIX, UM::SOCK_STREAM, 0)
|
|
538
|
+
machine.write(w, 'foobar')
|
|
539
|
+
machine.close(w)
|
|
540
|
+
|
|
541
|
+
stream = UM::Stream.new(machine, r, :bp_recv)
|
|
542
|
+
assert_equal :bp_recv, stream.mode
|
|
543
|
+
buf = stream.get_string(0)
|
|
544
|
+
assert_equal 'foobar', buf
|
|
545
|
+
ensure
|
|
546
|
+
machine.close(r) rescue nil
|
|
547
|
+
machine.close(w) rescue nil
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
def test_stream_ssl_mode
|
|
551
|
+
authority = Localhost::Authority.fetch
|
|
552
|
+
@server_ctx = authority.server_context
|
|
553
|
+
sock1, sock2 = UNIXSocket.pair
|
|
554
|
+
|
|
555
|
+
s1 = OpenSSL::SSL::SSLSocket.new(sock1, @server_ctx)
|
|
556
|
+
s1.sync_close = true
|
|
557
|
+
s2 = OpenSSL::SSL::SSLSocket.new(sock2, OpenSSL::SSL::SSLContext.new)
|
|
558
|
+
s2.sync_close = true
|
|
559
|
+
|
|
560
|
+
@machine.ssl_set_bio(s1)
|
|
561
|
+
@machine.ssl_set_bio(s2)
|
|
562
|
+
assert_equal true, s1.instance_variable_get(:@__um_bio__)
|
|
563
|
+
assert_equal true, s2.instance_variable_get(:@__um_bio__)
|
|
564
|
+
|
|
565
|
+
f = machine.spin { s1.accept rescue nil }
|
|
566
|
+
|
|
567
|
+
s2.connect
|
|
568
|
+
refute_equal 0, @machine.metrics[:total_ops]
|
|
569
|
+
|
|
570
|
+
buf = "foobar\nbaz"
|
|
571
|
+
assert_equal 10, @machine.ssl_write(s1, buf, buf.bytesize)
|
|
572
|
+
buf = +''
|
|
573
|
+
|
|
574
|
+
stream = UM::Stream.new(machine, s2, :ssl)
|
|
575
|
+
assert_equal "foobar", stream.get_line(0)
|
|
576
|
+
|
|
577
|
+
buf = "buh"
|
|
578
|
+
@machine.ssl_write(s1, buf, buf.bytesize)
|
|
579
|
+
|
|
580
|
+
assert_equal "baz", stream.get_string(0)
|
|
581
|
+
assert_equal "buh", stream.get_string(0)
|
|
582
|
+
|
|
583
|
+
s1.close
|
|
584
|
+
|
|
585
|
+
assert_nil stream.get_string(0)
|
|
586
|
+
rescue => e
|
|
587
|
+
p e
|
|
588
|
+
p e.backtrace
|
|
589
|
+
exit!
|
|
590
|
+
ensure
|
|
591
|
+
machine.join(f)
|
|
592
|
+
sock1&.close rescue nil
|
|
593
|
+
sock2&.close rescue nil
|
|
594
|
+
end
|
|
595
|
+
end
|