itsi 0.1.0 → 0.1.3
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/Cargo.lock +524 -44
- data/Rakefile +22 -33
- data/crates/itsi_error/Cargo.toml +2 -0
- data/crates/itsi_error/src/from.rs +70 -0
- data/crates/itsi_error/src/lib.rs +10 -37
- data/crates/itsi_instrument_entry/Cargo.toml +15 -0
- data/crates/itsi_instrument_entry/src/lib.rs +31 -0
- data/crates/itsi_rb_helpers/Cargo.toml +2 -0
- data/crates/itsi_rb_helpers/src/heap_value.rs +121 -0
- data/crates/itsi_rb_helpers/src/lib.rs +90 -10
- data/crates/itsi_scheduler/Cargo.toml +9 -1
- data/crates/itsi_scheduler/extconf.rb +1 -1
- data/crates/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
- data/crates/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
- data/crates/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
- data/crates/itsi_scheduler/src/itsi_scheduler.rs +308 -0
- data/crates/itsi_scheduler/src/lib.rs +31 -10
- data/crates/itsi_server/Cargo.toml +14 -2
- data/crates/itsi_server/extconf.rb +1 -1
- data/crates/itsi_server/src/body_proxy/big_bytes.rs +104 -0
- data/crates/itsi_server/src/body_proxy/itsi_body_proxy.rs +122 -0
- data/crates/itsi_server/src/body_proxy/mod.rs +2 -0
- data/crates/itsi_server/src/lib.rs +58 -7
- data/crates/itsi_server/src/request/itsi_request.rs +238 -104
- data/crates/itsi_server/src/response/itsi_response.rs +347 -0
- data/crates/itsi_server/src/response/mod.rs +1 -0
- data/crates/itsi_server/src/server/bind.rs +50 -20
- data/crates/itsi_server/src/server/bind_protocol.rs +37 -0
- data/crates/itsi_server/src/server/io_stream.rs +104 -0
- data/crates/itsi_server/src/server/itsi_ca/itsi_ca.crt +11 -30
- data/crates/itsi_server/src/server/itsi_ca/itsi_ca.key +3 -50
- data/crates/itsi_server/src/server/itsi_server.rs +196 -134
- data/crates/itsi_server/src/server/lifecycle_event.rs +9 -0
- data/crates/itsi_server/src/server/listener.rs +184 -127
- data/crates/itsi_server/src/server/mod.rs +7 -1
- data/crates/itsi_server/src/server/process_worker.rs +196 -0
- data/crates/itsi_server/src/server/serve_strategy/cluster_mode.rs +254 -0
- data/crates/itsi_server/src/server/serve_strategy/mod.rs +27 -0
- data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +241 -0
- data/crates/itsi_server/src/server/signal.rs +70 -0
- data/crates/itsi_server/src/server/thread_worker.rs +368 -0
- data/crates/itsi_server/src/server/tls.rs +42 -28
- data/crates/itsi_tracing/Cargo.toml +4 -0
- data/crates/itsi_tracing/src/lib.rs +36 -6
- data/gems/scheduler/Cargo.lock +219 -23
- data/gems/scheduler/Rakefile +7 -1
- data/gems/scheduler/ext/itsi_error/Cargo.toml +2 -0
- data/gems/scheduler/ext/itsi_error/src/from.rs +70 -0
- data/gems/scheduler/ext/itsi_error/src/lib.rs +10 -37
- data/gems/scheduler/ext/itsi_instrument_entry/Cargo.toml +15 -0
- data/gems/scheduler/ext/itsi_instrument_entry/src/lib.rs +31 -0
- data/gems/scheduler/ext/itsi_rb_helpers/Cargo.toml +2 -0
- data/gems/scheduler/ext/itsi_rb_helpers/src/heap_value.rs +121 -0
- data/gems/scheduler/ext/itsi_rb_helpers/src/lib.rs +90 -10
- data/gems/scheduler/ext/itsi_scheduler/Cargo.toml +9 -1
- data/gems/scheduler/ext/itsi_scheduler/extconf.rb +1 -1
- data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
- data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
- data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
- data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler.rs +308 -0
- data/gems/scheduler/ext/itsi_scheduler/src/lib.rs +31 -10
- data/gems/scheduler/ext/itsi_server/Cargo.toml +41 -0
- data/gems/scheduler/ext/itsi_server/extconf.rb +6 -0
- data/gems/scheduler/ext/itsi_server/src/body_proxy/big_bytes.rs +104 -0
- data/gems/scheduler/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs +122 -0
- data/gems/scheduler/ext/itsi_server/src/body_proxy/mod.rs +2 -0
- data/gems/scheduler/ext/itsi_server/src/lib.rs +103 -0
- data/gems/scheduler/ext/itsi_server/src/request/itsi_request.rs +277 -0
- data/gems/scheduler/ext/itsi_server/src/request/mod.rs +1 -0
- data/gems/scheduler/ext/itsi_server/src/response/itsi_response.rs +347 -0
- data/gems/scheduler/ext/itsi_server/src/response/mod.rs +1 -0
- data/gems/scheduler/ext/itsi_server/src/server/bind.rs +168 -0
- data/gems/scheduler/ext/itsi_server/src/server/bind_protocol.rs +37 -0
- data/gems/scheduler/ext/itsi_server/src/server/io_stream.rs +104 -0
- data/gems/scheduler/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +13 -0
- data/gems/scheduler/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +5 -0
- data/gems/scheduler/ext/itsi_server/src/server/itsi_server.rs +244 -0
- data/gems/scheduler/ext/itsi_server/src/server/lifecycle_event.rs +9 -0
- data/gems/scheduler/ext/itsi_server/src/server/listener.rs +275 -0
- data/gems/scheduler/ext/itsi_server/src/server/mod.rs +11 -0
- data/gems/scheduler/ext/itsi_server/src/server/process_worker.rs +196 -0
- data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +254 -0
- data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/mod.rs +27 -0
- data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/single_mode.rs +241 -0
- data/gems/scheduler/ext/itsi_server/src/server/signal.rs +70 -0
- data/gems/scheduler/ext/itsi_server/src/server/thread_worker.rs +368 -0
- data/gems/scheduler/ext/itsi_server/src/server/tls.rs +152 -0
- data/gems/scheduler/ext/itsi_tracing/Cargo.toml +4 -0
- data/gems/scheduler/ext/itsi_tracing/src/lib.rs +36 -6
- data/gems/scheduler/itsi-scheduler.gemspec +2 -3
- data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
- data/gems/scheduler/lib/itsi/scheduler.rb +137 -1
- data/gems/scheduler/test/helpers/test_helper.rb +24 -0
- data/gems/scheduler/test/test_active_record.rb +158 -0
- data/gems/scheduler/test/test_address_resolve.rb +23 -0
- data/gems/scheduler/test/test_block_unblock.rb +229 -0
- data/gems/scheduler/test/test_file_io.rb +193 -0
- data/gems/scheduler/test/test_itsi_scheduler.rb +24 -1
- data/gems/scheduler/test/test_kernel_sleep.rb +91 -0
- data/gems/scheduler/test/test_nested_fibers.rb +286 -0
- data/gems/scheduler/test/test_network_io.rb +274 -0
- data/gems/scheduler/test/test_process_wait.rb +26 -0
- data/gems/server/exe/itsi +88 -28
- data/gems/server/ext/itsi_error/Cargo.toml +2 -0
- data/gems/server/ext/itsi_error/src/from.rs +70 -0
- data/gems/server/ext/itsi_error/src/lib.rs +10 -37
- data/gems/server/ext/itsi_instrument_entry/Cargo.toml +15 -0
- data/gems/server/ext/itsi_instrument_entry/src/lib.rs +31 -0
- data/gems/server/ext/itsi_rb_helpers/Cargo.toml +2 -0
- data/gems/server/ext/itsi_rb_helpers/src/heap_value.rs +121 -0
- data/gems/server/ext/itsi_rb_helpers/src/lib.rs +90 -10
- data/gems/server/ext/itsi_scheduler/Cargo.toml +24 -0
- data/gems/server/ext/itsi_scheduler/extconf.rb +6 -0
- data/gems/server/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
- data/gems/server/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
- data/gems/server/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
- data/gems/server/ext/itsi_scheduler/src/itsi_scheduler.rs +308 -0
- data/gems/server/ext/itsi_scheduler/src/lib.rs +38 -0
- data/gems/server/ext/itsi_server/Cargo.toml +14 -2
- data/gems/server/ext/itsi_server/extconf.rb +1 -1
- data/gems/server/ext/itsi_server/src/body_proxy/big_bytes.rs +104 -0
- data/gems/server/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs +122 -0
- data/gems/server/ext/itsi_server/src/body_proxy/mod.rs +2 -0
- data/gems/server/ext/itsi_server/src/lib.rs +58 -7
- data/gems/server/ext/itsi_server/src/request/itsi_request.rs +238 -104
- data/gems/server/ext/itsi_server/src/response/itsi_response.rs +347 -0
- data/gems/server/ext/itsi_server/src/response/mod.rs +1 -0
- data/gems/server/ext/itsi_server/src/server/bind.rs +50 -20
- data/gems/server/ext/itsi_server/src/server/bind_protocol.rs +37 -0
- data/gems/server/ext/itsi_server/src/server/io_stream.rs +104 -0
- data/gems/server/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +11 -30
- data/gems/server/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +3 -50
- data/gems/server/ext/itsi_server/src/server/itsi_server.rs +196 -134
- data/gems/server/ext/itsi_server/src/server/lifecycle_event.rs +9 -0
- data/gems/server/ext/itsi_server/src/server/listener.rs +184 -127
- data/gems/server/ext/itsi_server/src/server/mod.rs +7 -1
- data/gems/server/ext/itsi_server/src/server/process_worker.rs +196 -0
- data/gems/server/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +254 -0
- data/gems/server/ext/itsi_server/src/server/serve_strategy/mod.rs +27 -0
- data/gems/server/ext/itsi_server/src/server/serve_strategy/single_mode.rs +241 -0
- data/gems/server/ext/itsi_server/src/server/signal.rs +70 -0
- data/gems/server/ext/itsi_server/src/server/thread_worker.rs +368 -0
- data/gems/server/ext/itsi_server/src/server/tls.rs +42 -28
- data/gems/server/ext/itsi_tracing/Cargo.toml +4 -0
- data/gems/server/ext/itsi_tracing/src/lib.rs +36 -6
- data/gems/server/itsi-server.gemspec +4 -5
- data/gems/server/lib/itsi/request.rb +30 -14
- data/gems/server/lib/itsi/server/rack/handler/itsi.rb +25 -0
- data/gems/server/lib/itsi/server/scheduler_mode.rb +6 -0
- data/gems/server/lib/itsi/server/version.rb +1 -1
- data/gems/server/lib/itsi/server.rb +82 -2
- data/gems/server/lib/itsi/signals.rb +23 -0
- data/gems/server/lib/itsi/stream_io.rb +38 -0
- data/gems/server/test/test_helper.rb +2 -0
- data/gems/server/test/test_itsi_server.rb +1 -1
- data/lib/itsi/version.rb +1 -1
- data/tasks.txt +18 -0
- metadata +102 -12
- data/crates/itsi_server/src/server/transfer_protocol.rs +0 -23
- data/crates/itsi_server/src/stream_writer/mod.rs +0 -21
- data/gems/scheduler/test/test_helper.rb +0 -6
- data/gems/server/ext/itsi_server/src/server/transfer_protocol.rs +0 -23
- data/gems/server/ext/itsi_server/src/stream_writer/mod.rs +0 -21
@@ -0,0 +1,286 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
|
5
|
+
class TestNestedFibers < Minitest::Test
|
6
|
+
include Itsi::Scheduler::TestHelper
|
7
|
+
|
8
|
+
def each_pop(queue)
|
9
|
+
loop do
|
10
|
+
if item = queue.pop
|
11
|
+
sleep 0.001
|
12
|
+
yield item
|
13
|
+
else
|
14
|
+
break
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_async_queue
|
20
|
+
results = []
|
21
|
+
queue = Queue.new
|
22
|
+
|
23
|
+
# In the thread running the scheduler, we use an enumerator, which
|
24
|
+
# uses a new Fiber per iteration
|
25
|
+
with_scheduler do
|
26
|
+
Fiber.schedule do
|
27
|
+
each_pop(queue) do |item|
|
28
|
+
results << item
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Non scheduler thread pushes to queue every 0.1 seconds
|
33
|
+
Thread.new do
|
34
|
+
1.times do |i|
|
35
|
+
queue.push(i)
|
36
|
+
sleep 0.1
|
37
|
+
end
|
38
|
+
queue.push(nil)
|
39
|
+
end.join
|
40
|
+
end
|
41
|
+
|
42
|
+
assert_equal [*0...1], results
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_base_unowned_no_scheduler_transfer
|
46
|
+
results = []
|
47
|
+
scheduler = nil
|
48
|
+
outer = Fiber.new do
|
49
|
+
inner = Fiber.new do
|
50
|
+
results << 0
|
51
|
+
sleep 0.02
|
52
|
+
scheduler.transfer
|
53
|
+
results << 4
|
54
|
+
end
|
55
|
+
|
56
|
+
results << 1
|
57
|
+
inner.transfer
|
58
|
+
results << 2
|
59
|
+
inner.transfer
|
60
|
+
end
|
61
|
+
|
62
|
+
scheduler = Fiber.new do
|
63
|
+
outer.transfer while outer.alive?
|
64
|
+
end
|
65
|
+
scheduler.transfer
|
66
|
+
|
67
|
+
assert_equal [1, 0, 2, 4], results
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_base_unowned_no_scheduler_yield
|
71
|
+
results = []
|
72
|
+
|
73
|
+
Fiber.new do
|
74
|
+
inner = Fiber.new do
|
75
|
+
results << 0
|
76
|
+
Fiber.yield
|
77
|
+
results << 4
|
78
|
+
end
|
79
|
+
|
80
|
+
results << 1
|
81
|
+
inner.resume
|
82
|
+
results << 2
|
83
|
+
inner.resume
|
84
|
+
end.resume
|
85
|
+
|
86
|
+
assert_equal [1, 0, 2, 4], results
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_base_unowned_no_scheduler_combine_resume_and_transfer
|
90
|
+
results = []
|
91
|
+
scheduler = nil
|
92
|
+
inner = nil
|
93
|
+
outer = Fiber.new do
|
94
|
+
inner = Fiber.new do
|
95
|
+
results << 0
|
96
|
+
scheduler.transfer(inner)
|
97
|
+
results << 4
|
98
|
+
end
|
99
|
+
results << 1
|
100
|
+
inner.resume
|
101
|
+
results << 2
|
102
|
+
end
|
103
|
+
scheduler = Fiber.new do |fib|
|
104
|
+
fib = fib.transfer while fib
|
105
|
+
end
|
106
|
+
scheduler.transfer(outer)
|
107
|
+
|
108
|
+
assert_equal [1, 0, 4, 2], results
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_base_unowned_no_scheduler_combine_resume_and_transfer_with_scheduler
|
112
|
+
results = []
|
113
|
+
with_scheduler do |scheduler|
|
114
|
+
results = []
|
115
|
+
inner = nil
|
116
|
+
outer = Fiber.new do
|
117
|
+
inner = Fiber.new do
|
118
|
+
results << 0
|
119
|
+
sleep 0.1
|
120
|
+
results << 4
|
121
|
+
end
|
122
|
+
results << 1
|
123
|
+
inner.resume
|
124
|
+
results << 2
|
125
|
+
end
|
126
|
+
outer.transfer
|
127
|
+
end
|
128
|
+
|
129
|
+
assert_equal [1, 0, 2, 4], results
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
def test_base_owned_with_scheduler
|
134
|
+
results = []
|
135
|
+
with_scheduler do |scheduler|
|
136
|
+
scheduler = nil
|
137
|
+
|
138
|
+
Fiber.schedule do
|
139
|
+
Fiber.schedule do
|
140
|
+
Fiber.schedule do
|
141
|
+
results << 0
|
142
|
+
sleep 0.01
|
143
|
+
results << 4
|
144
|
+
end
|
145
|
+
|
146
|
+
results << 1
|
147
|
+
sleep 0.03
|
148
|
+
results << 2
|
149
|
+
sleep 0.01
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
assert_equal [0, 1, 4, 2], results
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_nested_owned_fibers
|
157
|
+
results = []
|
158
|
+
with_scheduler do |scheduler|
|
159
|
+
Fiber.schedule do
|
160
|
+
Fiber.schedule do
|
161
|
+
results << 0
|
162
|
+
sleep 0.02
|
163
|
+
results << 4
|
164
|
+
end
|
165
|
+
|
166
|
+
Fiber.schedule do
|
167
|
+
results << 1
|
168
|
+
sleep 0.2
|
169
|
+
Fiber.schedule do
|
170
|
+
results << 5
|
171
|
+
sleep 0.2
|
172
|
+
results << 7
|
173
|
+
end
|
174
|
+
results << 6
|
175
|
+
end
|
176
|
+
results << 2
|
177
|
+
sleep 0.01
|
178
|
+
results << 3
|
179
|
+
end
|
180
|
+
end
|
181
|
+
assert_equal [0, 1, 2, 3, 4, 5, 6, 7], results
|
182
|
+
end
|
183
|
+
|
184
|
+
def test_nested_unowned_fibers_with_scheduler
|
185
|
+
results = []
|
186
|
+
out, err = capture_subprocess_io do
|
187
|
+
begin
|
188
|
+
with_scheduler do |scheduler|
|
189
|
+
Fiber.new do
|
190
|
+
fib = Fiber.new do
|
191
|
+
results << 4
|
192
|
+
sleep 0.001
|
193
|
+
results << 5
|
194
|
+
sleep 0.001
|
195
|
+
results << 6
|
196
|
+
end
|
197
|
+
|
198
|
+
Fiber.new do
|
199
|
+
results << 0
|
200
|
+
sleep 0.001
|
201
|
+
Fiber.new do
|
202
|
+
results << 1
|
203
|
+
sleep 0.1
|
204
|
+
results << 8
|
205
|
+
end.transfer
|
206
|
+
results << 2
|
207
|
+
end.resume
|
208
|
+
|
209
|
+
results << 3
|
210
|
+
|
211
|
+
fib.resume
|
212
|
+
sleep 0.01
|
213
|
+
results << 7
|
214
|
+
end.transfer
|
215
|
+
end
|
216
|
+
rescue
|
217
|
+
end
|
218
|
+
end
|
219
|
+
# Transfered fibers are not resumed after yielding out.
|
220
|
+
assert_equal [0, 3, 4, 1, 5, 6], results
|
221
|
+
# assert_match /attempt to yield on a not resumed fiber/, out
|
222
|
+
end
|
223
|
+
|
224
|
+
def test_nested_unowned_fibers_no_scheduler
|
225
|
+
results = []
|
226
|
+
Fiber.new do
|
227
|
+
fib = Fiber.new do
|
228
|
+
results << 5
|
229
|
+
sleep 0.001
|
230
|
+
results << 6
|
231
|
+
sleep 0.001
|
232
|
+
results << 7
|
233
|
+
end
|
234
|
+
|
235
|
+
Fiber.new do
|
236
|
+
results << 0
|
237
|
+
sleep 0.001
|
238
|
+
Fiber.new do
|
239
|
+
results << 1
|
240
|
+
sleep 0.1
|
241
|
+
results << 2
|
242
|
+
end.resume
|
243
|
+
results << 3
|
244
|
+
end.resume
|
245
|
+
|
246
|
+
results << 4
|
247
|
+
fib.resume
|
248
|
+
sleep 0.01
|
249
|
+
results << 8
|
250
|
+
end.transfer
|
251
|
+
|
252
|
+
assert_equal [0, 1, 2, 3, 4, 5, 6, 7, 8], results
|
253
|
+
end
|
254
|
+
|
255
|
+
def test_nested_owned_fibers_resume
|
256
|
+
results = []
|
257
|
+
with_scheduler do |scheduler|
|
258
|
+
Fiber.schedule do
|
259
|
+
fib = Fiber.new do
|
260
|
+
results << 2
|
261
|
+
sleep 0.001
|
262
|
+
results << 5
|
263
|
+
sleep 0.001
|
264
|
+
results << 6
|
265
|
+
end
|
266
|
+
|
267
|
+
Fiber.schedule do
|
268
|
+
results << 0
|
269
|
+
sleep 0.001
|
270
|
+
Fiber.schedule do
|
271
|
+
results << 3
|
272
|
+
sleep 0.1
|
273
|
+
results << 8
|
274
|
+
end
|
275
|
+
results << 4
|
276
|
+
end
|
277
|
+
|
278
|
+
results << 1
|
279
|
+
fib.resume
|
280
|
+
sleep 0.01
|
281
|
+
results << 7
|
282
|
+
end
|
283
|
+
end
|
284
|
+
assert_equal [0, 1, 2, 3, 4, 5, 6, 7, 8], results
|
285
|
+
end
|
286
|
+
end
|
@@ -0,0 +1,274 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "socket"
|
4
|
+
require "timeout"
|
5
|
+
require "debug"
|
6
|
+
|
7
|
+
class TestNetworkIO < Minitest::Test
|
8
|
+
include Itsi::Scheduler::TestHelper
|
9
|
+
|
10
|
+
def test_tcp_echo
|
11
|
+
message = "Hello, Itsi!"
|
12
|
+
response = nil
|
13
|
+
|
14
|
+
with_scheduler do |_scheduler|
|
15
|
+
server = TCPServer.new("127.0.0.1", 0)
|
16
|
+
port = server.addr[1]
|
17
|
+
|
18
|
+
# Server fiber: accept one connection and echo data.
|
19
|
+
Fiber.schedule do
|
20
|
+
client = server.accept
|
21
|
+
data = client.read(message.size)
|
22
|
+
client.write(data)
|
23
|
+
client.close
|
24
|
+
server.close
|
25
|
+
end
|
26
|
+
|
27
|
+
# Client fiber: connect, send message, and read echo.
|
28
|
+
Fiber.schedule do
|
29
|
+
client = TCPSocket.new("127.0.0.1", port)
|
30
|
+
client.write(message)
|
31
|
+
response = client.read(message.size)
|
32
|
+
client.close
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
assert_equal message, response
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_concurrent_tcp_clients
|
40
|
+
server = TCPServer.new("127.0.0.1", 0)
|
41
|
+
port = server.addr[1]
|
42
|
+
messages = %w[first second third fourth]
|
43
|
+
responses = {}
|
44
|
+
|
45
|
+
with_scheduler do |_scheduler|
|
46
|
+
# Server fiber: accept several connections.
|
47
|
+
Fiber.schedule do
|
48
|
+
messages.size.times do
|
49
|
+
client = server.accept
|
50
|
+
# Spawn a fiber for each connection to echo the data.
|
51
|
+
Fiber.schedule do
|
52
|
+
data = client.readpartial(1024)
|
53
|
+
client.write(data)
|
54
|
+
client.close
|
55
|
+
end
|
56
|
+
end
|
57
|
+
server.close
|
58
|
+
end
|
59
|
+
|
60
|
+
# Client fibers: connect concurrently and send messages.
|
61
|
+
messages.each do |msg|
|
62
|
+
Fiber.schedule do
|
63
|
+
sleep rand(0.01..0.05) # random delay for interleaving
|
64
|
+
client = TCPSocket.new("127.0.0.1", port)
|
65
|
+
client.write(msg)
|
66
|
+
responses[msg] = client.read(msg.size)
|
67
|
+
client.close
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
messages.each do |msg|
|
73
|
+
assert_equal msg, responses[msg]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_interleaved_network_and_sleep
|
78
|
+
server = TCPServer.new("127.0.0.1", 0)
|
79
|
+
port = server.addr[1]
|
80
|
+
responses = {}
|
81
|
+
|
82
|
+
with_scheduler do |_scheduler|
|
83
|
+
# Echo server that delays between chunks.
|
84
|
+
Fiber.schedule do
|
85
|
+
client = server.accept
|
86
|
+
data = "".dup
|
87
|
+
# Read in chunks until we have received 12 bytes.
|
88
|
+
while data.size < 12
|
89
|
+
begin
|
90
|
+
chunk = client.readpartial(3)
|
91
|
+
rescue EOFError
|
92
|
+
break
|
93
|
+
end
|
94
|
+
data << chunk
|
95
|
+
sleep 0.02 # delay between chunks
|
96
|
+
end
|
97
|
+
# Echo back the reversed data.
|
98
|
+
client.write(data.reverse)
|
99
|
+
client.close
|
100
|
+
server.close
|
101
|
+
end
|
102
|
+
|
103
|
+
# Client fiber: send data in small chunks.
|
104
|
+
Fiber.schedule do
|
105
|
+
sleep 0.05 # allow server to start
|
106
|
+
client = TCPSocket.new("127.0.0.1", port)
|
107
|
+
"HelloWorld!!".chars.each_slice(3) do |slice|
|
108
|
+
client.write(slice.join)
|
109
|
+
sleep 0.01
|
110
|
+
end
|
111
|
+
responses[:result] = client.read(12)
|
112
|
+
client.close
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
expected = "HelloWorld!!".reverse[0, 12]
|
117
|
+
assert_equal expected, responses[:result]
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_tcp_timeout
|
121
|
+
server = TCPServer.new("127.0.0.1", 0)
|
122
|
+
port = server.addr[1]
|
123
|
+
result = nil
|
124
|
+
|
125
|
+
with_scheduler do |_scheduler|
|
126
|
+
# Server fiber: accept the connection but delay sending.
|
127
|
+
Fiber.schedule do
|
128
|
+
client = server.accept
|
129
|
+
sleep 0.2 # delay long enough to force a timeout on the client
|
130
|
+
client.write("late data")
|
131
|
+
client.close
|
132
|
+
server.close
|
133
|
+
end
|
134
|
+
|
135
|
+
# Client fiber: connect and wait for data with a short timeout.
|
136
|
+
Fiber.schedule do
|
137
|
+
sleep 0.05
|
138
|
+
client = TCPSocket.new("127.0.0.1", port)
|
139
|
+
result = if client.wait_readable(0.1)
|
140
|
+
client.readpartial(1024)
|
141
|
+
else
|
142
|
+
nil
|
143
|
+
end
|
144
|
+
client.close
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# The client should time out (i.e. result remains nil) because the server waits too long.
|
149
|
+
assert_nil result
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_multiple_fibers_on_same_socket
|
153
|
+
server = TCPServer.new("127.0.0.1", 0)
|
154
|
+
port = server.addr[1]
|
155
|
+
results = []
|
156
|
+
|
157
|
+
with_scheduler do |_scheduler|
|
158
|
+
# Server fiber: accept a connection and send a short message.
|
159
|
+
Fiber.schedule do
|
160
|
+
client1 = server.accept
|
161
|
+
client2 = server.accept
|
162
|
+
client1.write("network")
|
163
|
+
client2.write("network")
|
164
|
+
client1.close
|
165
|
+
client2.close
|
166
|
+
server.close
|
167
|
+
end
|
168
|
+
|
169
|
+
# Two separate client fibers using separate connections.
|
170
|
+
# (In many schedulers, if two fibers wait on the same IO object,
|
171
|
+
# only one may be resumed when data becomes available.)
|
172
|
+
2.times do |i|
|
173
|
+
Fiber.schedule do
|
174
|
+
sleep 0.001
|
175
|
+
client = TCPSocket.new("127.0.0.1", port)
|
176
|
+
res = client.wait_readable(0.05) ? "readable" : "timeout"
|
177
|
+
results << res
|
178
|
+
client.close
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
assert_equal 2, results.size
|
184
|
+
assert_includes results, "readable"
|
185
|
+
end
|
186
|
+
|
187
|
+
def test_two_fibers_only_one_connects
|
188
|
+
server = TCPServer.new("127.0.0.1", 0)
|
189
|
+
port = server.addr[1]
|
190
|
+
results = []
|
191
|
+
|
192
|
+
with_scheduler do |_scheduler|
|
193
|
+
# Server fiber: accept a connection and send a short message.
|
194
|
+
Fiber.schedule do
|
195
|
+
client1 = server.accept
|
196
|
+
client1.write("network")
|
197
|
+
client1.close
|
198
|
+
sleep 0.1
|
199
|
+
server.close
|
200
|
+
end
|
201
|
+
|
202
|
+
# Two separate client fibers using separate connections.
|
203
|
+
# (In many schedulers, if two fibers wait on the same IO object,
|
204
|
+
# only one may be resumed when data becomes available.)
|
205
|
+
2.times do |i|
|
206
|
+
Fiber.schedule do
|
207
|
+
sleep 0.001
|
208
|
+
client = TCPSocket.new("127.0.0.1", port)
|
209
|
+
res = client.wait_readable(0.05) ? "readable" : "timeout"
|
210
|
+
results << res
|
211
|
+
client.close
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
assert_equal 2, results.size
|
217
|
+
assert_includes results, "readable"
|
218
|
+
assert_includes results, "timeout"
|
219
|
+
end
|
220
|
+
|
221
|
+
def test_udp_and_tcp_interleaving
|
222
|
+
# Set up a UDP server.
|
223
|
+
udp_server = UDPSocket.new
|
224
|
+
udp_server.bind("127.0.0.1", 0)
|
225
|
+
udp_port = udp_server.addr[1]
|
226
|
+
|
227
|
+
# Set up a TCP server.
|
228
|
+
tcp_server = TCPServer.new("127.0.0.1", 0)
|
229
|
+
tcp_port = tcp_server.addr[1]
|
230
|
+
|
231
|
+
udp_result = nil
|
232
|
+
tcp_result = nil
|
233
|
+
|
234
|
+
with_scheduler do |_scheduler|
|
235
|
+
# UDP server fiber: wait for a datagram.
|
236
|
+
Fiber.schedule do
|
237
|
+
udp_result = udp_server.recvfrom(1024)[0]
|
238
|
+
udp_server.close
|
239
|
+
end
|
240
|
+
|
241
|
+
# TCP server fiber: accept a connection and read data.
|
242
|
+
Fiber.schedule do
|
243
|
+
client = tcp_server.accept
|
244
|
+
tcp_result = client.readpartial(1024)
|
245
|
+
client.close
|
246
|
+
tcp_server.close
|
247
|
+
end
|
248
|
+
|
249
|
+
# UDP client fiber: send a datagram.
|
250
|
+
Fiber.schedule do
|
251
|
+
sleep 0.02
|
252
|
+
udp_client = UDPSocket.new
|
253
|
+
udp_client.send("udp data", 0, "127.0.0.1", udp_port)
|
254
|
+
udp_client.close
|
255
|
+
end
|
256
|
+
|
257
|
+
# TCP client fiber: connect and send data.
|
258
|
+
Fiber.schedule do
|
259
|
+
sleep 0.02
|
260
|
+
client = TCPSocket.new("127.0.0.1", tcp_port)
|
261
|
+
client.write("tcp data")
|
262
|
+
client.close
|
263
|
+
end
|
264
|
+
|
265
|
+
# Additional fiber to interleave a sleep.
|
266
|
+
Fiber.schedule do
|
267
|
+
sleep 0.05
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
assert_equal "udp data", udp_result
|
272
|
+
assert_equal "tcp data", tcp_result
|
273
|
+
end
|
274
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'debug'
|
3
|
+
|
4
|
+
|
5
|
+
class TestProcessWait < Minitest::Test
|
6
|
+
include Itsi::Scheduler::TestHelper
|
7
|
+
|
8
|
+
def test_process_wait
|
9
|
+
start_time = Time.now
|
10
|
+
pids = []
|
11
|
+
|
12
|
+
with_scheduler do |_scheduler|
|
13
|
+
3.times do
|
14
|
+
pids << Process.spawn("sleep 0.25")
|
15
|
+
end
|
16
|
+
3.times do |i|
|
17
|
+
Fiber.schedule do
|
18
|
+
Process.wait(pids[i])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end_time = Time.now
|
23
|
+
assert_in_delta(0.25, end_time - start_time, 0.1)
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|