polyphony 0.68 → 0.72
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/CHANGELOG.md +32 -5
- 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/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/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 +24 -6
- data/ext/polyphony/backend_common.h +1 -0
- data/ext/polyphony/backend_io_uring.c +27 -40
- 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 +13 -11
- data/ext/polyphony/extconf.rb +24 -13
- data/ext/polyphony/queue.c +2 -2
- data/ext/polyphony/runqueue_ring_buffer.c +3 -2
- data/lib/polyphony/adapters/irb.rb +11 -1
- data/lib/polyphony/core/global_api.rb +3 -3
- data/lib/polyphony/core/timer.rb +2 -2
- data/lib/polyphony/debugger.rb +3 -3
- data/lib/polyphony/extensions/fiber.rb +30 -15
- data/lib/polyphony/extensions/io.rb +3 -3
- data/lib/polyphony/extensions/openssl.rb +21 -6
- data/lib/polyphony/extensions/socket.rb +4 -5
- data/lib/polyphony/net.rb +0 -1
- data/lib/polyphony/version.rb +1 -1
- 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 +2 -3
- data/test/test_io.rb +3 -3
- data/test/test_process_supervision.rb +38 -9
- data/test/test_queue.rb +6 -6
- 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 +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2af189faeecbe2eecb367a7400005306ab74370e56c61de1d703edd40d851d92
|
4
|
+
data.tar.gz: c0888b10d03b9bf29f1d10a41b0050231db655c587d06035039b4c09e1c68baa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b86fd5682ce52e635658a8d620031f8ae841ef5f7876d6239f74772d8f700054eb3bb7732488e246433a91e5ab4279479c6f18bb0d1a154172d90f351ab8149e
|
7
|
+
data.tar.gz: 713ba61a64ef73c5c16971bd0d678d6575b7bd37062a29348f02ec8e0578821d7ec41f826f1868cd017bede9c70d47192e19a0217469b331f17ff66afac21054
|
data/.github/FUNDING.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
github: ciconia
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,30 @@
|
|
1
|
+
## 0.72 2021-11-22
|
2
|
+
|
3
|
+
- Add support for supervising added child fibers
|
4
|
+
- Do not use io_uring under LinuxKit (#66)
|
5
|
+
- Fix error message in `Fiber#restart`
|
6
|
+
- refactor `runqueue_ring_buffer_mark`
|
7
|
+
- Fix setting up test server on random port
|
8
|
+
- Enhance `Fiber#supervise`
|
9
|
+
- Add support for specifying `restart: true` (same as `restart: :always`)
|
10
|
+
- Add support for supervising child fibers (when fibers are not specified)
|
11
|
+
- Add `Fiber#attach_all_children_to`
|
12
|
+
- Improve `SSLServer#accept` when `servername_cb` is set
|
13
|
+
- Fix `#supervise` in Ruby 3.0
|
14
|
+
|
15
|
+
## 0.71 2021-08-20
|
16
|
+
|
17
|
+
- Fix compilation on Ruby 3.0
|
18
|
+
|
19
|
+
## 0.70 2021-08-19
|
20
|
+
|
21
|
+
- New implementation for `#supervise`
|
22
|
+
- Reset backend state and runqueue on fork
|
23
|
+
|
24
|
+
## 0.69 2021-08-16
|
25
|
+
|
26
|
+
- Rename `#__polyphony_read_method__` to `#__parser_read_method__`
|
27
|
+
|
1
28
|
## 0.68 2021-08-13
|
2
29
|
|
3
30
|
- Fix missing default value in socket classes' `#readpartial`
|
@@ -14,11 +41,11 @@
|
|
14
41
|
## 0.66 2021-08-01
|
15
42
|
|
16
43
|
- Fix all splicing APIs on non-linux OSes (#63)
|
17
|
-
- Add GC marking of buffers when cancelling read/write ops in io_uring backend
|
44
|
+
- Add GC marking of buffers when cancelling read/write ops in io_uring backend
|
18
45
|
|
19
46
|
## 0.65 2021-07-29
|
20
47
|
|
21
|
-
- Add `#
|
48
|
+
- Add `#__parser_read_method__` method for read method detection
|
22
49
|
|
23
50
|
## 0.64 2021-07-26
|
24
51
|
|
@@ -78,7 +105,7 @@
|
|
78
105
|
## 0.55.0 2021-06-17
|
79
106
|
|
80
107
|
- Finish io_uring implementation of Backend#chain
|
81
|
-
- Reimplement io_uring op_context acquire/release algorithm (using ref count)
|
108
|
+
- Reimplement io_uring op_context acquire/release algorithm (using ref count)
|
82
109
|
- Fix #gets on sockets
|
83
110
|
- Redesign event anti-starvation mechanism
|
84
111
|
|
@@ -173,7 +200,7 @@
|
|
173
200
|
|
174
201
|
## 0.47.0 2020-11-10
|
175
202
|
|
176
|
-
- Implement `#spin_scope` used for creating blocking fiber scopes
|
203
|
+
- Implement `#spin_scope` used for creating blocking fiber scopes
|
177
204
|
- Reimplement `move_on_after`, `cancel_after`, `Timeout.timeout` using
|
178
205
|
`Backend#timeout` (avoids creating canceller fiber for most common use case)
|
179
206
|
- Implement `Backend#timeout` API
|
@@ -268,7 +295,7 @@
|
|
268
295
|
- Reimplement Event using `Agent#wait_event`
|
269
296
|
- Improve Queue shift queue performance
|
270
297
|
- Introduce `Agent#wait_event` API for waiting on asynchronous events
|
271
|
-
- Minimize `fcntl` syscalls in IO operations
|
298
|
+
- Minimize `fcntl` syscalls in IO operations
|
272
299
|
|
273
300
|
## 0.43.7 2020-07-20
|
274
301
|
|
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:
|
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
|
data/examples/io/rack_server.rb
CHANGED
data/examples/io/tunnel.rb
CHANGED
@@ -31,10 +31,28 @@ inline void backend_base_mark(struct Backend_base *base) {
|
|
31
31
|
runqueue_mark(&base->parked_runqueue);
|
32
32
|
}
|
33
33
|
|
34
|
+
void backend_base_reset(struct Backend_base *base) {
|
35
|
+
runqueue_finalize(&base->runqueue);
|
36
|
+
runqueue_finalize(&base->parked_runqueue);
|
37
|
+
|
38
|
+
runqueue_initialize(&base->runqueue);
|
39
|
+
runqueue_initialize(&base->parked_runqueue);
|
40
|
+
|
41
|
+
base->currently_polling = 0;
|
42
|
+
base->op_count = 0;
|
43
|
+
base->switch_count = 0;
|
44
|
+
base->poll_count = 0;
|
45
|
+
base->pending_count = 0;
|
46
|
+
base->idle_gc_period = 0;
|
47
|
+
base->idle_gc_last_time = 0;
|
48
|
+
base->idle_proc = Qnil;
|
49
|
+
base->trace_proc = Qnil;
|
50
|
+
}
|
51
|
+
|
34
52
|
const unsigned int ANTI_STARVE_SWITCH_COUNT_THRESHOLD = 64;
|
35
53
|
|
36
54
|
inline void conditional_nonblocking_poll(VALUE backend, struct Backend_base *base, VALUE current, VALUE next) {
|
37
|
-
if ((base->switch_count % ANTI_STARVE_SWITCH_COUNT_THRESHOLD) == 0 || next == current)
|
55
|
+
if ((base->switch_count % ANTI_STARVE_SWITCH_COUNT_THRESHOLD) == 0 || next == current)
|
38
56
|
Backend_poll(backend, Qnil);
|
39
57
|
}
|
40
58
|
|
@@ -44,7 +62,7 @@ VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base) {
|
|
44
62
|
unsigned int pending_ops_count = base->pending_count;
|
45
63
|
unsigned int backend_was_polled = 0;
|
46
64
|
unsigned int idle_tasks_run_count = 0;
|
47
|
-
|
65
|
+
|
48
66
|
base->switch_count++;
|
49
67
|
COND_TRACE(base, 2, SYM_fiber_switchpoint, current_fiber);
|
50
68
|
|
@@ -64,7 +82,7 @@ VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base) {
|
|
64
82
|
|
65
83
|
break;
|
66
84
|
}
|
67
|
-
|
85
|
+
|
68
86
|
if (!idle_tasks_run_count) {
|
69
87
|
idle_tasks_run_count++;
|
70
88
|
backend_run_idle_tasks(base);
|
@@ -94,7 +112,7 @@ void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_bas
|
|
94
112
|
|
95
113
|
COND_TRACE(base, 4, SYM_fiber_schedule, fiber, value, prioritize ? Qtrue : Qfalse);
|
96
114
|
|
97
|
-
runqueue_t *runqueue = rb_ivar_get(fiber, ID_ivar_parked) == Qtrue ?
|
115
|
+
runqueue_t *runqueue = rb_ivar_get(fiber, ID_ivar_parked) == Qtrue ?
|
98
116
|
&base->parked_runqueue : &base->runqueue;
|
99
117
|
|
100
118
|
(prioritize ? runqueue_unshift : runqueue_push)(runqueue, fiber, value, already_runnable);
|
@@ -282,7 +300,7 @@ inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
|
|
282
300
|
int flags = fcntl(fptr->fd, F_GETFL);
|
283
301
|
if (flags == -1) return;
|
284
302
|
int is_nonblocking = flags & O_NONBLOCK;
|
285
|
-
|
303
|
+
|
286
304
|
if (blocking == Qtrue) {
|
287
305
|
if (!is_nonblocking) return;
|
288
306
|
flags &= ~O_NONBLOCK;
|
@@ -357,7 +375,7 @@ void backend_setup_stats_symbols() {
|
|
357
375
|
SYM_switch_count = ID2SYM(rb_intern("switch_count"));
|
358
376
|
SYM_poll_count = ID2SYM(rb_intern("poll_count"));
|
359
377
|
SYM_pending_ops = ID2SYM(rb_intern("pending_ops"));
|
360
|
-
|
378
|
+
|
361
379
|
rb_global_variable(&SYM_runqueue_size);
|
362
380
|
rb_global_variable(&SYM_runqueue_length);
|
363
381
|
rb_global_variable(&SYM_runqueue_max_length);
|
@@ -32,6 +32,7 @@ struct Backend_base {
|
|
32
32
|
void backend_base_initialize(struct Backend_base *base);
|
33
33
|
void backend_base_finalize(struct Backend_base *base);
|
34
34
|
void backend_base_mark(struct Backend_base *base);
|
35
|
+
void backend_base_reset(struct Backend_base *base);
|
35
36
|
VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base);
|
36
37
|
void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_base *base, VALUE fiber, VALUE value, int prioritize);
|
37
38
|
void backend_base_park_fiber(struct Backend_base *base, VALUE fiber);
|