async 1.26.1 → 1.26.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.
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
data/Rakefile DELETED
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:test)
7
-
8
- task :default => :test
9
-
10
- def clone_and_test(name)
11
- path = "external/#{name}"
12
- FileUtils.rm_rf path
13
- FileUtils.mkdir_p path
14
-
15
- sh("git clone https://git@github.com/socketry/#{name} #{path}")
16
-
17
- # I tried using `bundle config --local local.async ../` but it simply doesn't work.
18
- # system("bundle", "config", "--local", "local.async", __dir__, chdir: path)
19
-
20
- File.open("#{path}/Gemfile", "a") do |file|
21
- file.puts('gem "async", path: "../../"')
22
- end
23
-
24
- sh("cd #{path} && bundle install && bundle exec rspec")
25
- end
26
-
27
- task :external do
28
- Bundler.with_clean_env do
29
- clone_and_test("async-io")
30
- clone_and_test("async-websocket")
31
- clone_and_test("async-dns")
32
- clone_and_test("async-http")
33
- clone_and_test("falcon")
34
- clone_and_test("async-rest")
35
- end
36
- end
37
-
38
- task :coverage do
39
- ENV['COVERAGE'] = 'y'
40
- end
@@ -1,34 +0,0 @@
1
-
2
- require_relative 'lib/async/version'
3
-
4
- Gem::Specification.new do |spec|
5
- spec.name = "async"
6
- spec.version = Async::VERSION
7
- spec.authors = ["Samuel Williams"]
8
- spec.email = ["samuel.williams@oriontransfer.co.nz"]
9
- spec.description = <<-EOF
10
- Async is a modern concurrency framework for Ruby. It implements the
11
- reactor pattern, providing both non-blocking I/O and timer events.
12
- EOF
13
- spec.summary = "Async is a concurrency framework for Ruby."
14
- spec.homepage = "https://github.com/socketry/async"
15
- spec.license = "MIT"
16
-
17
- spec.files = `git ls-files`.split($/)
18
- spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
- spec.require_paths = ["lib"]
21
-
22
- spec.required_ruby_version = ">= 2.5.0"
23
-
24
- spec.add_runtime_dependency "nio4r", "~> 2.3"
25
- spec.add_runtime_dependency "timers", "~> 4.1"
26
- spec.add_runtime_dependency "console", "~> 1.0"
27
-
28
- spec.add_development_dependency "async-rspec", "~> 1.1"
29
-
30
- spec.add_development_dependency "covered", "~> 0.10"
31
- spec.add_development_dependency "bundler"
32
- spec.add_development_dependency "rspec", "~> 3.6"
33
- spec.add_development_dependency "bake-bundler"
34
- end
data/bake.rb DELETED
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- def external
4
- require 'bundler'
5
-
6
- Bundler.with_clean_env do
7
- clone_and_test("async-io")
8
- clone_and_test("async-websocket")
9
- clone_and_test("async-dns")
10
- clone_and_test("async-http")
11
- clone_and_test("falcon")
12
- clone_and_test("async-rest")
13
- end
14
- end
15
-
16
- private
17
-
18
- def clone_and_test(name)
19
- path = "external/#{name}"
20
- FileUtils.rm_rf path
21
- FileUtils.mkdir_p path
22
-
23
- system("git clone https://git@github.com/socketry/#{name} #{path}")
24
-
25
- # I tried using `bundle config --local local.async ../` but it simply doesn't work.
26
- # system("bundle", "config", "--local", "local.async", __dir__, chdir: path)
27
-
28
- File.open("#{path}/Gemfile", "a") do |file|
29
- file.puts('gem "async", path: "../../"')
30
- end
31
-
32
- system("cd #{path} && bundle install && bundle exec rspec")
33
- end
@@ -1,84 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'async'
5
- require 'lightio'
6
-
7
- require 'benchmark/ips'
8
-
9
- #
10
- # It's hard to know exactly how to interpret these results. When running parallel
11
- # instances, resource contention is more likely to be a problem, and yet with
12
- # async, the performance between a single task and several tasks is roughly the
13
- # same, while in the case of lightio, there is an obvious performance gap.
14
- #
15
- # The main takeaway is that contention causes issues and if systems are not
16
- # designed with that in mind, it will impact performance.
17
- #
18
- # $ ruby async_vs_lightio.rb
19
- # Warming up --------------------------------------
20
- # lightio (synchronous)
21
- # 2.439k i/100ms
22
- # async (synchronous) 2.115k i/100ms
23
- # lightio (parallel) 211.000 i/100ms
24
- # async (parallel) 449.000 i/100ms
25
- # Calculating -------------------------------------
26
- # lightio (synchronous)
27
- # 64.502k (± 3.9%) i/s - 643.896k in 10.002151s
28
- # async (synchronous) 161.195k (± 1.6%) i/s - 1.612M in 10.000976s
29
- # lightio (parallel) 49.827k (±17.5%) i/s - 477.704k in 9.999579s
30
- # async (parallel) 166.862k (± 6.2%) i/s - 1.662M in 10.000365s
31
- #
32
- # Comparison:
33
- # async (parallel): 166862.3 i/s
34
- # async (synchronous): 161194.6 i/s - same-ish: difference falls within error
35
- # lightio (synchronous): 64502.5 i/s - 2.59x slower
36
- # lightio (parallel): 49827.3 i/s - 3.35x slower
37
-
38
-
39
- DURATION = 0.000001
40
-
41
- def run_async(count, repeats = 10000)
42
- Async::Reactor.run do |task|
43
- count.times.map do
44
- task.async do |subtask|
45
- repeats.times do
46
- subtask.sleep(DURATION)
47
- end
48
- end
49
- end.each(&:wait)
50
- end
51
- end
52
-
53
- def run_lightio(count, repeats = 10000)
54
- count.times.map do
55
- LightIO::Beam.new do
56
- repeats.times do
57
- LightIO.sleep(DURATION)
58
- end
59
- end
60
- end.each(&:join)
61
- end
62
-
63
- Benchmark.ips do |benchmark|
64
- benchmark.time = 10
65
- benchmark.warmup = 2
66
-
67
- benchmark.report("lightio (synchronous)") do |count|
68
- run_lightio(1, count)
69
- end
70
-
71
- benchmark.report("async (synchronous)") do |count|
72
- run_async(1, count)
73
- end
74
-
75
- benchmark.report("lightio (parallel)") do |count|
76
- run_lightio(32, count/32)
77
- end
78
-
79
- benchmark.report("async (parallel)") do |count|
80
- run_async(32, count/32)
81
- end
82
-
83
- benchmark.compare!
84
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- fibers = []
4
-
5
- (1..).each do |i|
6
- fibers << Fiber.new{}
7
- fibers.last.resume
8
- puts i
9
- end
10
-
@@ -1,51 +0,0 @@
1
- # (All) Rubies Benchmark
2
-
3
- This is a simple benchmark, which reads and writes data over a pipe.
4
-
5
- It is designed to work as far back as Ruby 1.9.3 at the expense of code clarity. It also works on JRuby and TruffleRuby.
6
-
7
- ## Usage
8
-
9
- The simplest way is to use RVM.
10
-
11
- rvm all do ./benchmark.rb
12
-
13
- ## Results
14
-
15
- General improvements.
16
-
17
- ruby 1.9.3p551 (2014-11-13 revision 48407) [x86_64-linux]
18
- #<struct Struct::Tms utime=63.41, stime=7.15, cutime=0.0, cstime=0.0>
19
-
20
- ruby 2.0.0p648 (2015-12-16 revision 53162) [x86_64-linux]
21
- #<struct Struct::Tms utime=59.5, stime=6.57, cutime=0.0, cstime=0.0>
22
-
23
- ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-linux]
24
- #<struct Process::Tms utime=40.53, stime=6.87, cutime=0.0, cstime=0.0>
25
-
26
- ruby 2.2.10p489 (2018-03-28 revision 63023) [x86_64-linux]
27
- #<struct Process::Tms utime=41.26, stime=6.62, cutime=0.0, cstime=0.0>
28
-
29
- ruby 2.3.8p459 (2018-10-18 revision 65136) [x86_64-linux]
30
- #<struct Process::Tms utime=31.85, stime=6.55, cutime=0.0, cstime=0.0>
31
-
32
- ruby 2.4.6p354 (2019-04-01 revision 67394) [x86_64-linux]
33
- #<struct Process::Tms utime=41.89, stime=6.72, cutime=0.0, cstime=0.0>
34
-
35
- ruby 2.5.3p105 (2018-10-18 revision 65156) [x86_64-linux]
36
- #<struct Process::Tms utime=26.446285, stime=6.549777, cutime=0.0, cstime=0.0>
37
-
38
- Native fiber implementation & reduced syscalls (https://bugs.ruby-lang.org/issues/14739).
39
-
40
- ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
41
- #<struct Process::Tms utime=20.045192, stime=5.5941600000000005, cutime=0.0, cstime=0.0>
42
-
43
- Performance regression (https://bugs.ruby-lang.org/issues/16009).
44
-
45
- ruby 2.7.0preview1 (2019-05-31 trunk c55db6aa271df4a689dc8eb0039c929bf6ed43ff) [x86_64-linux]
46
- #<struct Process::Tms utime=25.193268, stime=5.808202, cutime=0.0, cstime=0.0>
47
-
48
- Improve fiber performance using pool alloation strategy (https://bugs.ruby-lang.org/issues/15997).
49
-
50
- ruby 2.7.0dev (2019-10-02T08:19:14Z trunk 9759e3c9f0) [x86_64-linux]
51
- #<struct Process::Tms utime=19.110835, stime=5.738776, cutime=0.0, cstime=0.0>
@@ -1,220 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'socket'
5
- require 'fiber'
6
-
7
- puts
8
- puts RUBY_DESCRIPTION
9
-
10
- if RUBY_VERSION < "2.0"
11
- class String
12
- def b
13
- self
14
- end
15
- end
16
- end
17
-
18
- # TODO: make these much larger, see if we're effectively batching
19
- # even if we don't mean to...
20
- QUERY_TEXT = "STATUS".freeze
21
- RESPONSE_TEXT = "OK".freeze
22
-
23
- NUM_WORKERS = (ARGV[0] || 10_000).to_i
24
- NUM_REQUESTS = (ARGV[1] || 100).to_i
25
-
26
- # Fiber reactor code taken from
27
- # https://www.codeotaku.com/journal/2018-11/fibers-are-the-right-solution/index
28
- class Reactor
29
- def initialize
30
- @readable = {}
31
- @writable = {}
32
- end
33
-
34
- def run
35
- while @readable.any? or @writable.any?
36
- readable, writable = IO.select(@readable.keys, @writable.keys, [])
37
-
38
- readable.each do |io|
39
- @readable[io].resume
40
- end
41
-
42
- writable.each do |io|
43
- @writable[io].resume
44
- end
45
- end
46
- end
47
-
48
- def wait_readable(io)
49
- @readable[io] = Fiber.current
50
- Fiber.yield
51
- @readable.delete(io)
52
- end
53
-
54
- def wait_writable(io)
55
- @writable[io] = Fiber.current
56
- Fiber.yield
57
- @writable.delete(io)
58
- end
59
- end
60
-
61
- class Wrapper
62
- def initialize(io, reactor)
63
- @io = io
64
- @reactor = reactor
65
- end
66
-
67
- if RUBY_VERSION >= "2.3"
68
- def read_nonblock(length, buffer)
69
- while true
70
- case result = @io.read_nonblock(length, buffer, exception: false)
71
- when :wait_readable
72
- @reactor.wait_readable(@io)
73
- when :wait_writable
74
- @reactor.wait_writable(@io)
75
- else
76
- return result
77
- end
78
- end
79
-
80
- end
81
-
82
- def write_nonblock(buffer)
83
- while true
84
- case result = @io.write_nonblock(buffer, exception: false)
85
- when :wait_readable
86
- @reactor.wait_readable(@io)
87
- when :wait_writable
88
- @reactor.wait_writable(@io)
89
- else
90
- return result
91
- end
92
- end
93
- end
94
- else
95
- def read_nonblock(length, buffer)
96
- while true
97
- begin
98
- return @io.read_nonblock(length, buffer)
99
- rescue IO::WaitReadable
100
- @reactor.wait_readable(@io)
101
- rescue IO::WaitWritable
102
- @reactor.wait_writable(@io)
103
- end
104
- end
105
- end
106
-
107
- def write_nonblock(buffer)
108
- while true
109
- begin
110
- return @io.write_nonblock(buffer)
111
- rescue IO::WaitReadable
112
- @reactor.wait_readable(@io)
113
- rescue IO::WaitWritable
114
- @reactor.wait_writable(@io)
115
- end
116
- end
117
- end
118
- end
119
-
120
- def read(length, buffer = nil)
121
- if buffer
122
- buffer.clear
123
- else
124
- buffer = String.new.b
125
- end
126
-
127
- result = self.read_nonblock(length - buffer.bytesize, buffer)
128
-
129
- if result == length
130
- return result
131
- end
132
-
133
- chunk = String.new.b
134
- while chunk = self.read_nonblock(length - buffer.bytesize, chunk)
135
- buffer << chunk
136
-
137
- break if buffer.bytesize == length
138
- end
139
-
140
- return buffer
141
- end
142
-
143
- def write(buffer)
144
- remaining = buffer.dup
145
-
146
- while true
147
- result = self.write_nonblock(remaining)
148
-
149
- if result == remaining.bytesize
150
- return buffer.bytesize
151
- else
152
- remaining = remaining.byteslice(result, remaining.bytesize - result)
153
- end
154
- end
155
- end
156
- end
157
-
158
- reactor = Reactor.new
159
-
160
- worker_read = []
161
- worker_write = []
162
-
163
- master_read = []
164
- master_write = []
165
-
166
- workers = []
167
-
168
- # puts "Setting up pipes..."
169
- NUM_WORKERS.times do |i|
170
- r, w = IO.pipe
171
- worker_read.push Wrapper.new(r, reactor)
172
- master_write.push Wrapper.new(w, reactor)
173
-
174
- r, w = IO.pipe
175
- worker_write.push Wrapper.new(w, reactor)
176
- master_read.push Wrapper.new(r, reactor)
177
- end
178
-
179
- # puts "Setting up fibers..."
180
- NUM_WORKERS.times do |i|
181
- f = Fiber.new do
182
- # Worker code
183
- NUM_REQUESTS.times do |req_num|
184
- q = worker_read[i].read(QUERY_TEXT.size)
185
- if q != QUERY_TEXT
186
- raise "Fail! Expected #{QUERY_TEXT.inspect} but got #{q.inspect} on request #{req_num.inspect}!"
187
- end
188
- worker_write[i].write(RESPONSE_TEXT)
189
- end
190
- end
191
- workers.push f
192
- end
193
-
194
- workers.each { |f| f.resume }
195
-
196
- master_fiber = Fiber.new do
197
- NUM_WORKERS.times do |worker_num|
198
- f = Fiber.new do
199
- NUM_REQUESTS.times do |req_num|
200
- master_write[worker_num].write(QUERY_TEXT)
201
- buffer = master_read[worker_num].read(RESPONSE_TEXT.size)
202
- if buffer != RESPONSE_TEXT
203
- raise "Error! Fiber no. #{worker_num} on req #{req_num} expected #{RESPONSE_TEXT.inspect} but got #{buf.inspect}!"
204
- end
205
- end
206
- end
207
- f.resume
208
- end
209
- end
210
-
211
- master_fiber.resume
212
-
213
- # puts "Starting reactor..."
214
- reactor.run
215
-
216
- # puts "Done, finished all reactor Fibers!"
217
-
218
- puts Process.times
219
-
220
- # Exit