polyphony 1.4 → 1.6

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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/CHANGELOG.md +22 -0
  4. data/TODO.md +5 -14
  5. data/examples/pipes/http_server.rb +42 -12
  6. data/examples/pipes/http_server2.rb +45 -0
  7. data/ext/polyphony/backend_common.h +5 -0
  8. data/ext/polyphony/backend_io_uring.c +174 -121
  9. data/ext/polyphony/backend_io_uring_context.c +24 -18
  10. data/ext/polyphony/backend_io_uring_context.h +4 -2
  11. data/ext/polyphony/backend_libev.c +46 -22
  12. data/ext/polyphony/event.c +21 -0
  13. data/ext/polyphony/extconf.rb +25 -19
  14. data/ext/polyphony/fiber.c +0 -2
  15. data/ext/polyphony/pipe.c +1 -1
  16. data/ext/polyphony/polyphony.c +2 -20
  17. data/ext/polyphony/polyphony.h +5 -5
  18. data/ext/polyphony/ring_buffer.c +1 -0
  19. data/ext/polyphony/runqueue_ring_buffer.c +1 -0
  20. data/ext/polyphony/thread.c +63 -0
  21. data/ext/polyphony/win_uio.h +18 -0
  22. data/lib/polyphony/adapters/open3.rb +190 -0
  23. data/lib/polyphony/core/sync.rb +83 -13
  24. data/lib/polyphony/core/timer.rb +7 -25
  25. data/lib/polyphony/extensions/exception.rb +15 -0
  26. data/lib/polyphony/extensions/fiber.rb +14 -13
  27. data/lib/polyphony/extensions/io.rb +56 -14
  28. data/lib/polyphony/extensions/kernel.rb +1 -1
  29. data/lib/polyphony/extensions/object.rb +1 -13
  30. data/lib/polyphony/extensions/process.rb +76 -1
  31. data/lib/polyphony/extensions/socket.rb +0 -14
  32. data/lib/polyphony/extensions/thread.rb +19 -27
  33. data/lib/polyphony/extensions/timeout.rb +5 -1
  34. data/lib/polyphony/version.rb +1 -1
  35. data/lib/polyphony.rb +11 -5
  36. data/test/helper.rb +46 -4
  37. data/test/open3/envutil.rb +380 -0
  38. data/test/open3/find_executable.rb +24 -0
  39. data/test/stress.rb +11 -7
  40. data/test/test_backend.rb +11 -4
  41. data/test/test_event.rb +10 -3
  42. data/test/test_ext.rb +16 -1
  43. data/test/test_fiber.rb +16 -4
  44. data/test/test_global_api.rb +17 -16
  45. data/test/test_io.rb +39 -0
  46. data/test/test_kernel.rb +2 -2
  47. data/test/test_monitor.rb +356 -0
  48. data/test/test_open3.rb +338 -0
  49. data/test/test_signal.rb +5 -1
  50. data/test/test_socket.rb +6 -98
  51. data/test/test_sync.rb +46 -0
  52. data/test/test_thread.rb +10 -1
  53. data/test/test_thread_pool.rb +5 -0
  54. data/test/test_throttler.rb +1 -1
  55. data/test/test_timer.rb +8 -2
  56. data/test/test_trace.rb +2 -0
  57. data/vendor/liburing/.github/workflows/build.yml +8 -0
  58. data/vendor/liburing/.gitignore +1 -0
  59. data/vendor/liburing/CHANGELOG +8 -0
  60. data/vendor/liburing/configure +17 -25
  61. data/vendor/liburing/debian/liburing-dev.manpages +2 -0
  62. data/vendor/liburing/debian/rules +2 -1
  63. data/vendor/liburing/examples/Makefile +2 -1
  64. data/vendor/liburing/examples/io_uring-udp.c +11 -3
  65. data/vendor/liburing/examples/rsrc-update-bench.c +100 -0
  66. data/vendor/liburing/liburing.spec +1 -1
  67. data/vendor/liburing/make-debs.sh +4 -2
  68. data/vendor/liburing/src/Makefile +5 -5
  69. data/vendor/liburing/src/arch/aarch64/lib.h +1 -1
  70. data/vendor/liburing/src/include/liburing/io_uring.h +41 -16
  71. data/vendor/liburing/src/include/liburing.h +86 -11
  72. data/vendor/liburing/src/int_flags.h +1 -0
  73. data/vendor/liburing/src/liburing-ffi.map +12 -0
  74. data/vendor/liburing/src/liburing.map +8 -0
  75. data/vendor/liburing/src/register.c +7 -2
  76. data/vendor/liburing/src/setup.c +373 -81
  77. data/vendor/liburing/test/232c93d07b74.c +3 -3
  78. data/vendor/liburing/test/Makefile +10 -3
  79. data/vendor/liburing/test/accept.c +2 -1
  80. data/vendor/liburing/test/buf-ring.c +35 -75
  81. data/vendor/liburing/test/connect-rep.c +204 -0
  82. data/vendor/liburing/test/coredump.c +59 -0
  83. data/vendor/liburing/test/fallocate.c +9 -0
  84. data/vendor/liburing/test/fd-pass.c +34 -3
  85. data/vendor/liburing/test/file-verify.c +27 -6
  86. data/vendor/liburing/test/helpers.c +3 -1
  87. data/vendor/liburing/test/io_uring_register.c +25 -28
  88. data/vendor/liburing/test/io_uring_setup.c +1 -1
  89. data/vendor/liburing/test/poll-cancel-all.c +29 -5
  90. data/vendor/liburing/test/poll-race-mshot.c +6 -22
  91. data/vendor/liburing/test/read-write.c +53 -0
  92. data/vendor/liburing/test/recv-msgall.c +21 -23
  93. data/vendor/liburing/test/reg-fd-only.c +55 -0
  94. data/vendor/liburing/test/reg-hint.c +56 -0
  95. data/vendor/liburing/test/regbuf-merge.c +91 -0
  96. data/vendor/liburing/test/ringbuf-read.c +2 -10
  97. data/vendor/liburing/test/send_recvmsg.c +5 -16
  98. data/vendor/liburing/test/shutdown.c +2 -1
  99. data/vendor/liburing/test/socket-io-cmd.c +215 -0
  100. data/vendor/liburing/test/socket-rw-eagain.c +2 -1
  101. data/vendor/liburing/test/socket-rw-offset.c +2 -1
  102. data/vendor/liburing/test/socket-rw.c +2 -1
  103. data/vendor/liburing/test/timeout.c +276 -0
  104. data/vendor/liburing/test/xattr.c +38 -25
  105. metadata +20 -7
  106. data/vendor/liburing/test/timeout-overflow.c +0 -204
data/test/test_io.rb CHANGED
@@ -100,6 +100,12 @@ class IOTest < MiniTest::Test
100
100
  assert_equal '', i.read(0)
101
101
  end
102
102
 
103
+ def test_read_empty_pipe
104
+ i, o = IO.pipe
105
+ o.close
106
+ assert_equal '', i.read
107
+ end
108
+
103
109
  def test_readpartial
104
110
  i, o = IO.pipe
105
111
 
@@ -345,6 +351,39 @@ class IOTest < MiniTest::Test
345
351
  assert_equal [6, 0], splice_lens
346
352
  end
347
353
 
354
+ def test_copy_stream
355
+ p1 = Polyphony::Pipe.new
356
+ p2 = Polyphony::Pipe.new
357
+
358
+ spin { p1 << 'foobar'; p1.close }
359
+
360
+ count = IO.copy_stream(p1, p2)
361
+ p2.close
362
+ assert_equal 6, count
363
+ assert_equal 'foobar', p2.read
364
+ end
365
+
366
+ def test_copy_stream_with_length
367
+ p1 = Polyphony::Pipe.new
368
+ p2 = Polyphony::Pipe.new
369
+
370
+ spin { p1 << 'foobar'; p1.close }
371
+
372
+ count = IO.copy_stream(p1, p2, 3)
373
+ p2.close
374
+ assert_equal 3, count
375
+ assert_equal 'foo', p2.read
376
+ end
377
+
378
+ def test_copy_stream_with_length_and_offset
379
+ p2 = Polyphony::Pipe.new
380
+
381
+ count = IO.copy_stream(__FILE__, p2, 34, 4)
382
+ p2.close
383
+ assert_equal 34, count
384
+ assert_equal IO.read(__FILE__)[4, 34], p2.read
385
+ end
386
+
348
387
  def test_splice_from_to_eof
349
388
  i1, o1 = IO.pipe
350
389
  i2, o2 = IO.pipe
data/test/test_kernel.rb CHANGED
@@ -8,9 +8,9 @@ class KernelTest < MiniTest::Test
8
8
  FileUtils.rm(fn) rescue nil
9
9
 
10
10
  counter = 0
11
- timer = spin { throttled_loop(200) { counter += 1 } }
11
+ timer = spin { throttled_loop(20) { counter += 1 } }
12
12
 
13
- system('sleep 0.01')
13
+ system('sleep 0.13')
14
14
  assert(counter >= 2)
15
15
 
16
16
  system('echo "hello" > ' + fn)
@@ -0,0 +1,356 @@
1
+ # Adapted from: https://github.com/ruby/ruby/blob/master/test/monitor/test_monitor.rb
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require_relative 'helper'
6
+
7
+ class TestMonitor < MiniTest::Test
8
+ Queue = Polyphony::Queue
9
+
10
+ def setup
11
+ super
12
+ @monitor = Polyphony::Monitor.new
13
+ end
14
+
15
+ def test_enter_in_different_fibers
16
+ @monitor.enter
17
+ Fiber.new {
18
+ assert_equal false, @monitor.try_enter
19
+ }.resume
20
+ end
21
+
22
+ def test_enter
23
+ ary = []
24
+ queue = Thread::Queue.new
25
+ f1 = spin {
26
+ queue.pop
27
+ @monitor.enter
28
+ for i in 6 .. 10
29
+ ary.push(i)
30
+ snooze
31
+ end
32
+ @monitor.exit
33
+ }
34
+ f2 = spin {
35
+ @monitor.enter
36
+ queue.enq(nil)
37
+ for i in 1 .. 5
38
+ ary.push(i)
39
+ snooze
40
+ end
41
+ @monitor.exit
42
+ }
43
+ Fiber.await(f1, f2)
44
+ assert_equal((1..10).to_a, ary)
45
+ end
46
+
47
+ def test_exit
48
+ m = Polyphony::Monitor.new
49
+ m.enter
50
+ assert_equal true, m.mon_owned?
51
+ m.exit
52
+ assert_equal false, m.mon_owned?
53
+
54
+ assert_raises ThreadError do
55
+ m.exit
56
+ end
57
+
58
+ assert_equal false, m.mon_owned?
59
+
60
+ m.enter
61
+ Thread.new{
62
+ assert_raises ThreadError do
63
+ m.exit
64
+ end
65
+ true
66
+ }.join
67
+ assert_equal true, m.mon_owned?
68
+ m.exit
69
+ end
70
+
71
+ def test_enter_second_after_killed_thread
72
+ th = Thread.new {
73
+ @monitor.enter
74
+ Thread.current.kill
75
+ @monitor.exit
76
+ }
77
+ th.join
78
+ @monitor.enter
79
+ @monitor.exit
80
+ th2 = Thread.new {
81
+ @monitor.enter
82
+ @monitor.exit
83
+ }
84
+ assert_join_threads([th, th2])
85
+ end
86
+
87
+ def test_synchronize
88
+ ary = []
89
+ f1 = spin {
90
+ receive
91
+ @monitor.synchronize do
92
+ for i in 6 .. 10
93
+ ary.push(i)
94
+ snooze
95
+ end
96
+ end
97
+ }
98
+ f2 = spin {
99
+ @monitor.synchronize do
100
+ f1 << :continue
101
+ for i in 1 .. 5
102
+ ary.push(i)
103
+ snooze
104
+ end
105
+ end
106
+ }
107
+ Fiber.await(f1, f2)
108
+ assert_equal((1..10).to_a, ary)
109
+ end
110
+
111
+ def test_killed_thread_in_synchronize
112
+ ary = []
113
+ queue = Thread::Queue.new
114
+ t1 = Thread.new {
115
+ queue.pop
116
+ @monitor.synchronize {
117
+ ary << :t1
118
+ }
119
+ }
120
+ t2 = Thread.new {
121
+ queue.pop
122
+ @monitor.synchronize {
123
+ ary << :t2
124
+ }
125
+ }
126
+ t3 = Thread.new {
127
+ @monitor.synchronize do
128
+ queue.enq(nil)
129
+ queue.enq(nil)
130
+ assert_equal([], ary)
131
+ t1.kill
132
+ t2.kill
133
+ ary << :main
134
+ end
135
+ assert_equal([:main], ary)
136
+ }
137
+ assert_join_threads([t1, t2, t3])
138
+ end
139
+
140
+ def test_try_enter
141
+ queue1 = Thread::Queue.new
142
+ queue2 = Thread::Queue.new
143
+ th = Thread.new {
144
+ queue1.deq
145
+ @monitor.enter
146
+ queue2.enq(nil)
147
+ queue1.deq
148
+ @monitor.exit
149
+ queue2.enq(nil)
150
+ }
151
+ th2 = Thread.new {
152
+ assert_equal(true, @monitor.try_enter)
153
+ @monitor.exit
154
+ queue1.enq(nil)
155
+ queue2.deq
156
+ assert_equal(false, @monitor.try_enter)
157
+ queue1.enq(nil)
158
+ queue2.deq
159
+ assert_equal(true, @monitor.try_enter)
160
+ }
161
+ assert_join_threads([th, th2])
162
+ end
163
+
164
+ def test_try_enter_second_after_killed_thread
165
+ th = Thread.new {
166
+ assert_equal(true, @monitor.try_enter)
167
+ Thread.current.kill
168
+ @monitor.exit
169
+ }
170
+ th.join
171
+ assert_equal(true, @monitor.try_enter)
172
+ @monitor.exit
173
+ th2 = Thread.new {
174
+ assert_equal(true, @monitor.try_enter)
175
+ @monitor.exit
176
+ }
177
+ assert_join_threads([th, th2])
178
+ end
179
+
180
+ def test_mon_locked_and_owned
181
+ queue1 = Thread::Queue.new
182
+ queue2 = Thread::Queue.new
183
+ th = Thread.new {
184
+ @monitor.enter
185
+ queue1.enq(nil)
186
+ queue2.deq
187
+ @monitor.exit
188
+ queue1.enq(nil)
189
+ }
190
+ queue1.deq
191
+ assert(@monitor.mon_locked?)
192
+ assert(!@monitor.mon_owned?)
193
+
194
+ queue2.enq(nil)
195
+ queue1.deq
196
+ assert(!@monitor.mon_locked?)
197
+
198
+ @monitor.enter
199
+ assert @monitor.mon_locked?
200
+ assert @monitor.mon_owned?
201
+ @monitor.exit
202
+
203
+ @monitor.synchronize do
204
+ assert @monitor.mon_locked?
205
+ assert @monitor.mon_owned?
206
+ end
207
+ ensure
208
+ th.join
209
+ end
210
+
211
+ def test_cond
212
+ cond = @monitor.new_cond
213
+
214
+ a = "foo"
215
+ queue1 = Thread::Queue.new
216
+ th = Thread.new do
217
+ queue1.deq
218
+ @monitor.synchronize do
219
+ a = "bar"
220
+ cond.signal
221
+ end
222
+ end
223
+ th2 = Thread.new do
224
+ @monitor.synchronize do
225
+ queue1.enq(nil)
226
+ assert_equal("foo", a)
227
+ result1 = cond.wait
228
+ assert_equal(true, result1)
229
+ assert_equal("bar", a)
230
+ end
231
+ end
232
+ assert_join_threads([th, th2])
233
+ end
234
+
235
+ class NewCondTest
236
+ include ::MonitorMixin
237
+ attr_reader :cond
238
+ def initialize
239
+ @cond = new_cond
240
+ super # mon_initialize
241
+ end
242
+ end
243
+
244
+ def test_new_cond_before_initialize
245
+ assert NewCondTest.new.cond.instance_variable_get(:@monitor) != nil
246
+ end
247
+
248
+ class KeywordInitializeParent
249
+ def initialize(x:)
250
+ end
251
+ end
252
+
253
+ class KeywordInitializeChild < KeywordInitializeParent
254
+ include ::MonitorMixin
255
+ def initialize
256
+ super(x: 1)
257
+ end
258
+ end
259
+
260
+ def test_initialize_with_keyword_arg
261
+ assert KeywordInitializeChild.new
262
+ end
263
+
264
+ def test_timedwait
265
+ cond = @monitor.new_cond
266
+ b = "foo"
267
+ queue2 = Thread::Queue.new
268
+ th = Thread.new do
269
+ queue2.deq
270
+ @monitor.synchronize do
271
+ b = "bar"
272
+ cond.signal
273
+ end
274
+ end
275
+ result2 = nil
276
+ @monitor.synchronize do
277
+ queue2.enq(nil)
278
+ assert_equal("foo", b)
279
+ result2 = cond.wait(0.1)
280
+ assert_equal(true, result2)
281
+ assert_equal("bar", b)
282
+ end
283
+ th.join
284
+
285
+ c = "foo"
286
+ queue3 = Thread::Queue.new
287
+ th = Thread.new do
288
+ queue3.deq
289
+ @monitor.synchronize do
290
+ c = "bar"
291
+ cond.signal
292
+ end
293
+ end
294
+ th2 = Thread.new do
295
+ @monitor.synchronize do
296
+ assert_equal("foo", c)
297
+ result3 = cond.wait(0.1)
298
+ assert_equal(false, result3)
299
+ assert_equal("foo", c)
300
+ queue3.enq(nil)
301
+ result4 = cond.wait
302
+ assert_equal(true, result4)
303
+ assert_equal("bar", c)
304
+ end
305
+ end
306
+ assert_join_threads([th, th2])
307
+
308
+ # d = "foo"
309
+ # cumber_thread = Thread.new {
310
+ # loop do
311
+ # @monitor.synchronize do
312
+ # d = "foo"
313
+ # end
314
+ # end
315
+ # }
316
+ # queue3 = Thread::Queue.new
317
+ # Thread.new do
318
+ # queue3.pop
319
+ # @monitor.synchronize do
320
+ # d = "bar"
321
+ # cond.signal
322
+ # end
323
+ # end
324
+ # @monitor.synchronize do
325
+ # queue3.enq(nil)
326
+ # assert_equal("foo", d)
327
+ # result5 = cond.wait
328
+ # assert_equal(true, result5)
329
+ # # this thread has priority over cumber_thread
330
+ # assert_equal("bar", d)
331
+ # end
332
+ # cumber_thread.kill
333
+ end
334
+
335
+ def test_wait_interruption
336
+ cond = @monitor.new_cond
337
+
338
+ th = Thread.new {
339
+ @monitor.synchronize do
340
+ begin
341
+ cond.wait(0.1)
342
+ @monitor.mon_owned?
343
+ rescue Interrupt
344
+ @monitor.mon_owned?
345
+ end
346
+ end
347
+ }
348
+ sleep(0.1)
349
+ th.raise(Interrupt)
350
+
351
+ begin
352
+ assert_equal true, th.value
353
+ rescue Interrupt
354
+ end
355
+ end
356
+ end