polyphony 0.19 → 0.20

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 (186) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.rubocop.yml +87 -1
  4. data/CHANGELOG.md +35 -0
  5. data/Gemfile.lock +17 -6
  6. data/README.md +200 -139
  7. data/Rakefile +4 -4
  8. data/TODO.md +35 -7
  9. data/bin/poly +11 -0
  10. data/docs/getting-started/getting-started.md +1 -1
  11. data/docs/summary.md +3 -0
  12. data/docs/technical-overview/exception-handling.md +94 -0
  13. data/docs/technical-overview/fiber-scheduling.md +99 -0
  14. data/examples/core/cancel.rb +8 -4
  15. data/examples/core/channel_echo.rb +18 -17
  16. data/examples/core/defer.rb +12 -0
  17. data/examples/core/enumerator.rb +4 -4
  18. data/examples/core/fiber_error.rb +9 -0
  19. data/examples/core/fiber_error_with_backtrace.rb +73 -0
  20. data/examples/core/fork.rb +6 -6
  21. data/examples/core/genserver.rb +16 -8
  22. data/examples/core/lock.rb +3 -3
  23. data/examples/core/move_on.rb +4 -3
  24. data/examples/core/move_on_twice.rb +5 -5
  25. data/examples/core/move_on_with_ensure.rb +8 -11
  26. data/examples/core/move_on_with_value.rb +14 -0
  27. data/examples/core/{multiple_spawn.rb → multiple_spin.rb} +5 -5
  28. data/examples/core/nested_cancel.rb +5 -5
  29. data/examples/core/{nested_multiple_spawn.rb → nested_multiple_spin.rb} +6 -6
  30. data/examples/core/nested_spin.rb +17 -0
  31. data/examples/core/pingpong.rb +21 -0
  32. data/examples/core/pulse.rb +4 -5
  33. data/examples/core/resource.rb +6 -4
  34. data/examples/core/resource_cancel.rb +6 -9
  35. data/examples/core/resource_delegate.rb +3 -3
  36. data/examples/core/sleep.rb +3 -3
  37. data/examples/core/sleep_spin.rb +19 -0
  38. data/examples/core/snooze.rb +32 -0
  39. data/examples/core/spin.rb +14 -0
  40. data/examples/core/{spawn_cancel.rb → spin_cancel.rb} +6 -7
  41. data/examples/core/spin_error.rb +17 -0
  42. data/examples/core/spin_error_backtrace.rb +30 -0
  43. data/examples/core/spin_uncaught_error.rb +15 -0
  44. data/examples/core/supervisor.rb +8 -8
  45. data/examples/core/supervisor_with_cancel_scope.rb +7 -7
  46. data/examples/core/supervisor_with_error.rb +8 -8
  47. data/examples/core/supervisor_with_manual_move_on.rb +6 -7
  48. data/examples/core/suspend.rb +13 -0
  49. data/examples/core/thread.rb +1 -1
  50. data/examples/core/thread_cancel.rb +9 -11
  51. data/examples/core/thread_pool.rb +18 -14
  52. data/examples/core/throttle.rb +7 -7
  53. data/examples/core/timeout.rb +3 -3
  54. data/examples/fs/read.rb +7 -9
  55. data/examples/http/config.ru +7 -3
  56. data/examples/http/cuba.ru +22 -0
  57. data/examples/http/happy_eyeballs.rb +6 -4
  58. data/examples/http/http_client.rb +1 -1
  59. data/examples/http/http_get.rb +1 -1
  60. data/examples/http/http_parse_experiment.rb +21 -16
  61. data/examples/http/http_proxy.rb +28 -26
  62. data/examples/http/http_server.rb +10 -10
  63. data/examples/http/http_server_forked.rb +6 -5
  64. data/examples/http/http_server_throttled.rb +3 -3
  65. data/examples/http/http_ws_server.rb +11 -11
  66. data/examples/http/https_raw_client.rb +1 -1
  67. data/examples/http/https_server.rb +8 -8
  68. data/examples/http/https_wss_server.rb +13 -11
  69. data/examples/http/rack_server.rb +2 -2
  70. data/examples/http/rack_server_https.rb +4 -4
  71. data/examples/http/rack_server_https_forked.rb +5 -5
  72. data/examples/http/websocket_secure_server.rb +6 -6
  73. data/examples/http/websocket_server.rb +5 -5
  74. data/examples/interfaces/pg_client.rb +4 -4
  75. data/examples/interfaces/pg_pool.rb +13 -6
  76. data/examples/interfaces/pg_transaction.rb +5 -4
  77. data/examples/interfaces/redis_channels.rb +15 -11
  78. data/examples/interfaces/redis_client.rb +2 -2
  79. data/examples/interfaces/redis_pubsub.rb +2 -1
  80. data/examples/interfaces/redis_pubsub_perf.rb +13 -9
  81. data/examples/io/backticks.rb +11 -0
  82. data/examples/io/cat.rb +4 -5
  83. data/examples/io/echo_client.rb +9 -4
  84. data/examples/io/echo_client_from_stdin.rb +20 -0
  85. data/examples/io/echo_pipe.rb +7 -8
  86. data/examples/io/echo_server.rb +8 -6
  87. data/examples/io/echo_server_with_timeout.rb +13 -10
  88. data/examples/io/echo_stdin.rb +3 -3
  89. data/examples/io/httparty.rb +2 -2
  90. data/examples/io/httparty_multi.rb +8 -4
  91. data/examples/io/httparty_threaded.rb +6 -2
  92. data/examples/io/io_read.rb +2 -2
  93. data/examples/io/irb.rb +16 -4
  94. data/examples/io/net-http.rb +3 -3
  95. data/examples/io/open.rb +17 -0
  96. data/examples/io/system.rb +3 -3
  97. data/examples/io/tcpserver.rb +15 -0
  98. data/examples/io/tcpsocket.rb +6 -5
  99. data/examples/performance/multi_snooze.rb +29 -0
  100. data/examples/performance/{perf_snooze.rb → snooze.rb} +7 -5
  101. data/examples/performance/snooze_raw.rb +39 -0
  102. data/ext/gyro/async.c +165 -0
  103. data/ext/gyro/child.c +167 -0
  104. data/ext/{ev → gyro}/extconf.rb +4 -3
  105. data/ext/gyro/gyro.c +316 -0
  106. data/ext/{ev/ev.h → gyro/gyro.h} +12 -7
  107. data/ext/gyro/gyro_ext.c +23 -0
  108. data/ext/{ev → gyro}/io.c +65 -57
  109. data/ext/{ev → gyro}/libev.h +0 -0
  110. data/ext/gyro/signal.c +117 -0
  111. data/ext/{ev → gyro}/socket.c +61 -6
  112. data/ext/gyro/timer.c +199 -0
  113. data/ext/libev/Changes +35 -0
  114. data/ext/libev/README +2 -1
  115. data/ext/libev/ev.c +213 -151
  116. data/ext/libev/ev.h +95 -88
  117. data/ext/libev/ev_epoll.c +26 -15
  118. data/ext/libev/ev_kqueue.c +11 -5
  119. data/ext/libev/ev_linuxaio.c +642 -0
  120. data/ext/libev/ev_poll.c +13 -8
  121. data/ext/libev/ev_port.c +5 -2
  122. data/ext/libev/ev_vars.h +14 -3
  123. data/ext/libev/ev_wrap.h +16 -0
  124. data/lib/ev_ext.bundle +0 -0
  125. data/lib/polyphony.rb +46 -50
  126. data/lib/polyphony/auto_run.rb +12 -0
  127. data/lib/polyphony/core/cancel_scope.rb +11 -7
  128. data/lib/polyphony/core/channel.rb +16 -9
  129. data/lib/polyphony/core/coprocess.rb +101 -51
  130. data/lib/polyphony/core/exceptions.rb +14 -12
  131. data/lib/polyphony/core/resource_pool.rb +21 -8
  132. data/lib/polyphony/core/supervisor.rb +10 -5
  133. data/lib/polyphony/core/sync.rb +7 -6
  134. data/lib/polyphony/core/thread.rb +4 -4
  135. data/lib/polyphony/core/thread_pool.rb +4 -4
  136. data/lib/polyphony/core/throttler.rb +6 -4
  137. data/lib/polyphony/extensions/core.rb +253 -0
  138. data/lib/polyphony/extensions/io.rb +28 -16
  139. data/lib/polyphony/extensions/openssl.rb +2 -1
  140. data/lib/polyphony/extensions/socket.rb +47 -52
  141. data/lib/polyphony/http.rb +4 -3
  142. data/lib/polyphony/http/agent.rb +68 -57
  143. data/lib/polyphony/http/server.rb +5 -5
  144. data/lib/polyphony/http/server/http1.rb +268 -0
  145. data/lib/polyphony/http/server/http2.rb +62 -0
  146. data/lib/polyphony/http/server/http2_stream.rb +104 -0
  147. data/lib/polyphony/http/server/rack.rb +64 -0
  148. data/lib/polyphony/http/server/request.rb +119 -0
  149. data/lib/polyphony/net.rb +26 -15
  150. data/lib/polyphony/postgres.rb +17 -13
  151. data/lib/polyphony/redis.rb +16 -15
  152. data/lib/polyphony/version.rb +1 -1
  153. data/lib/polyphony/websocket.rb +11 -4
  154. data/polyphony.gemspec +13 -9
  155. data/test/eg.rb +27 -0
  156. data/test/helper.rb +25 -0
  157. data/test/run.rb +5 -0
  158. data/test/test_async.rb +33 -0
  159. data/test/test_coprocess.rb +239 -77
  160. data/test/test_core.rb +95 -61
  161. data/test/test_gyro.rb +148 -0
  162. data/test/test_http_server.rb +313 -0
  163. data/test/test_io.rb +79 -27
  164. data/test/test_kernel.rb +22 -12
  165. data/test/test_signal.rb +36 -0
  166. data/test/test_timer.rb +24 -0
  167. metadata +89 -33
  168. data/examples/core/nested_async.rb +0 -17
  169. data/examples/core/next_tick.rb +0 -12
  170. data/examples/core/sleep_spawn.rb +0 -19
  171. data/examples/core/spawn.rb +0 -14
  172. data/examples/core/spawn_error.rb +0 -28
  173. data/examples/performance/perf_multi_snooze.rb +0 -21
  174. data/ext/ev/async.c +0 -168
  175. data/ext/ev/child.c +0 -169
  176. data/ext/ev/ev_ext.c +0 -23
  177. data/ext/ev/ev_module.c +0 -242
  178. data/ext/ev/signal.c +0 -119
  179. data/ext/ev/timer.c +0 -197
  180. data/lib/polyphony/core/fiber_pool.rb +0 -98
  181. data/lib/polyphony/extensions/kernel.rb +0 -169
  182. data/lib/polyphony/http/http1_adapter.rb +0 -254
  183. data/lib/polyphony/http/http2_adapter.rb +0 -157
  184. data/lib/polyphony/http/rack.rb +0 -25
  185. data/lib/polyphony/http/request.rb +0 -66
  186. data/test/test_ev.rb +0 -110
@@ -1,157 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- export_default :Protocol
4
-
5
- require 'http/2'
6
-
7
- Request = import('./request')
8
-
9
- class StreamWrapper
10
- def initialize(stream, parse_fiber)
11
- @stream = stream
12
- @parse_fiber = parse_fiber
13
- @parsing = true
14
-
15
- stream.on(:data) { |data| on_body(data) }
16
- stream.on(:half_close) { on_message_complete }
17
- end
18
-
19
- # Reads body chunk from connection
20
- def get_body_chunk
21
- @calling_fiber = Fiber.current
22
- @read_body = true
23
- @parse_fiber.safe_transfer
24
- end
25
-
26
- # Wait for request to finish
27
- def consume_request
28
- return unless @parsing
29
-
30
- @calling_fiber = Fiber.current
31
- @read_body = false
32
- @parse_fiber.safe_transfer while @parsing
33
- end
34
-
35
- def on_body(data)
36
- @calling_fiber.transfer(data) if @read_body
37
- end
38
-
39
- def on_message_complete
40
- @parsing = false
41
- @calling_fiber.transfer nil
42
- end
43
-
44
- # response API
45
- def respond(chunk, headers)
46
- consume_request if @parsing
47
-
48
- headers[':status'] ||= '200'
49
- @stream.headers(headers, end_stream: false)
50
- @stream.data(chunk, end_stream: true)
51
- @headers_sent = true
52
- end
53
-
54
- def send_headers(headers, empty_response = false)
55
- return if @headers_sent
56
-
57
- consume_request if @parsing
58
-
59
- headers[':status'] ||= (empty_response ? 204 : 200).to_s
60
- @stream.headers(headers, end_stream: false)
61
- @headers_sent = true
62
- end
63
-
64
- def send_body_chunk(chunk, done: false)
65
- send_headers({}, false) unless @headers_sent
66
- @stream.data(chunk, end_stream: done)
67
- end
68
-
69
- def finish
70
- consume_request if @parsing
71
-
72
- unless @headers_sent
73
- headers[':status'] ||= '204'
74
- @stream.headers(headers, end_stream: true)
75
- else
76
- @stream.close
77
- end
78
- end
79
- end
80
-
81
- class Protocol
82
- def self.upgrade_each(socket, opts, headers, &block)
83
- adapter = new(socket, opts, headers)
84
- adapter.each(&block)
85
- end
86
-
87
- def initialize(conn, opts, upgrade_headers = nil)
88
- @conn = conn
89
- @opts = opts
90
-
91
- @interface = ::HTTP2::Server.new
92
- @interface.on(:frame) { |bytes| conn << bytes }
93
- @interface.on(:stream) { |stream| start_stream(stream) }
94
- @parse_fiber = Fiber.new { parse_loop(upgrade_headers) }
95
- end
96
-
97
- def start_stream(stream)
98
- stream.on(:headers) do |headers|
99
- @calling_fiber.transfer([stream, headers.to_h])
100
- end
101
- end
102
-
103
- def protocol
104
- 'h2'
105
- end
106
-
107
- def parse_loop(upgrade_headers)
108
- upgrade(upgrade_headers) if upgrade_headers
109
-
110
- while (data = @conn.readpartial(8192))
111
- @interface << data
112
- snooze
113
- end
114
- @calling_fiber.transfer nil
115
- rescue SystemCallError, IOError
116
- # ignore
117
- @calling_fiber.transfer nil
118
- rescue => error
119
- # an error return value will be raised by the receiving fiber
120
- @calling_fiber.transfer error
121
- end
122
-
123
- # request API
124
-
125
- UPGRADE_MESSAGE = <<~HTTP.gsub("\n", "\r\n")
126
- HTTP/1.1 101 Switching Protocols
127
- Connection: Upgrade
128
- Upgrade: h2c
129
-
130
- HTTP
131
-
132
- def upgrade(headers)
133
- settings = headers['HTTP2-Settings']
134
- @conn << UPGRADE_MESSAGE
135
- @interface.upgrade(settings, headers, '')
136
- end
137
-
138
- # Iterates over incoming requests
139
- def each(&block)
140
- can_upgrade = true
141
- while @parse_fiber.alive?
142
- stream, headers = get_headers
143
- break unless stream
144
- wrapper = StreamWrapper.new(stream, @parse_fiber)
145
- request = Request.new(headers, wrapper)
146
- Fiber.new { block.(request) }.resume
147
- end
148
- ensure
149
- @conn.close rescue nil
150
- end
151
-
152
- # Reads headers from connection
153
- def get_headers
154
- @calling_fiber = Fiber.current
155
- @parse_fiber.safe_transfer
156
- end
157
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- export :load
4
-
5
- def run(app)
6
- ->(req) {
7
- response = app.(env(req))
8
- respond(req, response)
9
- }
10
- end
11
-
12
- def load(path)
13
- src = IO.read(path)
14
- instance_eval(src)
15
- end
16
-
17
- def env(request)
18
- { }
19
- end
20
-
21
- def respond(request, (status_code, headers, body))
22
- headers[':status'] = status_code.to_s
23
- body = body.first
24
- request.respond(body, headers)
25
- end
@@ -1,66 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- export_default :Request
4
-
5
- require 'uri'
6
-
7
- class Request
8
- attr_reader :headers, :adapter
9
-
10
- def initialize(headers, adapter)
11
- @headers = headers
12
- @adapter = adapter
13
- end
14
-
15
- def protocol
16
- @adapter.protocol
17
- end
18
-
19
- def method
20
- @method ||= @headers[':method']
21
- end
22
-
23
- def scheme
24
- @scheme ||= @headers[':scheme']
25
- end
26
-
27
- def uri
28
- @uri ||= URI.parse(@headers[':path'] || '')
29
- end
30
-
31
- def path
32
- @path ||= uri.path
33
- end
34
-
35
- def query
36
- @uri ||= URI.parse(@headers[':path'] || '')
37
- return @query if @query
38
-
39
- if (q = uri.query)
40
- @query = q.split('&').each_with_object({}) do |kv, h|
41
- k, v = kv.split('=')
42
- h[k.to_sym] = URI.decode_www_form_component(v)
43
- end
44
- else
45
- @query = {}
46
- end
47
- end
48
-
49
- EMPTY_HASH = {}
50
-
51
- def respond(chunk, headers = EMPTY_HASH)
52
- @adapter.respond(chunk, headers)
53
- end
54
-
55
- def send_headers(headers = EMPTY_HASH, empty_response = false)
56
- @adapter.send_headers(headers, empty_response)
57
- end
58
-
59
- def send_body_chunk(body, done: false)
60
- @adapter.send_body_chunk(body, done: done)
61
- end
62
-
63
- def finish
64
- @adapter.finish
65
- end
66
- end
data/test/test_ev.rb DELETED
@@ -1,110 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'bundler/setup'
3
- require 'polyphony'
4
-
5
- class EVRunTest < Minitest::Test
6
- def setup
7
- EV.rerun
8
- end
9
-
10
- def test_that_run_loop_returns_immediately_if_no_watchers
11
- t0 = Time.now
12
- suspend
13
- t1 = Time.now
14
- assert (t1 - t0) < 0.001
15
- end
16
- end
17
-
18
- class EVTimerTest < MiniTest::Test
19
- def setup
20
- EV.rerun
21
- end
22
-
23
- def test_that_one_shot_timer_works
24
- count = 0
25
- t = EV::Timer.new(0.01, 0)
26
- t.start { count += 1}
27
- suspend
28
- assert_equal(1, count)
29
- end
30
-
31
- def test_that_repeating_timer_works
32
- count = 0
33
- t = EV::Timer.new(0.001, 0.001)
34
- t.start { count += 1; t.stop if count >= 3}
35
- suspend
36
- assert_equal(3, count)
37
- end
38
- end
39
-
40
- class EVIOTest < MiniTest::Test
41
- def setup
42
- EV.rerun
43
- end
44
-
45
- def test_that_reading_works
46
- i, o = IO.pipe
47
- data = +''
48
- w = EV::IO.new(i, :r)
49
- w.start do
50
- i.read_nonblock(8192, data)
51
- w.stop unless data.empty?
52
- end
53
- EV::Timer.new(0, 0).start { o << 'hello' }
54
- suspend
55
- assert_equal('hello', data)
56
- end
57
- end
58
-
59
- class EVSignalTest < MiniTest::Test
60
- def setup
61
- EV.rerun
62
- EV.restart
63
- end
64
-
65
- def test_that_signal_watcher_receives_signal
66
- sig = Signal.list['USR1']
67
- count = 0
68
- w = EV::Signal.new(sig) { count += 1; w.stop }
69
- Thread.new { sync_sleep 0.001; Process.kill(:USR1, Process.pid) }
70
- suspend
71
- assert_equal(1, count)
72
- end
73
-
74
- def test_that_signal_watcher_receives_signal
75
- count = 0
76
- w = Polyphony.trap(:usr1, true) { count += 1; w.stop }
77
- assert_kind_of(EV::Signal, w)
78
- Thread.new { sync_sleep 0.001; Process.kill(:USR1, Process.pid) }
79
- suspend
80
- assert_equal(1, count)
81
- end
82
- end
83
-
84
- class EVAsyncTest < MiniTest::Test
85
- def setup
86
- EV.rerun
87
- end
88
-
89
- def test_that_async_watcher_receives_signal_across_threads
90
- count = 0
91
- a = EV::Async.new { count += 1; a.stop }
92
- Thread.new { sync_sleep 0.001; a.signal! }
93
- suspend
94
- assert_equal(1, count)
95
- end
96
-
97
- def test_that_async_watcher_coalesces_signals
98
- count = 0
99
- a = EV::Async.new do
100
- count += 1
101
- EV::Timer.new(0.01, 0).start { a.stop }
102
- end
103
- Thread.new do
104
- sync_sleep 0.001
105
- 3.times { a.signal! }
106
- end
107
- suspend
108
- assert_equal(1, count)
109
- end
110
- end