polyphony 0.40 → 0.41

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +11 -2
  3. data/.gitignore +2 -2
  4. data/.rubocop.yml +30 -0
  5. data/CHANGELOG.md +6 -2
  6. data/Gemfile.lock +9 -6
  7. data/Rakefile +2 -2
  8. data/TODO.md +18 -97
  9. data/docs/_includes/head.html +40 -0
  10. data/docs/_includes/nav.html +5 -5
  11. data/docs/api-reference/fiber.md +2 -2
  12. data/docs/main-concepts/design-principles.md +67 -9
  13. data/docs/main-concepts/extending.md +1 -1
  14. data/examples/core/xx-agent.rb +102 -0
  15. data/examples/core/xx-sleeping.rb +14 -6
  16. data/examples/io/xx-irb.rb +1 -1
  17. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
  18. data/examples/performance/thread-vs-fiber/polyphony_server.rb +14 -25
  19. data/ext/{gyro → polyphony}/extconf.rb +2 -2
  20. data/ext/{gyro → polyphony}/fiber.c +15 -19
  21. data/ext/{gyro → polyphony}/libev.c +0 -0
  22. data/ext/{gyro → polyphony}/libev.h +0 -0
  23. data/ext/polyphony/libev_agent.c +503 -0
  24. data/ext/polyphony/libev_queue.c +214 -0
  25. data/ext/{gyro/gyro.c → polyphony/polyphony.c} +16 -25
  26. data/ext/polyphony/polyphony.h +90 -0
  27. data/ext/polyphony/polyphony_ext.c +23 -0
  28. data/ext/{gyro → polyphony}/socket.c +14 -14
  29. data/ext/{gyro → polyphony}/thread.c +32 -115
  30. data/ext/{gyro → polyphony}/tracing.c +1 -1
  31. data/lib/polyphony.rb +16 -12
  32. data/lib/polyphony/adapters/irb.rb +1 -1
  33. data/lib/polyphony/adapters/postgres.rb +6 -5
  34. data/lib/polyphony/adapters/process.rb +5 -5
  35. data/lib/polyphony/adapters/trace.rb +28 -28
  36. data/lib/polyphony/core/channel.rb +3 -3
  37. data/lib/polyphony/core/exceptions.rb +1 -1
  38. data/lib/polyphony/core/global_api.rb +11 -9
  39. data/lib/polyphony/core/resource_pool.rb +3 -3
  40. data/lib/polyphony/core/sync.rb +2 -2
  41. data/lib/polyphony/core/thread_pool.rb +6 -6
  42. data/lib/polyphony/core/throttler.rb +13 -6
  43. data/lib/polyphony/event.rb +27 -0
  44. data/lib/polyphony/extensions/core.rb +20 -11
  45. data/lib/polyphony/extensions/fiber.rb +4 -4
  46. data/lib/polyphony/extensions/io.rb +56 -26
  47. data/lib/polyphony/extensions/openssl.rb +4 -8
  48. data/lib/polyphony/extensions/socket.rb +27 -9
  49. data/lib/polyphony/extensions/thread.rb +16 -9
  50. data/lib/polyphony/net.rb +9 -9
  51. data/lib/polyphony/version.rb +1 -1
  52. data/polyphony.gemspec +2 -2
  53. data/test/helper.rb +12 -1
  54. data/test/test_agent.rb +77 -0
  55. data/test/{test_async.rb → test_event.rb} +13 -7
  56. data/test/test_ext.rb +25 -4
  57. data/test/test_fiber.rb +19 -10
  58. data/test/test_global_api.rb +4 -4
  59. data/test/test_io.rb +46 -24
  60. data/test/test_queue.rb +74 -0
  61. data/test/test_signal.rb +3 -40
  62. data/test/test_socket.rb +33 -0
  63. data/test/test_thread.rb +37 -16
  64. data/test/test_trace.rb +6 -5
  65. metadata +24 -24
  66. data/ext/gyro/async.c +0 -132
  67. data/ext/gyro/child.c +0 -108
  68. data/ext/gyro/gyro.h +0 -158
  69. data/ext/gyro/gyro_ext.c +0 -33
  70. data/ext/gyro/io.c +0 -457
  71. data/ext/gyro/queue.c +0 -146
  72. data/ext/gyro/selector.c +0 -205
  73. data/ext/gyro/signal.c +0 -99
  74. data/ext/gyro/timer.c +0 -115
  75. data/test/test_timer.rb +0 -56
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2fac2ba159f62bc767c1c022a9ac84e0d682e9dc1cbb10f53e92ba53da376745
4
- data.tar.gz: 8d43be97359735d5b7ac6b9867716c06c420385bb41f14916edd0a2d2bbca454
3
+ metadata.gz: ebbaa936b265f2e46ff30cab71b7c85e5ebd31396f7b8ecb8fc01156f6e35f79
4
+ data.tar.gz: 677c90c266a7d677f124f964775d849a350f925c0441f9aa6cba3a4745ff1e5d
5
5
  SHA512:
6
- metadata.gz: 0350c4dbee7ad72ba68dfff4fc431870481f43a763d9e265e05ade0ce9545456adef2a79c5efd98ce7fc28b2314ab5558a9e69f9e9ef92759e5ae320c78435be
7
- data.tar.gz: 7c7774de233962a1b06bac02623a6127f9b716e36852bc6e5849227db80fc0e910549756c83441f6d40dd75a22c26253fcfeef7e50d62482c1cc7603f6c4b2f3
6
+ metadata.gz: 732f1eaa117ec2483661451b98a43192ec3e757bde24b4f0eb115fee057efd55fa306af2ed484d3fa943b09e2913a76b9b34a4a8f7eed3a64ea06c27c44b3c17
7
+ data.tar.gz: b9a0595dbf7f338c0b1d67f121090261f0a6efb8c1cb342717a5b94b54a346e1eeadee9b62fd916c4cc3ad400d5466119b3a806f4f611b863682eef10b6b2cfe
@@ -4,12 +4,21 @@ on: [push]
4
4
 
5
5
  jobs:
6
6
  build:
7
- runs-on: ubuntu-latest
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ os: [ubuntu-latest]
11
+ ruby: [2.6, 2.7]
12
+
13
+ name: >-
14
+ ${{matrix.os}}, ${{matrix.ruby}}
15
+
16
+ runs-on: ${{matrix.os}}
8
17
  steps:
9
18
  - uses: actions/checkout@v1
10
19
  - uses: actions/setup-ruby@v1
11
20
  with:
12
- ruby-version: 2.6.5
21
+ ruby-version: ${{matrix.ruby}}
13
22
  - name: Install dependencies
14
23
  run: |
15
24
  gem install bundler
data/.gitignore CHANGED
@@ -52,8 +52,8 @@ build-iPhoneSimulator/
52
52
  test.rb
53
53
  .vscode
54
54
 
55
- lib/gyro_ext.bundle
56
- lib/gyro_ext.so
55
+ lib/*.bundle
56
+ lib/*.so
57
57
 
58
58
  _site
59
59
  .sass-cache
@@ -143,3 +143,33 @@ Style/HashTransformKeys:
143
143
 
144
144
  Style/HashTransformValues:
145
145
  Enabled: true
146
+
147
+ Layout/EmptyLinesAroundAttributeAccessor:
148
+ Enabled: true
149
+
150
+ Layout/SpaceAroundMethodCallOperator:
151
+ Enabled: true
152
+
153
+ Lint/DeprecatedOpenSSLConstant:
154
+ Enabled: true
155
+
156
+ Lint/MixedRegexpCaptureTypes:
157
+ Enabled: true
158
+
159
+ Lint/RaiseException:
160
+ Enabled: true
161
+
162
+ Lint/StructNewOverride:
163
+ Enabled: true
164
+
165
+ Style/ExponentialNotation:
166
+ Enabled: true
167
+
168
+ Style/RedundantRegexpCharacterClass:
169
+ Enabled: true
170
+
171
+ Style/RedundantRegexpEscape:
172
+ Enabled: true
173
+
174
+ Style/SlicingWithRange:
175
+ Enabled: true
@@ -1,3 +1,7 @@
1
+ ## 0.41 2020-06-27
2
+
3
+ * Introduce System Agent design, remove all `Gyro` classes
4
+
1
5
  ## 0.40 2020-05-04
2
6
 
3
7
  * More improvements to stability after fork
@@ -21,13 +25,13 @@
21
25
 
22
26
  * Rename `Fiber#cancel!` to `Fiber#cancel`
23
27
  * Rename `Gyro::Async#signal!` to `Gyro::Async#signal`
24
- * Use `Fiber#auto_async` in thread pool, thread extension
28
+ * Use `Fiber#auto_watcher` in thread pool, thread extension
25
29
  * Implement `Fiber#auto_io` for reusing IO watcher instances
26
30
  * Refactor C code
27
31
 
28
32
  ## 0.34 2020-03-25
29
33
 
30
- * Add `Fiber#auto_async` mainly for use in places like `Gyro::Queue#shift`
34
+ * Add `Fiber#auto_watcher` mainly for use in places like `Gyro::Queue#shift`
31
35
  * Refactor C extension
32
36
  * Improved GC'ing for watchers
33
37
  * Implement process supervisor (`Polyphony::ProcessSupervisor`)
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.40)
4
+ polyphony (0.41)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -27,7 +27,6 @@ GEM
27
27
  multi_xml (>= 0.5.2)
28
28
  i18n (0.9.5)
29
29
  concurrent-ruby (~> 1.0)
30
- jaro_winkler (1.5.4)
31
30
  jekyll (3.8.6)
32
31
  addressable (~> 2.4)
33
32
  colorator (~> 1.0)
@@ -88,16 +87,20 @@ GEM
88
87
  rb-inotify (0.10.1)
89
88
  ffi (~> 1.0)
90
89
  redis (4.1.0)
90
+ regexp_parser (1.7.1)
91
91
  rexml (3.2.4)
92
92
  rouge (3.15.0)
93
- rubocop (0.80.0)
94
- jaro_winkler (~> 1.5.1)
93
+ rubocop (0.85.1)
95
94
  parallel (~> 1.10)
96
95
  parser (>= 2.7.0.1)
97
96
  rainbow (>= 2.2.2, < 4.0)
97
+ regexp_parser (>= 1.7)
98
98
  rexml
99
+ rubocop-ast (>= 0.0.3)
99
100
  ruby-progressbar (~> 1.7)
100
- unicode-display_width (>= 1.4.0, < 1.7)
101
+ unicode-display_width (>= 1.4.0, < 2.0)
102
+ rubocop-ast (0.0.3)
103
+ parser (>= 2.7.0.1)
101
104
  ruby-progressbar (1.10.1)
102
105
  rubyzip (2.0.0)
103
106
  safe_yaml (1.0.5)
@@ -131,7 +134,7 @@ DEPENDENCIES
131
134
  polyphony!
132
135
  rake-compiler (= 1.0.5)
133
136
  redis (= 4.1.0)
134
- rubocop (= 0.80.0)
137
+ rubocop (= 0.85.1)
135
138
  simplecov (= 0.17.1)
136
139
 
137
140
  BUNDLED WITH
data/Rakefile CHANGED
@@ -4,8 +4,8 @@ require "bundler/gem_tasks"
4
4
  require "rake/clean"
5
5
 
6
6
  require "rake/extensiontask"
7
- Rake::ExtensionTask.new("gyro_ext") do |ext|
8
- ext.ext_dir = "ext/gyro"
7
+ Rake::ExtensionTask.new("polyphony_ext") do |ext|
8
+ ext.ext_dir = "ext/polyphony"
9
9
  end
10
10
 
11
11
  task :recompile => [:clean, :compile]
data/TODO.md CHANGED
@@ -1,42 +1,8 @@
1
- - Would it be possible to spin up a fiber on another thread?
2
-
3
- The use case is being able to supervise fibers that run on separate threads.
4
- This might be useful for distributing jobs (such as handling HTTP connections)
5
- over multiple threads.
1
+ ## 0.42 Update docs
6
2
 
7
- For this we need:
3
+ -
8
4
 
9
- - A way to communicate to a thread that it needs to spin up a fiber, the
10
- simplest solution is to start a fiber accepting spin requests for each
11
- thread (in `Thread#initialize`).
12
- - An API:
13
-
14
- ```ruby
15
- spin(on_thread: thread) { do_something_important }
16
- ```
17
-
18
- An alternative is to turn the main fiber of spawned threads into a child of
19
- the spawning fiber. But since a lot of people might start threads without any
20
- regard to fibers, it might be better to implement this in a new API. An
21
- example of the top of my head for threads that shouldn't be children of the
22
- spawning fiber is our own test helper, which kills all child fibers after each
23
- test. MiniTest has some threads it spawns for running tests in parallel, and
24
- we don't want to stop them after each test!
25
-
26
- So, a good solution would be:
27
-
28
- ```ruby
29
- t = Thread.new { do_stuff }
30
- t.parent_fiber = Fiber.current
31
- # or otherwise:
32
- Fiber.current.add_child_fiber(t.main_fiber)
33
- ```
34
-
35
-
36
-
37
-
38
-
39
- ## 0.40 Some more API work, more docs
5
+ ## 0.43 Some more API work, more docs
40
6
 
41
7
  - Debugging
42
8
  - Eat your own dogfood: need a good tool to check what's going on when some
@@ -148,13 +114,9 @@
148
114
  - links to the interesting stuff
149
115
  - benchmarks
150
116
  - explain difference between `sleep` and `suspend`
151
- - add explanation about async vs sync, blocking vs non-blocking
152
117
  - discuss using `snooze` for ensuring responsiveness when executing CPU-bound work
153
118
 
154
- - Check why first call to `#sleep` returns too early in tests. Check the
155
- sleep behaviour in a spawned thread.
156
-
157
- ## 0.41 Sinatra / Sidekiq
119
+ ## 0.44 Sinatra / Sidekiq
158
120
 
159
121
  - sintra app with database access (postgresql)
160
122
 
@@ -164,13 +126,11 @@
164
126
  - test performance
165
127
  - proceed from there
166
128
 
167
- ## 0.42 Testing && Docs
129
+ ## 0.45 Testing && Docs
168
130
 
169
131
  - Pull out redis/postgres code, put into new `polyphony-xxx` gems
170
132
 
171
- ## 0.43 Integration
172
-
173
- ## 0.44 Real IO#gets and IO#read
133
+ ## 0.46 Real IO#gets and IO#read
174
134
 
175
135
  - More tests
176
136
  - Implement some basic stuff missing:
@@ -180,11 +140,11 @@
180
140
  - `IO.foreach`
181
141
  - `Process.waitpid`
182
142
 
183
- ## 0.45 Rails
143
+ ## 0.47 Rails
184
144
 
185
145
  - Rails?
186
146
 
187
- ## 0.46 DNS
147
+ ## 0.48 DNS
188
148
 
189
149
  ### DNS client
190
150
 
@@ -217,56 +177,17 @@ Prior art:
217
177
 
218
178
  - https://github.com/socketry/async-dns
219
179
 
220
- ### Work on API
221
-
222
- - Introduce mailbox limiting:
223
- - add API for limiting mailbox size:
224
-
225
- ```ruby
226
- Fiber.current.mailbox_limit = 1000
227
- ```
228
-
229
- - Add the limit for `Gyro::Queue`
230
-
231
- ```ruby
232
- Gyro::Queue.new(1000)
233
- ```
234
-
235
- - Pushing to a limited queue will block if limit is reached
236
-
237
- - Introduce selective receive:
238
-
239
- ```ruby
240
- # returns (or waits for) the first message for which the block returns true
241
- (_, item) = receive { |msg| msg.first == ref }
242
- ```
243
-
244
- Possible implementation:
245
-
246
- ```ruby
247
- def receive
248
- return @mailbox.shift unless block_given?
249
-
250
- loop
251
- msg = @mailbox.shift
252
- return msg if yield(msg)
253
-
254
- # message didn't match condition, put it back in queue
255
- @mailbox.push msg
256
- end
257
- end
258
- ```
180
+ ## Work on API
259
181
 
260
182
  - Add option for setting the exception raised on cancelling using `#cancel_after`:
261
183
 
262
- ```ruby
263
- cancel_after(3, with_error: MyErrorClass) do
264
- do_my_thing
265
- end
266
-
267
- # or a RuntimeError with message
268
- cancel_after(3, with_error: 'Cancelling due to timeout') do
269
- do_my_thing
270
- end
271
- ```
184
+ ```ruby
185
+ cancel_after(3, with_error: MyErrorClass) do
186
+ do_my_thing
187
+ end
188
+ # or a RuntimeError with message
189
+ cancel_after(3, with_error: 'Cancelled due to timeout') do
190
+ do_my_thing
191
+ end
192
+ ```
272
193
 
@@ -0,0 +1,40 @@
1
+ <head>
2
+ <meta charset="UTF-8">
3
+ <meta http-equiv="X-UA-Compatible" content="IE=Edge">
4
+
5
+ {% if site.plugins.jekyll-seo == nil %}
6
+ <title>{{ page.title }} - {{ site.title }}</title>
7
+
8
+ {% if page.description %}
9
+ <meta name="Description" content="{{ page.description }}">
10
+ {% endif %}
11
+ {% endif %}
12
+
13
+ <link rel="shortcut icon" href="{{ '/favicon.ico' | relative_url }}" type="image/x-icon">
14
+
15
+ <link rel="stylesheet" href="{{ '/assets/css/just-the-docs.css' | relative_url }}">
16
+
17
+ {% if site.ga_tracking != nil %}
18
+ <script async src="https://www.googletagmanager.com/gtag/js?id={{ site.ga_tracking }}"></script>
19
+ <script>
20
+ window.dataLayer = window.dataLayer || [];
21
+ function gtag(){dataLayer.push(arguments);}
22
+ gtag('js', new Date());
23
+
24
+ gtag('config', "{{ site.ga_tracking }}");
25
+ </script>
26
+
27
+ {% endif %}
28
+
29
+ {% if site.search_enabled != false %}
30
+ <script type="text/javascript" src="{{ '/assets/js/vendor/lunr.min.js' | relative_url }}"></script>
31
+ {% endif %}
32
+ <script type="text/javascript" src="{{ '/assets/js/just-the-docs.js' | relative_url }}"></script>
33
+
34
+ <meta name="viewport" content="width=device-width, initial-scale=1">
35
+
36
+ {% seo %}
37
+
38
+ {% include head_custom.html %}
39
+
40
+ </head>
@@ -9,12 +9,12 @@
9
9
  {% if node.section %}section-title{% endif %}
10
10
  ">
11
11
  {%- if page.parent == node.title or page.grand_parent == node.title -%}
12
- {%- assign first_level_url = node.section_link | node.url | absolute_url -%}
12
+ {%- assign first_level_url = node.section_link | node.url | relative_url -%}
13
13
  {%- endif -%}
14
14
  {%- if node.section -%}
15
15
  <span class="section-title">{{ node.title }}</span>
16
16
  {%- else -%}
17
- <a href="{{ node.url | absolute_url }}" class="navigation-list-link{% if page.url == node.url %} active{% endif %}">{{ node.title }}</a>
17
+ <a href="{{ node.url | relative_url }}" class="navigation-list-link{% if page.url == node.url %} active{% endif %}">{{ node.title }}</a>
18
18
  {%- endif -%}
19
19
  {%- if node.has_children -%}
20
20
  {%- if node.alphabetical_order -%}
@@ -26,15 +26,15 @@
26
26
  {%- for child in children_list -%}
27
27
  <li class="navigation-list-item {% if page.url == child.url or page.parent == child.title %} active{% endif %}">
28
28
  {%- if page.url == child.url or page.parent == child.title -%}
29
- {%- assign second_level_url = child.url | absolute_url -%}
29
+ {%- assign second_level_url = child.url | relative_url -%}
30
30
  {%- endif -%}
31
- <a href="{{ child.url | absolute_url }}" class="navigation-list-link{% if page.url == child.url %} active{% endif %}">{{ child.title }}</a>
31
+ <a href="{{ child.url | relative_url }}" class="navigation-list-link{% if page.url == child.url %} active{% endif %}">{{ child.title }}</a>
32
32
  {%- if child.has_children -%}
33
33
  {%- assign grand_children_list = site.html_pages | where: "parent", child.title | sort:"nav_order" -%}
34
34
  <ul class="navigation-list-child-list">
35
35
  {%- for grand_child in grand_children_list -%}
36
36
  <li class="navigation-list-item {% if page.url == grand_child.url %} active{% endif %}">
37
- <a href="{{ grand_child.url | absolute_url }}" class="navigation-list-link{% if page.url == grand_child.url %} active{% endif %}">{{ grand_child.title }}</a>
37
+ <a href="{{ grand_child.url | relative_url }}" class="navigation-list-link{% if page.url == grand_child.url %} active{% endif %}">{{ grand_child.title }}</a>
38
38
  </li>
39
39
  {%- endfor -%}
40
40
  </ul>
@@ -71,7 +71,7 @@ f << 2
71
71
  result = receive #=> 20
72
72
  ```
73
73
 
74
- ### #auto_async → async
74
+ ### #auto_watcher → async
75
75
 
76
76
  Returns a reusable `Gyro::Async` watcher instance associated with the fiber.
77
77
  This method provides a way to minimize watcher allocation. Instead of allocating
@@ -84,7 +84,7 @@ def work(async)
84
84
  async.signal
85
85
  end
86
86
 
87
- async = Fiber.current.auto_async
87
+ async = Fiber.current.auto_watcher
88
88
  spin { work(async) }
89
89
  async.await
90
90
  ```
@@ -1,18 +1,76 @@
1
1
  ---
2
2
  layout: page
3
- title: Design Principles
3
+ title: The Design of Polyphony
4
4
  nav_order: 5
5
5
  parent: Main Concepts
6
6
  permalink: /main-concepts/design-principles/
7
7
  prev_title: Extending Polyphony
8
8
  ---
9
- # Design Principles
9
+ # The Design of Polyphony
10
+
11
+ Polyphony is a new gem that aims to enable developing high-performance
12
+ concurrent applications in Ruby using a fluent, compact syntax and API.
13
+ Polyphony enables fine-grained concurrency - the splitting up of operations into
14
+ a large number of concurrent tasks, each concerned with small part of the whole
15
+ and advancing at its own pace. Polyphony aims to solve some of the problems
16
+ associated with concurrent Ruby programs using a novel design that sets it apart
17
+ from other approaches currently being used in Ruby.
18
+
19
+ ## Origins
20
+
21
+ The Ruby core language (at least in its MRI implementation) currently provides
22
+ two main constructs for performing concurrent work: threads and fibers. While
23
+ Ruby threads are basically wrappers for OS threads, fibers are essentially
24
+ continuations, allowing pausing and resuming distinct computations. Fibers have
25
+ been traditionally used mostly for implementing enumerators and generators.
26
+
27
+ In addition to the core Ruby concurrency primitives, some Ruby gems have been
28
+ offering an alternative solution to writing concurrent Ruby apps, most notably
29
+ [EventMachine](https://github.com/eventmachine/eventmachine/), which implements
30
+ an event reactor and offers an asynchronous callback-based API for writing
31
+ concurrent code.
32
+
33
+ In the last couple of years, however, fibers have been receiving more attention
34
+ as a possible constructs for writing concurrent programs. In particular, the
35
+ [Async](https://github.com/socketry/async) framework, created by [Samuel
36
+ Williams](https://github.com/ioquatix), offering a comprehensive set of
37
+ libraries, employs fibers in conjunction with an event reactor provided by the
38
+ [nio4r](https://github.com/socketry/nio4r) gem, which wraps the C
39
+ library [libev](http://software.schmorp.de/pkg/libev.html).
40
+
41
+ In addition, recently some effort was undertaken to provide a way to
42
+ [automatically switch between fibers](https://bugs.ruby-lang.org/issues/13618)
43
+ whenever a blocking operation is performed, or to [integrate a fiber
44
+ scheduler](https://bugs.ruby-lang.org/issues/16786) into the core Ruby code.
45
+
46
+ Nevertheless, while work is being done to harness fibers for providing a better
47
+ way to do concurrency in Ruby, fibers remain a mistery for most Ruby
48
+ programmers, a perplexing unfamiliar corner right at the heart of Ruby.
49
+
50
+ ## Design Principles
51
+
52
+ Polyphony started as an experiment, but over about two years of slow, jerky
53
+ evolution turned into something I'm really excited to share with the Ruby
54
+ community. Polyphony's design is both similar and different than the projects
55
+ mentioned above.
56
+
57
+ Polyphony today as nothing like the way it began. A careful examination of the
58
+ [CHANGELOG](https://github.com/digital-fabric/polyphony/blob/master/CHANGELOG.md)
59
+ would show how Polyphony explored not only different event reactor designs, but
60
+ also different API designs incorporating various concurrent paradigms such as
61
+ promises, async/await, fibers, and finally structured concurrency.
62
+
63
+ While Polyphony, like nio4r or EventMachine, uses an event reactor to turn
64
+ blocking operations into non-blocking ones, it completely embraces fibers and in
65
+ fact does not provide any callback-based APIs. Furthermore, Polyphony provides
66
+ fullblown fiber-aware implementations of blocking operations, such as
67
+ `read/write`, `sleep` or `waitpid`, instead of just event watching primitives.
68
+
69
+ Throughout the development process, it was my intention to create a programming
70
+ interface that would make highly-concurrent
71
+
72
+
10
73
 
11
- Polyphony was created in order to enable developing high-performance concurrent
12
- applications in Ruby using a fluent, compact syntax and API. Polyphony enables
13
- fine-grained concurrency - the splitting up of operations into a large number of
14
- concurrent tasks, each concerned with small part of the whole and advancing at
15
- its own pace.
16
74
 
17
75
 
18
76
 
@@ -46,8 +104,8 @@ library. Polyphony's design is based on the following principles:
46
104
  async callback-style APIs.
47
105
 
48
106
  ```ruby
49
- # in Polyphony, I/O ops block the current fiber, but implicitly yield to other
50
- # concurrent fibers:
107
+ # in Polyphony, I/O ops might block the current fiber, but implicitly yield to
108
+ # other concurrent fibers:
51
109
  clients.each { |client|
52
110
  spin { client.puts 'Elvis has left the chatroom' }
53
111
  }