polyphony 0.32 → 0.33

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75e18e7719d577fc1f07679874d2c3afe23b5a6b6184c1c6723006d0f9fcdf80
4
- data.tar.gz: 347608163b6d0ec934b2a70d1b28ffd1c0056b3da51f242f6619ac4a8e94a3ad
3
+ metadata.gz: fde67b1d9cd285b35d265f12648e48e43073fd3b8e27713f981883ab4b080c12
4
+ data.tar.gz: a567e1e171b4f61f5fb344b3345b4a9e6f32ec7a810258c99007a17614e5cd5e
5
5
  SHA512:
6
- metadata.gz: 41cfdd7550a444c590b08eff7c2bf716ce42a19250074bca0425334be4b32b96efba4d60bbe9b1257cf3b8616be31da940695709a25af8c5d03c9803dff8fb80
7
- data.tar.gz: 3a12af73aa84d4c3ae9e5c94c0dd55f99bd7eaf3ca49e1f59ab4b80d224dfe1dc068c68f4f8e09ab3d0bcc4996f61ae8d03c42158912f24458ad0ed1e09e6b2a
6
+ metadata.gz: 3e99f3e9f9acce3b2deadf516dc254ae6a305a64b9bd05a04e6207834f1189c61ae44481e485381fbb08eae3ee2f1da0843441ee518f64448f76c3529e1281a7
7
+ data.tar.gz: b6ebe93d0eb812010aafdcc2f9298e1237c14a6b0da6b3da30abe917217fb04fac24671e02792c0c3f790912660ed0b64355849e81e07bb35029dc647f413589
@@ -0,0 +1,20 @@
1
+ name: Tests
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v1
10
+ - uses: actions/setup-ruby@v1
11
+ with:
12
+ ruby-version: 2.6.5
13
+ - name: Install dependencies
14
+ run: |
15
+ gem install bundler
16
+ bundle install
17
+ - name: Compile C-extension
18
+ run: bundle exec rake compile
19
+ - name: Run tests
20
+ run: bundle exec rake test
@@ -129,4 +129,17 @@ Naming/MethodParameterName:
129
129
 
130
130
  Security/MarshalLoad:
131
131
  Exclude:
132
- - examples/**/*.rb
132
+ - examples/**/*.rb
133
+
134
+ Lint/ShadowedArgument:
135
+ Exclude:
136
+ - lib/polyphony/extensions/fiber.rb
137
+
138
+ Style/HashEachMethods:
139
+ Enabled: true
140
+
141
+ Style/HashTransformKeys:
142
+ Enabled: true
143
+
144
+ Style/HashTransformValues:
145
+ Enabled: true
@@ -1,3 +1,11 @@
1
+ ## 0.33 2020-03-08
2
+
3
+ * Implement `Fiber#supervise` (WIP)
4
+ * Add `Fiber#restart` API
5
+ * Fix race condition in `Thread#join`, `Thread#raise` (#14)
6
+ * Add `Exception#source_fiber` - references the fiber in which an uncaught
7
+ exception occurred
8
+
1
9
  ## 0.32 2020-02-29
2
10
 
3
11
  * Accept optional throttling rate in `#spin_loop`
@@ -6,8 +14,8 @@
6
14
  * Add `#receive_pending` global API.
7
15
  * Prevent race condition in `Gyro::Queue`.
8
16
  * Improve signal handling - `INT`, `TERM` signals are now always handled in the
9
- main fiber.
10
- * Fix adapter requires (redis and postgres).
17
+ main fiber
18
+ * Fix adapter requires (redis and postgres)
11
19
 
12
20
  ## 0.31 2020-02-20
13
21
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.32)
4
+ polyphony (0.33)
5
5
  modulation (~> 1.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Polyphony - Fine-Grained Concurrency for Ruby
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/polyphony.svg)](http://rubygems.org/gems/polyphony)
4
+ [![Modulation Test](https://github.com/digital-fabric/polyphony/workflows/Tests/badge.svg)](https://github.com/digital-fabric/polyphony/actions?query=workflow%3ATests)
5
+ [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/digital-fabric/polyphony/blob/master/LICENSE)
6
+
3
7
  [DOCS](https://digital-fabric.github.io/polyphony/) |
4
8
  [EXAMPLES](examples)
5
9
 
data/TODO.md CHANGED
@@ -1,4 +1,118 @@
1
- ## 0.32 Working Sinatra application
1
+ - Debugging
2
+ - Eat your own dogfood: need a good tool to check what's going on when some
3
+ test fails
4
+ - Needs to work with Pry (can write perhaps an extension for pry)
5
+ - First impl in Ruby using `TracePoint` API
6
+ - Mode of operation:
7
+ - Debugger runs on separate thread
8
+ - The `TracePoint` handler passes control to the debugger thread, and waits
9
+ for reply (probably using Fiber messages)
10
+ - Step over should return on the next line *for the same fiber*
11
+ - The event loop (all event loops?) should be suspended so timers are adjusted
12
+ accordingly, so on control passing to debugger we:
13
+
14
+ - call `ev_suspend()` for main thread ev_loop
15
+ - prompt and wait for input from user
16
+ - call `ev_resume()` for main thread ev_loop
17
+ - process user input
18
+
19
+ (We need to verify than `ev_suspend/resume` works for an ev_loop that's not
20
+ currently running.)
21
+ - Allow inspection of fiber tree, thread's run queue, fiber's scheduled values etc.
22
+
23
+ - UI
24
+ - `Kernel#breakpoint` is used to break into the debugger while running code
25
+
26
+ ```ruby
27
+ def test_sleep
28
+ f = spin { sleep 10 }
29
+ breakpoint
30
+ ...
31
+ end
32
+ ```
33
+
34
+ Hitting the breakpoint will show the current location in the source code
35
+ (with few lines before and after), and present a prompt for commands.
36
+
37
+ - commands:
38
+ - `step` / `up` / `skip` / `continue` etc. - step into, step out, step over, run
39
+ - `switch` - switch fiber
40
+ - how do we select a fiber?
41
+ - from a list?
42
+ - from an expression: `Fiber.current.children`
43
+ - maybe just `select f1` (where f1 is a local var)
44
+
45
+ - Fiber supervision
46
+ - can a fiber be restarted?
47
+ - Theoretically, we can take the same block and rerun it under a different
48
+ fiber
49
+ - (restart) strategy (taken from Erlang):
50
+ - `:one_for_one`: if a child terminates, it is restarted
51
+ - `:one_for_all`: if a child terminates, all other children are terminated,
52
+ then all are restarted
53
+ - `:simple_on_for_one`: same as `:one_for_one` except all children are
54
+ identical (run the same block)
55
+ - I'm not so sure this kind of supervision is what we need. We can envision
56
+ an API such as the following:
57
+
58
+ ```ruby
59
+ spin {
60
+ spin { do_a }
61
+ spin { do_b }
62
+ supervise(restart: :never, shutdown: :terminate)
63
+ }
64
+ ```
65
+
66
+ - The default for `#supervise` should be:
67
+ - wait for all fibers to terminate
68
+ - propagate exceptions
69
+ - no restart
70
+
71
+ - `#supervise` could also take a block:
72
+
73
+ ```ruby
74
+ supervise do |fiber, error|
75
+ # this block is called when a fiber is terminated, letting the developer
76
+ # decide how to react.
77
+
78
+ # We can propagate the error
79
+ raise error if error
80
+
81
+ # We can restart the fiber (the respun fiber will have the same parent,
82
+ # the same caller location, the same tag)
83
+ fiber.respin # TODO: implement Fiber#respin
84
+ end
85
+ ```
86
+
87
+ - Another option is to pass supervision parameters to `#spin`:
88
+
89
+ ```ruby
90
+ spin(supervise: true) do
91
+
92
+ end
93
+ ```
94
+
95
+ - Or maybe:
96
+
97
+ ```ruby
98
+ spin_supervisor do
99
+
100
+ end
101
+ ```
102
+
103
+ ## 0.32 Some more API work, more docs
104
+
105
+ - Allow calling `cancel_after` and `move_on_after` without blocks, just return
106
+ the cancelling fiber:
107
+
108
+ ```ruby
109
+ def foo
110
+ f = cancel_after(10)
111
+ do_something_slow
112
+ ensure
113
+ f.stop
114
+ end
115
+ ```
2
116
 
3
117
  - Docs
4
118
  - landing page:
@@ -128,3 +242,4 @@ Prior art:
128
242
  do_my_thing
129
243
  end
130
244
  ```
245
+
@@ -23,4 +23,8 @@ $link-color: $blue-000;
23
23
 
24
24
  #prevnext span.clear {
25
25
  clear: both;
26
+ }
27
+
28
+ .h-align-center {
29
+ text-align: center;
26
30
  }
@@ -26,12 +26,6 @@ span.section-title {
26
26
  color: rgba($body-text-color, 0.3);
27
27
  content: "";
28
28
  }
29
-
30
- &.active {
31
- &::before {
32
- color: $body-text-color;
33
- }
34
- }
35
29
  }
36
30
  }
37
31
 
@@ -40,6 +34,10 @@ a.navigation-list-link {
40
34
  font-weight: 600;
41
35
  }
42
36
 
37
+ a.navigation-list-link.active {
38
+ border-right: 4px #6ae solid;
39
+ }
40
+
43
41
  a.navigation-list-link:hover {
44
42
  color: $link-color;
45
43
  }
@@ -5,9 +5,9 @@ nav_order: 1
5
5
  parent: Getting Started
6
6
  permalink: /getting-started/installing/
7
7
  prev_title: Home
8
- next_title: A Gentle Introduction to Polyphony
8
+ next_title: Tutorial
9
9
  ---
10
- # Getting Started
10
+ # Installing Polyphony
11
11
 
12
12
  ## System Requirements
13
13
 
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  layout: page
3
- title: A Gentle Introduction to Polyphony
3
+ title: Tutorial
4
4
  nav_order: 2
5
5
  parent: Getting Started
6
6
  permalink: /getting-started/tutorial/
7
7
  prev_title: Installing Polyphony
8
8
  next_title: Concurrency the Easy Way
9
9
  ---
10
- # A Gentle Introduction to Polyphony
10
+ # Polyphony: a Tutorial
11
11
 
12
12
  Polyphony is a new Ruby library aimed at making writing concurrent Ruby apps
13
13
  easy and fun. In this article, we'll introduce Polyphony's fiber-based
@@ -170,7 +170,7 @@ rescue Errno::ECONNRESET
170
170
  end
171
171
  ```
172
172
 
173
- ### Adding Concurrency
173
+ ## Adding Concurrency
174
174
 
175
175
  Up until now, we did nothing about concurrency. In fact, our code will not be
176
176
  able to handle more than one client at a time, because the accept loop cannot
@@ -212,17 +212,17 @@ Let's consider the advantage of the Polyphony concurrency model:
212
212
 
213
213
  Now that we have a working concurrent echo server, let's add some bells and
214
214
  whistles. First of all, let's get rid of clients that are not active. We'll do
215
- this by using a Polyphony construct called a cancel scope. Cancel scopes define
216
- an execution context that can cancel any operation ocurring within its scope:
215
+ this by setting up a timeout fiber that cancels the fiber dealing with the connection
217
216
 
218
217
  ```ruby
219
218
  def handle_client(client)
220
- Polyphony::CancelScope.new(timeout: 10) do |scope|
221
- while (data = client.gets)
222
- scope.reset_timeout
223
- client.write('you said: ', data.chomp, "!\n")
224
- end
219
+ timeout = cancel_after(10)
220
+ while (data = client.gets)
221
+ timeout.reset
222
+ client << data
225
223
  end
224
+ rescue Polyphony::Cancel
225
+ client.puts 'Closing connection due to inactivity.'
226
226
  rescue Errno::ECONNRESET
227
227
  puts 'Connection reset by client'
228
228
  ensure
@@ -252,15 +252,17 @@ server = TCPServer.open('127.0.0.1', 1234)
252
252
  puts 'Echoing on port 1234...'
253
253
 
254
254
  def handle_client(client)
255
- Polyphony::CancelScope.new(timeout: 10) do |scope|
256
- while (data = client.gets)
257
- scope.reset_timeout
258
- client.write('you said: ', data.chomp, "!\n")
259
- end
255
+ timeout = cancel_after(10)
256
+ while (data = client.gets)
257
+ timeout.reset
258
+ client << data
260
259
  end
260
+ rescue Polyphony::Cancel
261
+ client.puts 'Closing connection due to inactivity.'
261
262
  rescue Errno::ECONNRESET
262
263
  puts 'Connection reset by client'
263
264
  ensure
265
+ timeout.stop
264
266
  client.close
265
267
  end
266
268
 
@@ -8,45 +8,40 @@ next_title: Installing Polyphony
8
8
 
9
9
  # Polyphony - fine-grained concurrency for Ruby
10
10
 
11
- > Polyphony \| pəˈlɪf\(ə\)ni \|
12
- > 1. _Music_ the style of simultaneously combining a number of parts, each
13
- > forming an individual melody and harmonizing with each other.
14
- > 2. _Programming_ a Ruby gem for concurrent programming focusing on performance
15
- > and developer happiness.
16
-
17
11
  Polyphony is a library for building concurrent applications in Ruby. Polyphony
18
- harnesses the power of [Ruby fibers](https://ruby-doc.org/core-2.5.1/Fiber.html)
19
- to provide a cooperative, sequential coroutine-based concurrency model. Under
20
- the hood, Polyphony uses [libev](https://github.com/enki/libev) as a
21
- high-performance event reactor that provides timers, I/O watchers and other
22
- asynchronous event primitives.
12
+ implements a comprehensive
13
+ [fiber](https://ruby-doc.org/core-2.5.1/Fiber.html)-based concurrency model,
14
+ using [libev](https://github.com/enki/libev) as a high-performance event reactor
15
+ for I/O, timers, and other asynchronous events.
16
+
17
+ [Take the tutorial](getting-started/tutorial){: .btn .btn-blue .text-gamma }
18
+ {: .mt-6 .h-align-center }
23
19
 
24
20
  ## Focused on Developer Happiness
25
21
 
26
22
  Polyphony is designed to make concurrent Ruby programming feel natural and
27
- fluent. Polyphony reduces the boilerplate usually associated with concurrent
28
- programming, and introduces concurrency primitives that are easy to use, easy to
29
- understand, and above all idiomatic.
23
+ fluent. The Polyphony API is easy to use, easy to understand, and above all
24
+ idiomatic.
30
25
 
31
26
  ## Optimized for High Performance
32
27
 
33
28
  Polyphony offers high performance for I/O bound Ruby apps. Distributing
34
- concurrent tasks over fibers, instead of threads or processes, minimizes memory
35
- consumption and reduces the cost of context-switching.
29
+ concurrent operations over fibers, instead of threads or processes, minimizes
30
+ memory consumption and reduces the cost of context-switching.
36
31
 
37
32
  ## Designed for Interoperability
38
33
 
39
- Polyphony makes it possible to use normal Ruby built-in classes like `IO`, and
40
- `Socket` in a concurrent multi-fiber environment. Polyphony takes care of
41
- context-switching automatically whenever a blocking call like `Socket#accept`,
42
- `IO#read` or `Kernel#sleep` is issued.
34
+ With Polyphony you can use any of the stock Ruby classes and modules like `IO`,
35
+ `Process`, `Socket` and `OpenSSL` in a concurrent multi-fiber environment. In
36
+ addition, Polyphony provides a structured model for exception handling that
37
+ builds on and enhances Ruby's exception handling system.
43
38
 
44
39
  ## A Growing Ecosystem
45
40
 
46
41
  Polyphony includes a full-blown HTTP server implementation with integrated
47
- support for HTTP 1, HTTP 2 and WebSockets, TLS/SSL termination, automatic
48
- ALPN protocol selection, and body streaming. Polyphony also includes fiber-aware
49
- extensions for PostgreSQL and Redis. More databases and services are forthcoming.
42
+ support for HTTP 2, WebSockets, TLS/SSL termination and more. Polyphony also
43
+ provides fiber-aware adapters for connecting to PostgreSQL and Redis. More
44
+ adapters are being developed.
50
45
 
51
46
  ## Features
52
47
 
@@ -4,7 +4,7 @@ title: Concurrency the Easy Way
4
4
  nav_order: 1
5
5
  parent: Main Concepts
6
6
  permalink: /main-concepts/concurrency/
7
- prev_title: A Gentle Introduction to Polyphony
7
+ prev_title: Tutorial
8
8
  next_title: How Fibers are Scheduled
9
9
  ---
10
10
  # Concurrency the Easy Way
@@ -121,6 +121,33 @@ VALUE Gyro_Async_await(VALUE self) {
121
121
  }
122
122
  }
123
123
 
124
+ VALUE Gyro_Async_await_no_raise(VALUE self) {
125
+ struct Gyro_Async *async;
126
+ VALUE ret;
127
+
128
+ GetGyro_Async(self, async);
129
+
130
+ async->fiber = rb_fiber_current();
131
+ if (!async->active) {
132
+ async->active = 1;
133
+ async->ev_loop = Gyro_Selector_current_thread_ev_loop();
134
+ ev_async_start(async->ev_loop, &async->ev_async);
135
+ }
136
+
137
+ ret = Fiber_await();
138
+ RB_GC_GUARD(ret);
139
+
140
+ if (async->active) {
141
+ async->active = 0;
142
+ async->fiber = Qnil;
143
+ ev_async_stop(async->ev_loop, &async->ev_async);
144
+ async->value = Qnil;
145
+ }
146
+
147
+ return ret;
148
+ }
149
+
150
+
124
151
  void Init_Gyro_Async() {
125
152
  cGyro_Async = rb_define_class_under(mGyro, "Async", rb_cData);
126
153
  rb_define_alloc_func(cGyro_Async, Gyro_Async_allocate);