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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9e9cbb377148d6f0ff52896452064a5868202f389ac19270dac9540ac68394d
4
- data.tar.gz: c205d75250c9ad2b1ad9187d21e3a3d11a102d7ab677156b9b591120dec09268
3
+ metadata.gz: b0dbae8c805f1cd5878161490bbb70a211ecf7ebd3252a55c81f8c901b583b82
4
+ data.tar.gz: 5691a8fa37149cd0a79d9363e4b3a98588bf3f48f8df3ba8a683f0adc1fc1548
5
5
  SHA512:
6
- metadata.gz: 61572f82e2720942866429556e299da485fbd9d74ed8ff19f6c3fa0ea0e593a214319e6bdf276a408f0d8e098efc8b0d351653ba8d06f5dd7a59c59b1528ee01
7
- data.tar.gz: 2de4971bf4275376cee035f6b677bbac48388ff379f0ae492652526c60e10202a1b5fbf6f28574d81beb6a12725a7eda959e5577ceb7f8990c502d8cc179bf82
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(512)
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
- result = peer.write(data)
53
+ peer.write(data)
54
+ peer.close_write
53
55
 
54
- message = peer.read(512)
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(512)
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(512) # If this takes more than 1 second, Async::TimeoutError will be raised.
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
@@ -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
@@ -40,6 +40,10 @@ module Async
40
40
  "\#<#{self.class} #{@endpoint}>"
41
41
  end
42
42
 
43
+ def address
44
+ @endpoint.address
45
+ end
46
+
43
47
  def hostname
44
48
  @options[:hostname] || @endpoint.hostname
45
49
  end
@@ -195,7 +195,6 @@ module Async
195
195
  flush
196
196
  rescue
197
197
  # We really can't do anything here unless we want #close to raise exceptions.
198
- Async.logger.error(self) {$!}
199
198
  ensure
200
199
  @io.close
201
200
  end
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module IO
23
- VERSION = "1.23.1"
23
+ VERSION = "1.23.3"
24
24
  end
25
25
  end
@@ -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) {RUBY_PLATFORM =~ /darwin/ ? 1000 : 10000}
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.instance_methods(false) - (ignore_methods || [])}
4
- let(:wrapped_instance_methods) {described_class.instance_methods}
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(10).percent_of(wait_duration)
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(10).percent_of(wait_duration)
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|
@@ -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(:USR1)}
26
+ subject {described_class.new(:USR2)}
27
27
 
28
28
  it "can ignore signal" do
29
29
  subject.ignore!
30
30
 
31
- Process.kill(:USR1, Process.pid)
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
- RSpec.describe Async::IO::TCPSocket do
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
@@ -1,12 +1,5 @@
1
1
 
2
2
  require 'covered/rspec'
3
-
4
- if RUBY_ENGINE == "truffleruby"
5
- warn "Workarounds for TruffleRuby enabled..."
6
- require_relative 'truffle'
7
- end
8
-
9
- # Shared rspec helpers:
10
3
  require "async/rspec"
11
4
 
12
5
  require_relative 'addrinfo'
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.1
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-05-09 00:00:00.000000000 Z
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.2
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