async 1.20.1 → 1.21.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/.github/FUNDING.yml +4 -0
- data/benchmark/fiber_count.rb +9 -0
- data/benchmark/thread_count.rb +8 -0
- data/benchmark/thread_vs_fiber.rb +44 -0
- data/examples/capture/capture.rb +22 -11
- data/examples/queue/producer.rb +27 -0
- data/examples/stop/condition.rb +30 -0
- data/examples/stop/sleep.rb +41 -0
- data/lib/async.rb +3 -7
- data/lib/async/reactor.rb +1 -1
- data/lib/async/task.rb +37 -17
- data/lib/async/version.rb +1 -1
- data/lib/async/wrapper.rb +20 -0
- data/lib/kernel/async.rb +28 -0
- data/lib/kernel/sync.rb +32 -0
- data/spec/async/condition_spec.rb +25 -0
- data/spec/async/node_spec.rb +1 -1
- data/spec/async/performance_spec.rb +20 -0
- data/spec/async/task_spec.rb +22 -1
- data/spec/async_spec.rb +0 -8
- data/spec/kernel/async_spec.rb +31 -0
- data/spec/kernel/sync_spec.rb +49 -0
- metadata +16 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9c9a081ed81fc6b4b0b34ebb5282063574994045189a9544b968c7729543cf1
|
4
|
+
data.tar.gz: df9d9266a02ff86895cfc9934fbafbeeb7159cb1b964f10705323d9f4798ae34
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f5014904c26006fd50462c9161f3ae24f867ed995569bc1faecebe67d3a1b8799eb4a91896121a07885512ae21361be18f9f9ea3614825140cb3596239cc13b
|
7
|
+
data.tar.gz: 26c13f37945ceb4d2542a2ef0736b6fe04cc9515d90053d4b2a00888a5501c008ce614849462b006216a94a1eece3fc781cf378f25f369ae65bceeede12dbfe6
|
data/.github/FUNDING.yml
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'benchmark/ips'
|
24
|
+
|
25
|
+
GC.disable
|
26
|
+
|
27
|
+
Benchmark.ips do |benchmark|
|
28
|
+
benchmark.time = 1
|
29
|
+
benchmark.warmup = 1
|
30
|
+
|
31
|
+
benchmark.report("Thread.new{}") do |count|
|
32
|
+
while count > 0
|
33
|
+
Thread.new{count -= 1}.join
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
benchmark.report("Fiber.new{}") do |count|
|
38
|
+
while count > 0
|
39
|
+
Fiber.new{count -= 1}.resume
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
benchmark.compare!
|
44
|
+
end
|
data/examples/capture/capture.rb
CHANGED
@@ -34,10 +34,10 @@ def parse(value)
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
def strace(pid, duration =
|
37
|
+
def strace(pid, duration = 60)
|
38
38
|
input, output = IO.pipe
|
39
39
|
|
40
|
-
pid = Process.spawn("strace", "-p", pid.to_s, "-cqf", "-w", "-e", "
|
40
|
+
pid = Process.spawn("strace", "-p", pid.to_s, "-cqf", "-w", "-e", "!futex", err: output)
|
41
41
|
|
42
42
|
output.close
|
43
43
|
|
@@ -46,15 +46,21 @@ def strace(pid, duration = 10)
|
|
46
46
|
Signal.trap(:INT, :DEFAULT)
|
47
47
|
end
|
48
48
|
|
49
|
+
Thread.new do
|
50
|
+
sleep duration
|
51
|
+
Process.kill(:INT, pid)
|
52
|
+
end
|
53
|
+
|
49
54
|
summary = {}
|
50
55
|
|
51
|
-
if
|
52
|
-
rule = input.gets # horizontal separator
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
if first_line = input.gets
|
57
|
+
if rule = input.gets # horizontal separator
|
58
|
+
pattern = Regexp.new(
|
59
|
+
rule.split(/\s/).map{|s| "(.{1,#{s.size}})"}.join(' ')
|
60
|
+
)
|
61
|
+
|
62
|
+
header = pattern.match(first_line).captures.map{|key| key.strip.to_sym}
|
63
|
+
end
|
58
64
|
|
59
65
|
while line = input.gets
|
60
66
|
break if line == rule
|
@@ -71,7 +77,11 @@ def strace(pid, duration = 10)
|
|
71
77
|
end
|
72
78
|
end
|
73
79
|
|
74
|
-
Process.
|
80
|
+
_, status = Process.waitpid2(pid)
|
81
|
+
|
82
|
+
Console.logger.error(status) do |buffer|
|
83
|
+
buffer.puts first_line
|
84
|
+
end unless status.success?
|
75
85
|
|
76
86
|
return summary
|
77
87
|
end
|
@@ -80,6 +90,7 @@ pids.each do |pid|
|
|
80
90
|
start_times = getrusage(pid)
|
81
91
|
Console.logger.info("Process #{pid} start times:", start_times)
|
82
92
|
|
93
|
+
# sleep 60
|
83
94
|
summary = strace(pid)
|
84
95
|
|
85
96
|
Console.logger.info("strace -p #{pid}") do |buffer|
|
@@ -94,7 +105,7 @@ pids.each do |pid|
|
|
94
105
|
if total = summary[:total]
|
95
106
|
process_duration = end_times.utime - start_times.utime
|
96
107
|
wait_duration = summary[:total][:seconds]
|
97
|
-
|
108
|
+
|
98
109
|
Console.logger.info("Process Waiting: #{wait_duration.round(4)}s out of #{process_duration.round(4)}s") do |buffer|
|
99
110
|
buffer.puts "Wait percentage: #{(wait_duration / process_duration * 100.0).round(2)}%"
|
100
111
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'async'
|
4
|
+
require 'async/queue'
|
5
|
+
|
6
|
+
Async do
|
7
|
+
# Queue of up to 10 items:
|
8
|
+
items = Async::LimitedQueue.new(10)
|
9
|
+
|
10
|
+
# Five producers:
|
11
|
+
5.times do
|
12
|
+
Async do |task|
|
13
|
+
while true
|
14
|
+
t = rand
|
15
|
+
task.sleep(t)
|
16
|
+
items.enqueue(t)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# A single consumer:
|
22
|
+
Async do |task|
|
23
|
+
while item = items.dequeue
|
24
|
+
puts "dequeue -> #{item}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# require 'async'; require 'async/queue'
|
4
|
+
|
5
|
+
require_relative '../../lib/async'; require_relative '../../lib/async/queue'
|
6
|
+
|
7
|
+
Async do |consumer|
|
8
|
+
consumer.annotate "consumer"
|
9
|
+
condition = Async::Condition.new
|
10
|
+
|
11
|
+
producer = Async do |subtask|
|
12
|
+
subtask.annotate "subtask"
|
13
|
+
|
14
|
+
(1..).each do |value|
|
15
|
+
puts "producer yielding"
|
16
|
+
subtask.yield # (1) Fiber.yield, (3) Reactor -> producer.resume
|
17
|
+
condition.signal(value) # (4) consumer.resume(value)
|
18
|
+
end
|
19
|
+
|
20
|
+
puts "producer exiting"
|
21
|
+
end
|
22
|
+
|
23
|
+
value = condition.wait # (2) value = Fiber.yield
|
24
|
+
puts "producer.stop"
|
25
|
+
producer.stop # (5) [producer is resumed already] producer.stop
|
26
|
+
|
27
|
+
puts "consumer exiting"
|
28
|
+
end
|
29
|
+
|
30
|
+
puts "Done."
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../../lib/async'
|
4
|
+
|
5
|
+
require 'async/http/endpoint'
|
6
|
+
require 'async/http/server'
|
7
|
+
|
8
|
+
require 'async/http/internet'
|
9
|
+
|
10
|
+
# To query the web server:
|
11
|
+
# curl http://localhost:9292/kittens
|
12
|
+
|
13
|
+
Async do |parent|
|
14
|
+
endpoint = Async::HTTP::Endpoint.parse("http://localhost:9292")
|
15
|
+
internet = Async::HTTP::Internet.new
|
16
|
+
|
17
|
+
server = Async::HTTP::Server.for(endpoint) do |request|
|
18
|
+
if request.path =~ /\/(.*)/
|
19
|
+
keyword = $1
|
20
|
+
|
21
|
+
response = internet.get("https://www.google.com/search?q=#{keyword}")
|
22
|
+
|
23
|
+
count = response.read.scan(keyword).size
|
24
|
+
|
25
|
+
Protocol::HTTP::Response[200, [], ["Google found #{count} instance(s) of #{keyword}.\n"]]
|
26
|
+
else
|
27
|
+
Protocol::HTTP::Response[404, [], []]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
tasks = server.run
|
32
|
+
|
33
|
+
#while true
|
34
|
+
parent.sleep(10)
|
35
|
+
parent.reactor.print_hierarchy
|
36
|
+
#end
|
37
|
+
|
38
|
+
parent.stop # -> Async::Stop
|
39
|
+
|
40
|
+
tasks.each(&:stop)
|
41
|
+
end
|
data/lib/async.rb
CHANGED
@@ -22,15 +22,11 @@ require_relative "async/version"
|
|
22
22
|
require_relative "async/logger"
|
23
23
|
require_relative "async/reactor"
|
24
24
|
|
25
|
+
require_relative "kernel/async"
|
26
|
+
|
25
27
|
module Async
|
26
28
|
# Invoke `Reactor.run` with all arguments/block.
|
27
29
|
def self.run(*args, &block)
|
28
30
|
Reactor.run(*args, &block)
|
29
31
|
end
|
30
|
-
end
|
31
|
-
|
32
|
-
module Kernel
|
33
|
-
def Async(*args, &block)
|
34
|
-
Async::Reactor.run(*args, &block)
|
35
|
-
end
|
36
|
-
end
|
32
|
+
end
|
data/lib/async/reactor.rb
CHANGED
@@ -201,7 +201,7 @@ module Async
|
|
201
201
|
interval = 0
|
202
202
|
end
|
203
203
|
|
204
|
-
# logger.debug(self) {"Selecting with #{@children&.
|
204
|
+
# logger.debug(self) {"Selecting with #{@children&.size} children with interval = #{interval.inspect}..."}
|
205
205
|
if monitors = @selector.select(interval)
|
206
206
|
monitors.each do |monitor|
|
207
207
|
monitor.value.resume
|
data/lib/async/task.rb
CHANGED
@@ -27,13 +27,26 @@ require_relative 'condition'
|
|
27
27
|
module Async
|
28
28
|
# Raised when a task is explicitly stopped.
|
29
29
|
class Stop < Exception
|
30
|
+
class Later
|
31
|
+
def initialize(task)
|
32
|
+
@task = task
|
33
|
+
end
|
34
|
+
|
35
|
+
def alive?
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
def resume
|
40
|
+
@task.stop
|
41
|
+
end
|
42
|
+
end
|
30
43
|
end
|
31
44
|
|
32
45
|
# A task represents the state associated with the execution of an asynchronous
|
33
46
|
# block.
|
34
47
|
class Task < Node
|
35
48
|
extend Forwardable
|
36
|
-
|
49
|
+
|
37
50
|
# Yield the unerlying `result` for the task. If the result
|
38
51
|
# is an Exception, then that result will be raised an its
|
39
52
|
# exception.
|
@@ -85,7 +98,7 @@ module Async
|
|
85
98
|
|
86
99
|
# Yield back to the reactor and allow other fibers to execute.
|
87
100
|
def yield
|
88
|
-
reactor.yield
|
101
|
+
Task.yield{reactor.yield}
|
89
102
|
end
|
90
103
|
|
91
104
|
# @attr fiber [Fiber] The fiber which is being used for the execution of this task.
|
@@ -137,29 +150,34 @@ module Async
|
|
137
150
|
# Stop the task and all of its children.
|
138
151
|
# @return [void]
|
139
152
|
def stop
|
140
|
-
if self.
|
141
|
-
# If we
|
142
|
-
return
|
143
|
-
|
144
|
-
|
145
|
-
|
153
|
+
if self.stopped?
|
154
|
+
# If we already stopped this task... don't try to stop it again:
|
155
|
+
return
|
156
|
+
end
|
157
|
+
|
158
|
+
if self.running?
|
146
159
|
if self.current?
|
147
160
|
raise Stop, "Stopping current fiber!"
|
148
161
|
elsif @fiber&.alive?
|
149
|
-
|
162
|
+
begin
|
163
|
+
@fiber.resume(Stop.new)
|
164
|
+
rescue FiberError
|
165
|
+
@reactor << Stop::Later.new(self)
|
166
|
+
end
|
150
167
|
end
|
168
|
+
else
|
169
|
+
# We are not running, but children might be, so transition directly into stopped state:
|
170
|
+
stop!
|
151
171
|
end
|
152
|
-
ensure
|
153
|
-
@children&.each(&:stop)
|
154
172
|
end
|
155
|
-
|
173
|
+
|
156
174
|
# Lookup the {Task} for the current fiber. Raise `RuntimeError` if none is available.
|
157
175
|
# @return [Async::Task]
|
158
176
|
# @raise [RuntimeError] if task was not {set!} for the current fiber.
|
159
177
|
def self.current
|
160
178
|
Thread.current[:async_task] or raise RuntimeError, "No async task available!"
|
161
179
|
end
|
162
|
-
|
180
|
+
|
163
181
|
# Check if there is a task defined for the current fiber.
|
164
182
|
# @return [Async::Task, nil]
|
165
183
|
def self.current?
|
@@ -175,7 +193,7 @@ module Async
|
|
175
193
|
def running?
|
176
194
|
@status == :running
|
177
195
|
end
|
178
|
-
|
196
|
+
|
179
197
|
# Whether we can remove this node from the reactor graph.
|
180
198
|
# @return [Boolean]
|
181
199
|
def finished?
|
@@ -217,7 +235,9 @@ module Async
|
|
217
235
|
end
|
218
236
|
|
219
237
|
def stop!
|
238
|
+
# logger.debug(self) {"Task was stopped with #{@children.size} children!"}
|
220
239
|
@status = :stopped
|
240
|
+
@children&.each(&:stop)
|
221
241
|
end
|
222
242
|
|
223
243
|
def make_fiber(&block)
|
@@ -227,7 +247,7 @@ module Async
|
|
227
247
|
begin
|
228
248
|
@result = yield(self, *args)
|
229
249
|
@status = :complete
|
230
|
-
# logger.debug("Task #{
|
250
|
+
# logger.debug(self) {"Task was completed with #{@children.size} children!"}
|
231
251
|
rescue Stop
|
232
252
|
stop!
|
233
253
|
rescue StandardError => error
|
@@ -235,7 +255,7 @@ module Async
|
|
235
255
|
rescue Exception => exception
|
236
256
|
fail!(exception, true)
|
237
257
|
ensure
|
238
|
-
# logger.debug("Task
|
258
|
+
# logger.debug(self) {"Task ensure $!=#{$!} with #{@children.size} children!"}
|
239
259
|
finish!
|
240
260
|
end
|
241
261
|
end
|
@@ -254,7 +274,7 @@ module Async
|
|
254
274
|
@finished.signal(@result)
|
255
275
|
end
|
256
276
|
end
|
257
|
-
|
277
|
+
|
258
278
|
# Set the current fiber's `:async_task` to this task.
|
259
279
|
def set!
|
260
280
|
# This is actually fiber-local:
|
data/lib/async/version.rb
CHANGED
data/lib/async/wrapper.rb
CHANGED
@@ -24,9 +24,29 @@ module Async
|
|
24
24
|
# Represents an asynchronous IO within a reactor.
|
25
25
|
class Wrapper
|
26
26
|
class Cancelled < StandardError
|
27
|
+
class From
|
28
|
+
def initialize
|
29
|
+
@backtrace = caller[5..-1]
|
30
|
+
end
|
31
|
+
|
32
|
+
attr :backtrace
|
33
|
+
|
34
|
+
def cause
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def message
|
39
|
+
"Cancelled"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
27
43
|
def initialize
|
28
44
|
super "The operation has been cancelled!"
|
45
|
+
|
46
|
+
@cause = From.new
|
29
47
|
end
|
48
|
+
|
49
|
+
attr :cause
|
30
50
|
end
|
31
51
|
|
32
52
|
# wait_readable, wait_writable and wait_any are not re-entrant, and will raise this failure.
|
data/lib/kernel/async.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Copyright, 2017, 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 "../async/reactor"
|
22
|
+
|
23
|
+
module Kernel
|
24
|
+
# Run the given block of code in a task, asynchronously, creating a reactor if necessary.
|
25
|
+
def Async(*args, &block)
|
26
|
+
::Async::Reactor.run(*args, &block)
|
27
|
+
end
|
28
|
+
end
|
data/lib/kernel/sync.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Copyright, 2017, 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 "../async/reactor"
|
22
|
+
|
23
|
+
module Kernel
|
24
|
+
# Run the given block of code synchronously, but within a reactor if not already in one.
|
25
|
+
def Sync(&block)
|
26
|
+
if task = ::Async::Task.current?
|
27
|
+
yield
|
28
|
+
else
|
29
|
+
::Async::Reactor.run(&block).wait
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -41,5 +41,30 @@ RSpec.describe Async::Condition do
|
|
41
41
|
task.stop
|
42
42
|
end
|
43
43
|
|
44
|
+
it 'can stop nested task' do
|
45
|
+
producer = nil
|
46
|
+
|
47
|
+
consumer = reactor.async do |task|
|
48
|
+
condition = Async::Condition.new
|
49
|
+
|
50
|
+
producer = task.async do |subtask|
|
51
|
+
subtask.yield
|
52
|
+
condition.signal
|
53
|
+
subtask.sleep(10)
|
54
|
+
end
|
55
|
+
|
56
|
+
condition.wait
|
57
|
+
expect do
|
58
|
+
producer.stop
|
59
|
+
end.to_not raise_error
|
60
|
+
end
|
61
|
+
|
62
|
+
consumer.wait
|
63
|
+
producer.wait
|
64
|
+
|
65
|
+
expect(producer.status).to be :stopped
|
66
|
+
expect(consumer.status).to be :complete
|
67
|
+
end
|
68
|
+
|
44
69
|
it_behaves_like Async::Condition
|
45
70
|
end
|
data/spec/async/node_spec.rb
CHANGED
@@ -54,7 +54,7 @@ RSpec.describe Async::Node do
|
|
54
54
|
it "can print hierarchy to bufffer" do
|
55
55
|
subject.print_hierarchy(buffer)
|
56
56
|
|
57
|
-
expect(lines.
|
57
|
+
expect(lines.size).to be 2
|
58
58
|
|
59
59
|
expect(lines[0]).to be =~ /#<Async::Node:0x\h+>\n/
|
60
60
|
expect(lines[1]).to be =~ /\t#<Async::Node:0x\h+>\n/
|
@@ -1,5 +1,25 @@
|
|
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.
|
1
20
|
|
2
21
|
require 'benchmark/ips'
|
22
|
+
require 'async'
|
3
23
|
|
4
24
|
RSpec.describe Async::Wrapper do
|
5
25
|
let(:pipe) {IO.pipe}
|
data/spec/async/task_spec.rb
CHANGED
@@ -181,7 +181,7 @@ RSpec.describe Async::Task do
|
|
181
181
|
expect(task).to be_stopped
|
182
182
|
end
|
183
183
|
|
184
|
-
it "should
|
184
|
+
it "should stop direct child" do
|
185
185
|
parent_task = child_task = nil
|
186
186
|
|
187
187
|
reactor.async do |task|
|
@@ -221,6 +221,27 @@ RSpec.describe Async::Task do
|
|
221
221
|
bottom_task.stop
|
222
222
|
expect(top_task.children).to include(middle_task)
|
223
223
|
end
|
224
|
+
|
225
|
+
it "can stop resumed task" do
|
226
|
+
items = [1, 2, 3]
|
227
|
+
|
228
|
+
Async do
|
229
|
+
condition = Async::Condition.new
|
230
|
+
|
231
|
+
producer = Async do |subtask|
|
232
|
+
while item = items.pop
|
233
|
+
subtask.yield # (1) Fiber.yield, (3) Reactor -> producer.resume
|
234
|
+
condition.signal(item) # (4) consumer.resume(value)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
value = condition.wait # (2) value = Fiber.yield
|
239
|
+
expect(value).to be == 3
|
240
|
+
producer.stop # (5) [producer is resumed already] producer.stop
|
241
|
+
end
|
242
|
+
|
243
|
+
expect(items).to be == [1]
|
244
|
+
end
|
224
245
|
end
|
225
246
|
|
226
247
|
describe '#sleep' do
|
data/spec/async_spec.rb
CHANGED
@@ -21,14 +21,6 @@
|
|
21
21
|
require 'async'
|
22
22
|
|
23
23
|
RSpec.describe Async do
|
24
|
-
describe '#Async' do
|
25
|
-
it "can run an asynchronous task" do
|
26
|
-
Async do |task|
|
27
|
-
expect(task).to be_a Async::Task
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
24
|
describe '.run' do
|
33
25
|
it "can run an asynchronous task" do
|
34
26
|
Async.run do |task|
|
@@ -0,0 +1,31 @@
|
|
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 'kernel/async'
|
22
|
+
|
23
|
+
RSpec.describe Async do
|
24
|
+
describe '#Async' do
|
25
|
+
it "can run an asynchronous task" do
|
26
|
+
Async do |task|
|
27
|
+
expect(task).to be_a Async::Task
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,49 @@
|
|
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 'kernel/sync'
|
22
|
+
|
23
|
+
RSpec.describe Kernel do
|
24
|
+
describe '#Sync' do
|
25
|
+
let(:value) {10}
|
26
|
+
|
27
|
+
it "can run a synchronous task" do
|
28
|
+
result = Sync do
|
29
|
+
expect(Async::Task.current).to_not be nil
|
30
|
+
|
31
|
+
next value
|
32
|
+
end
|
33
|
+
|
34
|
+
expect(result).to be == value
|
35
|
+
end
|
36
|
+
|
37
|
+
it "can run inside reactor" do
|
38
|
+
Async do |task|
|
39
|
+
result = Sync do
|
40
|
+
expect(Async::Task.current).to be task
|
41
|
+
|
42
|
+
next value
|
43
|
+
end
|
44
|
+
|
45
|
+
expect(result).to be == value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
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.21.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-09-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nio4r
|
@@ -132,6 +132,7 @@ extensions: []
|
|
132
132
|
extra_rdoc_files: []
|
133
133
|
files:
|
134
134
|
- ".editorconfig"
|
135
|
+
- ".github/FUNDING.yml"
|
135
136
|
- ".gitignore"
|
136
137
|
- ".rspec"
|
137
138
|
- ".travis.yml"
|
@@ -142,12 +143,18 @@ files:
|
|
142
143
|
- Rakefile
|
143
144
|
- async.gemspec
|
144
145
|
- benchmark/async_vs_lightio.rb
|
146
|
+
- benchmark/fiber_count.rb
|
147
|
+
- benchmark/thread_count.rb
|
148
|
+
- benchmark/thread_vs_fiber.rb
|
145
149
|
- examples/async_method.rb
|
146
150
|
- examples/callback/loop.rb
|
147
151
|
- examples/capture/README.md
|
148
152
|
- examples/capture/capture.rb
|
149
153
|
- examples/fibers.rb
|
154
|
+
- examples/queue/producer.rb
|
150
155
|
- examples/sleep_sort.rb
|
156
|
+
- examples/stop/condition.rb
|
157
|
+
- examples/stop/sleep.rb
|
151
158
|
- gems/event.gemfile
|
152
159
|
- lib/async.rb
|
153
160
|
- lib/async/clock.rb
|
@@ -163,6 +170,8 @@ files:
|
|
163
170
|
- lib/async/task.rb
|
164
171
|
- lib/async/version.rb
|
165
172
|
- lib/async/wrapper.rb
|
173
|
+
- lib/kernel/async.rb
|
174
|
+
- lib/kernel/sync.rb
|
166
175
|
- logo.png
|
167
176
|
- logo.svg
|
168
177
|
- papers/1982 Grossman.pdf
|
@@ -182,6 +191,8 @@ files:
|
|
182
191
|
- spec/async/wrapper_spec.rb
|
183
192
|
- spec/async_spec.rb
|
184
193
|
- spec/enumerator_spec.rb
|
194
|
+
- spec/kernel/async_spec.rb
|
195
|
+
- spec/kernel/sync_spec.rb
|
185
196
|
- spec/spec_helper.rb
|
186
197
|
homepage: https://github.com/socketry/async
|
187
198
|
licenses:
|
@@ -202,7 +213,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
202
213
|
- !ruby/object:Gem::Version
|
203
214
|
version: '0'
|
204
215
|
requirements: []
|
205
|
-
rubygems_version: 3.0.
|
216
|
+
rubygems_version: 3.0.4
|
206
217
|
signing_key:
|
207
218
|
specification_version: 4
|
208
219
|
summary: Async is an asynchronous I/O framework based on nio4r.
|
@@ -222,4 +233,6 @@ test_files:
|
|
222
233
|
- spec/async/wrapper_spec.rb
|
223
234
|
- spec/async_spec.rb
|
224
235
|
- spec/enumerator_spec.rb
|
236
|
+
- spec/kernel/async_spec.rb
|
237
|
+
- spec/kernel/sync_spec.rb
|
225
238
|
- spec/spec_helper.rb
|