async 1.26.1 → 1.26.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/lib/async/barrier.rb +1 -1
  3. data/lib/async/version.rb +1 -1
  4. metadata +54 -99
  5. data/.editorconfig +0 -6
  6. data/.github/workflows/development.yml +0 -55
  7. data/.gitignore +0 -14
  8. data/.rspec +0 -3
  9. data/.yardopts +0 -1
  10. data/Gemfile +0 -20
  11. data/Guardfile +0 -14
  12. data/README.md +0 -385
  13. data/Rakefile +0 -40
  14. data/async.gemspec +0 -34
  15. data/bake.rb +0 -33
  16. data/benchmark/async_vs_lightio.rb +0 -84
  17. data/benchmark/fiber_count.rb +0 -10
  18. data/benchmark/rubies/README.md +0 -51
  19. data/benchmark/rubies/benchmark.rb +0 -220
  20. data/benchmark/thread_count.rb +0 -9
  21. data/benchmark/thread_vs_fiber.rb +0 -45
  22. data/examples/async_method.rb +0 -60
  23. data/examples/callback/loop.rb +0 -44
  24. data/examples/capture/README.md +0 -59
  25. data/examples/capture/capture.rb +0 -116
  26. data/examples/fibers.rb +0 -178
  27. data/examples/queue/producer.rb +0 -28
  28. data/examples/sleep_sort.rb +0 -40
  29. data/examples/stop/condition.rb +0 -31
  30. data/examples/stop/sleep.rb +0 -42
  31. data/gems/event.gemfile +0 -4
  32. data/logo.png +0 -0
  33. data/logo.svg +0 -64
  34. data/papers/1982 Grossman.pdf +0 -0
  35. data/papers/1987 ODell.pdf +0 -0
  36. data/spec/async/barrier_spec.rb +0 -116
  37. data/spec/async/chainable_async_examples.rb +0 -13
  38. data/spec/async/clock_spec.rb +0 -37
  39. data/spec/async/condition_examples.rb +0 -105
  40. data/spec/async/condition_spec.rb +0 -72
  41. data/spec/async/logger_spec.rb +0 -65
  42. data/spec/async/node_spec.rb +0 -193
  43. data/spec/async/notification_spec.rb +0 -66
  44. data/spec/async/performance_spec.rb +0 -72
  45. data/spec/async/queue_spec.rb +0 -133
  46. data/spec/async/reactor/nested_spec.rb +0 -52
  47. data/spec/async/reactor_spec.rb +0 -253
  48. data/spec/async/semaphore_spec.rb +0 -169
  49. data/spec/async/task_spec.rb +0 -476
  50. data/spec/async/wrapper_spec.rb +0 -203
  51. data/spec/async_spec.rb +0 -33
  52. data/spec/enumerator_spec.rb +0 -83
  53. data/spec/kernel/async_spec.rb +0 -39
  54. data/spec/kernel/sync_spec.rb +0 -54
  55. data/spec/spec_helper.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af5004e8d05722b3187b65bad44d03c64514cef04b3ea84f604f2491b04c9441
4
- data.tar.gz: 3e00948623889a9a584767a5678b55d474b4015603da9031eea4ab420bea80e0
3
+ metadata.gz: 72c4f7456b611f436a18f0d143ba5b4c9bb4c7882ad241f3ba251a139c020945
4
+ data.tar.gz: 4270eef8e1de0d08b953eb373a665d3fb8c0b9348e13d99ac6c61e7aead63db5
5
5
  SHA512:
6
- metadata.gz: eb57c398f8f778ded45849d98412106133bd68294eeee34e734144b1792b63c1c7f4da9087cf36ed14d385efacd730a4e461f029ba22503a89efa56debd7dc5c
7
- data.tar.gz: d81812db0ca0f6ad8d81d6582ce82d6b1e210b437a6614d825ad0bc1be4cb200f2f3c930df51f6fc963b82e0c98bfc757fe339646a9227d6dacfab06e70b469e
6
+ metadata.gz: 359f4bcd33b438eca0518a00b77d336c25ab49ed3346180013bc135743d9a5447c178416680e5289085cbd9d75718772d7a06e25d44a0b0303cd35086da0c8f5
7
+ data.tar.gz: 420f280a2ed6d2cb4d363c6a4f80a695a1ad963ead1b040daa16ce9a18ceab9b8f3f6bcee707658a946acba3656f54578df9ed36e2bf8ffc0e6d151cdbbc9e9e
@@ -23,7 +23,7 @@
23
23
  require_relative 'task'
24
24
 
25
25
  module Async
26
- # A semaphore is used to control access to a common resource in a concurrent system. A useful way to think of a semaphore as used in the real-world systems is as a record of how many units of a particular resource are available, coupled with operations to adjust that record safely (i.e. to avoid race conditions) as units are required or become free, and, if necessary, wait until a unit of the resource becomes available.
26
+ # A barrier is used to synchronize multiple tasks, waiting for them all to complete before continuing.
27
27
  class Barrier
28
28
  def initialize(parent: nil)
29
29
  @tasks = []
@@ -21,5 +21,5 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
  module Async
24
- VERSION = "1.26.1"
24
+ VERSION = "1.26.2"
25
25
  end
metadata CHANGED
@@ -1,57 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.26.1
4
+ version: 1.26.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-14 00:00:00.000000000 Z
11
+ date: 2020-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: nio4r
14
+ name: console
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.3'
19
+ version: '1.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.3'
26
+ version: '1.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: timers
28
+ name: nio4r
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '4.1'
33
+ version: '2.3'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '4.1'
40
+ version: '2.3'
41
41
  - !ruby/object:Gem::Dependency
42
- name: console
42
+ name: timers
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.0'
47
+ version: '4.1'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '1.0'
54
+ version: '4.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: async-rspec
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -67,19 +67,33 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.1'
69
69
  - !ruby/object:Gem::Dependency
70
- name: covered
70
+ name: bake-bundler
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '0.10'
75
+ version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: '0.10'
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bake-modernize
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: bundler
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -95,21 +109,21 @@ dependencies:
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
- name: rspec
112
+ name: covered
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
115
  - - "~>"
102
116
  - !ruby/object:Gem::Version
103
- version: '3.6'
117
+ version: '0.10'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
- version: '3.6'
124
+ version: '0.10'
111
125
  - !ruby/object:Gem::Dependency
112
- name: bake-bundler
126
+ name: benchmark-ips
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
129
  - - ">="
@@ -122,41 +136,26 @@ dependencies:
122
136
  - - ">="
123
137
  - !ruby/object:Gem::Version
124
138
  version: '0'
125
- description: "\t\tAsync is a modern concurrency framework for Ruby. It implements
126
- the\n\t\treactor pattern, providing both non-blocking I/O and timer events.\n"
139
+ - !ruby/object:Gem::Dependency
140
+ name: rspec
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '3.6'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '3.6'
153
+ description:
127
154
  email:
128
- - samuel.williams@oriontransfer.co.nz
129
155
  executables: []
130
156
  extensions: []
131
157
  extra_rdoc_files: []
132
158
  files:
133
- - ".editorconfig"
134
- - ".github/workflows/development.yml"
135
- - ".gitignore"
136
- - ".rspec"
137
- - ".yardopts"
138
- - Gemfile
139
- - Guardfile
140
- - README.md
141
- - Rakefile
142
- - async.gemspec
143
- - bake.rb
144
- - benchmark/async_vs_lightio.rb
145
- - benchmark/fiber_count.rb
146
- - benchmark/rubies/README.md
147
- - benchmark/rubies/benchmark.rb
148
- - benchmark/thread_count.rb
149
- - benchmark/thread_vs_fiber.rb
150
- - examples/async_method.rb
151
- - examples/callback/loop.rb
152
- - examples/capture/README.md
153
- - examples/capture/capture.rb
154
- - examples/fibers.rb
155
- - examples/queue/producer.rb
156
- - examples/sleep_sort.rb
157
- - examples/stop/condition.rb
158
- - examples/stop/sleep.rb
159
- - gems/event.gemfile
160
159
  - lib/async.rb
161
160
  - lib/async/barrier.rb
162
161
  - lib/async/clock.rb
@@ -174,35 +173,11 @@ files:
174
173
  - lib/async/wrapper.rb
175
174
  - lib/kernel/async.rb
176
175
  - lib/kernel/sync.rb
177
- - logo.png
178
- - logo.svg
179
- - papers/1982 Grossman.pdf
180
- - papers/1987 ODell.pdf
181
- - spec/async/barrier_spec.rb
182
- - spec/async/chainable_async_examples.rb
183
- - spec/async/clock_spec.rb
184
- - spec/async/condition_examples.rb
185
- - spec/async/condition_spec.rb
186
- - spec/async/logger_spec.rb
187
- - spec/async/node_spec.rb
188
- - spec/async/notification_spec.rb
189
- - spec/async/performance_spec.rb
190
- - spec/async/queue_spec.rb
191
- - spec/async/reactor/nested_spec.rb
192
- - spec/async/reactor_spec.rb
193
- - spec/async/semaphore_spec.rb
194
- - spec/async/task_spec.rb
195
- - spec/async/wrapper_spec.rb
196
- - spec/async_spec.rb
197
- - spec/enumerator_spec.rb
198
- - spec/kernel/async_spec.rb
199
- - spec/kernel/sync_spec.rb
200
- - spec/spec_helper.rb
201
176
  homepage: https://github.com/socketry/async
202
177
  licenses:
203
178
  - MIT
204
179
  metadata: {}
205
- post_install_message:
180
+ post_install_message:
206
181
  rdoc_options: []
207
182
  require_paths:
208
183
  - lib
@@ -218,27 +193,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
218
193
  version: '0'
219
194
  requirements: []
220
195
  rubygems_version: 3.1.2
221
- signing_key:
196
+ signing_key:
222
197
  specification_version: 4
223
- summary: Async is a concurrency framework for Ruby.
224
- test_files:
225
- - spec/async/barrier_spec.rb
226
- - spec/async/chainable_async_examples.rb
227
- - spec/async/clock_spec.rb
228
- - spec/async/condition_examples.rb
229
- - spec/async/condition_spec.rb
230
- - spec/async/logger_spec.rb
231
- - spec/async/node_spec.rb
232
- - spec/async/notification_spec.rb
233
- - spec/async/performance_spec.rb
234
- - spec/async/queue_spec.rb
235
- - spec/async/reactor/nested_spec.rb
236
- - spec/async/reactor_spec.rb
237
- - spec/async/semaphore_spec.rb
238
- - spec/async/task_spec.rb
239
- - spec/async/wrapper_spec.rb
240
- - spec/async_spec.rb
241
- - spec/enumerator_spec.rb
242
- - spec/kernel/async_spec.rb
243
- - spec/kernel/sync_spec.rb
244
- - spec/spec_helper.rb
198
+ summary: A concurrency framework for Ruby.
199
+ test_files: []
@@ -1,6 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- indent_style = tab
5
- indent_size = 2
6
-
@@ -1,55 +0,0 @@
1
- name: Development
2
-
3
- on: [push, pull_request]
4
-
5
- jobs:
6
- test:
7
- runs-on: ${{matrix.os}}-latest
8
- continue-on-error: ${{matrix.experimental}}
9
-
10
- strategy:
11
- matrix:
12
- experimental: [false]
13
-
14
- os:
15
- - ubuntu
16
- - macos
17
-
18
- ruby:
19
- - 2.5
20
- - 2.6
21
- - 2.7
22
-
23
- include:
24
- - experimental: true
25
- os: ubuntu
26
- ruby: truffleruby
27
- env: JRUBY_OPTS="--debug -X+O"
28
- - experimental: true
29
- os: ubuntu
30
- ruby: jruby
31
- - experimental: true
32
- os: ubuntu
33
- ruby: head
34
- - experimental: true
35
- os: ubuntu
36
- ruby: 2.6
37
- env: COVERAGE=PartialSummary,Coveralls
38
-
39
- steps:
40
- - uses: actions/checkout@v1
41
- - uses: ruby/setup-ruby@v1
42
- with:
43
- ruby-version: ${{matrix.ruby}}
44
-
45
- - name: Install dependencies
46
- run: ${{matrix.env}} bundle install
47
-
48
- - name: Run tests
49
- timeout-minutes: 5
50
- run: ${{matrix.env}} bundle exec rspec
51
-
52
- - name: Run external tests
53
- timeout-minutes: 5
54
- if: matrix.experimental == false && matrix.os == 'ubuntu'
55
- run: ${{matrix.env}} bundle exec bake external
data/.gitignore DELETED
@@ -1,14 +0,0 @@
1
- .tags
2
-
3
- /.bundle/
4
- /.yardoc
5
- /Gemfile.lock
6
- /_yardoc/
7
- /coverage/
8
- /doc/
9
- /pkg/
10
- /spec/reports/
11
- /tmp/
12
-
13
- .rspec_status
14
- .covered.db
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --warnings
3
- --require spec_helper
data/.yardopts DELETED
@@ -1 +0,0 @@
1
- --markup markdown
data/Gemfile DELETED
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- gemspec
6
-
7
- group :development do
8
- gem 'pry'
9
- gem 'guard-rspec'
10
- gem 'guard-yard'
11
-
12
- gem 'yard'
13
- end
14
-
15
- group :test do
16
- gem 'benchmark-ips'
17
- gem 'ruby-prof', platforms: :mri
18
-
19
- gem 'covered', require: 'covered/rspec'
20
- end
data/Guardfile DELETED
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- directories %w(lib spec)
4
- clearing :on
5
-
6
- guard :rspec, cmd: "bundle exec rspec" do
7
- watch(%r{^spec/.+_spec\.rb$})
8
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
9
- watch("spec/spec_helper.rb") { "spec" }
10
- end
11
-
12
- guard 'yard', :port => '8808' do
13
- watch(%r{^lib/(.+)\.rb$})
14
- end
data/README.md DELETED
@@ -1,385 +0,0 @@
1
- # ![Async](logo.svg)
2
-
3
- Async is a composable asynchronous I/O framework for Ruby based on [nio4r] and [timers].
4
-
5
- [timers]: https://github.com/socketry/timers
6
- [nio4r]: https://github.com/socketry/nio4r
7
-
8
- [![Actions Status](https://github.com/socketry/async/workflows/Development/badge.svg)](https://github.com/socketry/async/actions?workflow=Development)
9
- [![Code Climate](https://codeclimate.com/github/socketry/async.svg)](https://codeclimate.com/github/socketry/async)
10
- [![Coverage Status](https://coveralls.io/repos/socketry/async/badge.svg)](https://coveralls.io/r/socketry/async)
11
- [![Gitter](https://badges.gitter.im/join.svg)](https://gitter.im/socketry/async)
12
-
13
- > "Lately I've been looking into `async`, as one of my projects – [tus-ruby-server](https://github.com/janko/tus-ruby-server) – would really benefit from non-blocking I/O. It's really beautifully designed." *– [janko](https://github.com/janko)*
14
-
15
- ## Motivation
16
-
17
- Several years ago, I was hosting websites on a server in my garage. Back then, my ADSL modem was very basic, and I wanted to have a DNS server which would resolve to an internal IP address when the domain itself resolved to my public IP. Thus was born [RubyDNS]. This project [was originally built on](https://github.com/ioquatix/rubydns/tree/v0.8.5) top of [EventMachine], but a lack of support for [IPv6 at the time](https://github.com/ioquatix/rubydns/issues/45) and [other problems](https://github.com/ioquatix/rubydns/issues/14), meant that I started looking for other options. Around that time [Celluloid] was picking up steam. I had not encountered actors before and I wanted to learn more about it. So, [I reimplemented RubyDNS on top of Celluloid](https://github.com/ioquatix/rubydns/tree/v0.9.0) and this eventually became the first stable release.
18
-
19
- Moving forward, I refactored the internals of RubyDNS into [Celluloid::DNS]. This rewrite helped solidify the design of RubyDNS and to a certain extent it works. However, [unfixed bugs and design problems](https://github.com/celluloid/celluloid/pull/710) in Celluloid meant that RubyDNS 2.0 was delayed by almost 2 years. I wasn't happy releasing it with known bugs and problems. After working on the issues for a while, and thinking about possible solutions, I decided to build a small event reactor using [nio4r] and [timers], the core parts of [Celluloid::IO] which made it work so well. The result is this project.
20
-
21
- One observation I made when looking at existing gems for asynchronous IO was a tendency to try and do everything within a single code-base. The design of this core library is deliberately simple. Additional libraries provide asynchronous networking, process management, etc. It's likely you will prefer to depend on [async-io] for actual wrappers around `IO` and `Socket`. This helps to ensure a clean separation of concerns.
22
-
23
- In designing this library, I also built a [similarly designed C++ library of the same name](https://github.com/kurocha/async). These two libraries share similar design principles.
24
-
25
- [Celluloid]: https://github.com/celluloid/celluloid
26
- [Celluloid::IO]: https://github.com/celluloid/celluloid-io
27
- [Celluloid::DNS]: https://github.com/celluloid/celluloid-dns
28
- [EventMachine]: https://github.com/eventmachine/eventmachine
29
- [RubyDNS]: https://github.com/ioquatix/rubydns
30
- [async-io]: https://github.com/socketry/async-io
31
-
32
- ## Installation
33
-
34
- Add this line to your application's Gemfile:
35
-
36
- ```ruby
37
- gem "async"
38
- ```
39
-
40
- And then execute:
41
-
42
- $ bundle
43
-
44
- Or install it yourself as:
45
-
46
- $ gem install async
47
-
48
- ## Usage
49
-
50
- Please [try the interactive online tutorial](https://katacoda.com/ioquatix/scenarios/async-introduction).
51
-
52
- ### Tasks
53
-
54
- An `Async::Task` runs using a `Fiber` and blocking operations e.g. `sleep`, `read`, `write` yield control until the operation can complete. There are two main methods to create tasks.
55
-
56
- #### `Async{...}`
57
-
58
- The highest level entry point is `Async{...}`. It's useful if you are building a library and you want well defined asynchronous semantics. This internally invokes `Async::Reactor.run{...}`.
59
-
60
- ```ruby
61
- def run_server
62
- Async do |task|
63
- # ... acccept connections
64
- end
65
- end
66
- ```
67
-
68
- If `Async(&block)` happens within an existing reactor, it will schedule an asynchronous task and return. If `Async(&block)` happens outside of an existing reactor, it will create a reactor, schedule the asynchronous task, and block until it completes. The task is scheduled by calling `Async::Reactor#async(&block)`.
69
-
70
- This allows the caller to have either blocking or non-blocking behaviour.
71
-
72
- ```ruby
73
- require 'async'
74
-
75
- def sleepy(duration = 1)
76
- Async do |task|
77
- task.sleep duration
78
- puts "I'm done sleeping, time for action!"
79
- end
80
- end
81
-
82
- # Synchronous operation:
83
- sleepy
84
-
85
- # Asynchronous operation:
86
- Async do
87
- # These two functions will sleep simultaneously.
88
- sleepy
89
- sleepy
90
- end
91
- ```
92
-
93
- The cost of using `Async{...}` is minimal for initialization/server setup, but is not ideal for per-connection tasks.
94
-
95
- #### `Async::Task#async`
96
-
97
- If you can guarantee you are running within a task, and have access to it (e.g. via an argument), you can efficiently schedule new tasks using the `Async::Task#async(&block)` method.
98
-
99
- ```ruby
100
- require 'async'
101
-
102
- def nested_sleepy(task: Async::Task.current)
103
- # Block caller
104
- task.sleep 0.1
105
-
106
- # Schedule nested task:
107
- subtask = task.async do |subtask|
108
- puts "I'm going to sleep..."
109
- subtask.sleep 1.0
110
- ensure
111
- puts "I'm waking up!"
112
- end
113
- end
114
-
115
- Async do |task|
116
- subtask = nested_sleepy(task: task)
117
- end
118
- ```
119
-
120
- This example creates a child `subtask` from the given parent `task`. It's the most efficient way to schedule a task. The task is executed until the first blocking operation, at which point it will yield control and `#async` will return. The result of this method is the task itself.
121
-
122
- ### Waiting for Results
123
-
124
- Like promises, `Async::Task` produces results. In order to wait for these results, you must invoke `Async::Task#wait`:
125
-
126
- ```ruby
127
- require 'async'
128
-
129
- task = Async do
130
- rand
131
- end
132
-
133
- puts task.wait
134
- ```
135
-
136
- ### Stopping Tasks
137
-
138
- Use `Async::Task#stop` to stop tasks. This function raises `Async::Stop` on the target task and all descendent tasks.
139
-
140
- ```ruby
141
- require 'async'
142
-
143
- Async do
144
- sleepy = Async do |task|
145
- task.sleep 1000
146
- end
147
-
148
- sleepy.stop
149
- end
150
- ```
151
-
152
- When you design a server, you should return the task back to the caller. They can use this task to stop the server if needed, independently of any other unrelated tasks within the reactor, and it will correctly clean up all related tasks.
153
-
154
- ### Reactors
155
-
156
- `Async::Reactor` is the top level IO reactor, and runs multiple tasks asynchronously. The reactor itself is not thread-safe, so you'd typically have [one reactor per thread or process](https://github.com/socketry/async-container).
157
-
158
- #### Hierarchy
159
-
160
- `Async::Reactor` and `Async::Task` form nodes in a tree. Reactors and tasks can spawn children tasks. When you invoke `Async::Reactor#async`, the parent task is determined by calling `Async::Task.current?` which uses fiber local storage. A slightly more efficient method is to use `Async::Task#async`, which uses `self` as the parent task.
161
-
162
-
163
- ```ruby
164
- require 'async'
165
-
166
- def sleepy(duration, task: Async::Task.current)
167
- task.async do |subtask|
168
- subtask.annotate "I'm going to sleep #{duration}s..."
169
- subtask.sleep duration
170
- puts "I'm done sleeping!"
171
- end
172
- end
173
-
174
- def nested_sleepy(task: Async::Task.current)
175
- task.async do |subtask|
176
- subtask.annotate "Invoking sleepy 5 times..."
177
- 5.times do |index|
178
- sleepy(index, task: subtask)
179
- end
180
- end
181
- end
182
-
183
- Async do |task|
184
- task.annotate "Invoking nested_sleepy..."
185
- subtask = nested_sleepy
186
-
187
- # Print out all running tasks in a tree:
188
- task.print_hierarchy($stderr)
189
-
190
- # Kill the subtask
191
- subtask.stop
192
- end
193
- ```
194
-
195
- #### Embedding Reactors
196
-
197
- `Async::Reactor#run` will run until the reactor runs out of work to do. To run a single iteration of the reactor, use `Async::Reactor#run_once`
198
-
199
- ```ruby
200
- require 'async'
201
-
202
- Async.logger.debug!
203
- reactor = Async::Reactor.new
204
-
205
- # Run the reactor for 1 second:
206
- reactor.async do |task|
207
- task.sleep 1
208
- puts "Finished!"
209
- end
210
-
211
- while reactor.run_once
212
- # Round and round we go!
213
- end
214
- ```
215
-
216
- You can use this approach to embed the reactor in another event loop.
217
-
218
- #### Stopping Reactors
219
-
220
- `Async::Reactor#stop` will stop the current reactor and all children tasks.
221
-
222
- #### Interrupting Reactors
223
-
224
- `Async::Reactor#interrupt` can be called safely from a different thread (or signal handler) and will cause the reactor to invoke `#stop`.
225
-
226
- ### Resource Management
227
-
228
- In order to ensure your resources are cleaned up correctly, make sure you wrap resources appropriately, e.g.:
229
-
230
- ```ruby
231
- Async::Reactor.run do
232
- socket = connect(remote_address) # May raise Async::Stop
233
-
234
- begin
235
- socket.write(...) # May raise Async::Stop
236
- socket.read(...) # May raise Async::Stop
237
- ensure
238
- socket.close
239
- end
240
- end
241
- ```
242
-
243
- As tasks run synchronously until they yield back to the reactor, you can guarantee this model works correctly. While in theory `IO#autoclose` allows you to automatically close file descriptors when they go out of scope via the GC, it may produce unpredictable behavour (exhaustion of file descriptors, flushing data at odd times), so it's not recommended.
244
-
245
- ### Exception Handling
246
-
247
- `Async::Task` captures and logs exceptions. All unhandled exceptions will cause the enclosing task to enter the `:failed` state. Non-`StandardError` exceptions are re-raised immediately and will generally cause the reactor to fail. This ensures that exceptions will always be visible and cause the program to fail appropriately.
248
-
249
- ```ruby
250
- require 'async'
251
-
252
- task = Async do
253
- # Exception will be logged and task will be failed.
254
- raise "Boom"
255
- end
256
-
257
- puts task.status # failed
258
- puts task.result # raises RuntimeError: Boom
259
- ```
260
-
261
- #### Propagating Exceptions
262
-
263
- If a task has finished due to an exception, calling `Task#wait` will re-raise the exception.
264
-
265
- ```ruby
266
- require 'async'
267
-
268
- Async do
269
- task = Async do
270
- raise "Boom"
271
- end
272
-
273
- begin
274
- task.wait # Re-raises above exception.
275
- rescue
276
- puts "It went #{$!}!"
277
- end
278
- end
279
- ```
280
-
281
- #### Timeouts
282
-
283
- You can wrap asynchronous operations in a timeout. This ensures that malicious services don't cause your code to block indefinitely.
284
-
285
- ```ruby
286
- require 'async'
287
-
288
- Async do |task|
289
- task.with_timeout(1) do
290
- task.sleep 100
291
- rescue Async::TimeoutError
292
- puts "I timed out!"
293
- end
294
- end
295
- ```
296
-
297
- ### Reoccurring Timers
298
-
299
- Sometimes you need to do some periodic work in a loop.
300
-
301
- ```ruby
302
- require 'async'
303
-
304
- Async do |task|
305
- while true
306
- puts Time.now
307
- task.sleep 1
308
- end
309
- end
310
- ```
311
-
312
- ## Caveats
313
-
314
- ### Enumerators
315
-
316
- Due to limitations within Ruby and the nature of this library, it is not possible to use `to_enum` on methods which invoke asynchronous behavior. We hope to [fix this issue in the future](https://github.com/socketry/async/issues/23).
317
-
318
- ### Blocking Methods in Standard Library
319
-
320
- Blocking Ruby methods such as `pop` in the `Queue` class require access to their own threads and will not yield control back to the reactor which can result in a deadlock. As a substitute for the standard library `Queue`, the `Async::Queue` class can be used.
321
-
322
- ## Conventions
323
-
324
- ### Nesting Tasks
325
-
326
- `Async::Barrier` and `Async::Semaphore` are designed to be compatible with each other, and with other tasks that nest `#async` invocations. There are other similar situations where you may want to pass in a parent task, e.g. `Async::IO::Endpoint#bind`.
327
-
328
- ```ruby
329
- barrier = Async::Barrier.new
330
- semaphore = Async::Semaphore.new(2)
331
-
332
- semaphore.async(parent: barrier) do
333
- # ...
334
- end
335
- ```
336
-
337
- A `parent:` in this context is anything that responds to `#async` in the same way that `Async::Task` responds to `#async`. In situations where you strictly depend on the interface of `Async::Task`, use the `task: Task.current` pattern.
338
-
339
- ## Contributing
340
-
341
- 1. Fork it
342
- 2. Create your feature branch (`git checkout -b my-new-feature`)
343
- 3. Commit your changes (`git commit -am 'Add some feature'`)
344
- 4. Push to the branch (`git push origin my-new-feature`)
345
- 5. Create new Pull Request
346
-
347
- ## See Also
348
-
349
- - [async-io](https://github.com/socketry/async-io) — Asynchronous networking and sockets.
350
- - [async-http](https://github.com/socketry/async-http) — Asynchronous HTTP client/server.
351
- - [async-process](https://github.com/socketry/async-process) — Asynchronous process spawning/waiting.
352
- - [async-websocket](https://github.com/socketry/async-websocket) — Asynchronous client and server websockets.
353
- - [async-dns](https://github.com/socketry/async-dns) — Asynchronous DNS resolver and server.
354
- - [async-rspec](https://github.com/socketry/async-rspec) — Shared contexts for running async specs.
355
-
356
- ### Projects Using Async
357
-
358
- - [ciri](https://github.com/ciri-ethereum/ciri) — An Ethereum implementation written in Ruby.
359
- - [falcon](https://github.com/socketry/falcon) — A rack compatible server built on top of `async-http`.
360
- - [rubydns](https://github.com/ioquatix/rubydns) — A easy to use Ruby DNS server.
361
- - [slack-ruby-bot](https://github.com/slack-ruby/slack-ruby-bot) — A client for making slack bots.
362
-
363
- ## License
364
-
365
- Released under the MIT license.
366
-
367
- Copyright, 2017, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
368
-
369
- Permission is hereby granted, free of charge, to any person obtaining a copy
370
- of this software and associated documentation files (the "Software"), to deal
371
- in the Software without restriction, including without limitation the rights
372
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
373
- copies of the Software, and to permit persons to whom the Software is
374
- furnished to do so, subject to the following conditions:
375
-
376
- The above copyright notice and this permission notice shall be included in
377
- all copies or substantial portions of the Software.
378
-
379
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
380
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
381
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
382
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
383
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
384
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
385
- THE SOFTWARE.