polyphony 0.19 → 0.20

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