polyphony 0.28 → 0.29
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +0 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +1 -1
- data/LICENSE +1 -1
- data/README.md +23 -21
- data/Rakefile +2 -0
- data/TODO.md +0 -3
- data/docs/_includes/prevnext.html +17 -0
- data/docs/_layouts/default.html +106 -0
- data/docs/_sass/custom/custom.scss +21 -0
- data/docs/faq.md +13 -10
- data/docs/getting-started/installing.md +2 -0
- data/docs/getting-started/tutorial.md +5 -3
- data/docs/index.md +4 -5
- data/docs/technical-overview/concurrency.md +21 -19
- data/docs/technical-overview/design-principles.md +12 -20
- data/docs/technical-overview/exception-handling.md +70 -1
- data/docs/technical-overview/extending.md +1 -0
- data/docs/technical-overview/fiber-scheduling.md +109 -88
- data/docs/user-guide/all-about-timers.md +126 -0
- data/docs/user-guide/web-server.md +2 -2
- data/docs/user-guide.md +1 -1
- data/examples/core/xx-deferring-an-operation.rb +2 -2
- data/examples/core/xx-sleep-forever.rb +9 -0
- data/examples/core/xx-snooze-starve.rb +16 -0
- data/examples/core/xx-spin_error_backtrace.rb +1 -1
- data/examples/core/xx-trace.rb +1 -2
- data/examples/core/xx-worker-thread.rb +30 -0
- data/examples/io/xx-happy-eyeballs.rb +37 -0
- data/ext/gyro/gyro.c +8 -3
- data/ext/gyro/gyro.h +7 -1
- data/ext/gyro/queue.c +35 -3
- data/ext/gyro/selector.c +31 -2
- data/ext/gyro/thread.c +18 -16
- data/lib/polyphony/core/global_api.rb +0 -1
- data/lib/polyphony/core/thread_pool.rb +5 -0
- data/lib/polyphony/core/throttler.rb +0 -1
- data/lib/polyphony/extensions/fiber.rb +14 -3
- data/lib/polyphony/extensions/thread.rb +16 -4
- data/lib/polyphony/irb.rb +7 -1
- data/lib/polyphony/trace.rb +44 -11
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +1 -0
- data/test/helper.rb +1 -3
- data/test/test_async.rb +1 -1
- data/test/test_cancel_scope.rb +3 -3
- data/test/test_fiber.rb +157 -54
- data/test/test_global_api.rb +51 -1
- data/test/test_gyro.rb +4 -156
- data/test/test_io.rb +1 -1
- data/test/test_supervisor.rb +2 -2
- data/test/test_thread.rb +72 -1
- data/test/test_thread_pool.rb +6 -2
- data/test/test_throttler.rb +7 -5
- data/test/test_trace.rb +6 -6
- metadata +10 -5
- data/examples/core/xx-extended_fibers.rb +0 -150
- data/examples/core/xx-mt-scheduler.rb +0 -349
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb34882b5a9cb5bbf2b1b27909795f96e5291fe953aeea53f6ebd4e95d8bd19f
|
4
|
+
data.tar.gz: c38af8c17d62b70fed44988182e70caba305eb9abbb3c4d1a8fdeda349031bae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab9f3c212357aa7eedaee5f43c1b85ff28337e8bb1cc3d693fcc72ff02c49ddf55b211daccfd0eb516cfca1544e02312f10f5e59eb0903c55279cc5983a69ca3
|
7
|
+
data.tar.gz: 9d871f6e1c8013a13fc03f191d6e0ac7fb7766e723c73be3afcf2d870cea9595bc750c4d45c7efb1a61104ef0611e00a430baae6b3a0c5afcd4b2a859c8677f5
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
0.29 2020-02-02
|
2
|
+
---------------
|
3
|
+
|
4
|
+
* Pass SignalException to main fiber
|
5
|
+
* Add (restore) default thread pool
|
6
|
+
* Prevent race condition in Thread#join
|
7
|
+
* Add support for cross-thread fiber scheduling
|
8
|
+
* Remove `#defer` global method
|
9
|
+
* Prevent starvation of waiting fibers when using snooze (#7)
|
10
|
+
* Improve tracing
|
11
|
+
* Fix IRB adapter
|
12
|
+
|
1
13
|
0.28 2020-01-27
|
2
14
|
---------------
|
3
15
|
|
data/Gemfile.lock
CHANGED
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,15 +1,22 @@
|
|
1
|
-
# Polyphony -
|
1
|
+
# Polyphony - Fine-Grained Concurrency for Ruby
|
2
2
|
|
3
|
-
[DOCS](https://
|
3
|
+
[DOCS](https://digital-fabric.github.io/polyphony/) |
|
4
4
|
[EXAMPLES](examples)
|
5
5
|
|
6
|
-
> Polyphony \| pəˈlɪf\(ə\)ni \|
|
6
|
+
> Polyphony \| pəˈlɪf\(ə\)ni \|
|
7
|
+
> 1. _Music_ the style of simultaneously combining a number of parts, each
|
8
|
+
> forming an individual melody and harmonizing with each other.
|
9
|
+
> 2. _Programming_ a Ruby gem for concurrent programming focusing on performance
|
10
|
+
> and developer happiness.
|
7
11
|
|
8
12
|
## What is Polyphony
|
9
13
|
|
10
|
-
Polyphony is a library for building concurrent applications in Ruby. Polyphony
|
11
|
-
|
12
|
-
|
14
|
+
Polyphony is a library for building concurrent applications in Ruby. Polyphony
|
15
|
+
harnesses the power of [Ruby fibers](https://ruby-doc.org/core-2.5.1/Fiber.html)
|
16
|
+
to provide a cooperative, sequential coroutine-based concurrency model. Under
|
17
|
+
the hood, Polyphony uses [libev](https://github.com/enki/libev) as a
|
18
|
+
high-performance event reactor that provides timers, I/O watchers and other
|
19
|
+
asynchronous event primitives.
|
13
20
|
|
14
21
|
## Features
|
15
22
|
|
@@ -18,26 +25,21 @@ Polyphony makes it possible to use normal Ruby built-in classes like `IO`, and `
|
|
18
25
|
* Natural, sequential programming style that makes it easy to reason about
|
19
26
|
concurrent code.
|
20
27
|
* Abstractions and constructs for controlling the execution of concurrent code:
|
21
|
-
|
28
|
+
supervisors, cancel scopes, throttling, resource pools etc.
|
22
29
|
* Code can use native networking classes and libraries, growing support for
|
23
30
|
third-party gems such as `pg` and `redis`.
|
24
|
-
* Use stdlib classes such as `TCPServer`, `TCPSocket` and
|
31
|
+
* Use stdlib classes such as `TCPServer`, `TCPSocket` and
|
32
|
+
`OpenSSL::SSL::SSLSocket`.
|
25
33
|
* Competitive performance and scalability characteristics, in terms of both
|
26
34
|
throughput and memory consumption.
|
27
35
|
|
28
|
-
## Prior Art
|
29
|
-
|
30
|
-
Polyphony draws inspiration from the following, in no particular order:
|
31
|
-
|
32
|
-
* [nio4r](https://github.com/socketry/nio4r/) and [async](https://github.com/socketry/async)
|
33
|
-
(Polyphony's C-extension code is largely a spinoff of
|
34
|
-
[nio4r's](https://github.com/socketry/nio4r/tree/master/ext))
|
35
|
-
* [EventMachine](https://github.com/eventmachine/eventmachine)
|
36
|
-
* [Trio](https://trio.readthedocs.io/)
|
37
|
-
* [Erlang supervisors](http://erlang.org/doc/man/supervisor.html) (and actually,
|
38
|
-
Erlang in general)
|
39
|
-
|
40
36
|
## Documentation
|
41
37
|
|
42
38
|
The complete documentation for Polyphony could be found on the
|
43
|
-
[Polyphony website](https://
|
39
|
+
[Polyphony website](https://digital-fabric.github.io/polyphony).
|
40
|
+
|
41
|
+
## Contributing to Polyphony
|
42
|
+
|
43
|
+
Issues and pull requests will be gladly accepted. Please use the [Polyphony git
|
44
|
+
repository](https://github.com/digital-fabric/polyphony) as your primary point
|
45
|
+
of departure for contributing.
|
data/Rakefile
CHANGED
data/TODO.md
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
## 0.29 Multithreaded fiber scheduling - some rough corners
|
2
2
|
|
3
3
|
- Docs: explain difference between `sleep` and `suspend`
|
4
|
-
- Write about threads: scheduling, etc
|
5
|
-
- `Gyro_schedule_fiber` - schedule using fiber's associated thread (store thread
|
6
|
-
ref in fiber), instead of current thread
|
7
4
|
- Check why first call to `#sleep` returns too early in tests. Check the
|
8
5
|
sleep behaviour in a spawned thread.
|
9
6
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
{%- for node in site.html_pages -%}
|
2
|
+
{%- if page.prev_title == node.title -%}
|
3
|
+
{%- assign prev_url = node.url -%}
|
4
|
+
{%- endif -%}
|
5
|
+
{%- if page.next_title == node.title -%}
|
6
|
+
{%- assign next_url = node.url -%}
|
7
|
+
{%- endif -%}
|
8
|
+
{%- endfor -%}
|
9
|
+
<div id="prevnext">
|
10
|
+
{%- if prev_url -%}
|
11
|
+
<span class="prev"><a href="{{prev_url | relative_url}}">☜ {{page.prev_title}}</a></span>
|
12
|
+
{%- endif -%}
|
13
|
+
{%- if next_url -%}
|
14
|
+
<span class="next"><a href="{{next_url | relative_url}}">{{page.next_title}} ☞</a></span>
|
15
|
+
{%- endif -%}
|
16
|
+
<span class="clear"> </span>
|
17
|
+
</div>
|
@@ -0,0 +1,106 @@
|
|
1
|
+
---
|
2
|
+
layout: table_wrappers
|
3
|
+
---
|
4
|
+
|
5
|
+
<!DOCTYPE html>
|
6
|
+
|
7
|
+
<html lang="{{ site.lang | default: "en-US" }}">
|
8
|
+
{% include head.html %}
|
9
|
+
<body>
|
10
|
+
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
11
|
+
<symbol id="link" viewBox="0 0 16 16">
|
12
|
+
<title>Link</title>
|
13
|
+
<path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path>
|
14
|
+
</symbol>
|
15
|
+
</svg>
|
16
|
+
|
17
|
+
<div class="page-wrap">
|
18
|
+
<div class="side-bar">
|
19
|
+
<div class="site-header">
|
20
|
+
<a href="{{ site.url }}{{ site.baseurl }}" class="site-title lh-tight">{% include title.html %}</a>
|
21
|
+
<button class="menu-button fs-3 js-main-nav-trigger" data-text-toggle="Hide" type="button">Menu</button>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<div class="navigation main-nav js-main-nav">
|
25
|
+
{% include nav.html %}
|
26
|
+
</div>
|
27
|
+
<footer class="site-footer">
|
28
|
+
<p class="text-small text-grey-dk-000 mb-4">This site uses <a href="https://github.com/pmarsceill/just-the-docs">Just the Docs</a>, a documentation theme for Jekyll.</p>
|
29
|
+
</footer>
|
30
|
+
</div>
|
31
|
+
<div class="main-content-wrap js-main-content" tabindex="0">
|
32
|
+
<div class="main-content">
|
33
|
+
<div class="page-header js-page-header">
|
34
|
+
{% if site.search_enabled != false %}
|
35
|
+
<div class="search">
|
36
|
+
<div class="search-input-wrap">
|
37
|
+
<input type="text" class="js-search-input search-input" tabindex="0" placeholder="Search {{ site.title }}" aria-label="Search {{ site.title }}" autocomplete="off">
|
38
|
+
<svg width="14" height="14" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg" class="search-icon"><title>Search</title><g fill-rule="nonzero"><path d="M17.332 20.735c-5.537 0-10-4.6-10-10.247 0-5.646 4.463-10.247 10-10.247 5.536 0 10 4.601 10 10.247s-4.464 10.247-10 10.247zm0-4c3.3 0 6-2.783 6-6.247 0-3.463-2.7-6.247-6-6.247s-6 2.784-6 6.247c0 3.464 2.7 6.247 6 6.247z"/><path d="M11.672 13.791L.192 25.271 3.02 28.1 14.5 16.62z"/></g></svg>
|
39
|
+
</div>
|
40
|
+
<div class="js-search-results search-results-wrap"></div>
|
41
|
+
</div>
|
42
|
+
{% endif %}
|
43
|
+
{% if site.aux_links != nil %}
|
44
|
+
<ul class="list-style-none text-small aux-nav">
|
45
|
+
{% for link in site.aux_links %}
|
46
|
+
<li class="d-inline-block my-0{% unless forloop.last %} mr-2{% endunless %}"><a href="{{ link.last }}">{{ link.first }}</a></li>
|
47
|
+
{% endfor %}
|
48
|
+
</ul>
|
49
|
+
{% endif %}
|
50
|
+
</div>
|
51
|
+
<div class="page">
|
52
|
+
{% unless page.url == "/" %}
|
53
|
+
{% if page.parent %}
|
54
|
+
<nav class="breadcrumb-nav">
|
55
|
+
<ol class="breadcrumb-nav-list">
|
56
|
+
{% if page.grand_parent %}
|
57
|
+
<li class="breadcrumb-nav-list-item"><a href="{{ first_level_url }}">{{ page.grand_parent }}</a></li>
|
58
|
+
<li class="breadcrumb-nav-list-item"><a href="{{ second_level_url }}">{{ page.parent }}</a></li>
|
59
|
+
{% else %}
|
60
|
+
<li class="breadcrumb-nav-list-item"><a href="{{ first_level_url }}">{{ page.parent }}</a></li>
|
61
|
+
{% endif %}
|
62
|
+
<li class="breadcrumb-nav-list-item"><span>{{ page.title }}</span></li>
|
63
|
+
</ol>
|
64
|
+
</nav>
|
65
|
+
{% endif %}
|
66
|
+
{% endunless %}
|
67
|
+
<div id="main-content" class="page-content" role="main">
|
68
|
+
{% if site.heading_anchors != false %}
|
69
|
+
{% include vendor/anchor_headings.html html=content beforeHeading="true" anchorBody="<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><use xlink:href=\"#link\"></use></svg>" anchorClass="anchor-heading" %}
|
70
|
+
{% else %}
|
71
|
+
{{ content }}
|
72
|
+
{% endif %}
|
73
|
+
|
74
|
+
{% if page.prev_title or page.next_title %}
|
75
|
+
{% include prevnext.html %}
|
76
|
+
{% endif %}
|
77
|
+
|
78
|
+
{% if page.has_children == true and page.has_toc != false %}
|
79
|
+
<hr>
|
80
|
+
<h2 class="text-delta">Table of contents</h2>
|
81
|
+
{% assign children_list = site.pages | sort:"nav_order" %}
|
82
|
+
<ul>
|
83
|
+
{% for child in children_list %}
|
84
|
+
{% if child.parent == page.title and child.title != page.title %}
|
85
|
+
<li>
|
86
|
+
<a href="{{ child.url | absolute_url }}">{{ child.title }}</a>{% if child.summary %} - {{ child.summary }}{% endif %}
|
87
|
+
</li>
|
88
|
+
{% endif %}
|
89
|
+
{% endfor %}
|
90
|
+
</ul>
|
91
|
+
{% endif %}
|
92
|
+
|
93
|
+
{% if site.footer_content != nil %}
|
94
|
+
<hr>
|
95
|
+
<footer role="contentinfo">
|
96
|
+
<p class="text-small text-grey-dk-000 mb-0">{{ site.footer_content }}</p>
|
97
|
+
</footer>
|
98
|
+
{% endif %}
|
99
|
+
|
100
|
+
</div>
|
101
|
+
</div>
|
102
|
+
</div>
|
103
|
+
</div>
|
104
|
+
|
105
|
+
</body>
|
106
|
+
</html>
|
@@ -1,5 +1,26 @@
|
|
1
|
+
$nav-child-link-color: #9d9b9e;
|
1
2
|
$link-color: $blue-000;
|
2
3
|
|
3
4
|
.img-figure {
|
4
5
|
text-align: center;
|
6
|
+
}
|
7
|
+
|
8
|
+
#prevnext {
|
9
|
+
padding-top: 1em;
|
10
|
+
}
|
11
|
+
|
12
|
+
#prevnext span {
|
13
|
+
}
|
14
|
+
|
15
|
+
#prevnext span.prev {
|
16
|
+
float: left;
|
17
|
+
padding-right: 4em;
|
18
|
+
}
|
19
|
+
|
20
|
+
#prevnext span.next {
|
21
|
+
float: right;
|
22
|
+
}
|
23
|
+
|
24
|
+
#prevnext span.clear {
|
25
|
+
clear: both;
|
5
26
|
}
|
data/docs/faq.md
CHANGED
@@ -14,8 +14,10 @@ base their entire API on the callback pattern.
|
|
14
14
|
reactor library for Ruby that uses callbacks for handling events.
|
15
15
|
|
16
16
|
Using callbacks means splitting your application logic into disjunct pieces of
|
17
|
-
code.
|
18
|
-
|
17
|
+
code. It also often means having to write elaborate state machines to keep track
|
18
|
+
of events happening at different points in time. Using fibers allows you to keep
|
19
|
+
state in local variables. Consider the following implementation of an echo
|
20
|
+
server using EventMachine:
|
19
21
|
|
20
22
|
```ruby
|
21
23
|
require 'eventmachine'
|
@@ -139,12 +141,12 @@ however important to note that Polyphony places the emphasis on a multi-fiber
|
|
139
141
|
concurrency model, which is highly beneficial for I/O-bound workloads, such as
|
140
142
|
web servers and web apps.
|
141
143
|
|
142
|
-
Because of Ruby's [global interpreter
|
143
|
-
multiple threads
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
system call.
|
144
|
+
Because of Ruby's [global interpreter
|
145
|
+
lock](https://en.wikipedia.org/wiki/Global_interpreter_lock), multiple threads
|
146
|
+
can not in fact run in parallel, and this is actually one of the reasons fibers
|
147
|
+
are such a better fit for I/O bound Ruby programs. Threads should really be used
|
148
|
+
when performing synchronous operations that are not fiber-aware, such as running
|
149
|
+
an expensive SQLite query, or some other expensive system call.
|
148
150
|
|
149
151
|
### How Does Polyphony Fit Into the Ruby's Future Concurrency Plans
|
150
152
|
|
@@ -172,8 +174,9 @@ support running Rails in an eventual release.
|
|
172
174
|
|
173
175
|
### How can I contribute to Polyphony?
|
174
176
|
|
175
|
-
The Polyphony repository is at
|
176
|
-
|
177
|
+
The Polyphony repository is at
|
178
|
+
[https://github.com/digital-fabric/polyphony](https://github.com/digital-fabric/polyphony).
|
179
|
+
Feel free to create issues and contribute pull requests.
|
177
180
|
|
178
181
|
### Who is behind this project?
|
179
182
|
|
@@ -4,6 +4,8 @@ title: A Gentle Introduction to Polyphony
|
|
4
4
|
nav_order: 2
|
5
5
|
parent: Getting Started
|
6
6
|
permalink: /getting-started/tutorial/
|
7
|
+
prev_title: Installing Polyphony
|
8
|
+
next_title: Design Principles
|
7
9
|
---
|
8
10
|
# A Gentle Introduction to Polyphony
|
9
11
|
|
@@ -369,9 +371,9 @@ machine's location). Also notice how we just used `httparty` with fiber-level
|
|
369
371
|
concurrency, without any boilerplate or employing special wrapper classes.
|
370
372
|
|
371
373
|
Just as before, we suspend the main fiber after spinning off the worker fibers,
|
372
|
-
in order to wait for everything else to be done. But if we needed to do
|
373
|
-
work? For example, we might want to collect the different local times into
|
374
|
-
hash to be processed later. In that case, we can use a `Supervisor`:
|
374
|
+
in order to wait for everything else to be done. But what if we needed to do
|
375
|
+
other work? For example, we might want to collect the different local times into
|
376
|
+
a hash to be processed later. In that case, we can use a `Supervisor`:
|
375
377
|
|
376
378
|
```ruby
|
377
379
|
def get_times(zones)
|
data/docs/index.md
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
layout: page
|
3
3
|
title: Home
|
4
4
|
nav_order: 1
|
5
|
-
description: Lorem ipsum
|
6
5
|
permalink: /
|
6
|
+
next_title: Installing Polyphony
|
7
7
|
---
|
8
8
|
|
9
9
|
# Polyphony - fine-grained concurrency for Ruby
|
@@ -21,7 +21,6 @@ the hood, Polyphony uses [libev](https://github.com/enki/libev) as a
|
|
21
21
|
high-performance event reactor that provides timers, I/O watchers and other
|
22
22
|
asynchronous event primitives.
|
23
23
|
|
24
|
-
|
25
24
|
## Focused on Developer Happiness
|
26
25
|
|
27
26
|
Polyphony is designed to make concurrent Ruby programming feel natural and
|
@@ -86,6 +85,6 @@ A thorough reference is forthcoming.
|
|
86
85
|
|
87
86
|
## Contributing to Polyphony
|
88
87
|
|
89
|
-
Issues and pull requests will be gladly accepted. Please use the git
|
90
|
-
|
91
|
-
departure for contributing.
|
88
|
+
Issues and pull requests will be gladly accepted. Please use the [Polyphony git
|
89
|
+
repository](https://github.com/digital-fabric/polyphony) as your primary point
|
90
|
+
of departure for contributing.
|
@@ -4,6 +4,8 @@ title: Concurrency the Easy Way
|
|
4
4
|
nav_order: 2
|
5
5
|
parent: Technical Overview
|
6
6
|
permalink: /technical-overview/concurrency/
|
7
|
+
prev_title: Design Principles
|
8
|
+
next_title: How Fibers are Scheduled
|
7
9
|
---
|
8
10
|
# Concurrency the Easy Way
|
9
11
|
|
@@ -36,37 +38,37 @@ resumed once the database has sent back its reply. Meanwhile, another
|
|
36
38
|
computation is started that opens a socket to a remote service, and then
|
37
39
|
suspends itself, waiting for the connection to be established.
|
38
40
|
|
39
|
-
This form of concurrency, called cooperative concurrency
|
40
|
-
pre-emptive concurrency, like threads and processes
|
41
|
+
This form of concurrency, called cooperative concurrency (in contrast to
|
42
|
+
pre-emptive concurrency, like in threads and processes), offers many advantages,
|
41
43
|
especially for applications that are [I/O
|
42
44
|
bound](https://en.wikipedia.org/wiki/I/O_bound). Fibers are very lightweight
|
43
|
-
|
45
|
+
(starting at about 10KB), can be context-switched faster than threads or
|
44
46
|
processes, and literally millions of them can be created on a single system -
|
45
47
|
the only limiting factor is available memory.
|
46
48
|
|
47
|
-
Polyphony takes Ruby's fibers and adds a way to schedule and switch between
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
49
|
+
Polyphony takes Ruby's fibers and adds a way to schedule and switch between them
|
50
|
+
automatically whenever a blocking operation is started, such as waiting for a
|
51
|
+
TCP connection to be established, for incoming data on an HTTP conection, or for
|
52
|
+
a timer to elapse. In addition, Polyphony patches the stock Ruby classes to
|
53
|
+
support its concurrency model, letting developers use all of Ruby's stdlib, for
|
54
|
+
example `Net::HTTP` and `Mail` while reaping the benefits of lightweight,
|
55
|
+
fine-grained, performant, fiber-based concurrency.
|
54
56
|
|
55
57
|
Writing concurrent applications using Polyphony's fiber-based concurrency model
|
56
|
-
offers a significant performance advantage.
|
57
|
-
down into many fine-grained concurrent operations
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
offers a significant performance advantage. Complex concurrent tasks can be
|
59
|
+
broken down into many fine-grained concurrent operations with very low overhead.
|
60
|
+
More importantly, this concurrency model lets developers express their ideas in
|
61
|
+
a sequential fashion, leading to source code that is much easier to read and
|
62
|
+
understand, compared to callback-style programming.
|
61
63
|
|
62
64
|
## Fibers - Polyphony's basic unit of concurrency
|
63
65
|
|
64
66
|
Polyphony extends the core `Fiber` class with additional functionality that
|
65
67
|
allows scheduling, synchronizing, interrupting and otherwise controlling running
|
66
|
-
fibers. Polyphony makes sure any exception raised while a is running is
|
67
|
-
correctly](exception-handling.md). Moreover, fibers can communicate
|
68
|
-
other using message passing, turning them into autonomous actors in a
|
69
|
-
|
68
|
+
fibers. Polyphony makes sure any exception raised while a fiber is running is
|
69
|
+
[handled correctly](exception-handling.md). Moreover, fibers can communicate
|
70
|
+
with each other using message passing, turning them into autonomous actors in a
|
71
|
+
highly concurrent environment.
|
70
72
|
|
71
73
|
## Higher-Order Concurrency Constructs
|
72
74
|
|
@@ -4,6 +4,8 @@ title: Design Principles
|
|
4
4
|
nav_order: 1
|
5
5
|
parent: Technical Overview
|
6
6
|
permalink: /technical-overview/design-principles/
|
7
|
+
prev_title: A Gentle Introduction to Polyphony
|
8
|
+
next_title: Concurrency the Easy Way
|
7
9
|
---
|
8
10
|
# Design Principles
|
9
11
|
|
@@ -13,9 +15,10 @@ applications in Ruby, by utilizing Ruby fibers together with the
|
|
13
15
|
library. Polyphony's design is based on the following principles:
|
14
16
|
|
15
17
|
- Polyphony's concurrency model should feel "baked-in". The API should allow
|
16
|
-
concurrency with minimal effort.
|
17
|
-
|
18
|
-
should be no calls to initialize the event reactor, or other ceremonial
|
18
|
+
concurrency with minimal effort. Polyphony should facilitate writing both
|
19
|
+
large apps and small scripts with as little boilerplate code as possible.
|
20
|
+
There should be no calls to initialize the event reactor, or other ceremonial
|
21
|
+
code:
|
19
22
|
|
20
23
|
```ruby
|
21
24
|
require 'polyphony'
|
@@ -25,12 +28,12 @@ library. Polyphony's design is based on the following principles:
|
|
25
28
|
|
26
29
|
puts 'going to sleep now'
|
27
30
|
# wait for other fibers to terminate
|
28
|
-
|
31
|
+
suspend
|
29
32
|
```
|
30
33
|
|
31
34
|
- Blocking operations should yield to other concurrent tasks without any
|
32
35
|
decoration or wrapper APIs. This means no `async/await` notation, and no
|
33
|
-
|
36
|
+
async callback-style APIs.
|
34
37
|
|
35
38
|
```ruby
|
36
39
|
# in Polyphony, I/O ops block the current fiber, but implicitly yield to other
|
@@ -53,6 +56,7 @@ library. Polyphony's design is based on the following principles:
|
|
53
56
|
}
|
54
57
|
}
|
55
58
|
```
|
59
|
+
|
56
60
|
- Polyphony should embrace Ruby's standard `raise/rescue/ensure` exception
|
57
61
|
handling mechanism:
|
58
62
|
|
@@ -81,20 +85,8 @@ library. Polyphony's design is based on the following principles:
|
|
81
85
|
}
|
82
86
|
```
|
83
87
|
|
84
|
-
- The
|
85
|
-
|
86
|
-
callbacks. The design for Polyphony should center on suspending and resuming
|
87
|
-
fibers:
|
88
|
-
|
89
|
-
```ruby
|
90
|
-
# psuedo-code for Gyro::Timer, the internal timer class
|
91
|
-
def Gyro::Timer.await
|
92
|
-
@fiber = Fiber.current
|
93
|
-
# the libev event reactor uses callbacks for handling events, Polyphony uses
|
94
|
-
# callbacks for switching between fibers
|
95
|
-
EV.start_timer(@interval) { @fiber.transfer }
|
96
|
-
end
|
97
|
-
```
|
88
|
+
- The entire design should embrace fibers. There should be no callback-based
|
89
|
+
asynchronous APIs.
|
98
90
|
|
99
91
|
- Use of extensive monkey patching of Ruby core modules and classes such as
|
100
92
|
`Kernel`, `Fiber`, `IO` and `Timeout`. This allows porting over non-Polyphony
|
@@ -115,5 +107,5 @@ library. Polyphony's design is based on the following principles:
|
|
115
107
|
end
|
116
108
|
```
|
117
109
|
|
118
|
-
- Development of techniques and tools for
|
110
|
+
- Development of techniques and tools for converting callback-based APIs to
|
119
111
|
fiber-based ones.
|
@@ -4,6 +4,8 @@ title: Exception Handling
|
|
4
4
|
nav_order: 4
|
5
5
|
parent: Technical Overview
|
6
6
|
permalink: /technical-overview/exception-handling/
|
7
|
+
prev_title: How Fibers are Scheduled
|
8
|
+
next_title: Extending Polyphony
|
7
9
|
---
|
8
10
|
# Exception Handling
|
9
11
|
|
@@ -75,7 +77,28 @@ Exception.__disable_sanitized_backtrace__ = true
|
|
75
77
|
...
|
76
78
|
```
|
77
79
|
|
78
|
-
##
|
80
|
+
## Exceptions and Fiber Scheduling
|
81
|
+
|
82
|
+
Polyphony takes advantages of Ruby's `Fiber#transfer` API to allow interrupting
|
83
|
+
fiber execution and raise cross-fiber exceptions. This is done by inspecting the
|
84
|
+
return value of `Fiber#transfer`, which returns when the fiber resumes, at every
|
85
|
+
[switchpoint](../fiber-scheduling/#switchpoints). If the return value is an
|
86
|
+
exception, it is raised in the context of the resumed fiber, and is then subject
|
87
|
+
to any `rescue` statements in the context of that fiber.
|
88
|
+
|
89
|
+
Exceptions can be passed to arbitrary fibers by using `Fiber#raise`. They can also be manually raised in fibers by using `Fiber#schedule`:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
f = spin do
|
93
|
+
suspend
|
94
|
+
rescue => e
|
95
|
+
puts e.message
|
96
|
+
end
|
97
|
+
|
98
|
+
f.schedule(RuntimeError.new('foo')) #=> will print 'foo'
|
99
|
+
```
|
100
|
+
|
101
|
+
## Cleaning Up After Exceptions - Using Ensure
|
79
102
|
|
80
103
|
A major issue when handling exceptions is cleaning up - freeing up resources
|
81
104
|
that have been allocated, cancelling ongoing operations, etc. Polyphony allows
|
@@ -133,3 +156,49 @@ will bubble up through the different enclosing fibers, until reaching the
|
|
133
156
|
top-most level, that of the root fiber, at which point the exception will cause
|
134
157
|
the program to halt and print an error message.
|
135
158
|
|
159
|
+
## MoveOn and Cancel - Interrupting Fiber Execution
|
160
|
+
|
161
|
+
In addition to enhancing Ruby's normal exception-handling mechanism, Polyphony
|
162
|
+
provides two exception classes that used exclusively to interrupt fiber
|
163
|
+
execution: `MoveOn` and `Cancel`. Both of these classes are used in various
|
164
|
+
fiber-control APIs, and `MoveOn` exceptions in particular are handled in a
|
165
|
+
particular manner by Polyphony. The difference between `MoveOn` and `Cancel` is
|
166
|
+
that `MoveOn` stops fiber execution without the exception bubbling up. It can
|
167
|
+
optionally provide an arbitrary return value for the fiber. `Cancel` will bubble
|
168
|
+
up like all exceptions.
|
169
|
+
|
170
|
+
The `MoveOn` and `Cancel` classes are normally used indirectly, through the
|
171
|
+
`Fiber#interrupt` and `Fiber#cancel` APIs, and also through the use of [cancel
|
172
|
+
scopes](#):
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
f1 = spin { sleep 100; return 'foo' }
|
176
|
+
f2 = spin { f1.await }
|
177
|
+
...
|
178
|
+
f1.interrupt('bar')
|
179
|
+
f2.result #=> 'bar'
|
180
|
+
|
181
|
+
f3 = spin { sleep 100 }
|
182
|
+
...
|
183
|
+
f3.cancel #=> will raise a Cancel exception
|
184
|
+
```
|
185
|
+
|
186
|
+
## Signal Handling and Termination
|
187
|
+
|
188
|
+
Polyphony does not normally intercept process signals, though it is possible to
|
189
|
+
intercept them using `Gyro::Signal` watchers. It is, however, recommended for
|
190
|
+
the time being to not interfere with Ruby's normal signal processing.
|
191
|
+
|
192
|
+
In Ruby there are three core exception classes are related to signal handling
|
193
|
+
and process termination: `Interrupt` - raised upon receiving an `INT` signal;
|
194
|
+
`SystemExit` - raised upon calling `Kernel#exit`; and `SignalException` - raised
|
195
|
+
upon receiving other signals.
|
196
|
+
|
197
|
+
These exceptions are raised on the main thread and in a multi-fiber environment
|
198
|
+
can occur in any fiber, as long as it is the currently running fiber. In
|
199
|
+
Polyphony, when these exceptions are raised in a fiber other than the main
|
200
|
+
fiber, they will be effectively tranferred to the main fiber for processing.
|
201
|
+
|
202
|
+
This means that any handlers for these three exception classes should be put
|
203
|
+
only in the main fiber. This mechanism also helps with showing a correct
|
204
|
+
backtrace for these exceptions.
|