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.
Files changed (164) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +524 -44
  3. data/Rakefile +22 -33
  4. data/crates/itsi_error/Cargo.toml +2 -0
  5. data/crates/itsi_error/src/from.rs +70 -0
  6. data/crates/itsi_error/src/lib.rs +10 -37
  7. data/crates/itsi_instrument_entry/Cargo.toml +15 -0
  8. data/crates/itsi_instrument_entry/src/lib.rs +31 -0
  9. data/crates/itsi_rb_helpers/Cargo.toml +2 -0
  10. data/crates/itsi_rb_helpers/src/heap_value.rs +121 -0
  11. data/crates/itsi_rb_helpers/src/lib.rs +90 -10
  12. data/crates/itsi_scheduler/Cargo.toml +9 -1
  13. data/crates/itsi_scheduler/extconf.rb +1 -1
  14. data/crates/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
  15. data/crates/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
  16. data/crates/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
  17. data/crates/itsi_scheduler/src/itsi_scheduler.rs +308 -0
  18. data/crates/itsi_scheduler/src/lib.rs +31 -10
  19. data/crates/itsi_server/Cargo.toml +14 -2
  20. data/crates/itsi_server/extconf.rb +1 -1
  21. data/crates/itsi_server/src/body_proxy/big_bytes.rs +104 -0
  22. data/crates/itsi_server/src/body_proxy/itsi_body_proxy.rs +122 -0
  23. data/crates/itsi_server/src/body_proxy/mod.rs +2 -0
  24. data/crates/itsi_server/src/lib.rs +58 -7
  25. data/crates/itsi_server/src/request/itsi_request.rs +238 -104
  26. data/crates/itsi_server/src/response/itsi_response.rs +347 -0
  27. data/crates/itsi_server/src/response/mod.rs +1 -0
  28. data/crates/itsi_server/src/server/bind.rs +50 -20
  29. data/crates/itsi_server/src/server/bind_protocol.rs +37 -0
  30. data/crates/itsi_server/src/server/io_stream.rs +104 -0
  31. data/crates/itsi_server/src/server/itsi_ca/itsi_ca.crt +11 -30
  32. data/crates/itsi_server/src/server/itsi_ca/itsi_ca.key +3 -50
  33. data/crates/itsi_server/src/server/itsi_server.rs +196 -134
  34. data/crates/itsi_server/src/server/lifecycle_event.rs +9 -0
  35. data/crates/itsi_server/src/server/listener.rs +184 -127
  36. data/crates/itsi_server/src/server/mod.rs +7 -1
  37. data/crates/itsi_server/src/server/process_worker.rs +196 -0
  38. data/crates/itsi_server/src/server/serve_strategy/cluster_mode.rs +254 -0
  39. data/crates/itsi_server/src/server/serve_strategy/mod.rs +27 -0
  40. data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +241 -0
  41. data/crates/itsi_server/src/server/signal.rs +70 -0
  42. data/crates/itsi_server/src/server/thread_worker.rs +368 -0
  43. data/crates/itsi_server/src/server/tls.rs +42 -28
  44. data/crates/itsi_tracing/Cargo.toml +4 -0
  45. data/crates/itsi_tracing/src/lib.rs +36 -6
  46. data/gems/scheduler/Cargo.lock +219 -23
  47. data/gems/scheduler/Rakefile +7 -1
  48. data/gems/scheduler/ext/itsi_error/Cargo.toml +2 -0
  49. data/gems/scheduler/ext/itsi_error/src/from.rs +70 -0
  50. data/gems/scheduler/ext/itsi_error/src/lib.rs +10 -37
  51. data/gems/scheduler/ext/itsi_instrument_entry/Cargo.toml +15 -0
  52. data/gems/scheduler/ext/itsi_instrument_entry/src/lib.rs +31 -0
  53. data/gems/scheduler/ext/itsi_rb_helpers/Cargo.toml +2 -0
  54. data/gems/scheduler/ext/itsi_rb_helpers/src/heap_value.rs +121 -0
  55. data/gems/scheduler/ext/itsi_rb_helpers/src/lib.rs +90 -10
  56. data/gems/scheduler/ext/itsi_scheduler/Cargo.toml +9 -1
  57. data/gems/scheduler/ext/itsi_scheduler/extconf.rb +1 -1
  58. data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
  59. data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
  60. data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
  61. data/gems/scheduler/ext/itsi_scheduler/src/itsi_scheduler.rs +308 -0
  62. data/gems/scheduler/ext/itsi_scheduler/src/lib.rs +31 -10
  63. data/gems/scheduler/ext/itsi_server/Cargo.toml +41 -0
  64. data/gems/scheduler/ext/itsi_server/extconf.rb +6 -0
  65. data/gems/scheduler/ext/itsi_server/src/body_proxy/big_bytes.rs +104 -0
  66. data/gems/scheduler/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs +122 -0
  67. data/gems/scheduler/ext/itsi_server/src/body_proxy/mod.rs +2 -0
  68. data/gems/scheduler/ext/itsi_server/src/lib.rs +103 -0
  69. data/gems/scheduler/ext/itsi_server/src/request/itsi_request.rs +277 -0
  70. data/gems/scheduler/ext/itsi_server/src/request/mod.rs +1 -0
  71. data/gems/scheduler/ext/itsi_server/src/response/itsi_response.rs +347 -0
  72. data/gems/scheduler/ext/itsi_server/src/response/mod.rs +1 -0
  73. data/gems/scheduler/ext/itsi_server/src/server/bind.rs +168 -0
  74. data/gems/scheduler/ext/itsi_server/src/server/bind_protocol.rs +37 -0
  75. data/gems/scheduler/ext/itsi_server/src/server/io_stream.rs +104 -0
  76. data/gems/scheduler/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +13 -0
  77. data/gems/scheduler/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +5 -0
  78. data/gems/scheduler/ext/itsi_server/src/server/itsi_server.rs +244 -0
  79. data/gems/scheduler/ext/itsi_server/src/server/lifecycle_event.rs +9 -0
  80. data/gems/scheduler/ext/itsi_server/src/server/listener.rs +275 -0
  81. data/gems/scheduler/ext/itsi_server/src/server/mod.rs +11 -0
  82. data/gems/scheduler/ext/itsi_server/src/server/process_worker.rs +196 -0
  83. data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +254 -0
  84. data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/mod.rs +27 -0
  85. data/gems/scheduler/ext/itsi_server/src/server/serve_strategy/single_mode.rs +241 -0
  86. data/gems/scheduler/ext/itsi_server/src/server/signal.rs +70 -0
  87. data/gems/scheduler/ext/itsi_server/src/server/thread_worker.rs +368 -0
  88. data/gems/scheduler/ext/itsi_server/src/server/tls.rs +152 -0
  89. data/gems/scheduler/ext/itsi_tracing/Cargo.toml +4 -0
  90. data/gems/scheduler/ext/itsi_tracing/src/lib.rs +36 -6
  91. data/gems/scheduler/itsi-scheduler.gemspec +2 -3
  92. data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
  93. data/gems/scheduler/lib/itsi/scheduler.rb +137 -1
  94. data/gems/scheduler/test/helpers/test_helper.rb +24 -0
  95. data/gems/scheduler/test/test_active_record.rb +158 -0
  96. data/gems/scheduler/test/test_address_resolve.rb +23 -0
  97. data/gems/scheduler/test/test_block_unblock.rb +229 -0
  98. data/gems/scheduler/test/test_file_io.rb +193 -0
  99. data/gems/scheduler/test/test_itsi_scheduler.rb +24 -1
  100. data/gems/scheduler/test/test_kernel_sleep.rb +91 -0
  101. data/gems/scheduler/test/test_nested_fibers.rb +286 -0
  102. data/gems/scheduler/test/test_network_io.rb +274 -0
  103. data/gems/scheduler/test/test_process_wait.rb +26 -0
  104. data/gems/server/exe/itsi +88 -28
  105. data/gems/server/ext/itsi_error/Cargo.toml +2 -0
  106. data/gems/server/ext/itsi_error/src/from.rs +70 -0
  107. data/gems/server/ext/itsi_error/src/lib.rs +10 -37
  108. data/gems/server/ext/itsi_instrument_entry/Cargo.toml +15 -0
  109. data/gems/server/ext/itsi_instrument_entry/src/lib.rs +31 -0
  110. data/gems/server/ext/itsi_rb_helpers/Cargo.toml +2 -0
  111. data/gems/server/ext/itsi_rb_helpers/src/heap_value.rs +121 -0
  112. data/gems/server/ext/itsi_rb_helpers/src/lib.rs +90 -10
  113. data/gems/server/ext/itsi_scheduler/Cargo.toml +24 -0
  114. data/gems/server/ext/itsi_scheduler/extconf.rb +6 -0
  115. data/gems/server/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
  116. data/gems/server/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
  117. data/gems/server/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
  118. data/gems/server/ext/itsi_scheduler/src/itsi_scheduler.rs +308 -0
  119. data/gems/server/ext/itsi_scheduler/src/lib.rs +38 -0
  120. data/gems/server/ext/itsi_server/Cargo.toml +14 -2
  121. data/gems/server/ext/itsi_server/extconf.rb +1 -1
  122. data/gems/server/ext/itsi_server/src/body_proxy/big_bytes.rs +104 -0
  123. data/gems/server/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs +122 -0
  124. data/gems/server/ext/itsi_server/src/body_proxy/mod.rs +2 -0
  125. data/gems/server/ext/itsi_server/src/lib.rs +58 -7
  126. data/gems/server/ext/itsi_server/src/request/itsi_request.rs +238 -104
  127. data/gems/server/ext/itsi_server/src/response/itsi_response.rs +347 -0
  128. data/gems/server/ext/itsi_server/src/response/mod.rs +1 -0
  129. data/gems/server/ext/itsi_server/src/server/bind.rs +50 -20
  130. data/gems/server/ext/itsi_server/src/server/bind_protocol.rs +37 -0
  131. data/gems/server/ext/itsi_server/src/server/io_stream.rs +104 -0
  132. data/gems/server/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +11 -30
  133. data/gems/server/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +3 -50
  134. data/gems/server/ext/itsi_server/src/server/itsi_server.rs +196 -134
  135. data/gems/server/ext/itsi_server/src/server/lifecycle_event.rs +9 -0
  136. data/gems/server/ext/itsi_server/src/server/listener.rs +184 -127
  137. data/gems/server/ext/itsi_server/src/server/mod.rs +7 -1
  138. data/gems/server/ext/itsi_server/src/server/process_worker.rs +196 -0
  139. data/gems/server/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +254 -0
  140. data/gems/server/ext/itsi_server/src/server/serve_strategy/mod.rs +27 -0
  141. data/gems/server/ext/itsi_server/src/server/serve_strategy/single_mode.rs +241 -0
  142. data/gems/server/ext/itsi_server/src/server/signal.rs +70 -0
  143. data/gems/server/ext/itsi_server/src/server/thread_worker.rs +368 -0
  144. data/gems/server/ext/itsi_server/src/server/tls.rs +42 -28
  145. data/gems/server/ext/itsi_tracing/Cargo.toml +4 -0
  146. data/gems/server/ext/itsi_tracing/src/lib.rs +36 -6
  147. data/gems/server/itsi-server.gemspec +4 -5
  148. data/gems/server/lib/itsi/request.rb +30 -14
  149. data/gems/server/lib/itsi/server/rack/handler/itsi.rb +25 -0
  150. data/gems/server/lib/itsi/server/scheduler_mode.rb +6 -0
  151. data/gems/server/lib/itsi/server/version.rb +1 -1
  152. data/gems/server/lib/itsi/server.rb +82 -2
  153. data/gems/server/lib/itsi/signals.rb +23 -0
  154. data/gems/server/lib/itsi/stream_io.rb +38 -0
  155. data/gems/server/test/test_helper.rb +2 -0
  156. data/gems/server/test/test_itsi_server.rb +1 -1
  157. data/lib/itsi/version.rb +1 -1
  158. data/tasks.txt +18 -0
  159. metadata +102 -12
  160. data/crates/itsi_server/src/server/transfer_protocol.rs +0 -23
  161. data/crates/itsi_server/src/stream_writer/mod.rs +0 -21
  162. data/gems/scheduler/test/test_helper.rb +0 -6
  163. data/gems/server/ext/itsi_server/src/server/transfer_protocol.rs +0 -23
  164. 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