async 1.25.2 → 1.28.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/async/barrier.rb +1 -1
- data/lib/async/clock.rb +33 -1
- data/lib/async/logger.rb +1 -6
- data/lib/async/node.rb +20 -2
- data/lib/async/queue.rb +5 -1
- data/lib/async/reactor.rb +73 -12
- data/lib/async/scheduler.rb +112 -0
- data/lib/async/task.rb +11 -3
- data/lib/async/version.rb +1 -1
- metadata +46 -104
- data/.editorconfig +0 -6
- data/.github/workflows/development.yml +0 -55
- data/.gitignore +0 -14
- data/.rspec +0 -3
- data/.yardopts +0 -1
- data/Gemfile +0 -20
- data/Guardfile +0 -14
- data/README.md +0 -385
- data/Rakefile +0 -40
- data/async.gemspec +0 -34
- data/bake.rb +0 -33
- data/benchmark/async_vs_lightio.rb +0 -84
- data/benchmark/fiber_count.rb +0 -10
- data/benchmark/rubies/README.md +0 -51
- data/benchmark/rubies/benchmark.rb +0 -220
- data/benchmark/thread_count.rb +0 -9
- data/benchmark/thread_vs_fiber.rb +0 -45
- data/examples/async_method.rb +0 -60
- data/examples/callback/loop.rb +0 -44
- data/examples/capture/README.md +0 -59
- data/examples/capture/capture.rb +0 -116
- data/examples/fibers.rb +0 -178
- data/examples/queue/producer.rb +0 -28
- data/examples/sleep_sort.rb +0 -40
- data/examples/stop/condition.rb +0 -31
- data/examples/stop/sleep.rb +0 -42
- data/gems/event.gemfile +0 -4
- data/logo.png +0 -0
- data/logo.svg +0 -64
- data/papers/1982 Grossman.pdf +0 -0
- data/papers/1987 ODell.pdf +0 -0
- data/spec/async/barrier_spec.rb +0 -116
- data/spec/async/chainable_async_examples.rb +0 -13
- data/spec/async/clock_spec.rb +0 -37
- data/spec/async/condition_examples.rb +0 -105
- data/spec/async/condition_spec.rb +0 -72
- data/spec/async/logger_spec.rb +0 -65
- data/spec/async/node_spec.rb +0 -193
- data/spec/async/notification_spec.rb +0 -66
- data/spec/async/performance_spec.rb +0 -72
- data/spec/async/queue_spec.rb +0 -129
- data/spec/async/reactor/nested_spec.rb +0 -52
- data/spec/async/reactor_spec.rb +0 -253
- data/spec/async/semaphore_spec.rb +0 -169
- data/spec/async/task_spec.rb +0 -476
- data/spec/async/wrapper_spec.rb +0 -203
- data/spec/async_spec.rb +0 -33
- data/spec/enumerator_spec.rb +0 -83
- data/spec/kernel/async_spec.rb +0 -33
- data/spec/kernel/sync_spec.rb +0 -54
- data/spec/spec_helper.rb +0 -18
data/benchmark/thread_count.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
5
|
-
#
|
6
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
-
# of this software and associated documentation files (the "Software"), to deal
|
8
|
-
# in the Software without restriction, including without limitation the rights
|
9
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
-
# copies of the Software, and to permit persons to whom the Software is
|
11
|
-
# furnished to do so, subject to the following conditions:
|
12
|
-
#
|
13
|
-
# The above copyright notice and this permission notice shall be included in
|
14
|
-
# all copies or substantial portions of the Software.
|
15
|
-
#
|
16
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
-
# THE SOFTWARE.
|
23
|
-
|
24
|
-
require 'benchmark/ips'
|
25
|
-
|
26
|
-
GC.disable
|
27
|
-
|
28
|
-
Benchmark.ips do |benchmark|
|
29
|
-
benchmark.time = 1
|
30
|
-
benchmark.warmup = 1
|
31
|
-
|
32
|
-
benchmark.report("Thread.new{}") do |count|
|
33
|
-
while count > 0
|
34
|
-
Thread.new{count -= 1}.join
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
benchmark.report("Fiber.new{}") do |count|
|
39
|
-
while count > 0
|
40
|
-
Fiber.new{count -= 1}.resume
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
benchmark.compare!
|
45
|
-
end
|
data/examples/async_method.rb
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require_relative '../lib/async'
|
5
|
-
|
6
|
-
module Async::Methods
|
7
|
-
def sleep(*args)
|
8
|
-
Async::Task.current.sleep(*args)
|
9
|
-
end
|
10
|
-
|
11
|
-
def async(name)
|
12
|
-
original_method = self.method(name)
|
13
|
-
|
14
|
-
define_method(name) do |*args|
|
15
|
-
Async::Reactor.run do |task|
|
16
|
-
original_method.call(*args)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def await(&block)
|
22
|
-
block.call.wait
|
23
|
-
end
|
24
|
-
|
25
|
-
def barrier!
|
26
|
-
Async::Task.current.children.each(&:wait)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
include Async::Methods
|
31
|
-
|
32
|
-
async def count_chickens(area_name)
|
33
|
-
3.times do |i|
|
34
|
-
sleep rand
|
35
|
-
|
36
|
-
puts "Found a chicken in the #{area_name}!"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
async def find_chicken(areas)
|
41
|
-
puts "Searching for chicken..."
|
42
|
-
|
43
|
-
sleep rand * 5
|
44
|
-
|
45
|
-
return areas.sample
|
46
|
-
end
|
47
|
-
|
48
|
-
async def count_all_chckens
|
49
|
-
# These methods all run at the same time.
|
50
|
-
count_chickens("garden")
|
51
|
-
count_chickens("house")
|
52
|
-
count_chickens("tree")
|
53
|
-
|
54
|
-
# Wait for all previous async work to complete...
|
55
|
-
barrier!
|
56
|
-
|
57
|
-
puts "There was a chicken in the #{find_chicken(["garden", "house", "tree"]).wait}"
|
58
|
-
end
|
59
|
-
|
60
|
-
count_all_chckens
|
data/examples/callback/loop.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'async/reactor'
|
4
|
-
|
5
|
-
class Callback
|
6
|
-
def initialize
|
7
|
-
@reactor = Async::Reactor.new
|
8
|
-
end
|
9
|
-
|
10
|
-
def close
|
11
|
-
@reactor.close
|
12
|
-
end
|
13
|
-
|
14
|
-
# If duration is 0, it will happen immediately after the task is started.
|
15
|
-
def run(duration = 0)
|
16
|
-
@reactor.run do |task|
|
17
|
-
@reactor.after(duration) do
|
18
|
-
@reactor.stop
|
19
|
-
end
|
20
|
-
|
21
|
-
yield(task) if block_given?
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
|
27
|
-
callback = Callback.new
|
28
|
-
|
29
|
-
begin
|
30
|
-
callback.run do |task|
|
31
|
-
while true
|
32
|
-
task.sleep(2)
|
33
|
-
puts "Hello from task!"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
while true
|
38
|
-
callback.run(0)
|
39
|
-
puts "Sleeping for 1 second"
|
40
|
-
sleep(1)
|
41
|
-
end
|
42
|
-
ensure
|
43
|
-
callback.close
|
44
|
-
end
|
data/examples/capture/README.md
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
# Capture
|
2
|
-
|
3
|
-
## Falcon
|
4
|
-
|
5
|
-
```
|
6
|
-
% wrk -t 8 -c 32 http://localhost:9292/
|
7
|
-
Running 10s test @ http://localhost:9292/
|
8
|
-
8 threads and 32 connections
|
9
|
-
Thread Stats Avg Stdev Max +/- Stdev
|
10
|
-
Latency 106.31ms 10.20ms 211.79ms 98.00%
|
11
|
-
Req/Sec 37.94 5.43 40.00 84.24%
|
12
|
-
3003 requests in 10.01s, 170.16KB read
|
13
|
-
Requests/sec: 299.98
|
14
|
-
Transfer/sec: 17.00KB
|
15
|
-
```
|
16
|
-
|
17
|
-
```
|
18
|
-
0.0s: Process 28065 start times:
|
19
|
-
| #<struct Process::Tms utime=2.38, stime=0.0, cutime=0.0, cstime=0.2>
|
20
|
-
^C15.11s: strace -p 28065
|
21
|
-
| ["sendto", {:"% time"=>57.34, :seconds=>0.595047, :"usecs/call"=>14, :calls=>39716, :errors=>32, :syscall=>"sendto"}]
|
22
|
-
| ["recvfrom", {:"% time"=>42.58, :seconds=>0.441867, :"usecs/call"=>12, :calls=>36718, :errors=>70, :syscall=>"recvfrom"}]
|
23
|
-
| ["read", {:"% time"=>0.07, :seconds=>0.000723, :"usecs/call"=>7, :calls=>98, :errors=>nil, :syscall=>"read"}]
|
24
|
-
| ["write", {:"% time"=>0.01, :seconds=>0.000112, :"usecs/call"=>56, :calls=>2, :errors=>nil, :syscall=>"write"}]
|
25
|
-
| [:total, {:"% time"=>100.0, :seconds=>1.037749, :"usecs/call"=>nil, :calls=>76534, :errors=>102, :syscall=>"total"}]
|
26
|
-
15.11s: Process 28065 end times:
|
27
|
-
| #<struct Process::Tms utime=3.93, stime=0.0, cutime=0.0, cstime=0.2>
|
28
|
-
15.11s: Process Waiting: 1.0377s out of 1.55s
|
29
|
-
| Wait percentage: 66.95%
|
30
|
-
```
|
31
|
-
|
32
|
-
## Puma
|
33
|
-
|
34
|
-
```
|
35
|
-
wrk -t 8 -c 32 http://localhost:9292/
|
36
|
-
Running 10s test @ http://localhost:9292/
|
37
|
-
8 threads and 32 connections
|
38
|
-
Thread Stats Avg Stdev Max +/- Stdev
|
39
|
-
Latency 108.83ms 3.50ms 146.38ms 86.58%
|
40
|
-
Req/Sec 34.43 6.70 40.00 92.68%
|
41
|
-
1371 requests in 10.01s, 81.67KB read
|
42
|
-
Requests/sec: 136.94
|
43
|
-
Transfer/sec: 8.16KB
|
44
|
-
```
|
45
|
-
|
46
|
-
```
|
47
|
-
0.0s: Process 28448 start times:
|
48
|
-
| #<struct Process::Tms utime=0.63, stime=0.0, cutime=0.0, cstime=0.2>
|
49
|
-
^C24.89s: strace -p 28448
|
50
|
-
| ["recvfrom", {:"% time"=>64.65, :seconds=>0.595275, :"usecs/call"=>13, :calls=>44476, :errors=>27769, :syscall=>"recvfrom"}]
|
51
|
-
| ["sendto", {:"% time"=>30.68, :seconds=>0.282467, :"usecs/call"=>18, :calls=>15288, :errors=>nil, :syscall=>"sendto"}]
|
52
|
-
| ["write", {:"% time"=>4.66, :seconds=>0.042921, :"usecs/call"=>15, :calls=>2772, :errors=>nil, :syscall=>"write"}]
|
53
|
-
| ["read", {:"% time"=>0.02, :seconds=>0.000157, :"usecs/call"=>8, :calls=>19, :errors=>1, :syscall=>"read"}]
|
54
|
-
| [:total, {:"% time"=>100.0, :seconds=>0.92082, :"usecs/call"=>nil, :calls=>62555, :errors=>27770, :syscall=>"total"}]
|
55
|
-
24.89s: Process 28448 end times:
|
56
|
-
| #<struct Process::Tms utime=3.19, stime=0.0, cutime=0.0, cstime=0.2>
|
57
|
-
24.89s: Process Waiting: 0.9208s out of 2.56s
|
58
|
-
| Wait percentage: 35.97%
|
59
|
-
```
|
data/examples/capture/capture.rb
DELETED
@@ -1,116 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require 'irb'
|
5
|
-
require 'console'
|
6
|
-
|
7
|
-
pids = ARGV.collect(&:to_i)
|
8
|
-
|
9
|
-
TICKS = Process.clock_getres(:TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID, :hertz).to_f
|
10
|
-
|
11
|
-
def getrusage(pid)
|
12
|
-
fields = File.read("/proc/#{pid}/stat").split(/\s+/)
|
13
|
-
|
14
|
-
return Process::Tms.new(
|
15
|
-
fields[14].to_f / TICKS,
|
16
|
-
fields[15].to_f / TICKS,
|
17
|
-
fields[16].to_f / TICKS,
|
18
|
-
fields[17].to_f / TICKS,
|
19
|
-
)
|
20
|
-
end
|
21
|
-
|
22
|
-
def parse(value)
|
23
|
-
case value
|
24
|
-
when /^\s*\d+\.\d+/
|
25
|
-
Float(value)
|
26
|
-
when /^\s*\d+/
|
27
|
-
Integer(value)
|
28
|
-
else
|
29
|
-
value = value.strip
|
30
|
-
if value.empty?
|
31
|
-
nil
|
32
|
-
else
|
33
|
-
value
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def strace(pid, duration = 60)
|
39
|
-
input, output = IO.pipe
|
40
|
-
|
41
|
-
pid = Process.spawn("strace", "-p", pid.to_s, "-cqf", "-w", "-e", "!futex", err: output)
|
42
|
-
|
43
|
-
output.close
|
44
|
-
|
45
|
-
Signal.trap(:INT) do
|
46
|
-
Process.kill(:INT, pid)
|
47
|
-
Signal.trap(:INT, :DEFAULT)
|
48
|
-
end
|
49
|
-
|
50
|
-
Thread.new do
|
51
|
-
sleep duration
|
52
|
-
Process.kill(:INT, pid)
|
53
|
-
end
|
54
|
-
|
55
|
-
summary = {}
|
56
|
-
|
57
|
-
if first_line = input.gets
|
58
|
-
if rule = input.gets # horizontal separator
|
59
|
-
pattern = Regexp.new(
|
60
|
-
rule.split(/\s/).map{|s| "(.{1,#{s.size}})"}.join(' ')
|
61
|
-
)
|
62
|
-
|
63
|
-
header = pattern.match(first_line).captures.map{|key| key.strip.to_sym}
|
64
|
-
end
|
65
|
-
|
66
|
-
while line = input.gets
|
67
|
-
break if line == rule
|
68
|
-
row = pattern.match(line).captures.map{|value| parse(value)}
|
69
|
-
fields = header.zip(row).to_h
|
70
|
-
|
71
|
-
summary[fields[:syscall]] = fields
|
72
|
-
end
|
73
|
-
|
74
|
-
if line = input.gets
|
75
|
-
row = pattern.match(line).captures.map{|value| parse(value)}
|
76
|
-
fields = header.zip(row).to_h
|
77
|
-
summary[:total] = fields
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
_, status = Process.waitpid2(pid)
|
82
|
-
|
83
|
-
Console.logger.error(status) do |buffer|
|
84
|
-
buffer.puts first_line
|
85
|
-
end unless status.success?
|
86
|
-
|
87
|
-
return summary
|
88
|
-
end
|
89
|
-
|
90
|
-
pids.each do |pid|
|
91
|
-
start_times = getrusage(pid)
|
92
|
-
Console.logger.info("Process #{pid} start times:", start_times)
|
93
|
-
|
94
|
-
# sleep 60
|
95
|
-
summary = strace(pid)
|
96
|
-
|
97
|
-
Console.logger.info("strace -p #{pid}") do |buffer|
|
98
|
-
summary.each do |fields|
|
99
|
-
buffer.puts fields.inspect
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
end_times = getrusage(pid)
|
104
|
-
Console.logger.info("Process #{pid} end times:", end_times)
|
105
|
-
|
106
|
-
if total = summary[:total]
|
107
|
-
process_duration = end_times.utime - start_times.utime
|
108
|
-
wait_duration = summary[:total][:seconds]
|
109
|
-
|
110
|
-
Console.logger.info("Process Waiting: #{wait_duration.round(4)}s out of #{process_duration.round(4)}s") do |buffer|
|
111
|
-
buffer.puts "Wait percentage: #{(wait_duration / process_duration * 100.0).round(2)}%"
|
112
|
-
end
|
113
|
-
else
|
114
|
-
Console.logger.warn("No system calls detected.")
|
115
|
-
end
|
116
|
-
end
|
data/examples/fibers.rb
DELETED
@@ -1,178 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'fiber'
|
4
|
-
|
5
|
-
class IO
|
6
|
-
READABLE = 1
|
7
|
-
WRITABLE = 2
|
8
|
-
|
9
|
-
# rb_wait_for_single_fd (int fd, int events, struct timeval *tv)
|
10
|
-
def self.wait(descriptor, events, duration)
|
11
|
-
fiber = Fiber.current
|
12
|
-
reactor = fiber.reactor
|
13
|
-
|
14
|
-
monitor = reactor.add_io(fiber, descriptor, state)
|
15
|
-
|
16
|
-
fiber.with_timeout(duration) do
|
17
|
-
result = Fiber.yield
|
18
|
-
raise result if result.is_a? Exception
|
19
|
-
end
|
20
|
-
|
21
|
-
return result
|
22
|
-
ensure
|
23
|
-
reactor.remove_io(monitor)
|
24
|
-
end
|
25
|
-
|
26
|
-
def wait_readable(duration = nil)
|
27
|
-
wait_any(READABLE)
|
28
|
-
end
|
29
|
-
|
30
|
-
def wait_writable(duration = nil)
|
31
|
-
wait_any(WRITABLE)
|
32
|
-
end
|
33
|
-
|
34
|
-
def wait_until(events = READABLE|WRITABLE, duration = nil)
|
35
|
-
IO.wait_for_io(self.fileno, events, duration)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
class Fiber
|
40
|
-
# Raised when a task times out.
|
41
|
-
class TimeoutError < RuntimeError
|
42
|
-
end
|
43
|
-
|
44
|
-
# This should be inherited by nested fibers.
|
45
|
-
attr :reactor
|
46
|
-
|
47
|
-
def timeout(duration)
|
48
|
-
reactor = self.reactor
|
49
|
-
backtrace = caller
|
50
|
-
|
51
|
-
timer = reactor.add_timer(duration) do
|
52
|
-
if self.alive?
|
53
|
-
error = Fiber::TimeoutError.new("execution expired")
|
54
|
-
error.set_backtrace backtrace
|
55
|
-
self.resume error
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
yield
|
60
|
-
ensure
|
61
|
-
reactor.cancel_timer(timer)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# Can be standard implementation, but could also be provided by external gem/library.
|
66
|
-
class Fiber::Reactor
|
67
|
-
# Add IO to the reactor. The reactor will call `fiber.resume` when the event is triggered.
|
68
|
-
# Returns an opaque monitor object which can be passed to `remove_io` to stop waiting for events.
|
69
|
-
def add_io(fiber, io, state)
|
70
|
-
# The motivation for add_io and remove_io is that it's how a lot of the underlying APIs work, where remove_io just takes the file descriptor.
|
71
|
-
# It also avoids the need for any memory allocation, and maps well to how it's typically used (i.e. in an implementation of `IO#read`).
|
72
|
-
# An efficient implementation might do it's job and then just:
|
73
|
-
return io
|
74
|
-
end
|
75
|
-
|
76
|
-
def remove_io(monitor)
|
77
|
-
end
|
78
|
-
|
79
|
-
# The reactor will call the block at some point after duration time has elapsed.
|
80
|
-
# Returns an opaque timer object which can be passed to `cancel_timer` to avoid this happening.
|
81
|
-
def add_timer(duration, &block)
|
82
|
-
end
|
83
|
-
|
84
|
-
def cancel_timer(timer)
|
85
|
-
end
|
86
|
-
|
87
|
-
# Run until idle (no registered io/timers), or duration time has passed if specified.
|
88
|
-
def run(duration = nil)
|
89
|
-
end
|
90
|
-
|
91
|
-
# Stop the reactor as soon as possible. Can be called from another thread.
|
92
|
-
def stop
|
93
|
-
end
|
94
|
-
|
95
|
-
def close
|
96
|
-
# Close the reactor so it can no longer be used.
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# Basic non-blocking task:
|
101
|
-
reactor = Fiber::Reactor.new
|
102
|
-
|
103
|
-
# User could provide their own reactor, it might even do other things, but the basic interface above should continue to work.
|
104
|
-
|
105
|
-
Fiber.new(reactor: reactor) do
|
106
|
-
# Blocking operations call Fiber.yield, which goes to...
|
107
|
-
end.resume
|
108
|
-
|
109
|
-
# ...here, which starts running the reactor which can also be controlled (e.g. duration, stopping)
|
110
|
-
reactor.run
|
111
|
-
|
112
|
-
# Here is a rough outline of the reactor concept implementation using NIO4R
|
113
|
-
# Can be standard implementation, but could also be provided by external gem/library.
|
114
|
-
class NIO::Reactor
|
115
|
-
def initialize
|
116
|
-
@selector = NIO::Selector.new
|
117
|
-
@timers = Timers::Group.new
|
118
|
-
|
119
|
-
@stopped = true
|
120
|
-
end
|
121
|
-
|
122
|
-
EVENTS = [
|
123
|
-
:r,
|
124
|
-
:w,
|
125
|
-
:rw
|
126
|
-
]
|
127
|
-
|
128
|
-
def add_io(fiber, io, event)
|
129
|
-
monitor = @selector.register(io, EVENTS[event])
|
130
|
-
monitor.value = fiber
|
131
|
-
end
|
132
|
-
|
133
|
-
def remove_io(monitor)
|
134
|
-
monitor.cancel
|
135
|
-
end
|
136
|
-
|
137
|
-
# The reactor will call `fiber.resume(Fiber::TimeoutError)` at some point after duration time has elapsed.
|
138
|
-
# Returns an opaque timer object which can be passed to `cancel_timer` to avoid this happening.
|
139
|
-
def add_timer(fiber, duration)
|
140
|
-
@timers.after(duration, &block)
|
141
|
-
end
|
142
|
-
|
143
|
-
def cancel_timer(timer)
|
144
|
-
timer.cancel
|
145
|
-
end
|
146
|
-
|
147
|
-
# Run until idle (no registered io/timers), or duration time has passed if specified.
|
148
|
-
def run(duration = nil)
|
149
|
-
@timers.wait do |interval|
|
150
|
-
# - nil: no timers
|
151
|
-
# - -ve: timers expired already
|
152
|
-
# - 0: timers ready to fire
|
153
|
-
# - +ve: timers waiting to fire
|
154
|
-
interval = 0 if interval && interval < 0
|
155
|
-
|
156
|
-
# If there is nothing to do, then finish:
|
157
|
-
return if @fibers.empty? && interval.nil?
|
158
|
-
|
159
|
-
if monitors = @selector.select(interval)
|
160
|
-
monitors.each do |monitor|
|
161
|
-
if fiber = monitor.value
|
162
|
-
fiber.resume
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end until @stopped
|
167
|
-
end
|
168
|
-
|
169
|
-
def stop
|
170
|
-
@stopped = true
|
171
|
-
@selector.wakeup
|
172
|
-
end
|
173
|
-
|
174
|
-
def close
|
175
|
-
@seletor.close
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|