polyphony 0.45.0 → 0.46.0

Sign up to get free protection for your applications and to get access to all the features.
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
- )