async 1.21.0 → 1.22.0
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/benchmark/rubies/README.md +51 -0
- data/benchmark/rubies/benchmark.rb +219 -0
- data/lib/async/barrier.rb +51 -0
- data/lib/async/reactor.rb +14 -13
- data/lib/async/semaphore.rb +2 -4
- data/lib/async/version.rb +1 -1
- data/spec/async/barrier_spec.rb +77 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4fa7e7ae29e8bff86757e97d1f63bb5fd297caa79f97b5e4ccdc1f4568d65609
|
4
|
+
data.tar.gz: 17239cfec1b3b5062fdf6745b7708ebbdd9f39c63199dc33cc6495362da016ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18566e7d62eca30c72491cc06f633baa1f2b027fbb474f7bb226d0c5ea09cc095e06b3f2943470f378d98a2174ff8e0b4d7875643f3abba751fe69c76e3f6657
|
7
|
+
data.tar.gz: 12aed7ad107c756e90292a2179336441205d66a4f457dcf1d55e5655d9bc361753a9b4deb48c644104ca5d349610a1eb33773e995c91a7e2b92a71c97df3a0cd
|
@@ -0,0 +1,51 @@
|
|
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>
|
@@ -0,0 +1,219 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'fiber'
|
5
|
+
|
6
|
+
puts
|
7
|
+
puts RUBY_DESCRIPTION
|
8
|
+
|
9
|
+
if RUBY_VERSION < "2.0"
|
10
|
+
class String
|
11
|
+
def b
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# TODO: make these much larger, see if we're effectively batching
|
18
|
+
# even if we don't mean to...
|
19
|
+
QUERY_TEXT = "STATUS".freeze
|
20
|
+
RESPONSE_TEXT = "OK".freeze
|
21
|
+
|
22
|
+
NUM_WORKERS = (ARGV[0] || 10_000).to_i
|
23
|
+
NUM_REQUESTS = (ARGV[1] || 100).to_i
|
24
|
+
|
25
|
+
# Fiber reactor code taken from
|
26
|
+
# https://www.codeotaku.com/journal/2018-11/fibers-are-the-right-solution/index
|
27
|
+
class Reactor
|
28
|
+
def initialize
|
29
|
+
@readable = {}
|
30
|
+
@writable = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def run
|
34
|
+
while @readable.any? or @writable.any?
|
35
|
+
readable, writable = IO.select(@readable.keys, @writable.keys, [])
|
36
|
+
|
37
|
+
readable.each do |io|
|
38
|
+
@readable[io].resume
|
39
|
+
end
|
40
|
+
|
41
|
+
writable.each do |io|
|
42
|
+
@writable[io].resume
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def wait_readable(io)
|
48
|
+
@readable[io] = Fiber.current
|
49
|
+
Fiber.yield
|
50
|
+
@readable.delete(io)
|
51
|
+
end
|
52
|
+
|
53
|
+
def wait_writable(io)
|
54
|
+
@writable[io] = Fiber.current
|
55
|
+
Fiber.yield
|
56
|
+
@writable.delete(io)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Wrapper
|
61
|
+
def initialize(io, reactor)
|
62
|
+
@io = io
|
63
|
+
@reactor = reactor
|
64
|
+
end
|
65
|
+
|
66
|
+
if RUBY_VERSION >= "2.3"
|
67
|
+
def read_nonblock(length, buffer)
|
68
|
+
while true
|
69
|
+
case result = @io.read_nonblock(length, buffer, exception: false)
|
70
|
+
when :wait_readable
|
71
|
+
@reactor.wait_readable(@io)
|
72
|
+
when :wait_writable
|
73
|
+
@reactor.wait_writable(@io)
|
74
|
+
else
|
75
|
+
return result
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
def write_nonblock(buffer)
|
82
|
+
while true
|
83
|
+
case result = @io.write_nonblock(buffer, exception: false)
|
84
|
+
when :wait_readable
|
85
|
+
@reactor.wait_readable(@io)
|
86
|
+
when :wait_writable
|
87
|
+
@reactor.wait_writable(@io)
|
88
|
+
else
|
89
|
+
return result
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
else
|
94
|
+
def read_nonblock(length, buffer)
|
95
|
+
while true
|
96
|
+
begin
|
97
|
+
return @io.read_nonblock(length, buffer)
|
98
|
+
rescue IO::WaitReadable
|
99
|
+
@reactor.wait_readable(@io)
|
100
|
+
rescue IO::WaitWritable
|
101
|
+
@reactor.wait_writable(@io)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def write_nonblock(buffer)
|
107
|
+
while true
|
108
|
+
begin
|
109
|
+
return @io.write_nonblock(buffer)
|
110
|
+
rescue IO::WaitReadable
|
111
|
+
@reactor.wait_readable(@io)
|
112
|
+
rescue IO::WaitWritable
|
113
|
+
@reactor.wait_writable(@io)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def read(length, buffer = nil)
|
120
|
+
if buffer
|
121
|
+
buffer.clear
|
122
|
+
else
|
123
|
+
buffer = String.new.b
|
124
|
+
end
|
125
|
+
|
126
|
+
result = self.read_nonblock(length - buffer.bytesize, buffer)
|
127
|
+
|
128
|
+
if result == length
|
129
|
+
return result
|
130
|
+
end
|
131
|
+
|
132
|
+
chunk = String.new.b
|
133
|
+
while chunk = self.read_nonblock(length - buffer.bytesize, chunk)
|
134
|
+
buffer << chunk
|
135
|
+
|
136
|
+
break if buffer.bytesize == length
|
137
|
+
end
|
138
|
+
|
139
|
+
return buffer
|
140
|
+
end
|
141
|
+
|
142
|
+
def write(buffer)
|
143
|
+
remaining = buffer.dup
|
144
|
+
|
145
|
+
while true
|
146
|
+
result = self.write_nonblock(remaining)
|
147
|
+
|
148
|
+
if result == remaining.bytesize
|
149
|
+
return buffer.bytesize
|
150
|
+
else
|
151
|
+
remaining = remaining.byteslice(result, remaining.bytesize - result)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
reactor = Reactor.new
|
158
|
+
|
159
|
+
worker_read = []
|
160
|
+
worker_write = []
|
161
|
+
|
162
|
+
master_read = []
|
163
|
+
master_write = []
|
164
|
+
|
165
|
+
workers = []
|
166
|
+
|
167
|
+
# puts "Setting up pipes..."
|
168
|
+
NUM_WORKERS.times do |i|
|
169
|
+
r, w = IO.pipe
|
170
|
+
worker_read.push Wrapper.new(r, reactor)
|
171
|
+
master_write.push Wrapper.new(w, reactor)
|
172
|
+
|
173
|
+
r, w = IO.pipe
|
174
|
+
worker_write.push Wrapper.new(w, reactor)
|
175
|
+
master_read.push Wrapper.new(r, reactor)
|
176
|
+
end
|
177
|
+
|
178
|
+
# puts "Setting up fibers..."
|
179
|
+
NUM_WORKERS.times do |i|
|
180
|
+
f = Fiber.new do
|
181
|
+
# Worker code
|
182
|
+
NUM_REQUESTS.times do |req_num|
|
183
|
+
q = worker_read[i].read(QUERY_TEXT.size)
|
184
|
+
if q != QUERY_TEXT
|
185
|
+
raise "Fail! Expected #{QUERY_TEXT.inspect} but got #{q.inspect} on request #{req_num.inspect}!"
|
186
|
+
end
|
187
|
+
worker_write[i].write(RESPONSE_TEXT)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
workers.push f
|
191
|
+
end
|
192
|
+
|
193
|
+
workers.each { |f| f.resume }
|
194
|
+
|
195
|
+
master_fiber = Fiber.new do
|
196
|
+
NUM_WORKERS.times do |worker_num|
|
197
|
+
f = Fiber.new do
|
198
|
+
NUM_REQUESTS.times do |req_num|
|
199
|
+
master_write[worker_num].write(QUERY_TEXT)
|
200
|
+
buffer = master_read[worker_num].read(RESPONSE_TEXT.size)
|
201
|
+
if buffer != RESPONSE_TEXT
|
202
|
+
raise "Error! Fiber no. #{worker_num} on req #{req_num} expected #{RESPONSE_TEXT.inspect} but got #{buf.inspect}!"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
f.resume
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
master_fiber.resume
|
211
|
+
|
212
|
+
# puts "Starting reactor..."
|
213
|
+
reactor.run
|
214
|
+
|
215
|
+
# puts "Done, finished all reactor Fibers!"
|
216
|
+
|
217
|
+
puts Process.times
|
218
|
+
|
219
|
+
# Exit
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require_relative 'task'
|
22
|
+
|
23
|
+
module Async
|
24
|
+
# 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.
|
25
|
+
class Barrier
|
26
|
+
def initialize
|
27
|
+
@tasks = []
|
28
|
+
end
|
29
|
+
|
30
|
+
# All tasks which have been invoked into the barrier.
|
31
|
+
attr :tasks
|
32
|
+
|
33
|
+
def async(*args, parent: Task.current, **options, &block)
|
34
|
+
task = parent.async(*args, **options, &block)
|
35
|
+
|
36
|
+
@tasks << task
|
37
|
+
|
38
|
+
return task
|
39
|
+
end
|
40
|
+
|
41
|
+
def empty?
|
42
|
+
@tasks.empty?
|
43
|
+
end
|
44
|
+
|
45
|
+
def wait
|
46
|
+
while task = @tasks.pop
|
47
|
+
task.wait
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/async/reactor.rb
CHANGED
@@ -116,9 +116,9 @@ module Async
|
|
116
116
|
# When calling an async block, we deterministically execute it until the
|
117
117
|
# first blocking operation. We don't *have* to do this - we could schedule
|
118
118
|
# it for later execution, but it's useful to:
|
119
|
-
# - Fail at the point of call where possible.
|
119
|
+
# - Fail at the point of the method call where possible.
|
120
120
|
# - Execute determinstically where possible.
|
121
|
-
# - Avoid overhead if no blocking operation is performed.
|
121
|
+
# - Avoid scheduler overhead if no blocking operation is performed.
|
122
122
|
task.run(*args)
|
123
123
|
|
124
124
|
# logger.debug "Initial execution of task #{fiber} complete (#{result} -> #{fiber.alive?})..."
|
@@ -168,7 +168,7 @@ module Async
|
|
168
168
|
|
169
169
|
initial_task = self.async(*args, &block) if block_given?
|
170
170
|
|
171
|
-
@
|
171
|
+
until @stopped
|
172
172
|
# logger.debug(self) {"@ready = #{@ready} @running = #{@running}"}
|
173
173
|
|
174
174
|
if @ready.any?
|
@@ -180,17 +180,16 @@ module Async
|
|
180
180
|
end
|
181
181
|
|
182
182
|
@running.clear
|
183
|
-
|
184
|
-
# if there are tasks ready to execute, don't sleep.
|
185
|
-
if @ready.any?
|
186
|
-
interval = 0
|
187
|
-
else
|
188
|
-
# The above tasks may schedule, cancel or affect timers in some way. We need to compute a new wait interval for the blocking selector call below:
|
189
|
-
interval = @timers.wait_interval
|
190
|
-
end
|
191
183
|
end
|
192
184
|
|
193
|
-
|
185
|
+
if @ready.empty?
|
186
|
+
interval = @timers.wait_interval
|
187
|
+
else
|
188
|
+
# if there are tasks ready to execute, don't sleep:
|
189
|
+
interval = 0
|
190
|
+
end
|
191
|
+
|
192
|
+
# If there is no interval to wait (thus no timers), and no tasks, we could be done:
|
194
193
|
if interval.nil?
|
195
194
|
if self.finished?
|
196
195
|
# If there is nothing to do, then finish:
|
@@ -207,7 +206,9 @@ module Async
|
|
207
206
|
monitor.value.resume
|
208
207
|
end
|
209
208
|
end
|
210
|
-
|
209
|
+
|
210
|
+
@timers.fire
|
211
|
+
end
|
211
212
|
|
212
213
|
return initial_task
|
213
214
|
ensure
|
data/lib/async/semaphore.rb
CHANGED
@@ -47,12 +47,10 @@ module Async
|
|
47
47
|
end
|
48
48
|
|
49
49
|
# Run an async task. Will wait until the semaphore is ready until spawning and running the task.
|
50
|
-
def async(*args)
|
51
|
-
parent = Task.current
|
52
|
-
|
50
|
+
def async(*args, parent: Task.current, **options)
|
53
51
|
wait
|
54
52
|
|
55
|
-
parent.async do |task|
|
53
|
+
parent.async(**options) do |task|
|
56
54
|
@count += 1
|
57
55
|
|
58
56
|
begin
|
data/lib/async/version.rb
CHANGED
@@ -0,0 +1,77 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'async/barrier'
|
22
|
+
require 'async/clock'
|
23
|
+
require 'async/rspec'
|
24
|
+
|
25
|
+
RSpec.describe Async::Barrier do
|
26
|
+
include_context Async::RSpec::Reactor
|
27
|
+
|
28
|
+
context '#async' do
|
29
|
+
let(:repeats) {40}
|
30
|
+
let(:delay) {0.1}
|
31
|
+
|
32
|
+
it 'should wait for all jobs to complete' do
|
33
|
+
finished = 0
|
34
|
+
|
35
|
+
repeats.times.map do |i|
|
36
|
+
subject.async do |task|
|
37
|
+
task.sleep(delay)
|
38
|
+
finished += 1
|
39
|
+
|
40
|
+
# This task is a child task but not part of the barrier.
|
41
|
+
task.async do
|
42
|
+
task.sleep(delay*3)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
expect(subject).to_not be_empty
|
48
|
+
expect(finished).to be < repeats
|
49
|
+
|
50
|
+
duration = Async::Clock.measure{subject.wait}
|
51
|
+
|
52
|
+
expect(duration).to be < (delay * 2)
|
53
|
+
expect(finished).to be == repeats
|
54
|
+
expect(subject).to be_empty
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context '#wait' do
|
59
|
+
it 'should wait for tasks even after exceptions' do
|
60
|
+
task1 = subject.async do
|
61
|
+
raise "Boom"
|
62
|
+
end
|
63
|
+
|
64
|
+
task2 = subject.async do
|
65
|
+
end
|
66
|
+
|
67
|
+
expect(task1).to be_failed
|
68
|
+
expect(task2).to be_finished
|
69
|
+
|
70
|
+
expect{subject.wait}.to raise_exception(/Boom/)
|
71
|
+
|
72
|
+
subject.wait until subject.empty?
|
73
|
+
|
74
|
+
expect(subject).to be_empty
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.22.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nio4r
|
@@ -144,6 +144,8 @@ files:
|
|
144
144
|
- async.gemspec
|
145
145
|
- benchmark/async_vs_lightio.rb
|
146
146
|
- benchmark/fiber_count.rb
|
147
|
+
- benchmark/rubies/README.md
|
148
|
+
- benchmark/rubies/benchmark.rb
|
147
149
|
- benchmark/thread_count.rb
|
148
150
|
- benchmark/thread_vs_fiber.rb
|
149
151
|
- examples/async_method.rb
|
@@ -157,6 +159,7 @@ files:
|
|
157
159
|
- examples/stop/sleep.rb
|
158
160
|
- gems/event.gemfile
|
159
161
|
- lib/async.rb
|
162
|
+
- lib/async/barrier.rb
|
160
163
|
- lib/async/clock.rb
|
161
164
|
- lib/async/condition.rb
|
162
165
|
- lib/async/debug/monitor.rb
|
@@ -176,6 +179,7 @@ files:
|
|
176
179
|
- logo.svg
|
177
180
|
- papers/1982 Grossman.pdf
|
178
181
|
- papers/1987 ODell.pdf
|
182
|
+
- spec/async/barrier_spec.rb
|
179
183
|
- spec/async/clock_spec.rb
|
180
184
|
- spec/async/condition_examples.rb
|
181
185
|
- spec/async/condition_spec.rb
|
@@ -218,6 +222,7 @@ signing_key:
|
|
218
222
|
specification_version: 4
|
219
223
|
summary: Async is an asynchronous I/O framework based on nio4r.
|
220
224
|
test_files:
|
225
|
+
- spec/async/barrier_spec.rb
|
221
226
|
- spec/async/clock_spec.rb
|
222
227
|
- spec/async/condition_examples.rb
|
223
228
|
- spec/async/condition_spec.rb
|