polyphony 0.28 → 0.29
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.
- 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.
|