polyphony 0.20 → 0.21

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitbook.yaml +1 -2
  3. data/.rubocop.yml +1 -0
  4. data/CHANGELOG.md +10 -0
  5. data/Gemfile.lock +1 -1
  6. data/README.md +18 -449
  7. data/TODO.md +0 -10
  8. data/docs/README.md +39 -0
  9. data/docs/getting-started/installing.md +28 -0
  10. data/docs/getting-started/tutorial.md +133 -0
  11. data/docs/summary.md +37 -3
  12. data/docs/technical-overview/concurrency.md +47 -0
  13. data/docs/technical-overview/design-principles.md +112 -0
  14. data/docs/technical-overview/exception-handling.md +34 -41
  15. data/docs/technical-overview/extending.md +80 -0
  16. data/docs/technical-overview/faq.md +74 -0
  17. data/docs/technical-overview/fiber-scheduling.md +23 -52
  18. data/docs/user-guide/web-server.md +129 -0
  19. data/examples/core/01-spinning-up-coprocesses.rb +21 -0
  20. data/examples/core/02-awaiting-coprocesses.rb +18 -0
  21. data/examples/core/03-interrupting.rb +34 -0
  22. data/examples/core/04-no-auto-run.rb +18 -0
  23. data/examples/core/mem-usage.rb +34 -0
  24. data/examples/core/spin_error.rb +0 -1
  25. data/examples/core/spin_uncaught_error.rb +0 -1
  26. data/examples/core/wait_for_signal.rb +14 -0
  27. data/examples/http/http_server_graceful.rb +25 -0
  28. data/examples/http/http_server_simple.rb +11 -0
  29. data/examples/interfaces/redis_pubsub_perf.rb +1 -1
  30. data/ext/gyro/async.c +4 -40
  31. data/ext/gyro/child.c +0 -42
  32. data/ext/gyro/io.c +0 -41
  33. data/lib/polyphony/core/coprocess.rb +8 -0
  34. data/lib/polyphony/core/supervisor.rb +29 -10
  35. data/lib/polyphony/extensions/core.rb +1 -1
  36. data/lib/polyphony/http/server/http2.rb +20 -4
  37. data/lib/polyphony/http/server/http2_stream.rb +35 -3
  38. data/lib/polyphony/version.rb +1 -1
  39. data/lib/polyphony.rb +17 -5
  40. data/test/test_async.rb +14 -7
  41. data/test/test_coprocess.rb +42 -12
  42. data/test/test_core.rb +26 -0
  43. data/test/test_io.rb +14 -5
  44. data/test/test_signal.rb +6 -10
  45. metadata +17 -5
  46. data/docs/getting-started/getting-started.md +0 -10
  47. data/examples/core/spin.rb +0 -14
  48. data/examples/core/spin_cancel.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca0e1fc13842ec06d7272fb19e0edc1621eddb4a007ff9b97e07794e60a9d814
4
- data.tar.gz: 78cab004907a41474ff1920cbb9f5aceb6851797e6805f9c16256f3d48457eff
3
+ metadata.gz: 38c4ab294136de3c31b69438b38aaae16f611b87ccd4730cbe4399a9b6e3a7c6
4
+ data.tar.gz: 35c69e1ff809d340dbe3a121536005338be13c051e8cf2d8c716fd2546c715bc
5
5
  SHA512:
6
- metadata.gz: 96de0a203333388768a41d8c81c333e80b9ab20ebf001b744e1a5d0dd8e6bb036ebc5b4d4abe7a85b4016e264afd4f3609cc8aeed9c8bbfc401010966c8090f2
7
- data.tar.gz: 30507005291c431dd277d30829f6e4c219d2b176a41eef111caadcc8975adaeb335dc4104dc08cb65c58649863bd7a39d21565adee55a795c58d84894419026d
6
+ metadata.gz: 9d41e1f35a06a5906e21defa0dece49f27faac107764401eceb6e55e9fc6cf68c09ff0fdb109afd8b5b53fee0fdb8ab9ee744dce08490f1c433cc5ec3fe5b7a0
7
+ data.tar.gz: c69ee7c201fb812fc1c13120884df61f74ba7b4410ffa31a4a70190a8a95e04b2736f8208422c3018373ad02ca025acadadcdc4898b5eaed16071b14b17c00c7
data/.gitbook.yaml CHANGED
@@ -1,5 +1,4 @@
1
1
  root: ./docs/
2
2
  structure:
3
- readme: ../README.md
3
+ readme: ./README.md
4
4
  summary: ./summary.md
5
- getting-started: ./getting-started.md
data/.rubocop.yml CHANGED
@@ -80,6 +80,7 @@ Lint/HandleExceptions:
80
80
  Exclude:
81
81
  - lib/polyphony/http/server/http1.rb
82
82
  - lib/polyphony/http/server/http2.rb
83
+ - lib/polyphony/http/server/http2_stream.rb
83
84
  - lib/polyphony/http/server.rb
84
85
  - examples/**/*.rb
85
86
 
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ 0.21 2019-12-12
2
+ ---------------
3
+
4
+ * Add Coprocess.await (for waiting for multiple coprocesses)
5
+ * Add Coprocess#caller, Coprocess#location methods
6
+ * Remove callback-oriented Gyro APIs
7
+ * Revise signal handling API
8
+ * Improve error handling in HTTP/2 adapter
9
+ * More documentation
10
+
1
11
  0.20 2019-11-27
2
12
  ---------------
3
13
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.20)
4
+ polyphony (0.21)
5
5
  http-2 (= 0.10.0)
6
6
  http_parser.rb (= 0.6.0)
7
7
  modulation (~> 0.25)
data/README.md CHANGED
@@ -1,63 +1,34 @@
1
- # Polyphony - lightweight concurrency for Ruby
1
+ # Polyphony - Easy Concurrency for Ruby
2
2
 
3
- [INSTALL](#installing-polyphony) |
4
- [TUTORIAL](#getting-started) |
5
- [EXAMPLES](examples) |
6
- [TEHNICAL OVERVIEW](#how-polyphony-works---a-technical-overview) |
7
- [REFERENCE](#api-reference) |
8
- [EXTENDING](#extending-polyphony)
3
+ [DOCS](https://dfab.gitbook.io/polyphony) |
4
+ [EXAMPLES](examples)
9
5
 
10
- > Polyphony | pəˈlɪf(ə)ni | *Music* - the style of simultaneously combining a
11
- > number of parts, each forming an individual melody and harmonizing with each
12
- > other.
13
-
14
- **Note**: Polyphony is experimental software. It is designed to work with recent
15
- versions of Ruby (2.6 and newer) and supports Linux and MacOS only.
6
+ > Polyphony \| pəˈlɪf\(ə\)ni \| _Music_ - the style of simultaneously combining a number of parts, each forming an individual melody and harmonizing with each other.
16
7
 
17
8
  ## What is Polyphony
18
9
 
19
- Polyphony is a library for building concurrent applications in Ruby. Polyphony
20
- harnesses the power of
21
- [Ruby fibers](https://ruby-doc.org/core-2.5.1/Fiber.html) to provide a
22
- cooperative, sequential coprocess-based concurrency model. Under the hood,
23
- Polyphony uses [libev](https://github.com/enki/libev) as a high-performance event
24
- reactor that provides timers, I/O watchers and other asynchronous event
25
- primitives.
10
+ Polyphony is a library for building concurrent applications in Ruby. Polyphony harnesses the power of [Ruby fibers](https://ruby-doc.org/core-2.5.1/Fiber.html) to provide a cooperative, sequential coprocess-based concurrency model. Under the hood, Polyphony uses [libev](https://github.com/enki/libev) as a high-performance event reactor that provides timers, I/O watchers and other asynchronous event primitives.
26
11
 
27
- Polyphony makes it possible to use normal Ruby built-in classes like `IO`, and
28
- `Socket` in a concurrent fashion without having to resort to threads. Polyphony
29
- takes care of context-switching automatically whenever a blocking call like
30
- `Socket#accept` or `IO#read` is issued.
12
+ Polyphony makes it possible to use normal Ruby built-in classes like `IO`, and `Socket` in a concurrent fashion without having to resort to threads. Polyphony takes care of context-switching automatically whenever a blocking call like `Socket#accept` or `IO#read` is issued.
31
13
 
32
14
  ## Features
33
15
 
34
- - **Full-blown, integrated, high-performance HTTP 1 / HTTP 2 / WebSocket server
16
+ * **Full-blown, integrated, high-performance HTTP 1 / HTTP 2 / WebSocket server
35
17
  with TLS/SSL termination, automatic ALPN protocol selection, and body
36
18
  streaming**.
37
- - Co-operative scheduling of concurrent tasks using Ruby fibers.
38
- - High-performance event reactor for handling I/O events and timers.
39
- - Natural, sequential programming style that makes it easy to reason about
19
+ * Co-operative scheduling of concurrent tasks using Ruby fibers.
20
+ * High-performance event reactor for handling I/O events and timers.
21
+ * Natural, sequential programming style that makes it easy to reason about
40
22
  concurrent code.
41
- - Abstractions and constructs for controlling the execution of concurrent code:
23
+ * Abstractions and constructs for controlling the execution of concurrent code:
42
24
  coprocesses, supervisors, cancel scopes, throttling, resource pools etc.
43
- - Code can use native networking classes and libraries, growing support for
25
+ * Code can use native networking classes and libraries, growing support for
44
26
  third-party gems such as `pg` and `redis`.
45
- - Use stdlib classes such as `TCPServer`, `TCPSocket` and
46
- - HTTP 1 / HTTP 2 client agent with persistent connections.
47
- - Competitive performance and scalability characteristics, in terms of both
27
+ * Use stdlib classes such as `TCPServer`, `TCPSocket` and
28
+ * HTTP 1 / HTTP 2 client agent with persistent connections.
29
+ * Competitive performance and scalability characteristics, in terms of both
48
30
  throughput and memory consumption.
49
31
 
50
- ## Why you should not use Polyphony
51
-
52
- - Polyphony does weird things to Ruby, like patching methods like `IO.read`,
53
- `Kernel#sleep`, and `Timeout.timeout` so they'll work concurrently without
54
- using threads.
55
- - Error backtraces might look weird.
56
- - There's currently no support for threads - any IO operations in threads will
57
- likely cause a bad crash.
58
- - Debugging might be confusing or not work at all.
59
- - The API is currently unstable.
60
-
61
32
  ## Prior Art
62
33
 
63
34
  Polyphony draws inspiration from the following, in no particular order:
@@ -70,409 +41,7 @@ Polyphony draws inspiration from the following, in no particular order:
70
41
  * [Erlang supervisors](http://erlang.org/doc/man/supervisor.html) (and actually,
71
42
  Erlang in general)
72
43
 
73
- ## Installing Polyphony
74
-
75
- ```bash
76
- $ gem install polyphony
77
- ```
78
-
79
- Or add it to your Gemfile, you know the drill.
80
-
81
- ## Getting Started
82
-
83
- Polyphony is designed to help you write high-performance, concurrent code in
84
- Ruby, without using threads. It does so by turning every call which might block,
85
- such as `Kernel#sleep` or `IO#read` into a concurrent operation, which yields
86
- control to an event reactor. The reactor, in turn, may schedule other operations
87
- once they can be resumed. In that manner, multiple ongoing operations may be
88
- processed concurrently.
89
-
90
- The simplest way to start a concurrent operation is using `Kernel#spin`:
91
-
92
- ```ruby
93
- require 'polyphony'
94
-
95
- spin do
96
- puts "A going to sleep"
97
- sleep 1
98
- puts "A woken up"
99
- end
100
-
101
- spin do
102
- puts "B going to sleep"
103
- sleep 1
104
- puts "B woken up"
105
- end
106
- ```
107
-
108
- In the above example, both `sleep` calls will be executed concurrently, and thus
109
- the program will take approximately only 1 second to execute. Note how the logic
110
- flow inside each `spin` block is purely sequential, and how the concurrent
111
- nature of the two blocks is expressed simply and cleanly.
112
-
113
- ## Coprocesses - Polyphony's basic unit of concurrency
114
-
115
- In Polyphony, concurrent operations take place inside coprocesses. A `Coprocess`
116
- is executed on top of a `Fiber`, which allows it to be suspended whenever a
117
- blocking operation is called, and resumed once that operation has been
118
- completed. Coprocesses offer significant advantages over threads - they consume
119
- only about 10KB, switching between them is much faster than switching threads,
120
- and literally millions of them can be spinned off without affecting
121
- performance*. Besides, Ruby does not yet allow parallel execution of threads
122
- (courtesy of the Ruby GVL).
123
-
124
- \* *This is a totally unsubstantiated claim and has not been proven in practice*.
125
-
126
- ## An echo server in Polyphony
127
-
128
- Let's now examine how networking is done using Polyphony. Here's a bare-bones
129
- echo server written using Polyphony:
130
-
131
- ```ruby
132
- require 'polyphony'
133
-
134
- server = TCPServer.open(1234)
135
- while client = server.accept
136
- spin do
137
- while (data = client.gets)
138
- client << data
139
- end
140
- end
141
- end
142
- ```
143
-
144
- This example demonstrates several features of Polyphony:
145
-
146
- - The code uses the native `TCPServer` class from Ruby's stdlib, to setup a TCP
147
- server. The result of `server.accept` is also a native `TCPSocket` object.
148
- There are no wrapper classes being used.
149
- - The only hint of the code being concurrent is the use of `Kernel#spin`,
150
- which starts a new coprocess on a dedicated fiber. This allows serving
151
- multiple clients at once. Whenever a blocking call is issued, such as
152
- `#accept` or `#read`, execution is *yielded* to the event reactor loop, which
153
- will resume only those coprocesses which are ready to be resumed.
154
- - Exception handling is done using the normal Ruby constructs `raise`, `rescue`
155
- and `ensure`. Exceptions never go unhandled (as might be the case with Ruby
156
- threads), and must be dealt with explicitly. An unhandled exception will by
157
- default cause the Ruby process to exit.
158
-
159
- ## Additional concurrency constructs
160
-
161
- In order to facilitate writing concurrent code, Polyphony provides additional
162
- mechanisms that make it easier to create and control concurrent tasks.
163
-
164
- ### Cancel scopes
165
-
166
- Cancel scopes, an idea borrowed from Python's
167
- [Trio](https://trio.readthedocs.io/) library, are used to cancel the execution
168
- of one or more coprocesses. The most common use of cancel scopes is a for
169
- implementing a timeout for the completion of a task. Any blocking operation can
170
- be cancelled. The programmer may choose to raise a `Cancel` exception when an
171
- operation has been cancelled, or alternatively to move on without any exception.
172
-
173
- Cancel scopes are typically started using `Kernel#cancel_after` and
174
- `Kernel#move_on_after` for cancelling with or without an exception,
175
- respectively. Cancel scopes will take a block of code to execute and run it,
176
- providing a reference to the cancel scope:
177
-
178
- ```ruby
179
- puts "going to sleep (but really only for 1 second)..."
180
- cancel_after(1) do
181
- sleep(60)
182
- end
183
- ```
184
-
185
- Patterns like closing a connection after X seconds of activity are greatly
186
- facilitated by timeout-based cancel scopes, which can be easily reset:
187
-
188
- ```ruby
189
- def echoer(client)
190
- # close connection after 10 seconds of inactivity
191
- move_on_after(10) do |scope|
192
- scope.when_cancelled { puts "closing connection due to inactivity" }
193
- loop do
194
- data = client.read
195
- scope.reset_timeout
196
- client.write
197
- end
198
- end
199
- client.close
200
- end
201
- ```
202
-
203
- Cancel scopes may also be manually cancelled by calling `CancelScope#cancel!`
204
- at any time:
205
-
206
- ```ruby
207
- def echoer(client)
208
- move_on_after(60) do |scope|
209
- loop do
210
- data = client.read
211
- scope.cancel! if data == 'stop'
212
- client.write
213
- end
214
- end
215
- client.close
216
- end
217
- ```
218
-
219
- ### Resource pools
220
-
221
- A resource pool is used to control access to one or more shared, usually
222
- identical resources. For example, a resource pool can be used to control
223
- concurrent access to database connections, or to limit concurrent
224
- requests to an external API:
225
-
226
- ```ruby
227
- # up to 5 concurrent connections
228
- Pool = Polyphony::ResourcePool.new(limit: 5) {
229
- # the block sets up the resource
230
- PG.connect(...)
231
- }
232
-
233
- 1000.times {
234
- spin {
235
- Pool.acquire { |db| p db.query('select 1') }
236
- }
237
- }
238
- ```
239
-
240
- You can also call arbitrary methods on the resource pool, which will be
241
- delegated to the resource using `#method_missing`:
242
-
243
- ```ruby
244
- # up to 5 concurrent connections
245
- Pool = Polyphony::ResourcePool.new(limit: 5) {
246
- # the block sets up the resource
247
- PG.connect(...)
248
- }
249
-
250
- 1000.times {
251
- spin { p Pool.query('select pg_sleep(0.01);') }
252
- }
253
- ```
254
-
255
- ### Supervisors
256
-
257
- A supervisor is used to control one or more coprocesses. It can be used to
258
- start, stop, restart and await the completion of multiple coprocesses. It is
259
- normally started using `Kernel#supervise`:
260
-
261
- ```ruby
262
- supervise { |s|
263
- s.spin { sleep 1 }
264
- s.spin { sleep 2 }
265
- s.spin { sleep 3 }
266
- }
267
- puts "done sleeping"
268
- ```
269
-
270
- The `Kernel#supervise` method will await the completion of all supervised
271
- coprocesses. If any supervised coprocess raises an error, the supervisor will
272
- automatically cancel all other supervised coprocesses.
273
-
274
- ### Throttlers
275
-
276
- A throttler is a mechanism for controlling the speed of an arbitrary task,
277
- such as sending of emails, or crawling a website. A throttler is normally
278
- created using `Kernel#throttle` or `Kernel#throttled_loop`, and can even be used
279
- to throttle operations across multiple coprocesses:
280
-
281
- ```ruby
282
- server = Polyphony::Net.tcp_listen(1234)
283
-
284
- # a shared throttler, up to 10 times per second
285
- throttler = throttle(rate: 10)
286
-
287
- while client = server.accept
288
- spin do
289
- throttler.call do
290
- while data = client.read
291
- client.write(data)
292
- end
293
- end
294
- end
295
- end
296
- ```
297
-
298
- `Kernel#throttled_loop` can be used to run throttled infinite loops:
299
-
300
- ```ruby
301
- throttled_loop(3) do
302
- STDOUT << '.'
303
- end
304
- ```
305
-
306
- ## Going further
307
-
308
- To learn more about using Polyphony to build concurrent applications, read the
309
- technical overview below, or look at the [included examples](examples). A
310
- thorough reference is forthcoming.
311
-
312
- ## How Polyphony Works - a Technical Overview
313
-
314
- ### Fiber-based concurrency
315
-
316
- The built-in `Fiber` class provides a very elegant, if low-level, foundation for
317
- implementing cooperative, light-weight concurrency (it can also be used for other stuff like generators). Fiber or continuation-based concurrency can be
318
- considered as the
319
- [*third way*](https://en.wikipedia.org/wiki/Fiber_(computer_science))
320
- of writing concurrent programs (the other two being multi-process concurrency
321
- and multi-thread concurrency), and can provide very good performance
322
- characteristics for I/O-bound applications.
323
-
324
- In contrast to callback-based concurrency (e.g. Node.js or EventMachine), fibers
325
- allow writing concurrent code in a sequential manner without having to split
326
- your logic into different locations, or submitting to
327
- [callback hell](http://callbackhell.com/).
328
-
329
- Polyphony builds on the foundation of Ruby fibers in order to facilitate writing
330
- high-performance I/O-bound applications in Ruby.
331
-
332
- ### Context-switching on blocking calls
333
-
334
- Ruby monkey-patches existing methods such as `sleep` or `IO#read` to setup an
335
- IO watcher and suspend the current fiber until the IO object is ready to be
336
- read. Once the IO watcher is signalled, the associated fiber is resumed and the
337
- method call can continue. Here's a simplified implementation of
338
- [`IO#read`](lib/polyphony/io.rb#24-36):
339
-
340
- ```ruby
341
- class IO
342
- def read(max = 8192)
343
- loop do
344
- result = read_nonblock(max, exception: false)
345
- case result
346
- when nil then raise IOError
347
- when :wait_readable then read_watcher.await
348
- else return result
349
- end
350
- end
351
- end
352
- end
353
- ```
354
-
355
- The magic starts in [`IOWatcher#await`](ext/ev/io.c#157-179), where the watcher
356
- is started and the current fiber is suspended (it "yields" in Ruby parlance).
357
- Here's a naïve implementation (the actual implementation is written in C):
358
-
359
- ```ruby
360
- class IOWatcher
361
- def await
362
- @fiber = Fiber.current
363
- start
364
- yield_to_reactor_fiber
365
- end
366
- end
367
- ```
368
-
369
- > **Running a high-performance event loop**: Polyphony runs a libev-based event
370
- > loop that watches events such as IO-readiness, elapsed timers, received
371
- > signals and other asynchronous happenings, and uses them to control fiber
372
- > execution. The event loop itself is run on a separate fiber, allowing the main
373
- > fiber as well to perform blocking operations.
374
-
375
- When the IO watcher is [signalled](ext/ev/io.c#99-116): the fiber associated
376
- with the watcher is resumed, and control is given back to the calling method.
377
- Here's a naïve implementation:
378
-
379
- ```ruby
380
- class IOWatcher
381
- def signal
382
- @fiber.transfer
383
- end
384
- end
385
- ```
386
-
387
- ## API Reference
388
-
389
- To be continued...
390
-
391
- ## Extending Polyphony
392
-
393
- Polyphony was designed to ease the transition from blocking APIs and
394
- callback-based API to non-blocking, fiber-based ones. It is important to
395
- understand that not all blocking calls can be easily converted into
396
- non-blocking calls. That might be the case with Ruby gems based on C-extensions,
397
- such as database libraries. In that case, Polyphony's built-in
398
- [thread pool](#threadpool) might be used for offloading such blocking calls.
399
-
400
- ### Adapting callback-based APIs
401
-
402
- Some of the most common patterns in Ruby APIs is the callback pattern, in which
403
- the API takes a block as a callback to be called upon completion of a task. One
404
- such example can be found in the excellent
405
- [http_parser.rb](https://github.com/tmm1/http_parser.rb/) gem, which is used by
406
- Polyphony itself to provide HTTP 1 functionality. The `HTTP:Parser` provides
407
- multiple hooks, or callbacks, for being notified when an HTTP request is
408
- complete. The typical callback-based setup is as follows:
409
-
410
- ```ruby
411
- require 'http/parser'
412
- @parser = Http::Parser.new
413
-
414
- def on_receive(data)
415
- @parser < data
416
- end
417
-
418
- @parser.on_message_complete do |env|
419
- process_request(env)
420
- end
421
- ```
422
-
423
- A program using `http_parser.rb` in conjunction with Polyphony might do the
424
- following:
425
-
426
- ```ruby
427
- require 'http/parser'
428
- require 'polyphony'
429
-
430
- def handle_client(client)
431
- parser = Http::Parser.new
432
- req = nil
433
- parser.on_message_complete { |env| req = env }
434
- loop do
435
- parser << client.read
436
- if req
437
- handle_request(req)
438
- req = nil
439
- end
440
- end
441
- end
442
- ```
443
-
444
- Another possibility would be to monkey-patch `Http::Parser` in order to
445
- encapsulate the state of the request:
446
-
447
- ```ruby
448
- class Http::Parser
449
- def setup
450
- self.on_message_complete = proc { @request_complete = true }
451
- end
452
-
453
- def parser(data)
454
- self << data
455
- return nil unless @request_complete
456
-
457
- @request_complete = nil
458
- self
459
- end
460
- end
461
-
462
- def handle_client(client)
463
- parser = Http::Parser.new
464
- loop do
465
- if req == parser.parse(client.read)
466
- handle_request(req)
467
- end
468
- end
469
- end
470
- ```
471
-
472
- ### Contributing to Polyphony
44
+ ## Documentation
473
45
 
474
- If there's some blocking behavior you'd like to see handled by Polyphony, please
475
- let us know by
476
- [creating an issue](https://github.com/digital-fabric/polyphony/issues). Our aim
477
- is for Polyphony to be a comprehensive solution for writing concurrent Ruby
478
- programs.
46
+ The complete documentation for Polyphony could be found on the
47
+ [Polyphony website](https://dfab.gitbook.io/polyphony).
data/TODO.md CHANGED
@@ -6,16 +6,6 @@
6
6
  - Work better mechanism supervising multiple coprocesses (`when_done` feels a
7
7
  bit hacky)
8
8
 
9
- ## 0.21 REPL usage, coprocess introspection, monitoring
10
-
11
- - Implement `move_on_after(1, with: nil) { ... }`
12
- - Implement `Coprocess.await` for waiting on multiple coprocesses without
13
- starting them in a supervisor, will also necessitate adding `Supervisor#add`
14
- - Implement `Coprocess#location`
15
- - Implement `Coprocess#alive?`
16
- - Implement `Coprocess#caller` - points to coprocess that called the coprocess
17
- - Implement `Coprocess.list` - a list of running coprocesses
18
-
19
9
  ## 0.22 Full Rack adapter implementation
20
10
 
21
11
  - Homogenize HTTP 1 and HTTP 2 headers - upcase ? downcase ?
data/docs/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Polyphony - Easy Concurrency for Ruby
2
+
3
+ > Polyphony \| pəˈlɪf\(ə\)ni \| _Music_ - the style of simultaneously combining a number of parts, each forming an individual melody and harmonizing with each other.
4
+
5
+ ## What is Polyphony
6
+
7
+ Polyphony is a library for building concurrent applications in Ruby. Polyphony harnesses the power of [Ruby fibers](https://ruby-doc.org/core-2.5.1/Fiber.html) to provide a cooperative, sequential coprocess-based concurrency model. Under the hood, Polyphony uses [libev](https://github.com/enki/libev) as a high-performance event reactor that provides timers, I/O watchers and other asynchronous event primitives.
8
+
9
+ Polyphony makes it possible to use normal Ruby built-in classes like `IO`, and `Socket` in a concurrent fashion without having to resort to threads. Polyphony takes care of context-switching automatically whenever a blocking call like `Socket#accept` or `IO#read` is issued.
10
+
11
+ ## Features
12
+
13
+ * **Full-blown, integrated, high-performance HTTP 1 / HTTP 2 / WebSocket server with TLS/SSL termination, automatic ALPN protocol selection, and body streaming**.
14
+ * Co-operative scheduling of concurrent tasks using Ruby fibers.
15
+ * High-performance event reactor for handling I/O events and timers.
16
+ * Natural, sequential programming style that makes it easy to reason about concurrent code.
17
+ * Abstractions and constructs for controlling the execution of concurrent code: coprocesses, supervisors, cancel scopes, throttling, resource pools etc.
18
+ * Code can use native networking classes and libraries, growing support for third-party gems such as `pg` and `redis`.
19
+ * Use stdlib classes such as `TCPServer` and `TCPSocket` and `Net::HTTP`.
20
+ * HTTP 1 / HTTP 2 client agent with persistent connections.
21
+ * Competitive performance and scalability characteristics, in terms of both throughput and memory consumption.
22
+
23
+ ## Prior Art
24
+
25
+ Polyphony draws inspiration from the following, in no particular order:
26
+
27
+ * [nio4r](https://github.com/socketry/nio4r/) and [async](https://github.com/socketry/async) (Polyphony's C-extension code is largely a spinoff of [nio4r's](https://github.com/socketry/nio4r/tree/master/ext))
28
+ * [EventMachine](https://github.com/eventmachine/eventmachine)
29
+ * [Trio](https://trio.readthedocs.io/)
30
+ * [Erlang supervisors](http://erlang.org/doc/man/supervisor.html) (and actually, Erlang in general)
31
+
32
+ ## Going further
33
+
34
+ To learn more about using Polyphony to build concurrent applications, read the technical overview below, or look at the [included examples](https://github.com/digital-fabric/polyphony/tree/9e0f3b09213156bdf376ef33684ef267517f06e8/examples/README.md). A thorough reference is forthcoming.
35
+
36
+ ## Contributing to Polyphony
37
+
38
+ If there's some blocking behavior you'd like to see handled by Polyphony, please let us know by [creating an issue](https://github.com/digital-fabric/polyphony/issues). Our aim is for Polyphony to be a comprehensive solution for writing concurrent Ruby programs.
39
+
@@ -0,0 +1,28 @@
1
+ ## System Requirements
2
+
3
+ In order to use Polyphony you need to have:
4
+
5
+ - Linux or MacOS (sorry, there are no plans to support Windows for the time
6
+ being)
7
+ - Ruby (MRI) 2.6 or newer
8
+
9
+ ## Installing Polyphony
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'polyphony'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ ```bash
20
+ $ bundle
21
+ ```
22
+
23
+ Or install it yourself as:
24
+
25
+ ```bash
26
+ $ gem install polyphony
27
+ ```
28
+