polyphony 0.99.4 → 0.99.6

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 (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