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,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- Exception.__disable_sanitized_backtrace__ = true
7
-
8
- buffer = +''
9
- socket = TCPSocket.new('google.com', 80)
10
- socket.send("GET /?q=time HTTP/1.1\r\nHost: google.com\r\n\r\n", 0)
11
- move_on_after(5) {
12
- while (data = socket.readpartial(8192))
13
- buffer << data
14
- end
15
- }
16
-
17
- puts "*" * 40
18
- p buffer
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- waiter = spin do
7
- puts 'Waiting for HUP'
8
- Polyphony.wait_for_signal('SIGHUP')
9
- puts 'Got HUP'
10
- end
11
-
12
- sleep 1
13
- puts 'Sending HUP'
14
- Process.kill('SIGHUP', Process.pid)
15
-
16
- suspend
@@ -1,9 +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 "press Ctrl-C!"
9
- sleep
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- Exception.__disable_sanitized_backtrace__ = true
7
-
8
- # spin {
9
- # 10.times {
10
- # STDOUT << '.'
11
- # sleep 0.1
12
- # }
13
- # }
14
-
15
- puts 'going to sleep...'
16
- sleep 1
17
- puts 'woke up'
18
-
19
- counter = 0
20
- t = Polyphony::Throttler.new(5)
21
- t.process do
22
- p counter
23
- counter += 1
24
- t.stop if counter > 5
25
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- Exception.__disable_sanitized_backtrace__ = true
7
-
8
- f = spin_loop {
9
- snooze
10
- }
11
-
12
- puts 'going to sleep...'
13
- sleep 1
14
- puts 'woke up'
15
- f.stop
16
-
@@ -1,49 +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
- def start_worker
11
- Polyphony.fork do
12
- p :sleep
13
- sleep 5
14
- p :done_sleeping
15
- ensure
16
- p :start_worker_fork_ensure
17
- end
18
- end
19
-
20
- f = spin do
21
- Polyphony::ProcessSupervisor.supervise do
22
- spin do
23
- spin do
24
- p :sleep
25
- sleep 5
26
- p :done_sleeping
27
- end.await
28
- end.await
29
- ensure
30
- p :start_worker_fork_ensure
31
- end
32
- # spin do
33
- # pid = start_worker
34
- # p [:before_child_await, pid]
35
- # Gyro::Child.new(pid).await
36
- # p :after_child_await
37
- # ensure
38
- # puts "child done"
39
- # end
40
- # supervise
41
- # ensure
42
- # puts "kill child"
43
- # Process.kill('TERM', pid) rescue nil
44
- end
45
-
46
- sleep 1
47
- puts "terminate worker"
48
- f.terminate
49
- f.await
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # require 'bundler/setup'
4
- # require 'polyphony'
5
-
6
- require 'fiber'
7
-
8
- class StateMachine < ::Fiber
9
- def initialize(state, rules)
10
- @state = state
11
- @rules = rules
12
- super() { |input| state_loop(input) }
13
- end
14
-
15
- attr_reader :state
16
- def state_loop(input)
17
- loop do
18
- @state = apply(input)
19
- input = Fiber.yield(@state)
20
- end
21
- end
22
-
23
- def apply(input)
24
- f = @rules[@state][input]
25
- return f.(@state) if f.is_a?(Proc)
26
-
27
- raise 'Invalid input'
28
- rescue => e
29
- @state
30
- end
31
-
32
- def transition(input)
33
- state = self.resume(input)
34
- # state.is_a?(Exception) ? (raise state) : state
35
- end
36
- end
37
-
38
- o = StateMachine.new(
39
- :off,
40
- {
41
- off: { turnon: ->(s) { :on } },
42
- on: { turnoff: ->(s) { :off } }
43
- }
44
- )
45
-
46
- loop do
47
- STDOUT << "#{o.state}: "
48
- input = gets.strip.to_sym
49
- # puts " command: #{input.inspect}"
50
- o.transition(input)
51
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- f1 = spin do
7
- f2 = spin { sleep 60 }
8
- f3 = spin { sleep 60 }
9
- sleep 60
10
- ensure
11
- p 1
12
- f2.stop
13
- p 2
14
- f3.stop
15
- p "should reach here!"
16
- end
17
-
18
- sleep 0.1
19
- f1.stop
20
- snooze
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- def my_sleep(t)
7
- puts "#{t} start"
8
- sleep(t)
9
- puts "#{t} done"
10
- end
11
-
12
- puts "#{Time.now} waiting..."
13
- supervise do |s|
14
- s.spin { my_sleep(1) }
15
- s.spin { my_sleep(2) }
16
- s.spin { my_sleep(3) }
17
- s.spin do
18
- puts "fiber count: #{Fiber.count}"
19
- end
20
- end
21
- puts "#{Time.now} done waiting"
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- # Thread.event_selector = Gyro::Selector
7
- # Thread.current.setup_fiber_scheduling
8
-
9
- t = Gyro::Timer.new(1, 1)
10
- s = spin {
11
- last = Time.now
12
- loop do
13
- t.await
14
- now = Time.now
15
- puts "elapsed: #{now - last}"
16
- last = now
17
- end
18
- }
19
- s.await
20
- exit!
21
-
22
- p :go_to_sleep
23
- sleep 1
24
- p :wake_up
25
-
26
- puts "*" * 60
27
-
28
- t = Thread.new {
29
- Thread.current.setup_fiber_scheduling
30
-
31
- spin {
32
- p :go_to_sleep1
33
- sleep 1
34
- p :wake_up1
35
- }
36
-
37
- spin {
38
- p :go_to_sleep2
39
- sleep 2
40
- p :wake_up2
41
- }
42
-
43
- p :waiting
44
- suspend
45
- }
46
-
47
- t.join
48
-
49
- at_exit {
50
- p :at_exit
51
- }
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- Exception.__disable_sanitized_backtrace__ = true
7
- Thread.event_selector = Gyro::Selector
8
- Thread.current.setup_fiber_scheduling
9
-
10
- def bm(fibers, iterations)
11
- count = {}
12
-
13
- t0 = Time.now
14
- threads = (1..1).map do |i|
15
- Thread.new do
16
- count[i] = 0
17
- supervise do |s|
18
- fibers.times do
19
- s.spin do
20
- iterations.times do
21
- snooze
22
- count[i] += 1
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
29
- threads.each(&:join)
30
- dt = Time.now - t0
31
- count = count.values.inject(0, &:+)
32
- puts "#{[fibers, iterations].inspect} count: #{count} #{count / dt.to_f}/s"
33
- end
34
-
35
- # GC.disable
36
-
37
- loop {
38
- puts "*" * 60
39
- bm(1, 1_000_000)
40
- bm(10, 100_000)
41
- bm(100, 10_000)
42
- bm(1_000, 1_000)
43
- bm(10_000, 100)
44
- bm(100_000, 10)
45
- # bm(1_000_000, 1)
46
- }
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- Exception.__disable_sanitized_backtrace__ = true
7
-
8
- def work
9
- puts "creating fibers..."
10
- 100000.times {
11
- spin {
12
- loop { snooze }
13
- }
14
- }
15
-
16
- puts "done"
17
- suspend
18
- end
19
-
20
- def work_thread
21
- t = Thread.new { work }
22
- t.join
23
- end
24
-
25
- main = Fiber.current
26
- p [:main, main]
27
-
28
- # trap('SIGINT') do
29
- # p [:SIGINT, Fiber.current]
30
- # p caller
31
- # exit!
32
- # end
33
-
34
- work
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- Exception.__disable_sanitized_backtrace__ = true
7
-
8
- timers = 10.times.map do
9
- spin do
10
- t = Gyro::Timer.new(1, 1)
11
- t.await
12
- end
13
- end
14
-
15
- sleep 0.1
16
- GC.start
17
- sleep 0.1
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/setup'
4
- require 'polyphony'
5
-
6
- Exception.__disable_sanitized_backtrace__ = true
7
-
8
-
9
- sleep 0
10
- $records = []
11
-
12
- Gyro.trace(true)
13
- trace = Polyphony::Trace.new { |r| $records << r }
14
- trace.enable
15
-
16
- f2 = spin(:f2) { 3.times { sleep 0.1 } }
17
-
18
- 10.times {
19
- spin { 3.times { sleep rand(0.05..0.15) } }
20
- }
21
-
22
- suspend
23
- trace.disable
24
- puts("record count: %d" % $records.size)
25
-
26
- analysis = Polyphony::Trace.analyze $records
27
-
28
- puts("fiber count: %d" % analysis[:by_fiber].size)
29
- puts
30
-
31
- worker_fibers = analysis[:by_fiber].keys - [Fiber.current]
32
-
33
- analysis[:by_fiber][f2].each { |r|
34
- case r[:event]
35
- when /^fiber_/
36
- STDOUT.orig_puts "#{r[:stamp]} #{r[:event]} (#{r[:value].inspect})"
37
- else
38
- STDOUT.orig_puts "#{r[:stamp]} #{r[:fiber]&.tag} #{r[:event]} (#{r[:value].inspect})"
39
- end
40
- }
41
-
42
- state = 0
43
- run_wait_stamp = nil
44
- schedule_stamp = nil
45
- run_time = 0
46
- wait_time = 0
47
- schedule_count = 0
48
- schedule_acc = 0
49
- worker_fibers.each do |f|
50
- analysis[:by_fiber][f].each { |r|
51
- case r[:event]
52
- when :fiber_create
53
- state = 0
54
- run_wait_stamp = r[:stamp]
55
- when :fiber_schedule
56
- schedule_count += 1
57
- schedule_stamp = r[:stamp]
58
- when :fiber_run
59
- schedule_acc += r[:stamp] - schedule_stamp
60
- wait_time += r[:stamp] - run_wait_stamp
61
- state = 1
62
- schedule_stamp = run_wait_stamp = r[:stamp]
63
- when :fiber_switchpoint, :fiber_terminate
64
- run_time += r[:stamp] - run_wait_stamp
65
- state = 0
66
- run_wait_stamp = r[:stamp]
67
- end
68
- }
69
- end
70
-
71
- puts(
72
- format(
73
- "f2 run: %f wait: %f schedule_count: %d avg schedule latency: %f",
74
- run_time,
75
- wait_time,
76
- schedule_count,
77
- schedule_acc / schedule_count
78
- )
79
- )