polyphony 0.40 → 0.43.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +11 -2
- data/.gitignore +2 -2
- data/.rubocop.yml +30 -0
- data/CHANGELOG.md +29 -2
- data/Gemfile.lock +13 -10
- data/README.md +0 -1
- data/Rakefile +3 -3
- data/TODO.md +27 -97
- data/docs/_config.yml +56 -7
- data/docs/_sass/custom/custom.scss +6 -26
- data/docs/_sass/overrides.scss +0 -46
- data/docs/{user-guide → _user-guide}/all-about-timers.md +0 -0
- data/docs/_user-guide/index.md +9 -0
- data/docs/{user-guide → _user-guide}/web-server.md +0 -0
- data/docs/api-reference/fiber.md +2 -2
- data/docs/api-reference/index.md +9 -0
- data/docs/api-reference/polyphony-process.md +1 -1
- data/docs/api-reference/thread.md +1 -1
- data/docs/faq.md +21 -11
- data/docs/favicon.ico +0 -0
- data/docs/getting-started/index.md +10 -0
- data/docs/getting-started/installing.md +2 -6
- data/docs/getting-started/overview.md +486 -0
- data/docs/getting-started/tutorial.md +27 -19
- data/docs/index.md +6 -2
- data/docs/main-concepts/concurrency.md +0 -5
- data/docs/main-concepts/design-principles.md +69 -21
- data/docs/main-concepts/extending.md +1 -1
- data/docs/main-concepts/index.md +9 -0
- data/docs/polyphony-logo.png +0 -0
- data/examples/adapters/redis_blpop.rb +12 -0
- data/examples/core/01-spinning-up-fibers.rb +1 -0
- data/examples/core/03-interrupting.rb +4 -1
- data/examples/core/04-handling-signals.rb +19 -0
- data/examples/core/xx-agent.rb +102 -0
- data/examples/core/xx-sleeping.rb +14 -6
- data/examples/io/xx-irb.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +13 -36
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
- data/examples/performance/xx-array.rb +11 -0
- data/examples/performance/xx-fiber-switch.rb +9 -0
- data/examples/performance/xx-snooze.rb +15 -0
- data/ext/{gyro → polyphony}/extconf.rb +2 -2
- data/ext/{gyro → polyphony}/fiber.c +15 -22
- data/ext/{gyro → polyphony}/libev.c +0 -0
- data/ext/{gyro → polyphony}/libev.h +0 -0
- data/ext/polyphony/libev_agent.c +725 -0
- data/ext/polyphony/libev_queue.c +217 -0
- data/ext/{gyro/gyro.c → polyphony/polyphony.c} +12 -37
- data/ext/polyphony/polyphony.h +90 -0
- data/ext/polyphony/polyphony_ext.c +21 -0
- data/ext/{gyro → polyphony}/thread.c +34 -151
- data/ext/{gyro → polyphony}/tracing.c +1 -1
- data/lib/polyphony.rb +19 -12
- data/lib/polyphony/adapters/irb.rb +1 -1
- data/lib/polyphony/adapters/postgres.rb +6 -5
- data/lib/polyphony/adapters/process.rb +5 -5
- data/lib/polyphony/adapters/redis.rb +3 -2
- data/lib/polyphony/adapters/trace.rb +28 -28
- data/lib/polyphony/core/channel.rb +3 -3
- data/lib/polyphony/core/exceptions.rb +1 -1
- data/lib/polyphony/core/global_api.rb +13 -11
- data/lib/polyphony/core/resource_pool.rb +3 -3
- data/lib/polyphony/core/sync.rb +2 -2
- data/lib/polyphony/core/thread_pool.rb +6 -6
- data/lib/polyphony/core/throttler.rb +13 -6
- data/lib/polyphony/event.rb +27 -0
- data/lib/polyphony/extensions/core.rb +22 -14
- data/lib/polyphony/extensions/fiber.rb +4 -4
- data/lib/polyphony/extensions/io.rb +59 -25
- data/lib/polyphony/extensions/openssl.rb +36 -16
- data/lib/polyphony/extensions/socket.rb +28 -10
- data/lib/polyphony/extensions/thread.rb +16 -9
- data/lib/polyphony/net.rb +9 -9
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +3 -3
- data/test/helper.rb +12 -1
- data/test/test_agent.rb +130 -0
- data/test/{test_async.rb → test_event.rb} +13 -7
- data/test/test_ext.rb +25 -4
- data/test/test_fiber.rb +19 -10
- data/test/test_global_api.rb +6 -6
- data/test/test_io.rb +46 -24
- data/test/test_queue.rb +74 -0
- data/test/test_signal.rb +3 -40
- data/test/test_socket.rb +34 -0
- data/test/test_thread.rb +37 -16
- data/test/test_trace.rb +6 -5
- metadata +39 -41
- data/docs/_includes/nav.html +0 -51
- data/docs/_includes/prevnext.html +0 -17
- data/docs/_layouts/default.html +0 -106
- data/docs/api-reference.md +0 -11
- data/docs/api-reference/gyro-async.md +0 -57
- data/docs/api-reference/gyro-child.md +0 -29
- data/docs/api-reference/gyro-queue.md +0 -44
- data/docs/api-reference/gyro-timer.md +0 -51
- data/docs/api-reference/gyro.md +0 -25
- data/docs/getting-started.md +0 -10
- data/docs/main-concepts.md +0 -10
- data/docs/user-guide.md +0 -10
- data/examples/core/forever_sleep.rb +0 -19
- data/ext/gyro/async.c +0 -132
- data/ext/gyro/child.c +0 -108
- data/ext/gyro/gyro.h +0 -158
- data/ext/gyro/gyro_ext.c +0 -33
- data/ext/gyro/io.c +0 -457
- data/ext/gyro/queue.c +0 -146
- data/ext/gyro/selector.c +0 -205
- data/ext/gyro/signal.c +0 -99
- data/ext/gyro/socket.c +0 -213
- data/ext/gyro/timer.c +0 -115
- data/test/test_timer.rb +0 -56
@@ -1,13 +1,20 @@
|
|
1
1
|
---
|
2
2
|
layout: page
|
3
3
|
title: Tutorial
|
4
|
-
nav_order: 2
|
5
4
|
parent: Getting Started
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
nav_order: 3
|
6
|
+
---
|
7
|
+
|
8
|
+
# Tutorial
|
9
|
+
{: .no_toc }
|
10
|
+
|
11
|
+
## Table of contents
|
12
|
+
{: .no_toc .text-delta }
|
13
|
+
|
14
|
+
- TOC
|
15
|
+
{:toc}
|
16
|
+
|
9
17
|
---
|
10
|
-
# Polyphony: a Tutorial
|
11
18
|
|
12
19
|
Polyphony is a new Ruby library aimed at making writing concurrent Ruby apps
|
13
20
|
easy and fun. In this article, we'll introduce Polyphony's fiber-based
|
@@ -116,7 +123,7 @@ suspend # The main fiber suspends, waiting for all other work to finish
|
|
116
123
|
sleep 1 # The sleeper fiber goes to sleep
|
117
124
|
Gyro::Timer.new(1, 0).await # A timer event watcher is setup and yields
|
118
125
|
Thread.current.switch_fiber # Polyphony looks for other runnable fibers
|
119
|
-
Thread.current.
|
126
|
+
Thread.current.agent.poll # With no work left, the event loop is ran
|
120
127
|
fiber.schedule # The timer event fires, scheduling the sleeper fiber
|
121
128
|
# <= The sleep method returns
|
122
129
|
puts "Woke up"
|
@@ -218,7 +225,7 @@ this by setting up a timeout fiber that cancels the fiber dealing with the conne
|
|
218
225
|
def handle_client(client)
|
219
226
|
timeout = cancel_after(10)
|
220
227
|
while (data = client.gets)
|
221
|
-
timeout.
|
228
|
+
timeout.restart
|
222
229
|
client << data
|
223
230
|
end
|
224
231
|
rescue Polyphony::Cancel
|
@@ -230,18 +237,19 @@ ensure
|
|
230
237
|
end
|
231
238
|
```
|
232
239
|
|
233
|
-
The
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
240
|
+
The call to `#cancel_after` spins up a new fiber that will sleep for 10 seconds,
|
241
|
+
then cancel its parent. The call to `client.gets` blocks until new data is
|
242
|
+
available. If no new data is available, the `timeout` fiber will finish
|
243
|
+
sleeping, and then cancel the client handling fiber by raising a
|
244
|
+
`Polyphony::Cancel` exception. However, if new data is received, the `timeout`
|
245
|
+
fiber is restarted, causing to begin sleeping again for 10 seconds. If the
|
246
|
+
client has closed the connection, or some other exception occurs, the `timeout`
|
247
|
+
fiber is automatically stopped as it is a child of the client handling fiber.
|
248
|
+
|
249
|
+
The habit of always cleaning up using `ensure` in the face of potential
|
250
|
+
interruptions is a fundamental element of using Polyphony correctly. This makes
|
251
|
+
your code robust, even in a highly chaotic concurrent execution environment
|
252
|
+
where tasks can be started, restarted and interrupted at any time.
|
245
253
|
|
246
254
|
## Implementing graceful shutdown
|
247
255
|
|
data/docs/index.md
CHANGED
@@ -6,7 +6,11 @@ permalink: /
|
|
6
6
|
next_title: Installing Polyphony
|
7
7
|
---
|
8
8
|
|
9
|
-
# Polyphony
|
9
|
+
# Polyphony
|
10
|
+
{:.text-center .logo-title}
|
11
|
+
|
12
|
+
## Fine-grained concurrency for Ruby
|
13
|
+
{:.text-center .logo-title}
|
10
14
|
|
11
15
|
Polyphony is a library for building concurrent applications in Ruby. Polyphony
|
12
16
|
implements a comprehensive
|
@@ -53,7 +57,7 @@ adapters are being developed.
|
|
53
57
|
* Natural, sequential programming style that makes it easy to reason about
|
54
58
|
concurrent code.
|
55
59
|
* Abstractions and constructs for controlling the execution of concurrent code:
|
56
|
-
supervisors,
|
60
|
+
supervisors, throttling, resource pools etc.
|
57
61
|
* Code can use native networking classes and libraries, growing support for
|
58
62
|
third-party gems such as `pg` and `redis`.
|
59
63
|
* Use stdlib classes such as `TCPServer` and `TCPSocket` and `Net::HTTP`.
|
@@ -130,16 +130,11 @@ Polyphony also provides several methods and constructs for controlling multiple
|
|
130
130
|
fibers. Methods like `cancel_after` and `move_on_after` allow interrupting a
|
131
131
|
fiber that's blocking on any arbitrary operation.
|
132
132
|
|
133
|
-
Cancel scopes \(borrowed from the brilliant Python library
|
134
|
-
[Trio](https://trio.readthedocs.io/en/stable/)\) allows cancelling ongoing
|
135
|
-
operations for any reason with more control over cancelling behaviour.
|
136
|
-
|
137
133
|
Some other constructs offered by Polyphony:
|
138
134
|
|
139
135
|
* `Mutex` - a mutex used to synchronize access to a single shared resource.
|
140
136
|
* `ResourcePool` - used for synchronizing access to a limited amount of shared
|
141
137
|
resources, for example a pool of database connections.
|
142
|
-
|
143
138
|
* `Throttler` - used for throttling repeating operations, for example throttling
|
144
139
|
access to a shared resource, or throttling incoming requests.
|
145
140
|
|
@@ -1,18 +1,76 @@
|
|
1
1
|
---
|
2
2
|
layout: page
|
3
|
-
title: Design
|
3
|
+
title: The Design of Polyphony
|
4
4
|
nav_order: 5
|
5
5
|
parent: Main Concepts
|
6
6
|
permalink: /main-concepts/design-principles/
|
7
7
|
prev_title: Extending Polyphony
|
8
8
|
---
|
9
|
-
# Design
|
9
|
+
# The Design of Polyphony
|
10
|
+
|
11
|
+
Polyphony is a new gem that aims to enable developing high-performance
|
12
|
+
concurrent applications in Ruby using a fluent, compact syntax and API.
|
13
|
+
Polyphony enables fine-grained concurrency - the splitting up of operations into
|
14
|
+
a large number of concurrent tasks, each concerned with small part of the whole
|
15
|
+
and advancing at its own pace. Polyphony aims to solve some of the problems
|
16
|
+
associated with concurrent Ruby programs using a novel design that sets it apart
|
17
|
+
from other approaches currently being used in Ruby.
|
18
|
+
|
19
|
+
## Origins
|
20
|
+
|
21
|
+
The Ruby core language (at least in its MRI implementation) currently provides
|
22
|
+
two main constructs for performing concurrent work: threads and fibers. While
|
23
|
+
Ruby threads are basically wrappers for OS threads, fibers are essentially
|
24
|
+
continuations, allowing pausing and resuming distinct computations. Fibers have
|
25
|
+
been traditionally used mostly for implementing enumerators and generators.
|
26
|
+
|
27
|
+
In addition to the core Ruby concurrency primitives, some Ruby gems have been
|
28
|
+
offering an alternative solution to writing concurrent Ruby apps, most notably
|
29
|
+
[EventMachine](https://github.com/eventmachine/eventmachine/), which implements
|
30
|
+
an event reactor and offers an asynchronous callback-based API for writing
|
31
|
+
concurrent code.
|
32
|
+
|
33
|
+
In the last couple of years, however, fibers have been receiving more attention
|
34
|
+
as a possible constructs for writing concurrent programs. In particular, the
|
35
|
+
[Async](https://github.com/socketry/async) framework, created by [Samuel
|
36
|
+
Williams](https://github.com/ioquatix), offering a comprehensive set of
|
37
|
+
libraries, employs fibers in conjunction with an event reactor provided by the
|
38
|
+
[nio4r](https://github.com/socketry/nio4r) gem, which wraps the C
|
39
|
+
library [libev](http://software.schmorp.de/pkg/libev.html).
|
40
|
+
|
41
|
+
In addition, recently some effort was undertaken to provide a way to
|
42
|
+
[automatically switch between fibers](https://bugs.ruby-lang.org/issues/13618)
|
43
|
+
whenever a blocking operation is performed, or to [integrate a fiber
|
44
|
+
scheduler](https://bugs.ruby-lang.org/issues/16786) into the core Ruby code.
|
45
|
+
|
46
|
+
Nevertheless, while work is being done to harness fibers for providing a better
|
47
|
+
way to do concurrency in Ruby, fibers remain a mistery for most Ruby
|
48
|
+
programmers, a perplexing unfamiliar corner right at the heart of Ruby.
|
49
|
+
|
50
|
+
## Design Principles
|
51
|
+
|
52
|
+
Polyphony started as an experiment, but over about two years of slow, jerky
|
53
|
+
evolution turned into something I'm really excited to share with the Ruby
|
54
|
+
community. Polyphony's design is both similar and different than the projects
|
55
|
+
mentioned above.
|
56
|
+
|
57
|
+
Polyphony today as nothing like the way it began. A careful examination of the
|
58
|
+
[CHANGELOG](https://github.com/digital-fabric/polyphony/blob/master/CHANGELOG.md)
|
59
|
+
would show how Polyphony explored not only different event reactor designs, but
|
60
|
+
also different API designs incorporating various concurrent paradigms such as
|
61
|
+
promises, async/await, fibers, and finally structured concurrency.
|
62
|
+
|
63
|
+
While Polyphony, like nio4r or EventMachine, uses an event reactor to turn
|
64
|
+
blocking operations into non-blocking ones, it completely embraces fibers and in
|
65
|
+
fact does not provide any callback-based APIs. Furthermore, Polyphony provides
|
66
|
+
fullblown fiber-aware implementations of blocking operations, such as
|
67
|
+
`read/write`, `sleep` or `waitpid`, instead of just event watching primitives.
|
68
|
+
|
69
|
+
Throughout the development process, it was my intention to create a programming
|
70
|
+
interface that would make highly-concurrent
|
71
|
+
|
72
|
+
|
10
73
|
|
11
|
-
Polyphony was created in order to enable developing high-performance concurrent
|
12
|
-
applications in Ruby using a fluent, compact syntax and API. Polyphony enables
|
13
|
-
fine-grained concurrency - the splitting up of operations into a large number of
|
14
|
-
concurrent tasks, each concerned with small part of the whole and advancing at
|
15
|
-
its own pace.
|
16
74
|
|
17
75
|
|
18
76
|
|
@@ -46,8 +104,8 @@ library. Polyphony's design is based on the following principles:
|
|
46
104
|
async callback-style APIs.
|
47
105
|
|
48
106
|
```ruby
|
49
|
-
# in Polyphony, I/O ops block the current fiber, but implicitly yield to
|
50
|
-
# concurrent fibers:
|
107
|
+
# in Polyphony, I/O ops might block the current fiber, but implicitly yield to
|
108
|
+
# other concurrent fibers:
|
51
109
|
clients.each { |client|
|
52
110
|
spin { client.puts 'Elvis has left the chatroom' }
|
53
111
|
}
|
@@ -85,18 +143,8 @@ library. Polyphony's design is based on the following principles:
|
|
85
143
|
end
|
86
144
|
```
|
87
145
|
|
88
|
-
- Concurrency primitives should allow creating higher-order concurrent
|
89
|
-
constructs through composition.
|
90
|
-
cancel scopes:
|
91
|
-
|
92
|
-
```ruby
|
93
|
-
# wait for multiple fibers
|
94
|
-
supervise { |s|
|
95
|
-
clients.each { |client|
|
96
|
-
s.spin { client.puts 'Elvis has left the chatroom' }
|
97
|
-
}
|
98
|
-
}
|
99
|
-
```
|
146
|
+
- Concurrency primitives should allow creating higher-order concurrent
|
147
|
+
constructs through composition.
|
100
148
|
|
101
149
|
- The entire design should embrace fibers. There should be no callback-based
|
102
150
|
asynchronous APIs.
|
Binary file
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony/adapters/redis'
|
5
|
+
# require 'redis'
|
6
|
+
|
7
|
+
redis = Redis.new(host: ENV['REDISHOST'] || 'localhost')
|
8
|
+
|
9
|
+
redis.lpush("queue_key", "omgvalue")
|
10
|
+
puts "len: #{redis.llen("queue_key")}"
|
11
|
+
result = redis.blpop("queue_key")
|
12
|
+
puts result.inspect
|
@@ -18,7 +18,8 @@ ensure
|
|
18
18
|
end
|
19
19
|
|
20
20
|
# The Kernel#cancel_after interrupts a blocking operation by raising a
|
21
|
-
# Polyphony::Cancel exception after the given timeout
|
21
|
+
# Polyphony::Cancel exception after the given timeout. If not rescued, the
|
22
|
+
# exception is propagated up the fiber hierarchy
|
22
23
|
spin do
|
23
24
|
# cancel after 1 second
|
24
25
|
cancel_after(1) { nap(:cancel, 2) }
|
@@ -26,6 +27,8 @@ rescue Polyphony::Cancel => e
|
|
26
27
|
puts "got exception: #{e}"
|
27
28
|
end
|
28
29
|
|
30
|
+
# The Kernel#move_on_after interrupts a blocking operation by raising a
|
31
|
+
# Polyphony::MoveOn exception, which is silently swallowed by the fiber
|
29
32
|
spin do
|
30
33
|
# move on after 1 second
|
31
34
|
move_on_after(1) do
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
# trap('TERM') do
|
7
|
+
# Polyphony.emit_signal_exception(::SystemExit)
|
8
|
+
# end
|
9
|
+
|
10
|
+
# trap('INT') do
|
11
|
+
# Polyphony.emit_signal_exception(::Interrupt)
|
12
|
+
# end
|
13
|
+
|
14
|
+
puts "go to sleep"
|
15
|
+
begin
|
16
|
+
sleep
|
17
|
+
ensure
|
18
|
+
puts "done sleeping"
|
19
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
Exception.__disable_sanitized_backtrace__ = true
|
7
|
+
|
8
|
+
class Test
|
9
|
+
def test_sleep
|
10
|
+
puts "going to sleep"
|
11
|
+
sleep 1
|
12
|
+
puts "done sleeping"
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_spin
|
16
|
+
spin {
|
17
|
+
10.times {
|
18
|
+
STDOUT << '.'
|
19
|
+
sleep 0.1
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
puts "going to sleep\n"
|
24
|
+
sleep 1
|
25
|
+
puts 'woke up'
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_file
|
29
|
+
f = File.open(__FILE__, 'r')
|
30
|
+
puts Thread.current.agent.read(f, +'', 10000, true)
|
31
|
+
|
32
|
+
Thread.current.agent.write(STDOUT, "Write something: ")
|
33
|
+
str = +''
|
34
|
+
Thread.current.agent.read(STDIN, str, 5, false)
|
35
|
+
puts str
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_fork
|
39
|
+
pid = fork do
|
40
|
+
Thread.current.agent.post_fork
|
41
|
+
puts 'child going to sleep'
|
42
|
+
sleep 1
|
43
|
+
puts 'child done sleeping'
|
44
|
+
exit(42)
|
45
|
+
end
|
46
|
+
|
47
|
+
puts "Waiting for pid #{pid}"
|
48
|
+
result = Thread.current.agent.waitpid(pid)
|
49
|
+
puts "Done waiting"
|
50
|
+
p result
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_async
|
54
|
+
async = Polyphony::Event.new
|
55
|
+
|
56
|
+
spin {
|
57
|
+
puts "signaller starting"
|
58
|
+
sleep 1
|
59
|
+
puts "signal"
|
60
|
+
async.signal(:foo)
|
61
|
+
}
|
62
|
+
|
63
|
+
puts "awaiting event"
|
64
|
+
p async.await
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_queue
|
68
|
+
q = Gyro::Queue.new
|
69
|
+
spin {
|
70
|
+
10.times {
|
71
|
+
q << Time.now.to_f
|
72
|
+
sleep 0.2
|
73
|
+
}
|
74
|
+
q << :STOP
|
75
|
+
}
|
76
|
+
|
77
|
+
loop do
|
78
|
+
value = q.shift
|
79
|
+
break if value == :STOP
|
80
|
+
|
81
|
+
p value
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_thread
|
86
|
+
t = Thread.new do
|
87
|
+
puts "thread going to sleep"
|
88
|
+
sleep 0.2
|
89
|
+
puts "thread done sleeping"
|
90
|
+
end
|
91
|
+
|
92
|
+
t.await
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
t = Test.new
|
97
|
+
|
98
|
+
t.methods.select { |m| m =~ /^test_/ }.each do |m|
|
99
|
+
puts '*' * 40
|
100
|
+
puts m
|
101
|
+
t.send(m)
|
102
|
+
end
|
@@ -5,13 +5,21 @@ require 'polyphony'
|
|
5
5
|
|
6
6
|
Exception.__disable_sanitized_backtrace__ = true
|
7
7
|
|
8
|
-
spin {
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
}
|
8
|
+
# spin {
|
9
|
+
# 10.times {
|
10
|
+
# STDOUT << '.'
|
11
|
+
# sleep 0.1
|
12
|
+
# }
|
13
|
+
# }
|
14
14
|
|
15
15
|
puts 'going to sleep...'
|
16
16
|
sleep 1
|
17
17
|
puts 'woke up'
|
18
|
+
|
19
|
+
counter = 0
|
20
|
+
t = Polyphony::Throttler.new(5)
|
21
|
+
t.process do
|
22
|
+
p counter
|
23
|
+
counter += 1
|
24
|
+
t.stop if counter > 5
|
25
|
+
end
|