async-io 1.23.1 → 1.23.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -5
- data/lib/async/io.rb +10 -0
- data/lib/async/io/generic.rb +1 -1
- data/lib/async/io/host_endpoint.rb +4 -0
- data/lib/async/io/ssl_endpoint.rb +4 -0
- data/lib/async/io/stream.rb +0 -1
- data/lib/async/io/version.rb +1 -1
- data/spec/async/io/c10k_spec.rb +15 -3
- data/spec/async/io/generic_examples.rb +21 -2
- data/spec/async/io/generic_spec.rb +5 -3
- data/spec/async/io/shared_endpoint/server_spec.rb +1 -1
- data/spec/async/io/ssl_server_spec.rb +4 -0
- data/spec/async/io/trap_spec.rb +2 -2
- data/spec/async/io/wrap/tcp_spec.rb +2 -1
- data/spec/spec_helper.rb +0 -7
- metadata +3 -5
- data/spec/truffle.rb +0 -58
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0dbae8c805f1cd5878161490bbb70a211ecf7ebd3252a55c81f8c901b583b82
|
4
|
+
data.tar.gz: 5691a8fa37149cd0a79d9363e4b3a98588bf3f48f8df3ba8a683f0adc1fc1548
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 49c21510120fe04d90283bdcf1041bd510057cf107b2d535e2d822d4aff0baff1c7e52f511903f7c8cc3e196985c2effc607b377bbd33dc8ccd50a34bae44785
|
7
|
+
data.tar.gz: 3bbefda69345a03fdee012b84466d15788686e5944b53b5c835176d81c230b5ed521290f555d98ffe9f54a72bb113f3069f7d93cd0c3ad1263606b840d414d79
|
data/README.md
CHANGED
@@ -36,12 +36,13 @@ def echo_server(endpoint)
|
|
36
36
|
# This is a synchronous block within the current task:
|
37
37
|
endpoint.accept do |client|
|
38
38
|
# This is an asynchronous block within the current reactor:
|
39
|
-
data = client.read
|
39
|
+
data = client.read
|
40
40
|
|
41
41
|
# This produces out-of-order responses.
|
42
42
|
task.sleep(rand * 0.01)
|
43
43
|
|
44
44
|
client.write(data.reverse)
|
45
|
+
client.close_write
|
45
46
|
end
|
46
47
|
end
|
47
48
|
end
|
@@ -49,9 +50,10 @@ end
|
|
49
50
|
def echo_client(endpoint, data)
|
50
51
|
Async do |task|
|
51
52
|
endpoint.connect do |peer|
|
52
|
-
|
53
|
+
peer.write(data)
|
54
|
+
peer.close_write
|
53
55
|
|
54
|
-
message = peer.read
|
56
|
+
message = peer.read
|
55
57
|
|
56
58
|
puts "Sent #{data}, got response: #{message}"
|
57
59
|
end
|
@@ -78,7 +80,7 @@ Timeouts add a temporal limit to the execution of your code. If the IO doesn't r
|
|
78
80
|
```ruby
|
79
81
|
message = task.with_timeout(5) do
|
80
82
|
begin
|
81
|
-
peer.read
|
83
|
+
peer.read
|
82
84
|
rescue Async::TimeoutError
|
83
85
|
nil # The timeout was triggered.
|
84
86
|
end
|
@@ -93,7 +95,7 @@ Asynchronous operations may block forever. You can assign a per-wrapper operatio
|
|
93
95
|
|
94
96
|
```ruby
|
95
97
|
peer.timeout = 1
|
96
|
-
peer.read
|
98
|
+
peer.read # If this takes more than 1 second, Async::TimeoutError will be raised.
|
97
99
|
```
|
98
100
|
|
99
101
|
The benefit of this approach is that it applies to all operations. Typically, this would be configured by the user, and set to something pretty high, e.g. 120 seconds.
|
data/lib/async/io.rb
CHANGED
@@ -26,3 +26,13 @@ require_relative "io/version"
|
|
26
26
|
|
27
27
|
require_relative "io/endpoint"
|
28
28
|
require_relative "io/endpoint/each"
|
29
|
+
|
30
|
+
module Async
|
31
|
+
module IO
|
32
|
+
@file_descriptor_limit = nil
|
33
|
+
|
34
|
+
def self.file_descriptor_limit
|
35
|
+
@file_descriptor_limit ||= Integer(`ulimit -n`) rescue nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/async/io/generic.rb
CHANGED
@@ -90,7 +90,7 @@ module Async
|
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
|
-
wraps ::IO, :external_encoding, :internal_encoding, :autoclose?, :autoclose=, :pid, :stat, :binmode, :flush, :set_encoding, :to_io, :to_i, :reopen, :fileno, :fsync, :fdatasync, :sync, :sync=, :tell, :seek, :rewind, :pos, :pos=, :eof, :eof?, :close_on_exec?, :close_on_exec=, :closed?, :close_read, :close_write, :isatty, :tty?, :binmode?, :sysseek, :advise, :ioctl, :fcntl, :nread, :ready?, :pread, :pwrite, :pathconf
|
93
|
+
wraps ::IO, :external_encoding, :internal_encoding, :autoclose?, :autoclose=, :pid, :stat, :binmode, :flush, :set_encoding, :set_encoding_by_bom, :to_io, :to_i, :reopen, :fileno, :fsync, :fdatasync, :sync, :sync=, :tell, :seek, :rewind, :pos, :pos=, :eof, :eof?, :close_on_exec?, :close_on_exec=, :closed?, :close_read, :close_write, :isatty, :tty?, :binmode?, :sysseek, :advise, :ioctl, :fcntl, :nread, :ready?, :pread, :pwrite, :pathconf
|
94
94
|
|
95
95
|
# Read the specified number of bytes from the input stream. This is fast path.
|
96
96
|
# @example
|
@@ -35,6 +35,10 @@ module Async
|
|
35
35
|
"\#<#{self.class} name=#{nodename.inspect} service=#{service.inspect} family=#{family.inspect} type=#{socktype.inspect} protocol=#{protocol.inspect} flags=#{flags.inspect}>"
|
36
36
|
end
|
37
37
|
|
38
|
+
def address
|
39
|
+
@specification
|
40
|
+
end
|
41
|
+
|
38
42
|
def hostname
|
39
43
|
@specification.first
|
40
44
|
end
|
data/lib/async/io/stream.rb
CHANGED
data/lib/async/io/version.rb
CHANGED
data/spec/async/io/c10k_spec.rb
CHANGED
@@ -20,16 +20,28 @@
|
|
20
20
|
|
21
21
|
require 'async/io'
|
22
22
|
require 'benchmark'
|
23
|
+
require 'open3'
|
23
24
|
|
24
|
-
require 'ruby-prof'
|
25
|
+
# require 'ruby-prof'
|
25
26
|
|
26
|
-
RSpec.describe "echo client/server" do
|
27
|
+
RSpec.describe "echo client/server", if: Process.respond_to?(:fork) do
|
27
28
|
# macOS has a rediculously hard time to do this.
|
28
29
|
# sudo sysctl -w net.inet.ip.portrange.first=10000
|
29
30
|
# sudo sysctl -w net.inet.ip.portrange.hifirst=10000
|
30
31
|
# Probably due to the use of select.
|
31
32
|
|
32
|
-
let(:repeats)
|
33
|
+
let(:repeats) do
|
34
|
+
if limit = Async::IO.file_descriptor_limit
|
35
|
+
if limit > 1024*10
|
36
|
+
10_000
|
37
|
+
else
|
38
|
+
[1, limit - 100].max
|
39
|
+
end
|
40
|
+
else
|
41
|
+
10_000
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
33
45
|
let(:server_address) {Async::IO::Address.tcp('0.0.0.0', 10101)}
|
34
46
|
|
35
47
|
def echo_server(server_address)
|
@@ -1,7 +1,26 @@
|
|
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
|
RSpec.shared_examples Async::IO::Generic do |ignore_methods|
|
3
|
-
let(:instance_methods) {described_class.wrapped_klass.
|
4
|
-
let(:wrapped_instance_methods) {described_class.
|
22
|
+
let(:instance_methods) {described_class.wrapped_klass.public_instance_methods(false) - (ignore_methods || [])}
|
23
|
+
let(:wrapped_instance_methods) {described_class.public_instance_methods}
|
5
24
|
|
6
25
|
it "should wrap a class" do
|
7
26
|
expect(described_class.wrapped_klass).to_not be_nil
|
@@ -27,10 +27,12 @@ RSpec.describe Async::IO::Generic do
|
|
27
27
|
include_context Async::RSpec::Reactor
|
28
28
|
|
29
29
|
CONSOLE_METHODS = [:beep, :cooked, :cooked!, :cursor, :cursor=, :echo=, :echo?,:getch, :getpass, :goto, :iflush, :ioflush, :noecho, :oflush,:pressed?, :raw, :raw!, :winsize, :winsize=]
|
30
|
+
# On TruffleRuby, IO#encode_with needs to be defined for YAML.dump as a public method, allow it
|
31
|
+
ignore = [:encode_with]
|
30
32
|
|
31
33
|
it_should_behave_like Async::IO::Generic, [
|
32
34
|
:bytes, :chars, :codepoints, :each, :each_byte, :each_char, :each_codepoint, :each_line, :getbyte, :getc, :gets, :lineno, :lineno=, :lines, :print, :printf, :putc, :puts, :readbyte, :readchar, :readline, :readlines, :ungetbyte, :ungetc
|
33
|
-
] + CONSOLE_METHODS
|
35
|
+
] + CONSOLE_METHODS + ignore
|
34
36
|
|
35
37
|
let(:message) {"Hello World!"}
|
36
38
|
|
@@ -64,7 +66,7 @@ RSpec.describe Async::IO::Generic do
|
|
64
66
|
input.wait(1, :read)
|
65
67
|
end
|
66
68
|
|
67
|
-
expect(duration).to be_within(
|
69
|
+
expect(duration).to be_within(50).percent_of(wait_duration)
|
68
70
|
expect(input.read(1024)).to be == message
|
69
71
|
|
70
72
|
input.close
|
@@ -90,7 +92,7 @@ RSpec.describe Async::IO::Generic do
|
|
90
92
|
expect(input.wait(wait_duration, :read)).to be_nil
|
91
93
|
end
|
92
94
|
|
93
|
-
expect(duration).to be_within(
|
95
|
+
expect(duration).to be_within(50).percent_of(wait_duration)
|
94
96
|
|
95
97
|
input.close
|
96
98
|
end
|
@@ -65,7 +65,7 @@ RSpec.shared_examples_for Async::IO::SharedEndpoint do |container_class|
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
RSpec.describe Async::Container::Forked do
|
68
|
+
RSpec.describe Async::Container::Forked, if: Process.respond_to?(:fork) do
|
69
69
|
it_behaves_like Async::IO::SharedEndpoint, described_class
|
70
70
|
end
|
71
71
|
|
@@ -36,6 +36,10 @@ RSpec.describe Async::IO::SSLServer do
|
|
36
36
|
|
37
37
|
let(:data) {"What one programmer can do in one month, two programmers can do in two months."}
|
38
38
|
|
39
|
+
it "can see through to address" do
|
40
|
+
expect(server_endpoint.address).to be == endpoint.address
|
41
|
+
end
|
42
|
+
|
39
43
|
it 'can accept_each connections' do
|
40
44
|
# Accept a single incoming connection and then finish.
|
41
45
|
server_task = reactor.async do |task|
|
data/spec/async/io/trap_spec.rb
CHANGED
@@ -23,12 +23,12 @@ require 'async/io/trap'
|
|
23
23
|
RSpec.describe Async::IO::Trap do
|
24
24
|
include_context Async::RSpec::Reactor
|
25
25
|
|
26
|
-
subject {described_class.new(:
|
26
|
+
subject {described_class.new(:USR2)}
|
27
27
|
|
28
28
|
it "can ignore signal" do
|
29
29
|
subject.ignore!
|
30
30
|
|
31
|
-
Process.kill(:
|
31
|
+
Process.kill(:USR2, Process.pid)
|
32
32
|
end
|
33
33
|
|
34
34
|
it "should wait for signal" do
|
@@ -49,7 +49,8 @@ module Wrap
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
|
52
|
+
# TruffleRuby uses Socket.tcp in Net::HTTP.get_response, not TCPSocket.open
|
53
|
+
RSpec.describe Async::IO::TCPSocket, if: RUBY_ENGINE != "truffleruby" do
|
53
54
|
describe "inside reactor" do
|
54
55
|
include_context Async::RSpec::Reactor
|
55
56
|
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-io
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.23.
|
4
|
+
version: 1.23.3
|
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-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -187,7 +187,6 @@ files:
|
|
187
187
|
- spec/async/io/wrap/http_rb_spec.rb
|
188
188
|
- spec/async/io/wrap/tcp_spec.rb
|
189
189
|
- spec/spec_helper.rb
|
190
|
-
- spec/truffle.rb
|
191
190
|
homepage: https://github.com/socketry/async-io
|
192
191
|
licenses: []
|
193
192
|
metadata: {}
|
@@ -206,7 +205,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
206
205
|
- !ruby/object:Gem::Version
|
207
206
|
version: '0'
|
208
207
|
requirements: []
|
209
|
-
rubygems_version: 3.0.
|
208
|
+
rubygems_version: 3.0.3
|
210
209
|
signing_key:
|
211
210
|
specification_version: 4
|
212
211
|
summary: Provides support for asynchonous TCP, UDP, UNIX and SSL sockets.
|
@@ -238,4 +237,3 @@ test_files:
|
|
238
237
|
- spec/async/io/wrap/http_rb_spec.rb
|
239
238
|
- spec/async/io/wrap/tcp_spec.rb
|
240
239
|
- spec/spec_helper.rb
|
241
|
-
- spec/truffle.rb
|
data/spec/truffle.rb
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'socket'
|
3
|
-
|
4
|
-
class IO
|
5
|
-
def write_nonblock(data, exception: true)
|
6
|
-
ensure_open_and_writable
|
7
|
-
|
8
|
-
data = String data
|
9
|
-
return 0 if data.empty?
|
10
|
-
|
11
|
-
@ibuffer.unseek!(self) unless @sync
|
12
|
-
|
13
|
-
self.nonblock = true
|
14
|
-
|
15
|
-
begin
|
16
|
-
Truffle::POSIX.write_string_nonblock(self, data)
|
17
|
-
rescue Errno::EAGAIN
|
18
|
-
if exception
|
19
|
-
raise EAGAINWaitWritable
|
20
|
-
else
|
21
|
-
return :wait_writable
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class IO::BidirectionalPipe
|
28
|
-
def write_nonblock(*args, **options)
|
29
|
-
@write.write_nonblock(*args, **options)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class Socket
|
34
|
-
def connect_nonblock(sockaddr, exception: true)
|
35
|
-
fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
|
36
|
-
|
37
|
-
if sockaddr.is_a?(Addrinfo)
|
38
|
-
sockaddr = sockaddr.to_sockaddr
|
39
|
-
end
|
40
|
-
|
41
|
-
status = Truffle::Socket::Foreign.connect(descriptor, sockaddr)
|
42
|
-
|
43
|
-
# There should be a better way to do this than raising an exception!?
|
44
|
-
if Errno.errno == Errno::EISCONN::Errno
|
45
|
-
raise Errno::EISCONN
|
46
|
-
end
|
47
|
-
|
48
|
-
if status < 0
|
49
|
-
if exception
|
50
|
-
Truffle::Socket::Error.write_nonblock('connect(2)')
|
51
|
-
else
|
52
|
-
:wait_writable
|
53
|
-
end
|
54
|
-
else
|
55
|
-
0
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|