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.
- checksums.yaml +4 -4
- data/lib/async/barrier.rb +1 -1
- data/lib/async/version.rb +1 -1
- metadata +54 -99
- 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 -133
- 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 -39
- 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
|
-
|