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.
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 +181 -133
  34. data/crates/itsi_server/src/server/lifecycle_event.rs +8 -0
  35. data/crates/itsi_server/src/server/listener.rs +169 -128
  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 +253 -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 +238 -0
  41. data/crates/itsi_server/src/server/signal.rs +57 -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 +230 -0
  79. data/gems/scheduler/ext/itsi_server/src/server/lifecycle_event.rs +8 -0
  80. data/gems/scheduler/ext/itsi_server/src/server/listener.rs +259 -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 +253 -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 +238 -0
  86. data/gems/scheduler/ext/itsi_server/src/server/signal.rs +57 -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 -2
  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 +181 -133
  135. data/gems/server/ext/itsi_server/src/server/lifecycle_event.rs +8 -0
  136. data/gems/server/ext/itsi_server/src/server/listener.rs +169 -128
  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 +253 -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 +238 -0
  142. data/gems/server/ext/itsi_server/src/server/signal.rs +57 -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 -4
  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 +68 -2
  153. data/gems/server/lib/itsi/signals.rb +18 -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 +17 -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,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