itsi 0.1.0 → 0.1.2
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 +181 -133
- data/crates/itsi_server/src/server/lifecycle_event.rs +8 -0
- data/crates/itsi_server/src/server/listener.rs +169 -128
- 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 +253 -0
- data/crates/itsi_server/src/server/serve_strategy/mod.rs +27 -0
- data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +238 -0
- data/crates/itsi_server/src/server/signal.rs +57 -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 +230 -0
- data/gems/scheduler/ext/itsi_server/src/server/lifecycle_event.rs +8 -0
- data/gems/scheduler/ext/itsi_server/src/server/listener.rs +259 -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 +253 -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 +238 -0
- data/gems/scheduler/ext/itsi_server/src/server/signal.rs +57 -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 -2
- 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 +181 -133
- data/gems/server/ext/itsi_server/src/server/lifecycle_event.rs +8 -0
- data/gems/server/ext/itsi_server/src/server/listener.rs +169 -128
- 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 +253 -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 +238 -0
- data/gems/server/ext/itsi_server/src/server/signal.rs +57 -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 -4
- 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 +68 -2
- data/gems/server/lib/itsi/signals.rb +18 -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 +17 -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,193 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
|
4
|
+
class TestFileIO < Minitest::Test
|
5
|
+
include Itsi::Scheduler::TestHelper
|
6
|
+
|
7
|
+
# Test that a fiber waiting to read 5 bytes from a pipe is resumed
|
8
|
+
# when another fiber writes "hello" to the pipe.
|
9
|
+
def test_io_read_resume
|
10
|
+
reader, writer = IO.pipe
|
11
|
+
result = nil
|
12
|
+
|
13
|
+
with_scheduler do |_scheduler|
|
14
|
+
Fiber.schedule do
|
15
|
+
result = reader.read(5)
|
16
|
+
end
|
17
|
+
|
18
|
+
Fiber.schedule do
|
19
|
+
sleep 0.05
|
20
|
+
writer.write("hello")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
reader.close
|
25
|
+
writer.close
|
26
|
+
|
27
|
+
assert_equal "hello", result
|
28
|
+
end
|
29
|
+
|
30
|
+
# Test that IO.wait_readable times out correctly when no data arrives.
|
31
|
+
def test_io_wait_readable_timeout
|
32
|
+
reader, writer = IO.pipe
|
33
|
+
|
34
|
+
result = nil
|
35
|
+
with_scheduler do |_scheduler|
|
36
|
+
Fiber.schedule do
|
37
|
+
# When no data is available, wait_readable should return nil after the timeout.
|
38
|
+
result = reader.wait_readable(0.05)
|
39
|
+
puts "Awoken"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
reader.close
|
44
|
+
writer.close
|
45
|
+
|
46
|
+
assert_nil result, "Expected nil on timeout when no data is available"
|
47
|
+
end
|
48
|
+
|
49
|
+
# Test that two different pipes can be read concurrently.
|
50
|
+
def test_multiple_io_reads
|
51
|
+
reader1, writer1 = IO.pipe
|
52
|
+
reader2, writer2 = IO.pipe
|
53
|
+
results = {}
|
54
|
+
|
55
|
+
with_scheduler do |_scheduler|
|
56
|
+
Fiber.schedule do
|
57
|
+
results[:first] = reader1.read(5)
|
58
|
+
end
|
59
|
+
|
60
|
+
Fiber.schedule do
|
61
|
+
results[:second] = reader2.read(6)
|
62
|
+
end
|
63
|
+
|
64
|
+
Fiber.schedule do
|
65
|
+
sleep 0.05
|
66
|
+
writer1.write("first")
|
67
|
+
writer2.write("second")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
reader1.close
|
72
|
+
writer1.close
|
73
|
+
reader2.close
|
74
|
+
writer2.close
|
75
|
+
|
76
|
+
assert_equal "first", results[:first]
|
77
|
+
assert_equal "second", results[:second]
|
78
|
+
end
|
79
|
+
|
80
|
+
# Test an interleaved read/write operation.
|
81
|
+
# One fiber writes the data in chunks (with short sleeps in between),
|
82
|
+
# while another fiber reads in fixed-size chunks until EOF.
|
83
|
+
def test_io_interleaved_read_write
|
84
|
+
reader, writer = IO.pipe
|
85
|
+
data_read = "".dup
|
86
|
+
|
87
|
+
with_scheduler do |_scheduler|
|
88
|
+
Fiber.schedule do
|
89
|
+
loop do
|
90
|
+
chunk = reader.read(3)
|
91
|
+
break if chunk.nil? || chunk.empty?
|
92
|
+
|
93
|
+
data_read << chunk
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
Fiber.schedule do
|
98
|
+
["Hel", "lo ", "Wor", "ld"].each do |part|
|
99
|
+
writer.write(part)
|
100
|
+
sleep 0.02
|
101
|
+
end
|
102
|
+
writer.close # signal EOF to the reader
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
reader.close
|
107
|
+
|
108
|
+
assert_equal "Hello World", data_read
|
109
|
+
end
|
110
|
+
|
111
|
+
# Test that a fiber using nonblocking I/O correctly waits for data.
|
112
|
+
def test_io_read_with_nonblocking_mode
|
113
|
+
reader, writer = IO.pipe
|
114
|
+
# Ensure the IOs are in synchronous mode.
|
115
|
+
reader.sync = true
|
116
|
+
writer.sync = true
|
117
|
+
result = nil
|
118
|
+
|
119
|
+
with_scheduler do |_scheduler|
|
120
|
+
Fiber.schedule do
|
121
|
+
# Use wait_readable to wait until data is available.
|
122
|
+
reader.wait_readable(0.2)
|
123
|
+
result = reader.read_nonblock(5)
|
124
|
+
end
|
125
|
+
|
126
|
+
Fiber.schedule do
|
127
|
+
sleep 0.05
|
128
|
+
writer.write("hello")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
reader.close
|
133
|
+
writer.close
|
134
|
+
|
135
|
+
assert_equal "hello", result
|
136
|
+
end
|
137
|
+
|
138
|
+
# Test that writing to an IO (here a pipe) succeeds immediately
|
139
|
+
# when the pipe is ready for writing.
|
140
|
+
def test_io_write_immediate
|
141
|
+
reader, writer = IO.pipe
|
142
|
+
bytes_written = nil
|
143
|
+
|
144
|
+
with_scheduler do |_scheduler|
|
145
|
+
Fiber.schedule do
|
146
|
+
# If the pipe is empty, write should not block.
|
147
|
+
bytes_written = writer.write("test")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
reader.close
|
152
|
+
writer.close
|
153
|
+
|
154
|
+
assert_equal 4, bytes_written
|
155
|
+
end
|
156
|
+
|
157
|
+
# Test what happens when two fibers are waiting on the same IO.
|
158
|
+
# With the current scheduler design, only one fiber will be resumed
|
159
|
+
# when data becomes available, and the other will eventually time out.
|
160
|
+
def test_multiple_fibers_waiting_on_same_fd
|
161
|
+
reader, writer = IO.pipe
|
162
|
+
results = []
|
163
|
+
data = "data"
|
164
|
+
|
165
|
+
with_scheduler do |_scheduler|
|
166
|
+
Fiber.schedule do
|
167
|
+
if res = reader.wait_readable(0.1)
|
168
|
+
reader.read(data.length)
|
169
|
+
end
|
170
|
+
results << (res ? "readable" : "timeout")
|
171
|
+
end
|
172
|
+
|
173
|
+
Fiber.schedule do
|
174
|
+
if res = reader.wait_readable(0.1)
|
175
|
+
reader.read(data.length)
|
176
|
+
end
|
177
|
+
results << (res ? "readable" : "timeout")
|
178
|
+
end
|
179
|
+
|
180
|
+
Fiber.schedule do
|
181
|
+
writer.write(data)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
writer.close
|
186
|
+
reader.close
|
187
|
+
|
188
|
+
# One fiber should be resumed with "readable" while the other times out.
|
189
|
+
assert_equal 2, results.size
|
190
|
+
assert_includes results, "readable"
|
191
|
+
assert_includes results, "timeout"
|
192
|
+
end
|
193
|
+
end
|
@@ -1,9 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "test_helper"
|
4
3
|
|
5
4
|
class TestItsiScheduler < Minitest::Test
|
5
|
+
include Itsi::Scheduler::TestHelper
|
6
|
+
|
6
7
|
def test_that_it_has_a_version_number
|
7
8
|
refute_nil ::Itsi::Scheduler::VERSION
|
8
9
|
end
|
10
|
+
|
11
|
+
def test_errors
|
12
|
+
results = []
|
13
|
+
start_at = Time.now
|
14
|
+
# Run the scheduler in a dedicated thread to avoid interference with the
|
15
|
+
# main thread’s scheduler state.
|
16
|
+
total = 0
|
17
|
+
out, err = capture_subprocess_io do
|
18
|
+
with_scheduler do |_scheduler|
|
19
|
+
9.times do |i|
|
20
|
+
Fiber.schedule do
|
21
|
+
sleep 0.05
|
22
|
+
raise i if i % 3 == 0
|
23
|
+
total += 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
assert_equal total, 6
|
30
|
+
assert_match /Failed to resume fiber /, out
|
31
|
+
end
|
9
32
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
|
4
|
+
class TestKernelSleep < Minitest::Test
|
5
|
+
include Itsi::Scheduler::TestHelper
|
6
|
+
|
7
|
+
def test_that_it_has_a_version_number
|
8
|
+
refute_nil ::Itsi::Scheduler::VERSION
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_it_can_sleep_concurrently
|
12
|
+
results = []
|
13
|
+
start_at = Time.now
|
14
|
+
# Run the scheduler in a dedicated thread to avoid interference with the
|
15
|
+
# main thread’s scheduler state.
|
16
|
+
with_scheduler do |_scheduler|
|
17
|
+
5.times do
|
18
|
+
Fiber.schedule do
|
19
|
+
sleep 0.05
|
20
|
+
results << "first"
|
21
|
+
sleep 0.05
|
22
|
+
results << "second"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
ends_at = Time.now
|
28
|
+
# We expect 10 sleep completions overall (2 per fiber).
|
29
|
+
assert_equal 10, results.size
|
30
|
+
# Because all sleeps run concurrently, the total elapsed time should be about 1 second.
|
31
|
+
assert_in_delta 0.1, ends_at - start_at, 0.02, "Total elapsed time should be close to 0.1 second"
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_sleep_zero_duration
|
35
|
+
with_scheduler do |_scheduler|
|
36
|
+
start = Time.now
|
37
|
+
result = sleep(0)
|
38
|
+
finish = Time.now
|
39
|
+
assert result, "sleep(0) should return a truthy value"
|
40
|
+
# Expect near-immediate return.
|
41
|
+
assert_operator finish - start, :<, 0.01, "sleep(0) should not delay execution"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_multiple_sleeps_in_single_fiber
|
46
|
+
order = []
|
47
|
+
with_scheduler do |_scheduler|
|
48
|
+
# Schedule one fiber that sleeps twice.
|
49
|
+
Fiber.schedule do
|
50
|
+
order << "start"
|
51
|
+
sleep 0.2
|
52
|
+
order << "after first sleep"
|
53
|
+
sleep 0.3
|
54
|
+
order << "after second sleep"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
assert_equal ["start", "after first sleep", "after second sleep"], order
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_fibers_wake_in_correct_order
|
62
|
+
order = []
|
63
|
+
with_scheduler do |_scheduler|
|
64
|
+
# Schedule three fibers with different sleep durations.
|
65
|
+
Fiber.schedule do
|
66
|
+
sleep 0.3
|
67
|
+
order << "fiber1"
|
68
|
+
end
|
69
|
+
Fiber.schedule do
|
70
|
+
sleep 0.1
|
71
|
+
order << "fiber2"
|
72
|
+
end
|
73
|
+
Fiber.schedule do
|
74
|
+
sleep 0.2
|
75
|
+
order << "fiber3"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Since the fibers sleep for 0.1, 0.2, and 0.3 seconds respectively,
|
80
|
+
# we expect the wake-up order to be: fiber2, then fiber3, then fiber1.
|
81
|
+
assert_equal %w[fiber2 fiber3 fiber1], order
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_invalid_sleep_value
|
85
|
+
with_scheduler do |_scheduler|
|
86
|
+
assert_raises(TypeError) do
|
87
|
+
sleep("not a number")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -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
|