polyphony 0.28 → 0.29

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -4
  3. data/CHANGELOG.md +12 -0
  4. data/Gemfile.lock +1 -1
  5. data/LICENSE +1 -1
  6. data/README.md +23 -21
  7. data/Rakefile +2 -0
  8. data/TODO.md +0 -3
  9. data/docs/_includes/prevnext.html +17 -0
  10. data/docs/_layouts/default.html +106 -0
  11. data/docs/_sass/custom/custom.scss +21 -0
  12. data/docs/faq.md +13 -10
  13. data/docs/getting-started/installing.md +2 -0
  14. data/docs/getting-started/tutorial.md +5 -3
  15. data/docs/index.md +4 -5
  16. data/docs/technical-overview/concurrency.md +21 -19
  17. data/docs/technical-overview/design-principles.md +12 -20
  18. data/docs/technical-overview/exception-handling.md +70 -1
  19. data/docs/technical-overview/extending.md +1 -0
  20. data/docs/technical-overview/fiber-scheduling.md +109 -88
  21. data/docs/user-guide/all-about-timers.md +126 -0
  22. data/docs/user-guide/web-server.md +2 -2
  23. data/docs/user-guide.md +1 -1
  24. data/examples/core/xx-deferring-an-operation.rb +2 -2
  25. data/examples/core/xx-sleep-forever.rb +9 -0
  26. data/examples/core/xx-snooze-starve.rb +16 -0
  27. data/examples/core/xx-spin_error_backtrace.rb +1 -1
  28. data/examples/core/xx-trace.rb +1 -2
  29. data/examples/core/xx-worker-thread.rb +30 -0
  30. data/examples/io/xx-happy-eyeballs.rb +37 -0
  31. data/ext/gyro/gyro.c +8 -3
  32. data/ext/gyro/gyro.h +7 -1
  33. data/ext/gyro/queue.c +35 -3
  34. data/ext/gyro/selector.c +31 -2
  35. data/ext/gyro/thread.c +18 -16
  36. data/lib/polyphony/core/global_api.rb +0 -1
  37. data/lib/polyphony/core/thread_pool.rb +5 -0
  38. data/lib/polyphony/core/throttler.rb +0 -1
  39. data/lib/polyphony/extensions/fiber.rb +14 -3
  40. data/lib/polyphony/extensions/thread.rb +16 -4
  41. data/lib/polyphony/irb.rb +7 -1
  42. data/lib/polyphony/trace.rb +44 -11
  43. data/lib/polyphony/version.rb +1 -1
  44. data/lib/polyphony.rb +1 -0
  45. data/test/helper.rb +1 -3
  46. data/test/test_async.rb +1 -1
  47. data/test/test_cancel_scope.rb +3 -3
  48. data/test/test_fiber.rb +157 -54
  49. data/test/test_global_api.rb +51 -1
  50. data/test/test_gyro.rb +4 -156
  51. data/test/test_io.rb +1 -1
  52. data/test/test_supervisor.rb +2 -2
  53. data/test/test_thread.rb +72 -1
  54. data/test/test_thread_pool.rb +6 -2
  55. data/test/test_throttler.rb +7 -5
  56. data/test/test_trace.rb +6 -6
  57. metadata +10 -5
  58. data/examples/core/xx-extended_fibers.rb +0 -150
  59. data/examples/core/xx-mt-scheduler.rb +0 -349
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96b63d0e57950ea2983cfadab521cceaba97180fce4269575715a856d1beebdd
4
- data.tar.gz: 42580ebe9a11620cc546474837983ae959aa239fba3a0c926eb62a40d9b66773
3
+ metadata.gz: fb34882b5a9cb5bbf2b1b27909795f96e5291fe953aeea53f6ebd4e95d8bd19f
4
+ data.tar.gz: c38af8c17d62b70fed44988182e70caba305eb9abbb3c4d1a8fdeda349031bae
5
5
  SHA512:
6
- metadata.gz: b4f4f02373ff3f9f1780a90b5df09bdd8efc95dbe28bed7787915a474961fe1f687baeb6cf46431eda65a6719c9e98a46b44807d71496723d84fbe7b0841a582
7
- data.tar.gz: 1582cdfa47593230dc64f1676a514cea09e36db37048e138f41f75a626a91acac0e6dd34d6962597b4016a57afb125a9913c1111474f3470543e75f1b4915b88
6
+ metadata.gz: ab9f3c212357aa7eedaee5f43c1b85ff28337e8bb1cc3d693fcc72ff02c49ddf55b211daccfd0eb516cfca1544e02312f10f5e59eb0903c55279cc5983a69ca3
7
+ data.tar.gz: 9d871f6e1c8013a13fc03f191d6e0ac7fb7766e723c73be3afcf2d870cea9595bc750c4d45c7efb1a61104ef0611e00a430baae6b3a0c5afcd4b2a859c8677f5
data/.rubocop.yml CHANGED
@@ -58,10 +58,6 @@ Style/GlobalVars:
58
58
  - lib/polyphony/extensions/core.rb
59
59
  - examples/**/*.rb
60
60
 
61
- Style/ClassVars:
62
- Exclude:
63
- - lib/polyphony/core/coprocess.rb
64
-
65
61
  Layout/HashAlignment:
66
62
  EnforcedColonStyle: table
67
63
  EnforcedHashRocketStyle: table
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.28)
4
+ polyphony (0.29)
5
5
  modulation (~> 1.0)
6
6
 
7
7
  GEM
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2018 Sharon Rosner
3
+ Copyright (c) 2020 Sharon Rosner
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,15 +1,22 @@
1
- # Polyphony - Easy Concurrency for Ruby
1
+ # Polyphony - Fine-Grained Concurrency for Ruby
2
2
 
3
- [DOCS](https://dfab.gitbook.io/polyphony) |
3
+ [DOCS](https://digital-fabric.github.io/polyphony/) |
4
4
  [EXAMPLES](examples)
5
5
 
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.
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 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.
11
-
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.
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
- coprocesses, supervisors, cancel scopes, throttling, resource pools etc.
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://dfab.gitbook.io/polyphony).
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
@@ -10,6 +10,8 @@ Rake::ExtensionTask.new("gyro_ext") do |ext|
10
10
  ext.ext_dir = "ext/gyro"
11
11
  end
12
12
 
13
+ task :recompile => [:clean, :compile]
14
+
13
15
  task :default => [:compile, :test]
14
16
  task :test do
15
17
  exec 'ruby test/run.rb'
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">&nbsp;</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. Consider the following implementation of an echo server using
18
- EventMachine:
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 lock](https://en.wikipedia.org/wiki/Global_interpreter_lock),
143
- multiple threads can not in fact run in parallel, and this is actually one of
144
- the reasons fibers are such a better fit for I/O bound Ruby programs. Threads
145
- should really be used when performing synchronous operations that are not
146
- fiber-aware, such as running an expensive SQLite query, or some other expensive
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 https://github.com/digital-fabric/polyphony. Feel
176
- free to create issues and contribute pull requests.
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: Installing Polyphony
4
4
  nav_order: 1
5
5
  parent: Getting Started
6
6
  permalink: /getting-started/installing/
7
+ prev_title: Home
8
+ next_title: A Gentle Introduction to Polyphony
7
9
  ---
8
10
  # Getting Started
9
11
 
@@ -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 other
373
- work? For example, we might want to collect the different local times into a
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 repository
90
- at https://github.com/digital-fabric/polyphony as your primary point of
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 \(in contrast to
40
- pre-emptive concurrency, like threads and processes\), offers many advantages,
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
- \(starting at about 20KB\), can be context-switched faster than threads or
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
- fibers automatically whenever a blocking operation is started, such as waiting
49
- for a TCP connection to be established, or waiting for an I/O object to be
50
- readable, or waiting for a timer to elapse. In addition, Polyphony patches the
51
- stock Ruby classes to support its concurrency model, letting developers use all
52
- of Ruby's stdlib, for example `Net::HTTP` and `Mail` while reaping the benefits
53
- of lightweight, highly performant, fiber-based concurrency.
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. Computational tasks can be broken
57
- down into many fine-grained concurrent operations that cost very little in
58
- memory and context-switching time. More importantly, this concurrency model lets
59
- developers express their ideas in a sequential manner, leading to source code
60
- that is easy to read and reason about.
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 [handled
67
- correctly](exception-handling.md). Moreover, fibers can communicate with each
68
- other using message passing, turning them into autonomous actors in a
69
- fine-grained concurrent environment.
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. Polyphny should allow creating small
17
- concurrent programs with as little boilerplate code as possible. There
18
- should be no calls to initialize the event reactor, or other ceremonial code:
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
- sleep
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
- built-in concept of deferred computation.
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 internal reactor design should embrace fibers rather than be based on
85
- invoking callbacks. The internal design of most reactor libraries is based on
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 coverting callback-based APIs to
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
- ## Cleaning up after exceptions
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.
@@ -4,6 +4,7 @@ title: Extending Polyphony
4
4
  nav_order: 5
5
5
  parent: Technical Overview
6
6
  permalink: /technical-overview/extending/
7
+ prev_title: Exception Handling
7
8
  ---
8
9
  # Extending Polyphony
9
10