polyphony 0.45.0 → 0.46.0

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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +2 -0
  3. data/.gitmodules +0 -0
  4. data/.rubocop.yml +1 -0
  5. data/CHANGELOG.md +38 -0
  6. data/Gemfile.lock +11 -3
  7. data/README.md +3 -3
  8. data/Rakefile +1 -1
  9. data/TODO.md +10 -18
  10. data/examples/adapters/redis_client.rb +3 -1
  11. data/examples/adapters/redis_pubsub_perf.rb +11 -8
  12. data/examples/adapters/sequel_mysql.rb +1 -1
  13. data/examples/adapters/sequel_pg.rb +24 -0
  14. data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
  15. data/examples/core/{xx-channels.rb → channels.rb} +0 -0
  16. data/examples/core/deferring-an-operation.rb +16 -0
  17. data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
  18. data/examples/core/{xx-forking.rb → forking.rb} +1 -1
  19. data/examples/core/handling-signals.rb +11 -0
  20. data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
  21. data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
  22. data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
  23. data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
  24. data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
  25. data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
  26. data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
  27. data/examples/core/supervisor.rb +20 -0
  28. data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
  29. data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
  30. data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
  31. data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
  32. data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
  33. data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
  34. data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
  35. data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
  36. data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
  37. data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
  38. data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
  39. data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
  40. data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
  41. data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
  42. data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
  43. data/examples/io/{xx-irb.rb → irb.rb} +0 -0
  44. data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
  45. data/examples/io/{xx-open.rb → open.rb} +0 -0
  46. data/examples/io/{xx-pry.rb → pry.rb} +0 -0
  47. data/examples/io/{xx-rack_server.rb → rack_server.rb} +0 -0
  48. data/examples/io/raw.rb +14 -0
  49. data/examples/io/reline.rb +18 -0
  50. data/examples/io/{xx-system.rb → system.rb} +1 -1
  51. data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
  52. data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
  53. data/examples/io/tunnel.rb +6 -1
  54. data/examples/io/{xx-zip.rb → zip.rb} +0 -0
  55. data/examples/performance/fiber_transfer.rb +2 -1
  56. data/examples/performance/fs_read.rb +5 -6
  57. data/examples/performance/multi_snooze.rb +0 -1
  58. data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
  59. data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
  60. data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
  61. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
  62. data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -2
  63. data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
  64. data/examples/performance/thread_pool_perf.rb +6 -7
  65. data/ext/liburing/liburing.h +585 -0
  66. data/ext/liburing/liburing/README.md +4 -0
  67. data/ext/liburing/liburing/barrier.h +73 -0
  68. data/ext/liburing/liburing/compat.h +15 -0
  69. data/ext/liburing/liburing/io_uring.h +343 -0
  70. data/ext/liburing/queue.c +333 -0
  71. data/ext/liburing/register.c +187 -0
  72. data/ext/liburing/setup.c +210 -0
  73. data/ext/liburing/syscall.c +54 -0
  74. data/ext/liburing/syscall.h +18 -0
  75. data/ext/polyphony/backend.h +1 -16
  76. data/ext/polyphony/backend_common.h +109 -0
  77. data/ext/polyphony/backend_io_uring.c +884 -0
  78. data/ext/polyphony/backend_io_uring_context.c +73 -0
  79. data/ext/polyphony/backend_io_uring_context.h +52 -0
  80. data/ext/polyphony/{libev_backend.c → backend_libev.c} +255 -345
  81. data/ext/polyphony/event.c +1 -1
  82. data/ext/polyphony/extconf.rb +31 -13
  83. data/ext/polyphony/fiber.c +111 -27
  84. data/ext/polyphony/libev.c +4 -0
  85. data/ext/polyphony/libev.h +8 -2
  86. data/ext/polyphony/liburing.c +8 -0
  87. data/ext/polyphony/playground.c +51 -0
  88. data/ext/polyphony/polyphony.c +6 -8
  89. data/ext/polyphony/polyphony.h +29 -25
  90. data/ext/polyphony/polyphony_ext.c +13 -6
  91. data/ext/polyphony/queue.c +3 -4
  92. data/ext/polyphony/ring_buffer.c +0 -1
  93. data/ext/polyphony/runqueue.c +102 -0
  94. data/ext/polyphony/runqueue_ring_buffer.c +85 -0
  95. data/ext/polyphony/runqueue_ring_buffer.h +31 -0
  96. data/ext/polyphony/thread.c +45 -92
  97. data/lib/polyphony.rb +2 -2
  98. data/lib/polyphony/adapters/fs.rb +1 -1
  99. data/lib/polyphony/adapters/process.rb +0 -3
  100. data/lib/polyphony/adapters/redis.rb +1 -1
  101. data/lib/polyphony/adapters/trace.rb +2 -2
  102. data/lib/polyphony/core/global_api.rb +9 -12
  103. data/lib/polyphony/core/sync.rb +6 -2
  104. data/lib/polyphony/extensions/core.rb +6 -24
  105. data/lib/polyphony/extensions/debug.rb +13 -0
  106. data/lib/polyphony/extensions/fiber.rb +21 -44
  107. data/lib/polyphony/extensions/io.rb +55 -10
  108. data/lib/polyphony/extensions/socket.rb +70 -12
  109. data/lib/polyphony/version.rb +1 -1
  110. data/polyphony.gemspec +3 -2
  111. data/test/helper.rb +36 -4
  112. data/test/io_uring_test.rb +55 -0
  113. data/test/stress.rb +5 -2
  114. data/test/test_backend.rb +4 -6
  115. data/test/test_ext.rb +1 -2
  116. data/test/test_fiber.rb +31 -24
  117. data/test/test_global_api.rb +58 -31
  118. data/test/test_io.rb +58 -0
  119. data/test/test_signal.rb +11 -8
  120. data/test/test_socket.rb +17 -0
  121. data/test/test_sync.rb +21 -0
  122. data/test/test_throttler.rb +3 -6
  123. data/test/test_trace.rb +7 -5
  124. metadata +86 -76
  125. data/examples/adapters/concurrent-ruby.rb +0 -9
  126. data/examples/core/04-handling-signals.rb +0 -19
  127. data/examples/core/xx-at_exit.rb +0 -29
  128. data/examples/core/xx-backend.rb +0 -102
  129. data/examples/core/xx-caller.rb +0 -12
  130. data/examples/core/xx-daemon.rb +0 -14
  131. data/examples/core/xx-deadlock.rb +0 -8
  132. data/examples/core/xx-deferring-an-operation.rb +0 -14
  133. data/examples/core/xx-exception-backtrace.rb +0 -40
  134. data/examples/core/xx-fork-cleanup.rb +0 -22
  135. data/examples/core/xx-fork-spin.rb +0 -42
  136. data/examples/core/xx-fork-terminate.rb +0 -27
  137. data/examples/core/xx-move_on.rb +0 -23
  138. data/examples/core/xx-queue-async.rb +0 -120
  139. data/examples/core/xx-readpartial.rb +0 -18
  140. data/examples/core/xx-signals.rb +0 -16
  141. data/examples/core/xx-sleep-forever.rb +0 -9
  142. data/examples/core/xx-sleeping.rb +0 -25
  143. data/examples/core/xx-snooze-starve.rb +0 -16
  144. data/examples/core/xx-spin-fork.rb +0 -49
  145. data/examples/core/xx-state-machine.rb +0 -51
  146. data/examples/core/xx-stop.rb +0 -20
  147. data/examples/core/xx-supervisors.rb +0 -21
  148. data/examples/core/xx-thread-selector-sleep.rb +0 -51
  149. data/examples/core/xx-thread-selector-snooze.rb +0 -46
  150. data/examples/core/xx-thread-snooze.rb +0 -34
  151. data/examples/core/xx-timer-gc.rb +0 -17
  152. data/examples/core/xx-trace.rb +0 -79
  153. data/examples/performance/xx-array.rb +0 -11
  154. data/examples/performance/xx-fiber-switch.rb +0 -9
  155. data/examples/performance/xx-snooze.rb +0 -15
  156. data/examples/xx-spin.rb +0 -32
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
- require 'concurrent'
6
-
7
- puts "Hello, concurrent-ruby"
8
-
9
- # this program should not hang with concurrent-ruby 1.1.6 (see issue #22)
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- # trap('TERM') do
7
- # Polyphony.emit_signal_exception(::SystemExit)
8
- # end
9
-
10
- # trap('INT') do
11
- # Polyphony.emit_signal_exception(::Interrupt)
12
- # end
13
-
14
- puts "go to sleep"
15
- begin
16
- sleep
17
- ensure
18
- puts "done sleeping"
19
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- Exception.__disable_sanitized_backtrace__ = true
7
-
8
- child_pid = Polyphony.fork do
9
- at_exit do
10
- puts "at_exit"
11
- f = spin { sleep 10 }
12
- trap('SIGINT') { f.stop }
13
- f.await
14
- end
15
-
16
- f1 = spin { sleep 100 }
17
-
18
- puts "pid: #{Process.pid}"
19
-
20
- pid = Process.pid
21
-
22
- f1.join
23
- end
24
-
25
- sleep 0.1
26
- Process.kill('INT', child_pid)
27
- sleep 0.1
28
- Process.kill('INT', child_pid)
29
- Process.wait(child_pid)
@@ -1,102 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- Exception.__disable_sanitized_backtrace__ = true
7
-
8
- class Test
9
- def test_sleep
10
- puts "going to sleep"
11
- sleep 1
12
- puts "done sleeping"
13
- end
14
-
15
- def test_spin
16
- spin {
17
- 10.times {
18
- STDOUT << '.'
19
- sleep 0.1
20
- }
21
- }
22
-
23
- puts "going to sleep\n"
24
- sleep 1
25
- puts 'woke up'
26
- end
27
-
28
- def test_file
29
- f = File.open(__FILE__, 'r')
30
- puts Thread.current.backend.read(f, +'', 10000, true)
31
-
32
- Thread.current.backend.write(STDOUT, "Write something: ")
33
- str = +''
34
- Thread.current.backend.read(STDIN, str, 5, false)
35
- puts str
36
- end
37
-
38
- def test_fork
39
- pid = fork do
40
- Thread.current.backend.post_fork
41
- puts 'child going to sleep'
42
- sleep 1
43
- puts 'child done sleeping'
44
- exit(42)
45
- end
46
-
47
- puts "Waiting for pid #{pid}"
48
- result = Thread.current.backend.waitpid(pid)
49
- puts "Done waiting"
50
- p result
51
- end
52
-
53
- def test_async
54
- async = Polyphony::Event.new
55
-
56
- spin {
57
- puts "signaller starting"
58
- sleep 1
59
- puts "signal"
60
- async.signal(:foo)
61
- }
62
-
63
- puts "awaiting event"
64
- p async.await
65
- end
66
-
67
- def test_queue
68
- q = Gyro::Queue.new
69
- spin {
70
- 10.times {
71
- q << Time.now.to_f
72
- sleep 0.2
73
- }
74
- q << :STOP
75
- }
76
-
77
- loop do
78
- value = q.shift
79
- break if value == :STOP
80
-
81
- p value
82
- end
83
- end
84
-
85
- def test_thread
86
- t = Thread.new do
87
- puts "thread going to sleep"
88
- sleep 0.2
89
- puts "thread done sleeping"
90
- end
91
-
92
- t.await
93
- end
94
- end
95
-
96
- t = Test.new
97
-
98
- t.methods.select { |m| m =~ /^test_/ }.each do |m|
99
- puts '*' * 40
100
- puts m
101
- t.send(m)
102
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- spin {
7
- spin {
8
- spin {
9
- pp Fiber.current.caller
10
- }.await
11
- }.await
12
- }.await
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- Exception.__disable_sanitized_backtrace__ = true
7
-
8
- puts "pid: #{Process.pid}"
9
-
10
- Process.daemon(true, true)
11
-
12
- Polyphony::ThreadPool.process do
13
- puts "Hello world from pid #{Process.pid}"
14
- end
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- spin { sleep }
7
-
8
- sleep
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- spin do
7
- puts 'two'
8
- spin { puts 'four' }
9
- puts 'three'
10
- end
11
-
12
- puts 'one'
13
-
14
- suspend
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
-
5
- class E < Exception
6
- def initialize(msg)
7
- super
8
- # set_backtrace(caller)
9
- end
10
-
11
- alias_method :orig_backtrace, :backtrace
12
- def backtrace
13
- b = orig_backtrace
14
- p [:backtrace, b, caller]
15
- b
16
- end
17
- end
18
-
19
- def e1
20
- e2
21
- end
22
-
23
- def e2
24
- E.new('foo')
25
- end
26
-
27
- def e3
28
- raise E, 'bar'
29
- end
30
-
31
- e = e1
32
- p e
33
- puts e.backtrace&.join("\n")
34
-
35
- begin
36
- e3
37
- rescue Exception => e
38
- p e
39
- puts e.backtrace.join("\n")
40
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- Exception.__disable_sanitized_backtrace__ = true
7
-
8
- t = Thread.new do
9
- async = Gyro::Async.new
10
- spin { async.await }
11
- sleep 100
12
- end
13
-
14
- sleep 0.5
15
-
16
- Polyphony.fork do
17
- puts "forked #{Process.pid}"
18
- sleep 1
19
- puts "done sleeping"
20
- end
21
-
22
- sleep 50
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- Exception.__disable_sanitized_backtrace__ = true
7
-
8
- puts "Parent pid: #{Process.pid}"
9
-
10
- i, o = IO.pipe
11
-
12
- pid = Polyphony.fork do
13
- puts "Child pid: #{Process.pid}"
14
- i.close
15
- spin do
16
- spin do
17
- p :sleep
18
- sleep 1
19
- rescue ::Interrupt => e
20
- p 1
21
- # the signal should be raised only in the main fiber
22
- o.puts "1-interrupt"
23
- end.await
24
- rescue Polyphony::Terminate
25
- puts "terminate!"
26
- end.await
27
- rescue ::Interrupt => e
28
- p 2
29
- o.puts "3-interrupt"
30
- ensure
31
- p 3
32
- o.close
33
- end
34
- sleep 0.2
35
- o.close
36
- watcher = Gyro::Child.new(pid)
37
- Process.kill('INT', pid)
38
- watcher.await
39
- buffer = i.read
40
-
41
- puts '*' * 40
42
- p buffer
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- Exception.__disable_sanitized_backtrace__ = true
7
-
8
- pid = Polyphony.fork do
9
- f = spin do
10
- p 1
11
- sleep 1
12
- p 2
13
- ensure
14
- p 2.5
15
- end
16
- p 3
17
- snooze
18
- p 4
19
- # f.stop
20
- # f.join
21
- # Fiber.current.terminate_all_children
22
- # Fiber.current.await_all_children
23
- p 5
24
- end
25
-
26
- puts "Child pid: #{pid}"
27
- Process.wait(pid)
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
- Exception.__disable_sanitized_backtrace__ = true
6
-
7
- puts 'going to sleep...'
8
- move_on_after(1) do
9
- sleep 60
10
- puts 'woke up'
11
- end
12
-
13
- puts 'going to sleep...'
14
- move_on_after(0.5) do
15
- t0 = Time.now
16
- sleep(60)
17
- ensure
18
- puts 'woke up'
19
- end
20
-
21
- puts 'going to sleep...'
22
- value = move_on_after(1, with_value: :bar) { sleep 60 }
23
- puts "got value #{value.inspect}"
@@ -1,120 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- Thread.event_selector = ->(thread) { Gyro::Selector.new(thread) }
7
-
8
- def bm(sym, x)
9
- t0 = Time.now
10
- send(sym, x)
11
- elapsed = Time.now - t0
12
- STDOUT.orig_puts "#{sym} #{x / elapsed}"
13
- end
14
-
15
- def test_queue(x)
16
- queue = Queue.new
17
- async = Gyro::Async.new
18
- t1 = Thread.new do
19
- Thread.current.setup_fiber_scheduling
20
- counter = 0
21
- loop {
22
- async.await if queue.empty?
23
- v = queue.pop
24
- counter += 1
25
- break if counter == x
26
- }
27
- ensure
28
- Thread.current.stop_event_selector
29
- end
30
- t2 = Thread.new do
31
- Thread.current.setup_fiber_scheduling
32
- x.times { |i|
33
- queue.push i
34
- async.signal!
35
- }
36
- ensure
37
- Thread.current.stop_event_selector
38
- end
39
- t1.join
40
- t2.join
41
- end
42
-
43
- def test_array_mutex(x)
44
- queue = []
45
- mutex = Mutex.new
46
- async = Gyro::Async.new
47
- t1 = Thread.new {
48
- Thread.current.setup_fiber_scheduling
49
- counter = 0
50
- loop {
51
- async.await if mutex.synchronize { queue.empty? }
52
- v = mutex.synchronize { queue.shift }
53
- counter += 1
54
- break if counter == x
55
- }
56
- }
57
- t2 = Thread.new {
58
- Thread.current.setup_fiber_scheduling
59
- x.times { |i|
60
- mutex.synchronize { queue.push i }
61
- async.signal!
62
- }
63
- }
64
- t1.join
65
- t2.join
66
- end
67
-
68
- # class Gyro::Queue
69
- # def initialize
70
- # @wait_queue = []
71
- # @queue = []
72
- # end
73
-
74
- # def <<(value)
75
- # async = @wait_queue.pop
76
- # if async
77
- # async.signal! value
78
- # else
79
- # @queue.push value
80
- # end
81
- # end
82
-
83
- # def shift
84
- # if @queue.empty?
85
- # async = Gyro::Async.new
86
- # @wait_queue << async
87
- # async.await
88
- # else
89
- # @queue.shift
90
- # end
91
- # end
92
- # end
93
-
94
- def test_gyro_queue(x)
95
- queue = Gyro::Queue.new
96
- x.times { |i| queue << i }
97
- t1 = Thread.new do
98
- Thread.current.setup_fiber_scheduling
99
- x.times { queue.shift }
100
- ensure
101
- Thread.current.stop_event_selector
102
- end
103
- t2 = Thread.new do
104
- Thread.current.setup_fiber_scheduling
105
- x.times { |i| queue << i }
106
- ensure
107
- Thread.current.stop_event_selector
108
- end
109
- t1.join
110
- t2.join
111
- end
112
-
113
- Thread.current.setup_fiber_scheduling
114
- # bm(:test_array_mutex, 1000000)
115
- loop {
116
- STDOUT.orig_puts "*" * 40
117
- bm(:test_queue, 1000000)
118
- bm(:test_gyro_queue, 1000000)
119
- }
120
-