polyphony 0.45.0 → 0.45.1

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