polyphony 0.99.4 → 0.99.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +11 -0
  3. data/.yardopts +0 -2
  4. data/README.md +1 -1
  5. data/docs/readme.md +1 -1
  6. data/docs/tutorial.md +2 -2
  7. data/examples/pipes/gzip_http_server.rb +2 -2
  8. data/examples/pipes/http_server.rb +1 -1
  9. data/examples/pipes/tcp_proxy.rb +1 -1
  10. data/ext/polyphony/backend_common.c +4 -4
  11. data/ext/polyphony/backend_io_uring.c +8 -8
  12. data/ext/polyphony/backend_libev.c +5 -5
  13. data/ext/polyphony/fiber.c +33 -42
  14. data/ext/polyphony/io_extensions.c +50 -37
  15. data/ext/polyphony/pipe.c +6 -20
  16. data/ext/polyphony/polyphony.c +72 -144
  17. data/ext/polyphony/queue.c +23 -63
  18. data/ext/polyphony/thread.c +4 -13
  19. data/lib/polyphony/adapters/process.rb +2 -5
  20. data/lib/polyphony/adapters/sequel.rb +2 -2
  21. data/lib/polyphony/core/debug.rb +1 -4
  22. data/lib/polyphony/core/exceptions.rb +1 -5
  23. data/lib/polyphony/core/resource_pool.rb +7 -8
  24. data/lib/polyphony/core/sync.rb +5 -8
  25. data/lib/polyphony/core/thread_pool.rb +3 -10
  26. data/lib/polyphony/core/throttler.rb +1 -5
  27. data/lib/polyphony/core/timer.rb +23 -30
  28. data/lib/polyphony/extensions/fiber.rb +513 -543
  29. data/lib/polyphony/extensions/io.rb +5 -14
  30. data/lib/polyphony/extensions/object.rb +283 -2
  31. data/lib/polyphony/extensions/openssl.rb +5 -26
  32. data/lib/polyphony/extensions/pipe.rb +6 -17
  33. data/lib/polyphony/extensions/socket.rb +24 -118
  34. data/lib/polyphony/extensions/thread.rb +3 -18
  35. data/lib/polyphony/extensions/timeout.rb +0 -1
  36. data/lib/polyphony/net.rb +5 -9
  37. data/lib/polyphony/version.rb +1 -1
  38. data/lib/polyphony.rb +2 -6
  39. data/test/test_io.rb +221 -221
  40. data/test/test_socket.rb +3 -3
  41. data/test/test_trace.rb +2 -2
  42. metadata +5 -9
  43. data/docs/index.md +0 -94
  44. data/docs/link_rewriter.rb +0 -17
  45. data/docs/main-concepts/index.md +0 -9
  46. data/lib/polyphony/core/global_api.rb +0 -309
  47. /data/{assets → docs/assets}/echo-fibers.svg +0 -0
  48. /data/{assets → docs/assets}/polyphony-logo.png +0 -0
  49. /data/{assets → docs/assets}/sleeping-fiber.svg +0 -0
data/test/test_socket.rb CHANGED
@@ -199,7 +199,7 @@ class TCPSocketWithRawBufferTest < MiniTest::Test
199
199
  [port, server]
200
200
  rescue Errno::EADDRINUSE
201
201
  retry
202
- end
202
+ end
203
203
 
204
204
  def setup
205
205
  super
@@ -337,13 +337,13 @@ class SSLSocketTest < MiniTest::Test
337
337
  def test_ssl_accept_loop
338
338
  authority = Localhost::Authority.fetch
339
339
  server_ctx = authority.server_context
340
-
340
+
341
341
  opts = {
342
342
  reuse_addr: true,
343
343
  dont_linger: true,
344
344
  secure_context: server_ctx
345
345
  }
346
-
346
+
347
347
  port = rand(10001..39999)
348
348
  server = Polyphony::Net.tcp_listen('127.0.0.1', port, opts)
349
349
  f = spin do
data/test/test_trace.rb CHANGED
@@ -49,7 +49,7 @@ class TraceTest < MiniTest::Test
49
49
  sleep 0
50
50
 
51
51
  Thread.backend.trace_proc = nil
52
-
52
+
53
53
  assert_equal [
54
54
  [:spin, f],
55
55
  [:schedule, f, nil, false],
@@ -143,7 +143,7 @@ class TraceTest < MiniTest::Test
143
143
  }
144
144
  }
145
145
  receive
146
-
146
+
147
147
  Polyphony::Trace.start_event_firehose { |e| receiver << e }
148
148
 
149
149
  f1 = spin(:f1) do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polyphony
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.99.4
4
+ version: 0.99.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-10 00:00:00.000000000 Z
11
+ date: 2023-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -144,9 +144,6 @@ files:
144
144
  - README.md
145
145
  - Rakefile
146
146
  - TODO.md
147
- - assets/echo-fibers.svg
148
- - assets/polyphony-logo.png
149
- - assets/sleeping-fiber.svg
150
147
  - bin/pdbg
151
148
  - bin/polyphony-debug
152
149
  - bin/stress.rb
@@ -154,15 +151,15 @@ files:
154
151
  - docs/_user-guide/all-about-timers.md
155
152
  - docs/_user-guide/index.md
156
153
  - docs/_user-guide/web-server.md
154
+ - docs/assets/echo-fibers.svg
155
+ - docs/assets/polyphony-logo.png
156
+ - docs/assets/sleeping-fiber.svg
157
157
  - docs/concurrency.md
158
158
  - docs/design-principles.md
159
159
  - docs/exception-handling.md
160
160
  - docs/extending.md
161
161
  - docs/faq.md
162
162
  - docs/fiber-scheduling.md
163
- - docs/index.md
164
- - docs/link_rewriter.rb
165
- - docs/main-concepts/index.md
166
163
  - docs/overview.md
167
164
  - docs/readme.md
168
165
  - docs/tutorial.md
@@ -335,7 +332,6 @@ files:
335
332
  - lib/polyphony/core/channel.rb
336
333
  - lib/polyphony/core/debug.rb
337
334
  - lib/polyphony/core/exceptions.rb
338
- - lib/polyphony/core/global_api.rb
339
335
  - lib/polyphony/core/resource_pool.rb
340
336
  - lib/polyphony/core/sync.rb
341
337
  - lib/polyphony/core/thread_pool.rb
data/docs/index.md DELETED
@@ -1,94 +0,0 @@
1
- ---
2
- layout: page
3
- title: Home
4
- nav_order: 1
5
- permalink: /
6
- next_title: Installing Polyphony
7
- ---
8
-
9
- <p align="center"><img src="{{ 'polyphony-logo.png' | absolute_url }}" /></p>
10
-
11
- # Polyphony
12
- {:.text-center .logo-title}
13
-
14
- ## Fine-grained concurrency for Ruby
15
- {:.text-center .logo-title}
16
-
17
- Polyphony is a library for building concurrent applications in Ruby. Polyphony
18
- implements a comprehensive
19
- [fiber](https://ruby-doc.org/core-2.5.1/Fiber.html)-based concurrency model,
20
- using [libev](https://github.com/enki/libev) as a high-performance event reactor
21
- for I/O, timers, and other asynchronous events.
22
-
23
- [Overview](getting-started/overview){: .btn .btn-green .text-gamma }
24
- [Take the tutorial](getting-started/tutorial){: .btn .btn-blue .text-gamma }
25
- [Source code](https://github.com/digital-fabric/polyphony){: .btn .btn-purple .text-gamma target="_blank" }
26
- {:.text-center .mt-6 .h-align-center }
27
-
28
- ## Focused on Developer Happiness
29
-
30
- Polyphony is designed to make concurrent Ruby programming feel natural and
31
- fluent. The Polyphony API is easy to use, easy to understand, and above all
32
- idiomatic.
33
-
34
- ## Optimized for High Performance
35
-
36
- Polyphony offers high performance for I/O bound Ruby apps. Distributing
37
- concurrent operations over fibers, instead of threads or processes, minimizes
38
- memory consumption and reduces the cost of context-switching.
39
-
40
- ## Designed for Interoperability
41
-
42
- With Polyphony you can use any of the stock Ruby classes and modules like `IO`,
43
- `Process`, `Socket` and `OpenSSL` in a concurrent multi-fiber environment. In
44
- addition, Polyphony provides a structured model for exception handling that
45
- builds on and enhances Ruby's exception handling system.
46
-
47
- ## A Growing Ecosystem
48
-
49
- Polyphony includes a full-blown HTTP server implementation with integrated
50
- support for HTTP 2, WebSockets, TLS/SSL termination and more. Polyphony also
51
- provides fiber-aware adapters for connecting to PostgreSQL and Redis. More
52
- adapters are being developed.
53
-
54
- ## Features
55
-
56
- * Co-operative scheduling of concurrent tasks using Ruby fibers.
57
- * High-performance event reactor for handling I/O events and timers.
58
- * Natural, sequential programming style that makes it easy to reason about
59
- concurrent code.
60
- * Abstractions and constructs for controlling the execution of concurrent code:
61
- supervisors, throttling, resource pools etc.
62
- * Code can use native networking classes and libraries, growing support for
63
- third-party gems such as `pg` and `redis`.
64
- * Use stdlib classes such as `TCPServer` and `TCPSocket` and `Net::HTTP`.
65
- * Competitive performance and scalability characteristics, in terms of both
66
- throughput and memory consumption.
67
-
68
- ## Prior Art
69
-
70
- Polyphony draws inspiration from the following, in no particular order:
71
-
72
- * [nio4r](https://github.com/socketry/nio4r/) and
73
- [async](https://github.com/socketry/async) (Polyphony's C-extension code
74
- started as a spinoff of
75
- [nio4r's](https://github.com/socketry/nio4r/tree/master/ext))
76
- * The [go scheduler](https://www.ardanlabs.com/blog/2018/08/scheduling-in-go-part2.html)
77
- * [EventMachine](https://github.com/eventmachine/eventmachine)
78
- * [Trio](https://trio.readthedocs.io/)
79
- * [Erlang supervisors](http://erlang.org/doc/man/supervisor.html) (and actually,
80
- Erlang in general)
81
-
82
- ## Developer Resources
83
-
84
- * [Tutorial](getting-started/tutorial)
85
- * [Main Concepts](main-concepts/concurrency/)
86
- * [User Guide](user-guide/all-about-timers/)
87
- * [API Reference](api-reference/exception/)
88
- * [Examples](https://github.com/digital-fabric/polyphony/tree/9e0f3b09213156bdf376ef33684ef267517f06e8/examples/README.md)
89
-
90
- ## Contributing to Polyphony
91
-
92
- Issues and pull requests will be gladly accepted. Please use the [Polyphony git
93
- repository](https://github.com/digital-fabric/polyphony) as your primary point
94
- of departure for contributing.
@@ -1,17 +0,0 @@
1
- require 'yard'
2
-
3
- # shamelessly copied from https://github.com/troessner/reek/blob/master/docs/yard_plugin.rb
4
-
5
- # Template helper to modify processing of links in HTML generated from our
6
- # markdown files.
7
- module LocalLinkHelper
8
- # Rewrites links to (assumed local) markdown files so they're processed as
9
- # {file: } directives.
10
- def resolve_links(text)
11
- text = text.gsub(%r{<a href="(docs/[^"]*.md)">([^<]*)</a>}, '{file:/\1 \2}')
12
- .gsub(%r{<img src="(assets/[^"]*)">}, '{rdoc-image:/\1}')
13
- super text
14
- end
15
- end
16
-
17
- YARD::Templates::Template.extra_includes << LocalLinkHelper
@@ -1,9 +0,0 @@
1
- ---
2
- layout: page
3
- title: Main Concepts
4
- has_children: true
5
- nav_order: 3
6
- ---
7
-
8
- # Main Concepts
9
- {: .no_toc }
@@ -1,309 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative './throttler'
4
-
5
- module Polyphony
6
-
7
- # Global API methods to be included in `::Object`
8
- module GlobalAPI
9
-
10
- # Spins up a fiber that will run the given block after sleeping for the
11
- # given delay.
12
- #
13
- # @param interval [Number] delay in seconds before running the given block
14
- # @yield [] block to run
15
- # @return [Fiber] spun fiber
16
- def after(interval, &block)
17
- spin do
18
- sleep interval
19
- block.()
20
- end
21
- end
22
-
23
- # call-seq:
24
- # cancel_after(interval) { ... }
25
- # cancel_after(interval, with_exception: exception) { ... }
26
- # cancel_after(interval, with_exception: [klass, message]) { ... }
27
- # cancel_after(interval) { |timeout| ... }
28
- # cancel_after(interval, with_exception: exception) { |timeout| ... }
29
- # cancel_after(interval, with_exception: [klass, message]) { |timeout| ... }
30
- #
31
- # Runs the given block after setting up a cancellation timer for
32
- # cancellation. If the cancellation timer elapses, the execution will be
33
- # interrupted with an exception defaulting to `Polyphony::Cancel`.
34
- #
35
- # This method should be used when a timeout should cause an exception to be
36
- # propagated down the call stack or up the fiber tree.
37
- #
38
- # Example of normal use:
39
- #
40
- # def read_from_io_with_timeout(io)
41
- # cancel_after(10) { io.read }
42
- # rescue Polyphony::Cancel
43
- # nil
44
- # end
45
- #
46
- # The timeout period can be reset by passing a block that takes a single
47
- # argument. The block will be provided with the canceller fiber. To reset
48
- # the timeout, use `Fiber#reset`, as shown in the following example:
49
- #
50
- # cancel_after(10) do |timeout|
51
- # loop do
52
- # msg = socket.gets
53
- # timeout.reset
54
- # handle_msg(msg)
55
- # end
56
- # end
57
- #
58
- # @param interval [Number] timout in seconds
59
- # @param with_exception [Class, Exception] exception or exception class
60
- # @yield [Fiber] block to execute
61
- # @return [any] block's return value
62
- def cancel_after(interval, with_exception: Polyphony::Cancel, &block)
63
- if block.arity > 0
64
- cancel_after_with_optional_reset(interval, with_exception, &block)
65
- else
66
- Polyphony.backend_timeout(interval, with_exception, &block)
67
- end
68
- end
69
-
70
- # Spins up a new fiber.
71
- #
72
- # @param tag [any] optional tag for the new fiber
73
- # @yield [any] fiber block
74
- # @return [Fiber] new fiber
75
- def spin(tag = nil, &block)
76
- Fiber.current.spin(tag, caller, &block)
77
- end
78
-
79
- # Spins up a new fiber, running the given block inside an infinite loop. If
80
- # `rate:` or `interval:` parameters are given, the loop is throttled
81
- # accordingly.
82
- #
83
- # @param tag [any] optional tag for the new fiber
84
- # @param rate [Number, nil] loop rate (times per second)
85
- # @param interval [Number, nil] interval between consecutive iterations in seconds
86
- # @yield [any] code to run
87
- # @return [Fiber] new fiber
88
- def spin_loop(tag = nil, rate: nil, interval: nil, &block)
89
- if rate || interval
90
- Fiber.current.spin(tag, caller) do
91
- throttled_loop(rate: rate, interval: interval, &block)
92
- end
93
- else
94
- spin_loop_without_throttling(tag, caller, block)
95
- end
96
- end
97
-
98
- # Runs the given code, then waits for any child fibers of the current fibers
99
- # to terminate.
100
- #
101
- # @yield [any] code to run
102
- # @return [any] given block's return value
103
- def spin_scope(&block)
104
- raise unless block
105
-
106
- spin do
107
- result = yield
108
- Fiber.current.await_all_children
109
- result
110
- end.await
111
- end
112
-
113
- # Runs the given block in an infinite loop with a regular interval between
114
- # consecutive iterations.
115
- #
116
- # @param interval [Number] interval between consecutive iterations in seconds
117
- # @yield [any] block to run
118
- # @return [void]
119
- def every(interval, &block)
120
- Polyphony.backend_timer_loop(interval, &block)
121
- end
122
-
123
- # call-seq:
124
- # move_on_after(interval) { ... }
125
- # move_on_after(interval, with_value: value) { ... }
126
- # move_on_after(interval) { |canceller| ... }
127
- # move_on_after(interval, with_value: value) { |canceller| ... }
128
- #
129
- # Runs the given block after setting up a cancellation timer for
130
- # cancellation. If the cancellation timer elapses, the execution will be
131
- # interrupted with a `Polyphony::MoveOn` exception, which will be rescued,
132
- # and with cause the operation to return the given value.
133
- #
134
- # This method should be used when a timeout is to be handled locally,
135
- # without generating an exception that is to propagated down the call stack
136
- # or up the fiber tree.
137
- #
138
- # Example of normal use:
139
- #
140
- # move_on_after(10) {
141
- # sleep 60
142
- # 42
143
- # } #=> nil
144
- #
145
- # move_on_after(10, with_value: :oops) {
146
- # sleep 60
147
- # 42
148
- # } #=> :oops
149
- #
150
- # The timeout period can be reset by passing a block that takes a single
151
- # argument. The block will be provided with the canceller fiber. To reset
152
- # the timeout, use `Fiber#reset`, as shown in the following example:
153
- #
154
- # move_on_after(10) do |timeout|
155
- # loop do
156
- # msg = socket.gets
157
- # timeout.reset
158
- # handle_msg(msg)
159
- # end
160
- # end
161
- #
162
- # @param interval [Number] timout in seconds
163
- # @param with_value [any] return value in case of timeout
164
- # @yield [Fiber] block to execute
165
- # @return [any] block's return value
166
- def move_on_after(interval, with_value: nil, &block)
167
- if block.arity > 0
168
- move_on_after_with_optional_reset(interval, with_value, &block)
169
- else
170
- Polyphony.backend_timeout(interval, nil, with_value, &block)
171
- end
172
- end
173
-
174
- # Returns the first message from the current fiber's mailbox. If the mailbox
175
- # is empty, blocks until a message is available.
176
- #
177
- # @return [any] received message
178
- def receive
179
- Fiber.current.receive
180
- end
181
-
182
- # Returns all messages currently pending on the current fiber's mailbox.
183
- #
184
- # @return [Array] array of received messages
185
- def receive_all_pending
186
- Fiber.current.receive_all_pending
187
- end
188
-
189
- # Supervises the current fiber's children. See `Fiber#supervise` for
190
- # options.
191
- #
192
- # @param args [Array] positional parameters
193
- # @param opts [Hash] named parameters
194
- # @yield [any] given block
195
- # @return [void]
196
- def supervise(*args, **opts, &block)
197
- Fiber.current.supervise(*args, **opts, &block)
198
- end
199
-
200
- # Sleeps for the given duration. If the duration is `nil`, sleeps
201
- # indefinitely.
202
- #
203
- # @param duration [Number, nil] duration
204
- # @return [void]
205
- def sleep(duration = nil)
206
- duration ?
207
- Polyphony.backend_sleep(duration) : Polyphony.backend_wait_event(true)
208
- end
209
-
210
- # call-seq:
211
- # throttled_loop(rate) { ... }
212
- # throttled_loop(interval: value) { ... }
213
- # throttled_loop(rate: value) { ... }
214
- # throttled_loop(rate, count: value) { ... }
215
- #
216
- # Starts a throttled loop with the given rate. If `count:` is given, the
217
- # loop is run for the given number of times. Otherwise, the loop is
218
- # infinite. The loop rate (times per second) can be given as the rate
219
- # parameter. The throttling can also be controlled by providing an
220
- # `interval:` or `rate:` named parameter.
221
- #
222
- # @param rate [Number, nil] loop rate (times per second)
223
- # @option opts [Number] :rate loop rate (times per second)
224
- # @option opts [Number] :interval loop interval in seconds
225
- # @option opts [Number] :count number of iterations (nil for infinite)
226
- # @yield [] code to run
227
- # @return [void]
228
- def throttled_loop(rate = nil, **opts, &block)
229
- throttler = Polyphony::Throttler.new(rate || opts)
230
- if opts[:count]
231
- opts[:count].times { |_i| throttler.(&block) }
232
- else
233
- while true
234
- throttler.(&block)
235
- end
236
- end
237
- rescue LocalJumpError, StopIteration
238
- # break called or StopIteration raised
239
- end
240
-
241
- private
242
-
243
- # Helper method for performing a `cancel_after` with optional reset.
244
- #
245
- # @param interval [Number] timeout interval in seconds
246
- # @param exception [Exception, Class, Array<class, message>] exception spec
247
- # @yield [Fiber] block to run
248
- # @return [any] block's return value
249
- def cancel_after_with_optional_reset(interval, exception, &block)
250
- fiber = Fiber.current
251
- canceller = spin do
252
- Polyphony.backend_sleep(interval)
253
- exception = cancel_exception(exception)
254
- exception.raising_fiber = Fiber.current
255
- fiber.cancel(exception)
256
- end
257
- block.call(canceller)
258
- ensure
259
- canceller.stop
260
- end
261
-
262
- # Converts the given exception spec to an exception instance.
263
- #
264
- # @param exception [Exception, Class, Array<class, message>] exception spec
265
- # @return [Exception] exception instance
266
- def cancel_exception(exception)
267
- case exception
268
- when Class then exception.new
269
- when Array then exception[0].new(exception[1])
270
- else RuntimeError.new(exception)
271
- end
272
- end
273
-
274
- # Helper method for performing `#spin_loop` without throttling. Spins up a
275
- # new fiber in which to run the loop.
276
- #
277
- # @param tag [any] new fiber's tag
278
- # @param caller [Array<String>] caller info
279
- # @param block [Proc] code to run
280
- # @return [void]
281
- def spin_loop_without_throttling(tag, caller, block)
282
- Fiber.current.spin(tag, caller) do
283
- block.call while true
284
- rescue LocalJumpError, StopIteration
285
- # break called or StopIteration raised
286
- end
287
- end
288
-
289
- # Helper method for performing `#move_on_after` with optional reset.
290
- #
291
- # @param interval [Number] timeout interval in seconds
292
- # @param value [any] return value in case of timeout
293
- # @yield [Fiber] code to run
294
- # @return [any] return value of given block or timeout value
295
- def move_on_after_with_optional_reset(interval, value, &block)
296
- fiber = Fiber.current
297
- canceller = spin do
298
- sleep interval
299
- fiber.move_on(value)
300
- end
301
- block.call(canceller)
302
- rescue Polyphony::MoveOn => e
303
- e.value
304
- ensure
305
- canceller.stop
306
- end
307
-
308
- end
309
- end
File without changes
File without changes
File without changes