polyphony 0.17 → 0.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/Gemfile.lock +11 -3
  4. data/README.md +18 -18
  5. data/TODO.md +5 -21
  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_cancel.rb +1 -1
  15. data/examples/core/sleep_spawn.rb +2 -2
  16. data/examples/core/spawn.rb +1 -1
  17. data/examples/core/spawn_cancel.rb +1 -1
  18. data/examples/core/spawn_error.rb +4 -4
  19. data/examples/core/supervisor.rb +1 -1
  20. data/examples/core/supervisor_with_error.rb +1 -1
  21. data/examples/core/supervisor_with_manual_move_on.rb +1 -1
  22. data/examples/core/thread.rb +2 -2
  23. data/examples/core/thread_cancel.rb +2 -2
  24. data/examples/core/thread_pool.rb +1 -1
  25. data/examples/core/throttle.rb +3 -3
  26. data/examples/core/timeout.rb +10 -0
  27. data/examples/fs/read.rb +1 -1
  28. data/examples/http/http_client.rb +1 -1
  29. data/examples/http/http_get.rb +7 -0
  30. data/examples/http/http_parse_experiment.rb +118 -0
  31. data/examples/http/http_proxy.rb +81 -0
  32. data/examples/http/http_server.rb +15 -4
  33. data/examples/http/http_server_forked.rb +2 -2
  34. data/examples/http/http_server_throttled.rb +1 -1
  35. data/examples/http/http_ws_server.rb +2 -2
  36. data/examples/http/https_server.rb +5 -1
  37. data/examples/http/https_wss_server.rb +1 -1
  38. data/examples/http/rack_server_https_forked.rb +1 -1
  39. data/examples/interfaces/pg_client.rb +1 -1
  40. data/examples/interfaces/pg_pool.rb +1 -1
  41. data/examples/interfaces/redis_channels.rb +5 -5
  42. data/examples/interfaces/redis_pubsub.rb +2 -2
  43. data/examples/interfaces/redis_pubsub_perf.rb +3 -3
  44. data/examples/io/echo_client.rb +2 -2
  45. data/examples/io/echo_pipe.rb +17 -0
  46. data/examples/io/echo_server.rb +1 -1
  47. data/examples/io/echo_server_with_timeout.rb +1 -1
  48. data/examples/io/httparty.rb +10 -0
  49. data/examples/io/httparty_multi.rb +29 -0
  50. data/examples/io/httparty_threaded.rb +25 -0
  51. data/examples/io/irb.rb +15 -0
  52. data/examples/io/net-http.rb +15 -0
  53. data/examples/io/system.rb +1 -1
  54. data/examples/io/tcpsocket.rb +18 -0
  55. data/examples/performance/perf_multi_snooze.rb +2 -2
  56. data/examples/performance/perf_snooze.rb +17 -20
  57. data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
  58. data/ext/ev/ev.h +9 -1
  59. data/ext/ev/ev_ext.c +4 -1
  60. data/ext/ev/ev_module.c +36 -22
  61. data/ext/ev/extconf.rb +1 -1
  62. data/ext/ev/io.c +23 -23
  63. data/ext/ev/signal.c +1 -1
  64. data/ext/ev/socket.c +161 -0
  65. data/lib/polyphony/core/coprocess.rb +1 -1
  66. data/lib/polyphony/core/fiber_pool.rb +2 -2
  67. data/lib/polyphony/core/supervisor.rb +2 -18
  68. data/lib/polyphony/extensions/io.rb +19 -6
  69. data/lib/polyphony/extensions/kernel.rb +17 -5
  70. data/lib/polyphony/extensions/socket.rb +40 -1
  71. data/lib/polyphony/http/agent.rb +56 -25
  72. data/lib/polyphony/http/http1_adapter.rb +254 -0
  73. data/lib/polyphony/http/http2_adapter.rb +157 -0
  74. data/lib/polyphony/http/{http2_request.rb → request.rb} +25 -22
  75. data/lib/polyphony/http/server.rb +19 -11
  76. data/lib/polyphony/net.rb +10 -6
  77. data/lib/polyphony/version.rb +1 -1
  78. data/polyphony.gemspec +6 -5
  79. data/test/test_coprocess.rb +9 -9
  80. data/test/test_core.rb +14 -14
  81. data/test/test_io.rb +4 -4
  82. data/test/test_kernel.rb +1 -1
  83. metadata +48 -23
  84. data/lib/polyphony/http/http1.rb +0 -124
  85. data/lib/polyphony/http/http1_request.rb +0 -83
  86. data/lib/polyphony/http/http2.rb +0 -65
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16bd848555e9b517c4114a4324fe65d27ed08b2ac3f638a94c10070f99eb1915
4
- data.tar.gz: 96dac9a4e8b5fe09f78ad6ffae157f048fdb499fca3c78028145bffdad45cfbd
3
+ metadata.gz: e79ca0a896fc0b5feffa415e44b9229074d145c2142155caf47f243b6c85078d
4
+ data.tar.gz: 6aa58f404376be6e6d472e588be4349d6fcaeb3031e5786a0a5ffe0970d9fa5d
5
5
  SHA512:
6
- metadata.gz: 39dbff6723dfe0e2a9919080d170d16b545081e8f57e8010a7a84402b185c3a7b0dbcb3b11390c854fdeee90f967a8d055d8b4e6adb4c0d4344ac79f7d8a4f94
7
- data.tar.gz: cb0a008a4234cab661e8a7e8ed6a70919093d210135c983704e201b043082a8e268ff2d8f11cf755aab64e1118c817ac3287d938282c62d39fe02dd66ef25d19
6
+ metadata.gz: accbeddc33fd7b1de40f411a421c445bc847e8ee744d98dcd1cf20b0a8fe3fc5c880ca373a020b026a0d68aac9cfc74f179bde2c3cebd1b0733a5adbe2b435b0
7
+ data.tar.gz: 7549c7ac4528bf4747049fb6c4eecbfe500f6aeed3586f8181bbf21506f8725ebb51c1170e369f15b24c748d59be439a2d076d26de1e5b1a1b6b34ae3e344ade
@@ -1,3 +1,18 @@
1
+ 0.19 2019-06-12
2
+ ---------------
3
+
4
+ * Rewrite HTTP server for better concurrency, sequential API
5
+ * Support 204 no-content response in HTTP 1
6
+ * Add optional count parameter to Kernel#throttled_loop for finite looping
7
+ * Implement Fiber#safe_transfer in C
8
+ * Optimize Kernel#next_tick implementation using ev_idle instead of ev_timer
9
+
10
+ 0.18 2019-06-08
11
+ ---------------
12
+
13
+ * Rename Kernel#coproc to Kernel#spin
14
+ * Rewrite Supervisor#spin
15
+
1
16
  0.17 2019-05-24
2
17
  ---------------
3
18
 
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.17)
4
+ polyphony (0.19)
5
5
  http-2 (= 0.10.0)
6
6
  http_parser.rb (= 0.6.0)
7
- modulation (= 0.24)
7
+ modulation (~> 0.25)
8
8
 
9
9
  GEM
10
10
  remote: https://rubygems.org/
@@ -12,9 +12,16 @@ GEM
12
12
  hiredis (0.6.3)
13
13
  http-2 (0.10.0)
14
14
  http_parser.rb (0.6.0)
15
+ httparty (0.17.0)
16
+ mime-types (~> 3.0)
17
+ multi_xml (>= 0.5.2)
15
18
  localhost (1.1.4)
19
+ mime-types (3.2.2)
20
+ mime-types-data (~> 3.2015)
21
+ mime-types-data (3.2019.0331)
16
22
  minitest (5.11.3)
17
- modulation (0.24)
23
+ modulation (0.25)
24
+ multi_xml (0.6.0)
18
25
  pg (1.1.3)
19
26
  rake (12.3.2)
20
27
  rake-compiler (1.0.5)
@@ -27,6 +34,7 @@ PLATFORMS
27
34
 
28
35
  DEPENDENCIES
29
36
  hiredis (= 0.6.3)
37
+ httparty (= 0.17.0)
30
38
  localhost (= 1.1.4)
31
39
  minitest (= 5.11.3)
32
40
  pg (= 1.1.3)
data/README.md CHANGED
@@ -11,8 +11,8 @@
11
11
  > number of parts, each forming an individual melody and harmonizing with each
12
12
  > other.
13
13
 
14
- **Note**: Polyphony is designed to work with recent versions of Ruby and
15
- supports Linux and MacOS only. This software is currently at the alpha stage.
14
+ **Note**: Polyphony is experimental software. It is designed to work with recent
15
+ versions of Ruby (2.5 and newer) and supports Linux and MacOS only.
16
16
 
17
17
  ## What is Polyphony
18
18
 
@@ -41,7 +41,7 @@ takes care of context-switching automatically whenever a blocking call like
41
41
  coprocesses, supervisors, cancel scopes, throttling, resource pools etc.
42
42
  - Code can use native networking classes and libraries, growing support for
43
43
  third-party gems such as `pg` and `redis`.
44
- - HTTP 1 / HTTP 2 client
44
+ - HTTP 1 / HTTP 2 client agent with persistent connections.
45
45
  - Competitive performance and scalability characteristics, in terms of both
46
46
  throughput and memory consumption.
47
47
 
@@ -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#coproc`:
73
+ which is `Kernel#spin`:
74
74
 
75
75
  ```ruby
76
76
  require 'polyphony'
77
77
 
78
- coproc do
78
+ spin do
79
79
  puts "A going to sleep"
80
80
  sleep 1
81
81
  puts "A woken up"
82
82
  end
83
83
 
84
- coproc do
84
+ spin 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 `coproc` block starts a
93
+ any boilerplate relating to concurrency. Each `spin` 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
@@ -99,8 +99,8 @@ any boilerplate relating to concurrency. Each `coproc` block starts a
99
99
  > called, and resumed once that operation has been completed. Coprocesses offer
100
100
  > significant advantages over threads - they consume only about 10KB, switching
101
101
  > between them is much faster than switching threads, and literally millions of
102
- > them can be spawned without affecting performance*. Besides, Ruby does not yet
103
- > allow parallel execution of threads.
102
+ > them can be spinned off without affecting performance*. Besides, Ruby does not
103
+ > yet allow parallel execution of threads (courtesy of the Ruby GVL).
104
104
  >
105
105
  > \* *This is a totally unsubstantiated claim which has not been proved in
106
106
  > practice*.
@@ -116,7 +116,7 @@ require 'polyphony'
116
116
  server = TCPServer.open(1234)
117
117
  while client = server.accept
118
118
  # coproc starts a new coprocess on a separate fiber
119
- coproc {
119
+ spin {
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#coproc`,
132
+ - The only hint of the code being concurrent is the use of `Kernel#spin`,
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
@@ -223,7 +223,7 @@ end
223
223
  ### Additional concurrency constructs
224
224
 
225
225
  In order to facilitate writing concurrent code, Polyphony provides additional
226
- constructs that make it easier to spawn concurrent tasks and to control them.
226
+ constructs that make it easier to create and control concurrent tasks.
227
227
 
228
228
  `CancelScope` - an abstraction used to cancel the execution of one or more
229
229
  coprocesses or supervisors. It usually works by defining a timeout for the
@@ -257,7 +257,7 @@ Pool = Polyphony::ResourcePool.new(limit: 5) {
257
257
  }
258
258
 
259
259
  1000.times {
260
- coproc {
260
+ spin {
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
- coproc { p Pool.query('select 1') }
277
+ spin { p Pool.query('select 1') }
278
278
  }
279
279
  ```
280
280
 
@@ -285,9 +285,9 @@ using `Kernel.supervise`:
285
285
 
286
286
  ```ruby
287
287
  supervise { |s|
288
- s.spawn { sleep 1 }
289
- s.spawn { sleep 2 }
290
- s.spawn { sleep 3 }
288
+ s.spin { sleep 1 }
289
+ s.spin { sleep 2 }
290
+ s.spin { sleep 3 }
291
291
  }
292
292
  puts "done sleeping"
293
293
  ```
@@ -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
- coproc {
316
+ spin {
317
317
  throttler.call {
318
318
  while data = client.read
319
319
  client.write(data)
data/TODO.md CHANGED
@@ -1,42 +1,26 @@
1
1
  # Roadmap:
2
2
 
3
- ## 0.17 Implement non-blocking versions of `IO` API using monkey patching
4
-
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)
11
-
12
- ## 0.18 Working net/http, httparty
13
-
14
- - implement `TCPSocket`/`TCPServer` functionality
15
- - test `socket` classes
16
- - test `Net::HTTP`
17
- - test `httparty`
18
-
19
- ## 0.19 Full Rack adapter implementation
3
+ ## 0.20 Full Rack adapter implementation
20
4
 
21
5
  - follow Rack specification (doesn't have to include stuff like streaming or
22
6
  websockets)
23
7
  - find some demo Rack apps and test with Polyphony
24
8
 
25
- ## 0.20 Working Rails application
9
+ ## 0.21 Working Rails application
26
10
 
27
11
  - app with database access (postgresql)
28
12
  - benchmarks!
29
13
 
30
- ## 0.21 Support for multi-threading
14
+ ## 0.22 Support for multi-threading
31
15
 
32
16
  - Separate event loop for each thread
33
17
 
34
- ## 0.22 Testing
18
+ ## 0.23 Testing
35
19
 
36
20
  - test thread / thread_pool modules
37
21
  - report test coverage
38
22
 
39
- ## 0.23 Documentation
23
+ ## 0.24 Documentation
40
24
 
41
25
  # DNS
42
26
 
@@ -14,9 +14,9 @@ end
14
14
 
15
15
  chan1, chan2 = Polyphony::Channel.new, Polyphony::Channel.new
16
16
 
17
- echoer = coproc { echo(chan1, chan2) }
17
+ echoer = spin { echo(chan1, chan2) }
18
18
 
19
- coproc do
19
+ spin 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 = coproc do
29
+ $main = spin 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
- coproc do
8
+ spin 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
- coproc do
11
+ spin 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 = coproc do
8
+ coprocess = spin 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
- coproc { loop_it(1, lock) }
18
- coproc { loop_it(2, lock) }
19
- coproc { loop_it(3, lock) }
17
+ spin { loop_it(1, lock) }
18
+ spin { loop_it(2, lock) }
19
+ spin { loop_it(3, lock) }
@@ -3,13 +3,13 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
5
 
6
- coproc do
6
+ spin do
7
7
  puts "1 >"
8
8
  sleep(1)
9
9
  puts "1 <"
10
10
  end
11
11
 
12
- coproc do
12
+ spin 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
- coproc do
6
+ spin 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
- coproc do
7
- coproc do
6
+ spin do
7
+ spin do
8
8
  puts "1 >"
9
9
  sleep(1)
10
10
  puts "1 <"
11
11
  end
12
12
 
13
- coproc do
13
+ spin do
14
14
  puts "2 >"
15
15
  sleep(1)
16
16
  puts "2 <"
@@ -26,7 +26,7 @@ def user(number)
26
26
  end
27
27
 
28
28
  6.times do |x|
29
- coproc { user(x) }
29
+ spin { user(x) }
30
30
  end
31
31
 
32
32
  t0 = Time.now
@@ -3,14 +3,14 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
5
 
6
- coproc {
6
+ spin {
7
7
  10.times { |i|
8
8
  sleep 0.1;
9
9
  p i
10
10
  }
11
11
  }
12
12
 
13
- coproc {
13
+ spin {
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
- coproc do
12
+ spin 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
- coproc do
6
+ spin 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 coproc_with_error
13
- coproc { error(2) }
12
+ def spin_with_error
13
+ spin { error(2) }
14
14
  end
15
15
 
16
- coproc do
16
+ spin 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
- coproc_with_error
26
+ spin_with_error
27
27
 
28
28
  puts "done coprocing"
@@ -14,7 +14,7 @@ supervise do |s|
14
14
  s.coproc my_sleep(1)
15
15
  s.coproc my_sleep(2)
16
16
  s.coproc my_sleep(3)
17
- s.coproc {
17
+ s.spin {
18
18
  puts "fiber count: #{Polyphony::FiberPool.size}"
19
19
  }
20
20
  end
@@ -8,7 +8,7 @@ async def my_sleep(t)
8
8
  raise "blah"
9
9
  end
10
10
 
11
- coproc do
11
+ spin do
12
12
  puts "#{Time.now} going to sleep..."
13
13
  supervise do |s|
14
14
  s.coproc my_sleep(1)
@@ -12,7 +12,7 @@ end
12
12
  puts "#{Time.now} going to sleep..."
13
13
  result = supervise do |s|
14
14
  fiber = Fiber.current
15
- coproc do
15
+ spin do
16
16
  sleep(0.5)
17
17
  puts "stopping supervisor..."
18
18
  s.stop!