polyphony 0.69 → 0.73
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/test.yml +2 -2
- data/.gitignore +3 -1
- data/CHANGELOG.md +33 -4
- data/Gemfile.lock +2 -2
- data/TODO.md +1 -24
- data/bin/pdbg +1 -1
- data/bin/polyphony-debug +0 -0
- data/bin/stress.rb +0 -0
- data/bin/test +0 -0
- data/docs/_user-guide/all-about-timers.md +1 -1
- data/docs/api-reference/exception.md +5 -1
- data/docs/api-reference/fiber.md +2 -2
- data/docs/faq.md +1 -1
- data/docs/getting-started/overview.md +8 -8
- data/docs/getting-started/tutorial.md +3 -3
- data/docs/main-concepts/concurrency.md +1 -1
- data/docs/main-concepts/extending.md +3 -3
- data/docs/main-concepts/fiber-scheduling.md +1 -1
- data/examples/core/calc.rb +37 -0
- data/examples/core/calc_with_restart.rb +40 -0
- data/examples/core/calc_with_supervise.rb +37 -0
- data/examples/core/message_based_supervision.rb +1 -1
- data/examples/core/ring.rb +29 -0
- data/examples/io/rack_server.rb +1 -1
- data/examples/io/tunnel.rb +1 -1
- data/examples/performance/fiber_transfer.rb +1 -1
- data/examples/performance/line_splitting.rb +1 -1
- data/examples/performance/thread-vs-fiber/compare.rb +1 -1
- data/ext/polyphony/backend_common.c +31 -7
- data/ext/polyphony/backend_common.h +2 -1
- data/ext/polyphony/backend_io_uring.c +57 -67
- data/ext/polyphony/backend_io_uring_context.c +1 -1
- data/ext/polyphony/backend_io_uring_context.h +1 -1
- data/ext/polyphony/backend_libev.c +38 -30
- data/ext/polyphony/extconf.rb +25 -13
- data/ext/polyphony/polyphony.h +5 -1
- data/ext/polyphony/queue.c +2 -2
- data/ext/polyphony/runqueue_ring_buffer.c +3 -2
- data/ext/polyphony/thread.c +1 -1
- data/lib/polyphony/adapters/irb.rb +11 -1
- data/lib/polyphony/{extensions → core}/debug.rb +0 -0
- data/lib/polyphony/core/global_api.rb +3 -6
- data/lib/polyphony/core/timer.rb +2 -2
- data/lib/polyphony/debugger.rb +3 -3
- data/lib/polyphony/extensions/exception.rb +45 -0
- data/lib/polyphony/extensions/fiber.rb +30 -16
- data/lib/polyphony/extensions/io.rb +2 -2
- data/lib/polyphony/extensions/{core.rb → kernel.rb} +0 -73
- data/lib/polyphony/extensions/openssl.rb +20 -5
- data/lib/polyphony/extensions/process.rb +19 -0
- data/lib/polyphony/extensions/socket.rb +3 -4
- data/lib/polyphony/extensions/timeout.rb +10 -0
- data/lib/polyphony/extensions.rb +9 -0
- data/lib/polyphony/net.rb +0 -1
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +2 -5
- data/polyphony.gemspec +1 -1
- data/test/coverage.rb +2 -2
- data/test/stress.rb +1 -1
- data/test/test_backend.rb +12 -12
- data/test/test_event.rb +1 -1
- data/test/test_ext.rb +1 -1
- data/test/test_fiber.rb +52 -12
- data/test/test_global_api.rb +16 -4
- data/test/test_io.rb +3 -3
- data/test/test_process_supervision.rb +39 -10
- data/test/test_queue.rb +6 -6
- data/test/test_signal.rb +20 -1
- data/test/test_socket.rb +12 -10
- data/test/test_supervise.rb +249 -81
- data/test/test_sync.rb +2 -2
- data/test/test_thread.rb +22 -2
- data/test/test_thread_pool.rb +1 -1
- data/test/test_throttler.rb +1 -1
- data/test/test_timer.rb +2 -2
- data/test/test_trace.rb +1 -1
- metadata +18 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cfcb66ef2171cc3c4216bc674c38468ef4bd23333031ec77d61d74bc17b32c33
|
4
|
+
data.tar.gz: 215cf149bd26e9d3d8c64b5c6f63eeccad86c5b6dc9ed5289bb6a1537e51ab56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9157512378e6edb0a362ea3db4fc35ecae3b6d2b54c025184a39ddf5e0e85528ac97aac1f3b829b461aa85e58a1655f35c7ad4d2aec0e8dda97c75d130625b34
|
7
|
+
data.tar.gz: f90758ee02b5dd28f488372d260b73413e4d01afa14756b8bfc787cd335c42ef43fa1b5907060cb33065727e804296bc6a25fee91d40b94e390e21c6b98d903e
|
data/.github/FUNDING.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
github: ciconia
|
data/.github/workflows/test.yml
CHANGED
@@ -16,7 +16,7 @@ jobs:
|
|
16
16
|
runs-on: ${{matrix.os}}
|
17
17
|
steps:
|
18
18
|
- uses: actions/checkout@v1
|
19
|
-
- uses:
|
19
|
+
- uses: ruby/setup-ruby@v1
|
20
20
|
with:
|
21
21
|
ruby-version: ${{matrix.ruby}}
|
22
22
|
- name: Install dependencies
|
@@ -24,7 +24,7 @@ jobs:
|
|
24
24
|
gem install bundler
|
25
25
|
bundle install
|
26
26
|
- name: Show Linux kernel version
|
27
|
-
run: uname -
|
27
|
+
run: uname -a
|
28
28
|
- name: Compile C-extension
|
29
29
|
run: POLYPHONY_USE_LIBEV=1 bundle exec rake compile
|
30
30
|
- name: Run tests
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,32 @@
|
|
1
|
+
## 0.73 2021-12-16
|
2
|
+
|
3
|
+
- Refactor core extensions into separate files for each class
|
4
|
+
- Improve compatibility with Ruby 3.1
|
5
|
+
- Improve accuracy of `timer_loop`
|
6
|
+
|
7
|
+
## 0.72 2021-11-22
|
8
|
+
|
9
|
+
- Add support for supervising added child fibers
|
10
|
+
- Do not use io_uring under LinuxKit (#66)
|
11
|
+
- Fix error message in `Fiber#restart`
|
12
|
+
- refactor `runqueue_ring_buffer_mark`
|
13
|
+
- Fix setting up test server on random port
|
14
|
+
- Enhance `Fiber#supervise`
|
15
|
+
- Add support for specifying `restart: true` (same as `restart: :always`)
|
16
|
+
- Add support for supervising child fibers (when fibers are not specified)
|
17
|
+
- Add `Fiber#attach_all_children_to`
|
18
|
+
- Improve `SSLServer#accept` when `servername_cb` is set
|
19
|
+
- Fix `#supervise` in Ruby 3.0
|
20
|
+
|
21
|
+
## 0.71 2021-08-20
|
22
|
+
|
23
|
+
- Fix compilation on Ruby 3.0
|
24
|
+
|
25
|
+
## 0.70 2021-08-19
|
26
|
+
|
27
|
+
- New implementation for `#supervise`
|
28
|
+
- Reset backend state and runqueue on fork
|
29
|
+
|
1
30
|
## 0.69 2021-08-16
|
2
31
|
|
3
32
|
- Rename `#__polyphony_read_method__` to `#__parser_read_method__`
|
@@ -18,7 +47,7 @@
|
|
18
47
|
## 0.66 2021-08-01
|
19
48
|
|
20
49
|
- Fix all splicing APIs on non-linux OSes (#63)
|
21
|
-
- Add GC marking of buffers when cancelling read/write ops in io_uring backend
|
50
|
+
- Add GC marking of buffers when cancelling read/write ops in io_uring backend
|
22
51
|
|
23
52
|
## 0.65 2021-07-29
|
24
53
|
|
@@ -82,7 +111,7 @@
|
|
82
111
|
## 0.55.0 2021-06-17
|
83
112
|
|
84
113
|
- Finish io_uring implementation of Backend#chain
|
85
|
-
- Reimplement io_uring op_context acquire/release algorithm (using ref count)
|
114
|
+
- Reimplement io_uring op_context acquire/release algorithm (using ref count)
|
86
115
|
- Fix #gets on sockets
|
87
116
|
- Redesign event anti-starvation mechanism
|
88
117
|
|
@@ -177,7 +206,7 @@
|
|
177
206
|
|
178
207
|
## 0.47.0 2020-11-10
|
179
208
|
|
180
|
-
- Implement `#spin_scope` used for creating blocking fiber scopes
|
209
|
+
- Implement `#spin_scope` used for creating blocking fiber scopes
|
181
210
|
- Reimplement `move_on_after`, `cancel_after`, `Timeout.timeout` using
|
182
211
|
`Backend#timeout` (avoids creating canceller fiber for most common use case)
|
183
212
|
- Implement `Backend#timeout` API
|
@@ -272,7 +301,7 @@
|
|
272
301
|
- Reimplement Event using `Agent#wait_event`
|
273
302
|
- Improve Queue shift queue performance
|
274
303
|
- Introduce `Agent#wait_event` API for waiting on asynchronous events
|
275
|
-
- Minimize `fcntl` syscalls in IO operations
|
304
|
+
- Minimize `fcntl` syscalls in IO operations
|
276
305
|
|
277
306
|
## 0.43.7 2020-07-20
|
278
307
|
|
data/Gemfile.lock
CHANGED
data/TODO.md
CHANGED
@@ -26,31 +26,8 @@
|
|
26
26
|
|
27
27
|
- Add support for `close` to io_uring backend
|
28
28
|
|
29
|
-
- Graceful shutdown again:
|
30
|
-
- What happens to children when doing a graceful shutdown?
|
31
|
-
- What are the implications of passing graceful shutdown flag to children?
|
32
|
-
- What about errors while doing a graceful shutdown?
|
33
|
-
- What about graceful restarts?
|
34
|
-
- Some interesting discussions:
|
35
|
-
- https://trio.discourse.group/search?q=graceful%20shutdown
|
36
|
-
- https://github.com/python-trio/trio/issues/147
|
37
|
-
- https://github.com/python-trio/trio/issues/143
|
38
|
-
- https://trio.discourse.group/t/graceful-shutdown/93/2
|
39
|
-
- https://250bpm.com/blog:146/
|
40
|
-
- https://www.rodrigoaraujo.me/posts/golang-pattern-graceful-shutdown-of-concurrent-events/
|
41
|
-
- https://github.com/tj/go-gracefully
|
42
|
-
- `Fiber#finalize_children` should pass graceful shutdown flag to children
|
43
|
-
- A good use case is an HTTP server that on graceful shutdown:
|
44
|
-
- stops listening
|
45
|
-
- waits for all ongoing requests to finish, optionally with a timeout
|
46
|
-
|
47
29
|
## Roadmap for Polyphony 1.0
|
48
30
|
|
49
|
-
- check integration with rb-inotify
|
50
|
-
|
51
|
-
- Improve `#supervise`. It does not work as advertised, and seems to exhibit an
|
52
|
-
inconsistent behaviour (see supervisor example).
|
53
|
-
|
54
31
|
- Add test that mimics the original design for Monocrono:
|
55
32
|
- 256 fibers each waiting for a message
|
56
33
|
- When message received do some blocking work using a `ThreadPool`
|
@@ -130,7 +107,7 @@
|
|
130
107
|
|
131
108
|
Hitting the breakpoint will show the current location in the source code
|
132
109
|
(with few lines before and after), and present a prompt for commands.
|
133
|
-
|
110
|
+
|
134
111
|
- commands:
|
135
112
|
- `step` / `up` / `skip` / `continue` etc. - step into, step out, step over, run
|
136
113
|
- `switch` - switch fiber
|
data/bin/pdbg
CHANGED
data/bin/polyphony-debug
CHANGED
File without changes
|
data/bin/stress.rb
CHANGED
File without changes
|
data/bin/test
CHANGED
File without changes
|
@@ -44,7 +44,7 @@ The `#sleep` forever call can be used for example in the main fiber when we do
|
|
44
44
|
all our work in other fibers, since once the main fiber terminates the program
|
45
45
|
exits.
|
46
46
|
|
47
|
-
## Doing Work Later
|
47
|
+
## Doing Work Later
|
48
48
|
|
49
49
|
While `#sleep` allows you to block execution of the current fiber, sometimes you
|
50
50
|
want to perform some work later, while not blocking the current fiber. This is done simply by spinning off another fiber:
|
@@ -3,15 +3,19 @@ layout: page
|
|
3
3
|
title: ::Exception
|
4
4
|
parent: API Reference
|
5
5
|
permalink: /api-reference/exception/
|
6
|
+
source_url: https://github.com/digital-fabric/polyphony/blob/master/lib/polyphony/extensions/core.rb
|
7
|
+
ruby_docs_url: https://rubyapi.org/3.0/o/exception
|
6
8
|
---
|
7
9
|
# ::Exception
|
8
10
|
|
9
|
-
[Ruby core Exception documentation](https://
|
11
|
+
[Ruby core Exception documentation](https://rubyapi.org/3.0/o/exception)
|
10
12
|
|
11
13
|
The core `Exception` class is enhanced to provide a better backtrace that takes
|
12
14
|
into account the fiber hierarchy. In addition, a `source_fiber` attribute allows
|
13
15
|
tracking the fiber from which an uncaught exception was propagated.
|
14
16
|
|
17
|
+
|
18
|
+
|
15
19
|
## Class Methods
|
16
20
|
|
17
21
|
## Instance methods
|
data/docs/api-reference/fiber.md
CHANGED
@@ -205,7 +205,7 @@ f.raise
|
|
205
205
|
Pops the first message from the fiber's mailbox. If no message is available,
|
206
206
|
`#receive` will block until a message is pushed to the mailbox. The received
|
207
207
|
message can be any kind of object. This method is complemented by
|
208
|
-
`Fiber#<<`/`Fiber#send`.
|
208
|
+
`Fiber#<<`/`Fiber#send`.
|
209
209
|
|
210
210
|
```ruby
|
211
211
|
spin { Fiber.current.parent << 'hello from child' }
|
@@ -328,7 +328,7 @@ Returns the fiber's current state, which can be any of the following:
|
|
328
328
|
Supervises all child fibers, optionally restarting any fiber that terminates.
|
329
329
|
|
330
330
|
The given `opts` argument controls the behaviour of the supervision. The
|
331
|
-
following options are currently supported:
|
331
|
+
following options are currently supported:
|
332
332
|
|
333
333
|
- `:restart`: restart options
|
334
334
|
- `nil` - Child fibers are not restarted (default behaviour).
|
data/docs/faq.md
CHANGED
@@ -99,7 +99,7 @@ In conclusion:
|
|
99
99
|
|
100
100
|
## If callbacks suck, why not use promises?
|
101
101
|
|
102
|
-
Promises have gained a lot of traction during the last few years as an
|
102
|
+
Promises have gained a lot of traction during the last few years as an
|
103
103
|
alternative to callbacks, above all in the Javascript community. While promises
|
104
104
|
have been at a certain point considered for use in Polyphony, they were not
|
105
105
|
found to offer enough of a benefit. Promises still cause split logic, are quite
|
@@ -114,7 +114,7 @@ single CPU core.
|
|
114
114
|
|
115
115
|
Nevertheless, Polyphony fully supports multithreading, with each thread having
|
116
116
|
its own fiber run queue and its own libev event loop. Polyphony even enables
|
117
|
-
cross-thread communication using [fiber messaging](#message-passing).
|
117
|
+
cross-thread communication using [fiber messaging](#message-passing).
|
118
118
|
|
119
119
|
## Fibers vs Callbacks
|
120
120
|
|
@@ -138,7 +138,7 @@ Fibers, in contrast, let the developer express the business logic in a
|
|
138
138
|
sequential, easy to read manner: do this, then that. State can be stored right
|
139
139
|
in the business logic, as local variables. And finally, the sequential
|
140
140
|
programming style makes it much easier to debug your code, since stack traces
|
141
|
-
contain the entire history of execution from the app's inception.
|
141
|
+
contain the entire history of execution from the app's inception.
|
142
142
|
|
143
143
|
## Structured Concurrency
|
144
144
|
|
@@ -229,7 +229,7 @@ normally or with an exception:
|
|
229
229
|
fiber1 = spin { sleep 1; raise 'foo' }
|
230
230
|
fiber2 = spin { sleep 1 }
|
231
231
|
|
232
|
-
supervise # blocks and then propagates the error raised in fiber1
|
232
|
+
supervise # blocks and then propagates the error raised in fiber1
|
233
233
|
```
|
234
234
|
|
235
235
|
## Message Passing
|
@@ -266,7 +266,7 @@ end
|
|
266
266
|
Notice how the state (the `subscribers` variable) stays local, and how the logic
|
267
267
|
of the chat room is expressed in a way that is both compact and easy to extend.
|
268
268
|
Also notice how the chat room is written as an infinite loop. This is a common
|
269
|
-
pattern in Polyphony, since fibers can always be stopped at any moment.
|
269
|
+
pattern in Polyphony, since fibers can always be stopped at any moment.
|
270
270
|
|
271
271
|
The code for handling a chat room user might be expressed as follows:
|
272
272
|
|
@@ -350,14 +350,14 @@ operations.
|
|
350
350
|
|
351
351
|
While a standard event loop-based solution would implement a blocking call
|
352
352
|
separately from the fiber scheduling, the system backend integrates the two to
|
353
|
-
create a blocking call that
|
353
|
+
create a blocking call that already knows how to switch and schedule fibers.
|
354
354
|
For example, in Polyphony all APIs having to do with reading from files or
|
355
355
|
sockets end up calling `Thread.current.backend.read`, which does all the work.
|
356
356
|
|
357
357
|
This design offers some major advantages over other designs. It minimizes memory
|
358
|
-
allocations
|
358
|
+
allocations of both Ruby objects and C structures. For example, instead of
|
359
359
|
having to allocate libev watchers on the heap and then pass them around, they
|
360
|
-
are allocated on the stack instead, which saves
|
360
|
+
are allocated on the stack instead, which saves both memory and CPU cycles.
|
361
361
|
|
362
362
|
In addition, the backend interface includes two methods that allow maximizing
|
363
363
|
server performance by accepting connections and reading from sockets in a tight
|
@@ -482,5 +482,5 @@ reach version 1.0. Here are some of the exciting directions we're working on.
|
|
482
482
|
|
483
483
|
- Support for more core and stdlib APIs
|
484
484
|
- More adapters for gems with C-extensions, such as `mysql`, `sqlite3` etc
|
485
|
-
- Use `io_uring` backend as alternative to the libev backend
|
485
|
+
- Use `io_uring` backend as alternative to the libev backend
|
486
486
|
- More concurrency constructs for building highly concurrent applications
|
@@ -51,7 +51,7 @@ multiple threads or processes, but this approach has numerous disavantages:
|
|
51
51
|
- Both threads and processes are limited to a few thousand at best on a single
|
52
52
|
machine. Trying to spawn a thread per client essentially limits the scaling
|
53
53
|
capacity of your system.
|
54
|
-
|
54
|
+
|
55
55
|
Polyphony eschews both threads and processes in favor of fibers as the basic
|
56
56
|
unit of concurrency. The idea is that any time a blocking I/O operation occurs,
|
57
57
|
the current fiber is paused, and another fiber which has been marked as
|
@@ -117,7 +117,7 @@ Here's the actual sequence of execution (in pseudo-code)
|
|
117
117
|
sleeper = spin { ... } # The main fiber spins up a new fiber marked as runnable
|
118
118
|
suspend # The main fiber suspends, waiting for all other work to finish
|
119
119
|
Thread.current.switch_fiber # Polyphony looks for other runnable fibers
|
120
|
-
|
120
|
+
|
121
121
|
# (sleeper fiber)
|
122
122
|
puts "Going to sleep..." # The sleeper fiber starts running
|
123
123
|
sleep 1 # The sleeper fiber goes to sleep
|
@@ -280,7 +280,7 @@ def handle_client(client)
|
|
280
280
|
rescue Polyphony::Cancel
|
281
281
|
client.puts 'Closing connection due to inactivity.'
|
282
282
|
rescue Polyphony::Terminate
|
283
|
-
# We add a handler for the Terminate exception, and give
|
283
|
+
# We add a handler for the Terminate exception, and give
|
284
284
|
client.puts 'Server is shutting down. You have 5 more seconds...'
|
285
285
|
move_on_after(5) do
|
286
286
|
client_loop(client)
|
@@ -133,7 +133,7 @@ fiber that's blocking on any arbitrary operation.
|
|
133
133
|
Some other constructs offered by Polyphony:
|
134
134
|
|
135
135
|
* `Mutex` - a mutex used to synchronize access to a single shared resource.
|
136
|
-
* `ResourcePool` - used for synchronizing access to a limited amount of shared
|
136
|
+
* `ResourcePool` - used for synchronizing access to a limited amount of shared
|
137
137
|
resources, for example a pool of database connections.
|
138
138
|
* `Throttler` - used for throttling repeating operations, for example throttling
|
139
139
|
access to a shared resource, or throttling incoming requests.
|
@@ -9,9 +9,9 @@ next_title: The Design of Polyphony
|
|
9
9
|
---
|
10
10
|
# Extending Polyphony
|
11
11
|
|
12
|
-
Polyphony was designed to ease the transition from blocking APIs and
|
12
|
+
Polyphony was designed to ease the transition from blocking APIs and
|
13
13
|
callback-based API to non-blocking, fiber-based ones. It is important to
|
14
|
-
understand that not all blocking calls can be easily converted into
|
14
|
+
understand that not all blocking calls can be easily converted into
|
15
15
|
non-blocking calls. That might be the case with Ruby gems based on C-extensions,
|
16
16
|
such as database libraries. In that case, Polyphony's built-in
|
17
17
|
[thread pool](#threadpool) might be used for offloading such blocking calls.
|
@@ -22,7 +22,7 @@ Some of the most common patterns in Ruby APIs is the callback pattern, in which
|
|
22
22
|
the API takes a block as a callback to be called upon completion of a task. One
|
23
23
|
such example can be found in the excellent
|
24
24
|
[http_parser.rb](https://github.com/tmm1/http_parser.rb/) gem, which is used by
|
25
|
-
Polyphony itself to provide HTTP 1 functionality. The `HTTP:Parser` provides
|
25
|
+
Polyphony itself to provide HTTP 1 functionality. The `HTTP:Parser` provides
|
26
26
|
multiple hooks, or callbacks, for being notified when an HTTP request is
|
27
27
|
complete. The typical callback-based setup is as follows:
|
28
28
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
# Kernel#spin starts a new fiber
|
7
|
+
@controller = spin do
|
8
|
+
@worker = spin do
|
9
|
+
loop do
|
10
|
+
# Each fiber has a mailbox for receiving messages
|
11
|
+
peer, op, x, y = receive
|
12
|
+
result = x.send(op, y)
|
13
|
+
# The result is sent back to the "client"
|
14
|
+
peer << result
|
15
|
+
end
|
16
|
+
end
|
17
|
+
# The controller fiber will block until the worker is done (but notice that
|
18
|
+
# the worker runs an infinite loop.)
|
19
|
+
@worker.await
|
20
|
+
end
|
21
|
+
|
22
|
+
def calc(op, x, y)
|
23
|
+
# Send the job to the worker fiber...
|
24
|
+
@worker << [Fiber.current, op, x, y]
|
25
|
+
# ... and wait for the result
|
26
|
+
receive
|
27
|
+
end
|
28
|
+
|
29
|
+
# wait for worker to start
|
30
|
+
snooze until @worker
|
31
|
+
|
32
|
+
p calc(:+, 2, 3)
|
33
|
+
p calc(:**, 2, 3)
|
34
|
+
p calc(:+, 2, nil)
|
35
|
+
|
36
|
+
# wait for the controller to terminate
|
37
|
+
@controller.await
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
# Kernel#spin starts a new fiber
|
7
|
+
@controller = spin do
|
8
|
+
@worker = spin do
|
9
|
+
loop do
|
10
|
+
# Each fiber has a mailbox for receiving messages
|
11
|
+
peer, op, x, y = receive
|
12
|
+
result = x.send(op, y)
|
13
|
+
# The result is sent back to the "client"
|
14
|
+
peer << result
|
15
|
+
end
|
16
|
+
end
|
17
|
+
# The controller fiber will block until the worker is done (but notice that
|
18
|
+
# the worker runs an infinite loop.)
|
19
|
+
@worker.await
|
20
|
+
rescue => e
|
21
|
+
puts "Uncaught exception in worker: #{e}. Restarting..."
|
22
|
+
@worker.restart
|
23
|
+
end
|
24
|
+
|
25
|
+
def calc(op, x, y)
|
26
|
+
# Send the job to the worker fiber...
|
27
|
+
@worker << [Fiber.current, op, x, y]
|
28
|
+
# ... and wait for the result
|
29
|
+
receive
|
30
|
+
end
|
31
|
+
|
32
|
+
# wait for worker to start
|
33
|
+
snooze until @worker
|
34
|
+
|
35
|
+
p calc(:+, 2, 3)
|
36
|
+
p calc(:**, 2, 3)
|
37
|
+
p calc(:+, 2, nil)
|
38
|
+
|
39
|
+
# wait for the controller to terminate
|
40
|
+
@controller.await
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
# Kernel#spin starts a new fiber
|
7
|
+
@controller = spin do
|
8
|
+
@worker = spin do
|
9
|
+
loop do
|
10
|
+
# Each fiber has a mailbox for receiving messages
|
11
|
+
peer, op, x, y = receive
|
12
|
+
result = x.send(op, y)
|
13
|
+
# The result is sent back to the "client"
|
14
|
+
peer << result
|
15
|
+
end
|
16
|
+
end
|
17
|
+
# The controller fiber will block until the worker is done (but notice that
|
18
|
+
# the worker runs an infinite loop.)
|
19
|
+
@worker.supervise(@worker, restart: :always)
|
20
|
+
end
|
21
|
+
|
22
|
+
def calc(op, x, y)
|
23
|
+
# Send the job to the worker fiber...
|
24
|
+
@worker << [Fiber.current, op, x, y]
|
25
|
+
# ... and wait for the result
|
26
|
+
receive
|
27
|
+
end
|
28
|
+
|
29
|
+
# wait for worker to start
|
30
|
+
snooze until @worker
|
31
|
+
|
32
|
+
p calc(:+, 2, 3)
|
33
|
+
p calc(:**, 2, 3)
|
34
|
+
p calc(:+, 2, nil)
|
35
|
+
|
36
|
+
# wait for the controller to terminate
|
37
|
+
@controller.await
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
M = 100
|
7
|
+
N = 10000
|
8
|
+
|
9
|
+
GC.disable
|
10
|
+
|
11
|
+
def monotonic_clock
|
12
|
+
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
13
|
+
end
|
14
|
+
|
15
|
+
def spin_proc(next_fiber)
|
16
|
+
spin_loop { next_fiber << receive }
|
17
|
+
end
|
18
|
+
|
19
|
+
last = Fiber.current
|
20
|
+
N.times { last = spin_proc(last) }
|
21
|
+
|
22
|
+
snooze
|
23
|
+
t0 = monotonic_clock
|
24
|
+
M.times do
|
25
|
+
last << 'hello'
|
26
|
+
receive
|
27
|
+
end
|
28
|
+
elapsed = monotonic_clock - t0
|
29
|
+
puts "M=#{M} N=#{N} elapsed: #{elapsed}"
|
data/examples/io/rack_server.rb
CHANGED
data/examples/io/tunnel.rb
CHANGED