polyphony 0.20 → 0.21

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