polyphony 0.16 → 0.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +11 -11
  5. data/TODO.md +14 -5
  6. data/examples/core/channel_echo.rb +3 -3
  7. data/examples/core/enumerator.rb +1 -1
  8. data/examples/core/fork.rb +1 -1
  9. data/examples/core/genserver.rb +1 -1
  10. data/examples/core/lock.rb +3 -3
  11. data/examples/core/multiple_spawn.rb +2 -2
  12. data/examples/core/nested_async.rb +1 -1
  13. data/examples/core/nested_multiple_spawn.rb +3 -3
  14. data/examples/core/resource.rb +1 -1
  15. data/examples/core/resource_cancel.rb +1 -1
  16. data/examples/core/resource_delegate.rb +1 -1
  17. data/examples/core/sleep_spawn.rb +2 -2
  18. data/examples/core/spawn.rb +1 -1
  19. data/examples/core/spawn_cancel.rb +1 -1
  20. data/examples/core/spawn_error.rb +5 -5
  21. data/examples/core/supervisor.rb +4 -4
  22. data/examples/core/supervisor_with_cancel_scope.rb +3 -3
  23. data/examples/core/supervisor_with_error.rb +4 -4
  24. data/examples/core/supervisor_with_manual_move_on.rb +4 -4
  25. data/examples/core/thread.rb +2 -2
  26. data/examples/core/thread_cancel.rb +2 -2
  27. data/examples/core/thread_pool.rb +2 -2
  28. data/examples/core/throttle.rb +3 -3
  29. data/examples/fs/read.rb +1 -1
  30. data/examples/http/happy_eyeballs.rb +1 -1
  31. data/examples/http/http_client.rb +1 -1
  32. data/examples/http/http_server.rb +1 -1
  33. data/examples/http/http_server_throttled.rb +1 -1
  34. data/examples/http/http_ws_server.rb +2 -2
  35. data/examples/http/https_wss_server.rb +1 -1
  36. data/examples/interfaces/pg_client.rb +1 -1
  37. data/examples/interfaces/pg_pool.rb +1 -1
  38. data/examples/interfaces/redis_channels.rb +5 -5
  39. data/examples/interfaces/redis_pubsub.rb +2 -2
  40. data/examples/interfaces/redis_pubsub_perf.rb +3 -3
  41. data/examples/io/cat.rb +13 -0
  42. data/examples/io/echo_client.rb +2 -2
  43. data/examples/io/echo_server.rb +1 -1
  44. data/examples/io/echo_server_with_timeout.rb +1 -1
  45. data/examples/io/echo_stdin.rb +1 -1
  46. data/examples/io/io_read.rb +9 -0
  47. data/examples/io/system.rb +11 -0
  48. data/examples/performance/perf_multi_snooze.rb +2 -2
  49. data/examples/performance/perf_snooze.rb +2 -2
  50. data/examples/performance/thread-vs-fiber/polyphony_server.rb +2 -2
  51. data/ext/ev/io.c +53 -4
  52. data/lib/polyphony/core/coprocess.rb +1 -0
  53. data/lib/polyphony/core/supervisor.rb +1 -1
  54. data/lib/polyphony/extensions/io.rb +97 -17
  55. data/lib/polyphony/extensions/kernel.rb +47 -27
  56. data/lib/polyphony/http/server.rb +1 -1
  57. data/lib/polyphony/postgres.rb +0 -4
  58. data/lib/polyphony/version.rb +1 -1
  59. data/test/test_coprocess.rb +13 -13
  60. data/test/test_core.rb +12 -12
  61. data/test/test_io.rb +95 -3
  62. data/test/test_kernel.rb +26 -0
  63. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 62006f0bdd66c2776a252724cf1f842c859a39d8bc150f8f3db839105bfc60b0
4
- data.tar.gz: e662adc4addce7d90c5dfd17a635618e6d8164cad911670f60901bac309c767a
3
+ metadata.gz: 16bd848555e9b517c4114a4324fe65d27ed08b2ac3f638a94c10070f99eb1915
4
+ data.tar.gz: 96dac9a4e8b5fe09f78ad6ffae157f048fdb499fca3c78028145bffdad45cfbd
5
5
  SHA512:
6
- metadata.gz: 402eaf9528351b93e8026a7c91492ea367c8279bee389834d9a5b2f4855c10d82f9f1d23749cce1e1f5b38ac7f136001f0ffe6570051261d19e7e25fa6ca5f65
7
- data.tar.gz: ed423faa2cfcf852277fc724e232e3836e8bdb0fcd98e009166fee7b8088f6d5db18aea1fa9e97c1fcc519e01c666d6a15fbaa9c87c337559bab04cc7f6957b3
6
+ metadata.gz: 39dbff6723dfe0e2a9919080d170d16b545081e8f57e8010a7a84402b185c3a7b0dbcb3b11390c854fdeee90f967a8d055d8b4e6adb4c0d4344ac79f7d8a4f94
7
+ data.tar.gz: cb0a008a4234cab661e8a7e8ed6a70919093d210135c983704e201b043082a8e268ff2d8f11cf755aab64e1118c817ac3287d938282c62d39fe02dd66ef25d19
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ 0.17 2019-05-24
2
+ ---------------
3
+
4
+ * Implement IO#read_watcher, IO#write_watcher in C for better performance
5
+ * Implement nonblocking (yielding) versions of Kernel#system, IO.popen,
6
+ Process.detach, IO#gets IO#puts, other IO singleton methods
7
+ * Add Coprocess#join as alias to Coprocess#await
8
+ * Rename Kernel#spawn to Kernel#coproc
9
+ * Fix encoding of strings read with IO#read, IO#readpartial
10
+ * Fix non-blocking behaviour of IO#read, IO#readpartial, IO#write
11
+
1
12
  0.16 2019-05-22
2
13
  ---------------
3
14
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.16)
4
+ polyphony (0.17)
5
5
  http-2 (= 0.10.0)
6
6
  http_parser.rb (= 0.6.0)
7
7
  modulation (= 0.24)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Polyphony - Fiber-Based Concurrency for Ruby
1
+ # Polyphony - lightweight concurrency for Ruby
2
2
 
3
3
  [INSTALL](#installing-polyphony) |
4
4
  [TUTORIAL](#getting-started) |
@@ -70,18 +70,18 @@ The reactor, in turn, may schedule other operations once they can be resumed. In
70
70
  that manner, multiple ongoing operations may be processed concurrently.
71
71
 
72
72
  There are multiple ways to start a concurrent operation, the most common of
73
- which is `Kernel#spawn`:
73
+ which is `Kernel#coproc`:
74
74
 
75
75
  ```ruby
76
76
  require 'polyphony'
77
77
 
78
- spawn do
78
+ coproc do
79
79
  puts "A going to sleep"
80
80
  sleep 1
81
81
  puts "A woken up"
82
82
  end
83
83
 
84
- spawn do
84
+ coproc do
85
85
  puts "B going to sleep"
86
86
  sleep 1
87
87
  puts "B woken up"
@@ -90,7 +90,7 @@ end
90
90
 
91
91
  In the above example, both `sleep` calls will be executed concurrently, and thus
92
92
  the program will take approximately only 1 second to execute. Note the lack of
93
- any boilerplate relating to concurrency. Each `spawn` block starts a
93
+ any boilerplate relating to concurrency. Each `coproc` block starts a
94
94
  *coprocess*, and is executed in sequential manner.
95
95
 
96
96
  > **Coprocesses - the basic unit of concurrency**: In Polyphony, concurrent
@@ -115,8 +115,8 @@ require 'polyphony'
115
115
 
116
116
  server = TCPServer.open(1234)
117
117
  while client = server.accept
118
- # spawn starts a new coprocess on a separate fiber
119
- spawn {
118
+ # coproc starts a new coprocess on a separate fiber
119
+ coproc {
120
120
  while data = client.read rescue nil
121
121
  client.write(data)
122
122
  end
@@ -129,7 +129,7 @@ This example demonstrates several features of Polyphony:
129
129
  - The code uses the native `TCPServer` class from Ruby's stdlib, to setup a TCP
130
130
  server. The result of `server.accept` is also a native `TCPSocket` object.
131
131
  There are no wrapper classes being used.
132
- - The only hint of the code being concurrent is the use of `Kernel#spawn`,
132
+ - The only hint of the code being concurrent is the use of `Kernel#coproc`,
133
133
  which starts a new coprocess on a dedicated fiber. This allows serving
134
134
  multiple clients at once. Whenever a blocking call is issued, such as
135
135
  `#accept` or `#read`, execution is *yielded* to the event loop, which will
@@ -257,7 +257,7 @@ Pool = Polyphony::ResourcePool.new(limit: 5) {
257
257
  }
258
258
 
259
259
  1000.times {
260
- spawn {
260
+ coproc {
261
261
  Pool.acquire { |db| p db.query('select 1') }
262
262
  }
263
263
  }
@@ -274,7 +274,7 @@ Pool = Polyphony::ResourcePool.new(limit: 5) {
274
274
  }
275
275
 
276
276
  1000.times {
277
- spawn { p Pool.query('select 1') }
277
+ coproc { p Pool.query('select 1') }
278
278
  }
279
279
  ```
280
280
 
@@ -313,7 +313,7 @@ server = Net.tcp_listen(1234)
313
313
  throttler = throttle(rate: 10) # up to 10 times per second
314
314
 
315
315
  while client = server.accept
316
- spawn {
316
+ coproc {
317
317
  throttler.call {
318
318
  while data = client.read
319
319
  client.write(data)
data/TODO.md CHANGED
@@ -1,9 +1,13 @@
1
1
  # Roadmap:
2
2
 
3
- ## 0.17 Full or almost full functionality of `IO` using monkey patching
3
+ ## 0.17 Implement non-blocking versions of `IO` API using monkey patching
4
4
 
5
- - testing - check conformance to Ruby `IO` API (as described in the Ruby docs)
6
- - implement as much as possible in C
5
+ - Don't worry about performance or completeness of behaviour
6
+ - Related global functions such as:
7
+ - `Kernel#system`
8
+ - `IO.popen`
9
+ - testing - check conformance to Ruby `IO` API (arguments and return values as
10
+ described in the Ruby docs)
7
11
 
8
12
  ## 0.18 Working net/http, httparty
9
13
 
@@ -20,14 +24,19 @@
20
24
 
21
25
  ## 0.20 Working Rails application
22
26
 
27
+ - app with database access (postgresql)
23
28
  - benchmarks!
24
29
 
25
- ## 0.21 Testing
30
+ ## 0.21 Support for multi-threading
31
+
32
+ - Separate event loop for each thread
33
+
34
+ ## 0.22 Testing
26
35
 
27
36
  - test thread / thread_pool modules
28
37
  - report test coverage
29
38
 
30
- ## 0.22 Documentation
39
+ ## 0.23 Documentation
31
40
 
32
41
  # DNS
33
42
 
@@ -14,9 +14,9 @@ end
14
14
 
15
15
  chan1, chan2 = Polyphony::Channel.new, Polyphony::Channel.new
16
16
 
17
- echoer = spawn { echo(chan1, chan2) }
17
+ echoer = coproc { echo(chan1, chan2) }
18
18
 
19
- spawn do
19
+ coproc do
20
20
  puts "start receiver"
21
21
  while msg = chan2.receive
22
22
  puts msg
@@ -26,7 +26,7 @@ ensure
26
26
  puts "receiver stopped"
27
27
  end
28
28
 
29
- $main = spawn do
29
+ $main = coproc do
30
30
  t0 = Time.now
31
31
  puts "send hello"
32
32
  chan1 << "hello"
@@ -5,7 +5,7 @@ require 'polyphony'
5
5
 
6
6
  enum = [1,2,3].each
7
7
 
8
- spawn do
8
+ coproc do
9
9
  while e = enum.next rescue nil
10
10
  puts e
11
11
  sleep 1
@@ -8,7 +8,7 @@ puts "parent pid: #{Process.pid}"
8
8
  pid = Polyphony.fork do
9
9
  puts "child pid: #{Process.pid}"
10
10
 
11
- spawn do
11
+ coproc do
12
12
  puts "child going to sleep 1..."
13
13
  sleep 1
14
14
  puts "child woke up 1"
@@ -5,7 +5,7 @@ require 'polyphony'
5
5
 
6
6
  class GenServer
7
7
  def self.start(receiver, *args)
8
- coprocess = spawn do
8
+ coprocess = coproc do
9
9
  state = receiver.initial_state(*args)
10
10
  loop do
11
11
  msg = receive
@@ -14,6 +14,6 @@ def loop_it(number, lock)
14
14
  end
15
15
 
16
16
  lock = Polyphony::Sync::Mutex.new
17
- spawn { loop_it(1, lock) }
18
- spawn { loop_it(2, lock) }
19
- spawn { loop_it(3, lock) }
17
+ coproc { loop_it(1, lock) }
18
+ coproc { loop_it(2, lock) }
19
+ coproc { loop_it(3, lock) }
@@ -3,13 +3,13 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
5
 
6
- spawn do
6
+ coproc do
7
7
  puts "1 >"
8
8
  sleep(1)
9
9
  puts "1 <"
10
10
  end
11
11
 
12
- spawn do
12
+ coproc do
13
13
  puts "2 >"
14
14
  sleep(1)
15
15
  puts "2 <"
@@ -3,7 +3,7 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
5
 
6
- spawn do
6
+ coproc do
7
7
  puts "going to sleep"
8
8
  result = async do
9
9
  async do
@@ -3,14 +3,14 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
5
 
6
- spawn do
7
- spawn do
6
+ coproc do
7
+ coproc do
8
8
  puts "1 >"
9
9
  sleep(1)
10
10
  puts "1 <"
11
11
  end
12
12
 
13
- spawn do
13
+ coproc do
14
14
  puts "2 >"
15
15
  sleep(1)
16
16
  puts "2 <"
@@ -21,7 +21,7 @@ async def user(number)
21
21
  end
22
22
 
23
23
  100.times do |x|
24
- spawn user(x)
24
+ coproc user(x)
25
25
  end
26
26
 
27
27
  t0 = Time.now
@@ -26,7 +26,7 @@ def user(number)
26
26
  end
27
27
 
28
28
  6.times do |x|
29
- spawn { user(x) }
29
+ coproc { user(x) }
30
30
  end
31
31
 
32
32
  t0 = Time.now
@@ -25,7 +25,7 @@ async def meet(number)
25
25
  end
26
26
  end
27
27
 
28
- 3.times { |x| spawn meet(x) }
28
+ 3.times { |x| coproc meet(x) }
29
29
 
30
30
  t0 = Time.now
31
31
  every(10) { puts "uptime: #{Time.now - t0}" }
@@ -3,14 +3,14 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
5
 
6
- spawn {
6
+ coproc {
7
7
  10.times { |i|
8
8
  sleep 0.1;
9
9
  p i
10
10
  }
11
11
  }
12
12
 
13
- spawn {
13
+ coproc {
14
14
  puts "going to sleep..."
15
15
  sleep 1
16
16
  puts "woke up"
@@ -9,6 +9,6 @@ def my_sleep(t)
9
9
  puts "woke up"
10
10
  end
11
11
 
12
- spawn do
12
+ coproc do
13
13
  async { my_sleep(1) }.await
14
14
  end
@@ -3,7 +3,7 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
5
 
6
- spawn do
6
+ coproc do
7
7
  puts "going to sleep..."
8
8
  cancel_after(1) do
9
9
  async {
@@ -9,11 +9,11 @@ def error(t)
9
9
  raise "hello #{t}"
10
10
  end
11
11
 
12
- def spawn_with_error
13
- spawn { error(2) }
12
+ def coproc_with_error
13
+ coproc { error(2) }
14
14
  end
15
15
 
16
- spawn do
16
+ coproc do
17
17
  error(1)
18
18
  rescue => e
19
19
  e.cleanup_backtrace
@@ -23,6 +23,6 @@ rescue => e
23
23
  puts
24
24
  end
25
25
 
26
- spawn_with_error
26
+ coproc_with_error
27
27
 
28
- puts "done spawning"
28
+ puts "done coprocing"
@@ -11,10 +11,10 @@ end
11
11
 
12
12
  puts "#{Time.now} waiting..."
13
13
  supervise do |s|
14
- s.spawn my_sleep(1)
15
- s.spawn my_sleep(2)
16
- s.spawn my_sleep(3)
17
- s.spawn {
14
+ s.coproc my_sleep(1)
15
+ s.coproc my_sleep(2)
16
+ s.coproc my_sleep(3)
17
+ s.coproc {
18
18
  puts "fiber count: #{Polyphony::FiberPool.size}"
19
19
  }
20
20
  end
@@ -14,9 +14,9 @@ puts "#{Time.now} going to sleep..."
14
14
  move_on_after(0.5) do
15
15
  supervise do |s|
16
16
  puts "supervise block"
17
- s.spawn my_sleep(1)
18
- s.spawn my_sleep(2)
19
- s.spawn my_sleep(3)
17
+ s.coproc my_sleep(1)
18
+ s.coproc my_sleep(2)
19
+ s.coproc my_sleep(3)
20
20
  end
21
21
  puts "supervisor done"
22
22
  end
@@ -8,12 +8,12 @@ async def my_sleep(t)
8
8
  raise "blah"
9
9
  end
10
10
 
11
- spawn do
11
+ coproc do
12
12
  puts "#{Time.now} going to sleep..."
13
13
  supervise do |s|
14
- s.spawn my_sleep(1)
15
- s.spawn my_sleep(2)
16
- s.spawn my_sleep(3)
14
+ s.coproc my_sleep(1)
15
+ s.coproc my_sleep(2)
16
+ s.coproc my_sleep(3)
17
17
  end
18
18
  rescue => e
19
19
  puts "exception from supervisor: #{e}"
@@ -12,13 +12,13 @@ end
12
12
  puts "#{Time.now} going to sleep..."
13
13
  result = supervise do |s|
14
14
  fiber = Fiber.current
15
- spawn do
15
+ coproc do
16
16
  sleep(0.5)
17
17
  puts "stopping supervisor..."
18
18
  s.stop!
19
19
  end
20
- s.spawn my_sleep(1)
21
- s.spawn my_sleep(2)
22
- s.spawn my_sleep(3)
20
+ s.coproc my_sleep(1)
21
+ s.coproc my_sleep(2)
22
+ s.coproc my_sleep(3)
23
23
  end
24
24
  puts "#{Time.now} woke up with #{result.inspect}"
@@ -18,8 +18,8 @@ end
18
18
 
19
19
  def threaded
20
20
  t0 = Time.now
21
- data = Polyphony::Thread.spawn { lengthy_op }.await
22
- X.times { Polyphony::Thread.spawn { lengthy_op }.await }
21
+ data = Polyphony::Thread.coproc { lengthy_op }.await
22
+ X.times { Polyphony::Thread.coproc { lengthy_op }.await }
23
23
  puts "read threaded #{data.bytesize} bytes (#{Time.now - t0}s)"
24
24
  end
25
25
 
@@ -13,10 +13,10 @@ def lengthy_op
13
13
  acc / count
14
14
  end
15
15
 
16
- spawn do
16
+ coproc do
17
17
  t0 = Time.now
18
18
  cancel_after(0.01) do
19
- data = Polyphony::Thread.spawn { lengthy_op }.await
19
+ data = Polyphony::Thread.coproc { lengthy_op }.await
20
20
  puts "read #{data.bytesize} bytes (#{Time.now - t0}s)"
21
21
  end
22
22
  rescue Exception => e