polyphony 0.45.0 → 0.45.1

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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/CHANGELOG.md +10 -0
  4. data/Gemfile.lock +9 -1
  5. data/TODO.md +6 -7
  6. data/examples/adapters/redis_client.rb +3 -1
  7. data/examples/adapters/redis_pubsub_perf.rb +11 -8
  8. data/examples/adapters/sequel_mysql.rb +1 -1
  9. data/examples/adapters/sequel_pg.rb +24 -0
  10. data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
  11. data/examples/core/{xx-channels.rb → channels.rb} +0 -0
  12. data/examples/core/deferring-an-operation.rb +16 -0
  13. data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
  14. data/examples/core/{xx-forking.rb → forking.rb} +1 -1
  15. data/examples/core/handling-signals.rb +11 -0
  16. data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
  17. data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
  18. data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
  19. data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
  20. data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
  21. data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
  22. data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
  23. data/examples/core/supervisor.rb +20 -0
  24. data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
  25. data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
  26. data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
  27. data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
  28. data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
  29. data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
  30. data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
  31. data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
  32. data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
  33. data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
  34. data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
  35. data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
  36. data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
  37. data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
  38. data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
  39. data/examples/io/{xx-irb.rb → irb.rb} +0 -0
  40. data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
  41. data/examples/io/{xx-open.rb → open.rb} +0 -0
  42. data/examples/io/{xx-pry.rb → pry.rb} +0 -0
  43. data/examples/io/{xx-rack_server.rb → rack_server.rb} +0 -0
  44. data/examples/io/{xx-system.rb → system.rb} +1 -1
  45. data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
  46. data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
  47. data/examples/io/tunnel.rb +6 -1
  48. data/examples/io/{xx-zip.rb → zip.rb} +0 -0
  49. data/examples/performance/fiber_transfer.rb +2 -1
  50. data/examples/performance/fs_read.rb +5 -6
  51. data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
  52. data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
  53. data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
  54. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
  55. data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
  56. data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
  57. data/examples/performance/thread_pool_perf.rb +6 -7
  58. data/ext/polyphony/backend.h +0 -1
  59. data/ext/polyphony/libev_backend.c +69 -68
  60. data/ext/polyphony/polyphony.c +0 -2
  61. data/ext/polyphony/polyphony.h +0 -13
  62. data/ext/polyphony/polyphony_ext.c +1 -2
  63. data/ext/polyphony/queue.c +3 -4
  64. data/ext/polyphony/ring_buffer.c +0 -1
  65. data/ext/polyphony/thread.c +3 -4
  66. data/lib/polyphony/adapters/fs.rb +1 -1
  67. data/lib/polyphony/adapters/redis.rb +1 -1
  68. data/lib/polyphony/core/global_api.rb +4 -4
  69. data/lib/polyphony/core/sync.rb +11 -9
  70. data/lib/polyphony/extensions/core.rb +1 -6
  71. data/lib/polyphony/extensions/io.rb +40 -6
  72. data/lib/polyphony/extensions/socket.rb +11 -2
  73. data/lib/polyphony/version.rb +1 -1
  74. data/polyphony.gemspec +2 -1
  75. data/test/test_io.rb +16 -0
  76. data/test/test_socket.rb +17 -0
  77. data/test/test_throttler.rb +1 -0
  78. metadata +58 -72
  79. data/examples/adapters/concurrent-ruby.rb +0 -9
  80. data/examples/core/04-handling-signals.rb +0 -19
  81. data/examples/core/xx-at_exit.rb +0 -29
  82. data/examples/core/xx-backend.rb +0 -102
  83. data/examples/core/xx-caller.rb +0 -12
  84. data/examples/core/xx-daemon.rb +0 -14
  85. data/examples/core/xx-deadlock.rb +0 -8
  86. data/examples/core/xx-deferring-an-operation.rb +0 -14
  87. data/examples/core/xx-exception-backtrace.rb +0 -40
  88. data/examples/core/xx-fork-cleanup.rb +0 -22
  89. data/examples/core/xx-fork-spin.rb +0 -42
  90. data/examples/core/xx-fork-terminate.rb +0 -27
  91. data/examples/core/xx-move_on.rb +0 -23
  92. data/examples/core/xx-queue-async.rb +0 -120
  93. data/examples/core/xx-readpartial.rb +0 -18
  94. data/examples/core/xx-signals.rb +0 -16
  95. data/examples/core/xx-sleep-forever.rb +0 -9
  96. data/examples/core/xx-sleeping.rb +0 -25
  97. data/examples/core/xx-snooze-starve.rb +0 -16
  98. data/examples/core/xx-spin-fork.rb +0 -49
  99. data/examples/core/xx-state-machine.rb +0 -51
  100. data/examples/core/xx-stop.rb +0 -20
  101. data/examples/core/xx-supervisors.rb +0 -21
  102. data/examples/core/xx-thread-selector-sleep.rb +0 -51
  103. data/examples/core/xx-thread-selector-snooze.rb +0 -46
  104. data/examples/core/xx-thread-snooze.rb +0 -34
  105. data/examples/core/xx-timer-gc.rb +0 -17
  106. data/examples/core/xx-trace.rb +0 -79
  107. data/examples/performance/xx-array.rb +0 -11
  108. data/examples/performance/xx-fiber-switch.rb +0 -9
  109. data/examples/performance/xx-snooze.rb +0 -15
  110. data/examples/xx-spin.rb +0 -32
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9c8ab74213c6cc5e3852f73ee027ee496f2b04c6844e09856c9771ea5e7839a
4
- data.tar.gz: 83d7c533024b6d6d633b9e18abc392911adfc07f728af826bd84cce35cafb20c
3
+ metadata.gz: 5f54b88851ca73f121975839812e0a1ad5686d017199b83a6365deda2b656d3a
4
+ data.tar.gz: 92671a26215f19e36c0b0f49edb990a6818e2c64d7a8ed8bc9c4f2621a5de170
5
5
  SHA512:
6
- metadata.gz: c42e49ebcd6fb10b384438cb7688801a387963bc2a6fb2c1d3de6d022a7731a7d88db138a34b364b35c9d71830b975a69cb1f14fb277166a9db1802e24e82dec
7
- data.tar.gz: 6c578eacded00dd7a77a35124c69ebc966e4f9aa42264913df82af147f43955a2ecd86087630c085fcd4d6e964aa705202c5aa24bb4032648d3ba278d60d4f63
6
+ metadata.gz: fd3caf340b523eccb1f86b10e06996ec26704ed1906bd82188363fb5142901b585992df8d88d0ca20adc188e51a010144fba8f10843ba79503d2984f63a08a05
7
+ data.tar.gz: 2b44d80a6ac0bf3f0f42ece1b7d2008c3d0b933c1df11be7f698bbc9dae80341bd1d302a1e0ccaa56b223468b607b7f024bbaa949a88ff1c343028d6cd256ba5
@@ -96,6 +96,7 @@ Metrics/ModuleLength:
96
96
  Metrics/ClassLength:
97
97
  Exclude:
98
98
  - lib/polyphony/http/server/http1.rb
99
+ - lib/polyphony/extensions/io.rb
99
100
  - test/**/*.rb
100
101
  - examples/**/*.rb
101
102
 
@@ -1,3 +1,13 @@
1
+ ## 0.45.1
2
+
3
+ * Fix Net::HTTP compatibility
4
+ * Fix fs adapter
5
+ * Improve performance of IO#puts
6
+ * Mutex#synchronize
7
+ * Fix Socket#connect
8
+ * Cleanup code
9
+ * Improve support for Ruby 3 keyword args
10
+
1
11
  ## 0.45.0
2
12
 
3
13
  * Cleanup code
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.45.0)
4
+ polyphony (0.45.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -23,6 +23,9 @@ GEM
23
23
  forwardable-extended (2.6.0)
24
24
  hiredis (0.6.3)
25
25
  http_parser.rb (0.6.0)
26
+ httparty (0.17.1)
27
+ mime-types (~> 3.0)
28
+ multi_xml (>= 0.5.2)
26
29
  i18n (0.9.5)
27
30
  concurrent-ruby (~> 1.0)
28
31
  jekyll (3.8.6)
@@ -60,12 +63,16 @@ GEM
60
63
  rb-inotify (~> 0.9, >= 0.9.10)
61
64
  mercenary (0.3.6)
62
65
  method_source (1.0.0)
66
+ mime-types (3.3.1)
67
+ mime-types-data (~> 3.2015)
68
+ mime-types-data (3.2020.0512)
63
69
  minitest (5.13.0)
64
70
  minitest-reporters (1.4.2)
65
71
  ansi
66
72
  builder
67
73
  minitest (>= 5.0)
68
74
  ruby-progressbar
75
+ multi_xml (0.6.0)
69
76
  mysql2 (0.5.3)
70
77
  parallel (1.19.1)
71
78
  parser (2.7.0.2)
@@ -122,6 +129,7 @@ PLATFORMS
122
129
  DEPENDENCIES
123
130
  hiredis (= 0.6.3)
124
131
  http_parser.rb (~> 0.6.0)
132
+ httparty (= 0.17.1)
125
133
  jekyll (~> 3.8.6)
126
134
  jekyll-remote-theme (~> 0.4.1)
127
135
  jekyll-seo-tag (~> 2.6.1)
data/TODO.md CHANGED
@@ -1,13 +1,12 @@
1
- 0.45
2
-
3
- - Review all code
4
- - Cleanup C code
5
- - Cleanup and annotate examples (and remove all the examples used for
6
- debugging). Focus on examples that serve as "how-to".
7
-
8
1
  0.45.1
9
2
 
10
3
  - Adapter for Pry and IRB (Which fixes #5 and #6)
4
+ - Redesign signal handling - the current mechanism is problematic in that it
5
+ does not address signals that do not kill, for instance HUP or USR1.
6
+ - Improve `#supervise`. It does not work as advertised, and seems to exhibit an
7
+ inconsistent behaviour (see supervisor example).
8
+ - Fix backtrace for `Timeout.timeout` API (see timeout example).
9
+ - Check why worker-thread example doesn't work.
11
10
 
12
11
  0.46.0
13
12
 
@@ -3,7 +3,9 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony/adapters/redis'
5
5
 
6
- redis = Redis.new
6
+ ::Exception.__disable_sanitized_backtrace__ = true
7
+
8
+ redis = Redis.new(host: ENV['REDIS_HOST'] || 'localhost')
7
9
 
8
10
  X = 10
9
11
 
@@ -6,7 +6,7 @@ require 'json'
6
6
 
7
7
  X_SESSIONS = 1000
8
8
  X_NODES = 10_000
9
- X_SUBSCRIPTIONS_PER_SESSION = 100
9
+ X_SUBSCRIPTIONS_PER_SESSION = 1000
10
10
 
11
11
  $sessions = []
12
12
  X_SESSIONS.times do
@@ -17,8 +17,11 @@ X_SESSIONS.times do
17
17
  }
18
18
  end
19
19
 
20
+ REDIS_HOST = ENV['REDIS_HOST'] || 'localhost'
21
+ p redis_host: REDIS_HOST
22
+
20
23
  spin do
21
- redis = Redis.new
24
+ redis = Redis.new(host: REDIS_HOST)
22
25
  redis.subscribe('events') do |on|
23
26
  on.message do |_, message|
24
27
  distribute_event(JSON.parse(message, symbolize_names: true))
@@ -30,18 +33,18 @@ $update_count = 0
30
33
 
31
34
  def distribute_event(event)
32
35
  $update_count += 1
33
- # t0 = Time.now
36
+ t0 = Time.now
34
37
  count = 0
35
38
  $sessions.each do |s|
36
39
  count += 1 if s[:subscriptions].include?(event[:path])
37
40
  end
38
- # elapsed = Time.now - t0
39
- # rate = X_SESSIONS / elapsed
40
- # puts "elapsed: #{elapsed} (#{rate}/s)" if $update_count % 100 == 0
41
+ elapsed = Time.now - t0
42
+ rate = X_SESSIONS / elapsed
43
+ puts "elapsed: #{elapsed} (#{rate}/s)" if $update_count % 100 == 0
41
44
  end
42
45
 
43
46
  spin do
44
- redis = Redis.new
47
+ redis = Redis.new(host: REDIS_HOST)
45
48
  throttled_loop(1000) do
46
49
  redis.publish('events', { path: "node#{rand(X_NODES)}" }.to_json)
47
50
  end
@@ -60,7 +63,7 @@ spin do
60
63
  end
61
64
  end
62
65
 
63
- trap(:int) do
66
+ trap('SIGINT') do
64
67
  puts 'bye...'
65
68
  exit!
66
69
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'bundler/setup'
4
4
  require 'polyphony/adapters/sequel'
5
- require 'polyphony/adapters/mysql2'
5
+ require 'polyphony/adapters/postgres'
6
6
 
7
7
  time_printer = spin do
8
8
  last = Time.now
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/adapters/sequel'
5
+ require 'polyphony/adapters/postgres'
6
+
7
+ URL = ENV['SEQUEL_URL'] || 'postgres://localhost/test'
8
+
9
+ x = 10000
10
+ query_count = 0
11
+
12
+ spin do
13
+ db = Sequel.connect(URL)
14
+ x.times { query_count += 1; db.execute('select 1 as test') }
15
+ end
16
+
17
+ spin do
18
+ db = Sequel.connect(URL)
19
+ x.times { query_count += 1; db.execute('select 2 as test') }
20
+ end
21
+
22
+ t0 = Time.now
23
+ Fiber.current.await_all_children
24
+ puts "query rate: #{query_count / (Time.now - t0)} reqs/s; count = #{query_count}"
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ spin do
7
+ puts 'two'
8
+ # spinning a fiber from the parent fiber allows us to schedule an operation to
9
+ # be performed even after the current fiber is terminated
10
+ Fiber.current.parent.spin { puts 'four' }
11
+ puts 'three'
12
+ end
13
+
14
+ puts 'one'
15
+
16
+ suspend
@@ -3,8 +3,10 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
5
 
6
- class GenServer
7
- def self.start(receiver, *args)
6
+ module GenServer
7
+ module_function
8
+
9
+ def start(receiver, *args)
8
10
  fiber = spin do
9
11
  state = receiver.initial_state(*args)
10
12
  loop do
@@ -14,11 +16,10 @@ class GenServer
14
16
  end
15
17
  end
16
18
  build_api(fiber, receiver)
17
- snooze
18
19
  fiber
19
20
  end
20
21
 
21
- def self.build_api(fiber, receiver)
22
+ def build_api(fiber, receiver)
22
23
  receiver.methods(false).each do |m|
23
24
  if m =~ /!$/
24
25
  fiber.define_singleton_method(m) do |*args|
@@ -32,7 +33,7 @@ class GenServer
32
33
  end
33
34
  end
34
35
 
35
- def self.cast(process, method, *args)
36
+ def cast(process, method, *args)
36
37
  process << {
37
38
  from: Fiber.current,
38
39
  method: method,
@@ -40,7 +41,7 @@ class GenServer
40
41
  }
41
42
  end
42
43
 
43
- def self.call(process, method, *args)
44
+ def call(process, method, *args)
44
45
  process << {
45
46
  from: Fiber.current,
46
47
  method: method,
@@ -50,21 +51,27 @@ class GenServer
50
51
  end
51
52
  end
52
53
 
54
+ # In a generic server the state is not held in an instance variable but rather
55
+ # passed as the first parameter to method calls. The return value of each method
56
+ # is an array consisting of the result and the potentially mutated state.
53
57
  module Map
54
- def self.initial_state(hash = {})
58
+ module_function
59
+
60
+ def initial_state(hash = {})
55
61
  hash
56
62
  end
57
63
 
58
- def self.get(state, key)
64
+ def get(state, key)
59
65
  [state[key], state]
60
66
  end
61
67
 
62
- def self.put!(state, key, value)
68
+ def put!(state, key, value)
63
69
  state[key] = value
64
70
  [:noreply, state]
65
71
  end
66
72
  end
67
73
 
74
+ # start server with initial state
68
75
  map_server = GenServer.start(Map, {foo: :bar})
69
76
 
70
77
  puts 'getting value from map server'
@@ -20,5 +20,5 @@ end
20
20
  puts "got child pid #{pid}"
21
21
 
22
22
  puts 'parent waiting for child'
23
- Gyro::Child.new(pid).await
23
+ Thread.current.backend.waitpid(pid)
24
24
  puts 'parent done waiting'
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ puts "going to sleep (press Ctrl-C to stop)"
7
+ begin
8
+ sleep
9
+ ensure
10
+ puts "done sleeping"
11
+ end
@@ -9,10 +9,12 @@ pong = spin_loop do
9
9
  ping << 'pong'
10
10
  end
11
11
 
12
- ping = spin_loop do
13
- pong << ['ping', Fiber.current]
14
- msg = receive
15
- puts msg
12
+ ping = spin do
13
+ 3.times do
14
+ pong << ['ping', Fiber.current]
15
+ msg = receive
16
+ puts msg
17
+ end
16
18
  end
17
19
 
18
- suspend
20
+ ping.await
@@ -3,7 +3,7 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
5
 
6
- move_on_after(3) do
6
+ move_on_after(3.1) do
7
7
  puts 'Start...'
8
8
  every(1) do
9
9
  puts Time.now
@@ -10,7 +10,7 @@ class Number
10
10
 
11
11
  def greet(other)
12
12
  puts "You are number #{other}, I am number #{@id}"
13
- sleep(0.05 + rand * 0.2)
13
+ sleep rand(0.2..0.3)
14
14
  end
15
15
  end
16
16
 
@@ -25,7 +25,6 @@ def meet(number)
25
25
  end
26
26
  end
27
27
 
28
- 3.times { |x| spin { meet(x) } }
28
+ (4..10).each { |x| spin { meet(x) } }
29
29
 
30
- t0 = Time.now
31
- every(10) { puts "uptime: #{Time.now - t0}" }
30
+ sleep 1
@@ -14,5 +14,5 @@ end
14
14
  spin { nap(:a, 1) }
15
15
  spin { nap(:b, 2) }
16
16
 
17
- # Calling suspend will block until all child fibers have terminated
17
+ # Calling suspend will block until no work is left to do
18
18
  suspend
@@ -9,7 +9,7 @@ end
9
9
 
10
10
  def deferred_error(t)
11
11
  puts "deferred_error"
12
- spin { de2(t) }
12
+ spin { de2(t) }.await
13
13
  end
14
14
 
15
15
  def de2(t)
@@ -8,7 +8,7 @@ Exception.__disable_sanitized_backtrace__ = true
8
8
  supervisor = spin do
9
9
  puts "parent pid #{Process.pid}"
10
10
 
11
- Polyphony::ProcessSupervisor.supervise do
11
+ Polyphony.watch_process do
12
12
  puts "child pid #{Process.pid}"
13
13
  puts "go to sleep"
14
14
  sleep 5
@@ -22,9 +22,12 @@ supervisor = spin do
22
22
  end
23
23
 
24
24
  begin
25
+ spin do
26
+ sleep 2.5
27
+ Process.kill('TERM', Process.pid)
28
+ end
29
+ supervisor.await
30
+ rescue SystemExit
31
+ supervisor.terminate
25
32
  supervisor.await
26
- rescue Interrupt
27
- exit!
28
- # supervisor.terminate
29
- # supervisor.await
30
33
  end
@@ -0,0 +1,20 @@
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
+ spin { my_sleep(1) }
13
+ spin { my_sleep(2) }
14
+ spin { my_sleep(3) }
15
+ spin { puts "fiber count: #{Fiber.current.children.count}" }
16
+ snooze
17
+
18
+ puts "#{Time.now} supervising..."
19
+ supervise
20
+ puts "#{Time.now} done supervising"
@@ -5,9 +5,9 @@ require 'polyphony'
5
5
 
6
6
  def do_work(client)
7
7
  result = yield
8
- client.schedule(result)
8
+ client << result
9
9
  rescue Exception => e
10
- client.schedule(e)
10
+ client << e
11
11
  end
12
12
 
13
13
  $worker = Thread.new do