async-io 1.29.0 → 1.30.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/lib/async/io/host_endpoint.rb +1 -1
- data/lib/async/io/trap.rb +4 -3
- data/lib/async/io/version.rb +1 -1
- metadata +39 -91
- data/.editorconfig +0 -6
- data/.github/workflows/development.yml +0 -55
- data/.gitignore +0 -13
- data/.rspec +0 -3
- data/.yardopts +0 -2
- data/Gemfile +0 -13
- data/README.md +0 -171
- data/async-io.gemspec +0 -30
- data/examples/allocations/byteslice.rb +0 -29
- data/examples/allocations/memory.rb +0 -16
- data/examples/allocations/read_chunks.rb +0 -18
- data/examples/chat/client.rb +0 -58
- data/examples/chat/server.rb +0 -83
- data/examples/defer/worker.rb +0 -29
- data/examples/echo/client.rb +0 -23
- data/examples/echo/server.rb +0 -58
- data/examples/issues/broken_ssl.rb +0 -15
- data/examples/issues/pipes.rb +0 -34
- data/examples/millions/client.rb +0 -44
- data/examples/millions/server.rb +0 -41
- data/examples/udp/client.rb +0 -14
- data/examples/udp/server.rb +0 -16
- data/gems/nio4r-2.3.gemfile +0 -3
- data/spec/addrinfo.rb +0 -16
- data/spec/async/io/buffer_spec.rb +0 -48
- data/spec/async/io/c10k_spec.rb +0 -138
- data/spec/async/io/echo_spec.rb +0 -75
- data/spec/async/io/endpoint_spec.rb +0 -105
- data/spec/async/io/generic_examples.rb +0 -73
- data/spec/async/io/generic_spec.rb +0 -107
- data/spec/async/io/notification_spec.rb +0 -46
- data/spec/async/io/protocol/line_spec.rb +0 -81
- data/spec/async/io/shared_endpoint/server_spec.rb +0 -72
- data/spec/async/io/shared_endpoint_spec.rb +0 -65
- data/spec/async/io/socket/tcp_spec.rb +0 -101
- data/spec/async/io/socket/udp_spec.rb +0 -65
- data/spec/async/io/socket_spec.rb +0 -149
- data/spec/async/io/ssl_server_spec.rb +0 -133
- data/spec/async/io/ssl_socket_spec.rb +0 -96
- data/spec/async/io/standard_spec.rb +0 -47
- data/spec/async/io/stream_context.rb +0 -30
- data/spec/async/io/stream_spec.rb +0 -337
- data/spec/async/io/tcp_socket_spec.rb +0 -84
- data/spec/async/io/threads_spec.rb +0 -59
- data/spec/async/io/trap_spec.rb +0 -52
- data/spec/async/io/udp_socket_spec.rb +0 -56
- data/spec/async/io/unix_endpoint_spec.rb +0 -106
- data/spec/async/io/unix_socket_spec.rb +0 -66
- data/spec/async/io/wrap/http_rb_spec.rb +0 -47
- data/spec/async/io/wrap/tcp_spec.rb +0 -79
- data/spec/spec_helper.rb +0 -15
data/async-io.gemspec
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
|
2
|
-
require_relative 'lib/async/io/version'
|
3
|
-
|
4
|
-
Gem::Specification.new do |spec|
|
5
|
-
spec.name = "async-io"
|
6
|
-
spec.version = Async::IO::VERSION
|
7
|
-
spec.licenses = ["MIT"]
|
8
|
-
spec.authors = ["Samuel Williams"]
|
9
|
-
spec.email = ["samuel.williams@oriontransfer.co.nz"]
|
10
|
-
|
11
|
-
spec.summary = "Provides support for asynchonous TCP, UDP, UNIX and SSL sockets."
|
12
|
-
spec.homepage = "https://github.com/socketry/async-io"
|
13
|
-
|
14
|
-
spec.files = `git ls-files`.split($/)
|
15
|
-
spec.executables = spec.files.grep(%r{^bin/}).map{|f| File.basename(f)}
|
16
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
|
-
spec.require_paths = ["lib"]
|
18
|
-
|
19
|
-
spec.add_dependency "async", "~> 1.14"
|
20
|
-
spec.add_development_dependency "async-rspec", "~> 1.10"
|
21
|
-
|
22
|
-
spec.required_ruby_version = '~> 2.5'
|
23
|
-
|
24
|
-
spec.add_development_dependency "async-container", "~> 0.15"
|
25
|
-
|
26
|
-
spec.add_development_dependency "covered"
|
27
|
-
spec.add_development_dependency "bundler"
|
28
|
-
spec.add_development_dependency "bake-bundler"
|
29
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
30
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require_relative 'memory'
|
5
|
-
|
6
|
-
string = nil
|
7
|
-
|
8
|
-
measure_memory("Initial allocation") do
|
9
|
-
string = "a" * 5*1024*1024
|
10
|
-
string.freeze
|
11
|
-
end # => 5.0 MB
|
12
|
-
|
13
|
-
measure_memory("Byteslice from start to middle") do
|
14
|
-
# Why does this need to allocate memory? Surely it can share the original allocation?
|
15
|
-
x = string.byteslice(0, string.bytesize / 2)
|
16
|
-
end # => 2.5 MB
|
17
|
-
|
18
|
-
measure_memory("Byteslice from middle to end") do
|
19
|
-
string.byteslice(string.bytesize / 2, string.bytesize)
|
20
|
-
end # => 0.0 MB
|
21
|
-
|
22
|
-
measure_memory("Slice! from start to middle") do
|
23
|
-
string.dup.slice!(0, string.bytesize / 2)
|
24
|
-
end # => 7.5 MB
|
25
|
-
|
26
|
-
measure_memory("Byte slice into two halves") do
|
27
|
-
head = string.byteslice(0, string.bytesize / 2) # 2.5 MB
|
28
|
-
remainder = string.byteslice(string.bytesize / 2, string.bytesize) # Shared
|
29
|
-
end # 2.5 MB
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
def measure_memory(annotation = "Memory allocated")
|
4
|
-
GC.disable
|
5
|
-
|
6
|
-
start_memory = `ps -p #{Process::pid} -o rss`.split("\n")[1].chomp.to_i
|
7
|
-
|
8
|
-
yield
|
9
|
-
|
10
|
-
ensure
|
11
|
-
end_memory = `ps -p #{Process::pid} -o rss`.split("\n")[1].chomp.to_i
|
12
|
-
memory_usage = (end_memory - start_memory).to_f / 1024
|
13
|
-
|
14
|
-
puts "#{memory_usage.round(1)} MB: #{annotation}"
|
15
|
-
GC.enable
|
16
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require_relative 'memory'
|
5
|
-
|
6
|
-
require_relative "../../lib/async/io/stream"
|
7
|
-
require "stringio"
|
8
|
-
|
9
|
-
measure_memory("Stream setup") do
|
10
|
-
@io = StringIO.new("a" * (50*1024*1024))
|
11
|
-
@stream = Async::IO::Stream.new(@io)
|
12
|
-
end # 50.0 MB
|
13
|
-
|
14
|
-
measure_memory("Read all chunks") do
|
15
|
-
while chunk = @stream.read_partial
|
16
|
-
chunk.clear
|
17
|
-
end
|
18
|
-
end # 0.5 MB
|
data/examples/chat/client.rb
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
$LOAD_PATH << File.expand_path("../../lib", __dir__)
|
5
|
-
|
6
|
-
require 'async'
|
7
|
-
require 'async/notification'
|
8
|
-
require 'async/io/stream'
|
9
|
-
require 'async/io/host_endpoint'
|
10
|
-
require 'async/io/protocol/line'
|
11
|
-
|
12
|
-
class User < Async::IO::Protocol::Line
|
13
|
-
end
|
14
|
-
|
15
|
-
endpoint = Async::IO::Endpoint.parse(ARGV.pop || "tcp://localhost:7138")
|
16
|
-
|
17
|
-
input = Async::IO::Protocol::Line.new(
|
18
|
-
Async::IO::Stream.new(
|
19
|
-
Async::IO::Generic.new($stdin)
|
20
|
-
)
|
21
|
-
)
|
22
|
-
|
23
|
-
Async do |task|
|
24
|
-
socket = endpoint.connect
|
25
|
-
stream = Async::IO::Stream.new(socket)
|
26
|
-
user = User.new(stream)
|
27
|
-
|
28
|
-
# This is used to track whether either reading from stdin failed or reading from network failed.
|
29
|
-
finished = Async::Notification.new
|
30
|
-
|
31
|
-
# Read lines from stdin and write to network.
|
32
|
-
terminal = task.async do
|
33
|
-
while line = input.read_line
|
34
|
-
user.write_lines line
|
35
|
-
end
|
36
|
-
rescue EOFError
|
37
|
-
# It's okay, we are disconnecting, because stdin has closed.
|
38
|
-
ensure
|
39
|
-
finished.signal
|
40
|
-
end
|
41
|
-
|
42
|
-
# Read lines from network and write to stdout.
|
43
|
-
network = task.async do
|
44
|
-
while line = user.read_line
|
45
|
-
puts line
|
46
|
-
end
|
47
|
-
ensure
|
48
|
-
finished.signal
|
49
|
-
end
|
50
|
-
|
51
|
-
# Wait for any of the above processes to finish:
|
52
|
-
finished.wait
|
53
|
-
ensure
|
54
|
-
# Stop all the nested tasks if we are exiting:
|
55
|
-
network.stop if network
|
56
|
-
terminal.stop if terminal
|
57
|
-
user.close if user
|
58
|
-
end
|
data/examples/chat/server.rb
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
$LOAD_PATH << File.expand_path("../../lib", __dir__)
|
5
|
-
|
6
|
-
require 'set'
|
7
|
-
|
8
|
-
require 'async'
|
9
|
-
require 'async/io/host_endpoint'
|
10
|
-
require 'async/io/protocol/line'
|
11
|
-
|
12
|
-
class User < Async::IO::Protocol::Line
|
13
|
-
attr_accessor :name
|
14
|
-
|
15
|
-
def login!
|
16
|
-
self.write_lines "Tell me your name, traveller:"
|
17
|
-
self.name = self.read_line
|
18
|
-
end
|
19
|
-
|
20
|
-
def to_s
|
21
|
-
@name || "unknown"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class Server
|
26
|
-
def initialize
|
27
|
-
@users = Set.new
|
28
|
-
end
|
29
|
-
|
30
|
-
def broadcast(*message)
|
31
|
-
puts *message
|
32
|
-
|
33
|
-
@users.each do |user|
|
34
|
-
begin
|
35
|
-
user.write_lines(*message)
|
36
|
-
rescue EOFError
|
37
|
-
# In theory, it's possible this will fail if the remote end has disconnected. Each user has it's own task running `#connected`, and eventually `user.read_line` will fail. When it does, the disconnection logic will be invoked. A better way to do this would be to have a message queue, but for the sake of keeping this example simple, this is by far the better option.
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def connected(user)
|
43
|
-
user.login!
|
44
|
-
|
45
|
-
broadcast("#{user} has joined")
|
46
|
-
|
47
|
-
user.write_lines("currently connected: #{@users.map(&:to_s).join(', ')}")
|
48
|
-
|
49
|
-
while message = user.read_line
|
50
|
-
broadcast("#{user.name}: #{message}")
|
51
|
-
end
|
52
|
-
rescue EOFError
|
53
|
-
# It's okay, client has disconnected.
|
54
|
-
ensure
|
55
|
-
disconnected(user)
|
56
|
-
end
|
57
|
-
|
58
|
-
def disconnected(user, reason = "quit")
|
59
|
-
@users.delete(user)
|
60
|
-
|
61
|
-
broadcast("#{user} has disconnected: #{reason}")
|
62
|
-
end
|
63
|
-
|
64
|
-
def run(endpoint)
|
65
|
-
Async do |task|
|
66
|
-
endpoint.accept do |peer|
|
67
|
-
stream = Async::IO::Stream.new(peer)
|
68
|
-
user = User.new(stream)
|
69
|
-
|
70
|
-
@users << user
|
71
|
-
|
72
|
-
connected(user)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
Async.logger.level = Logger::INFO
|
79
|
-
Async.logger.info("Starting server...")
|
80
|
-
server = Server.new
|
81
|
-
|
82
|
-
endpoint = Async::IO::Endpoint.parse(ARGV.pop || "tcp://localhost:7138")
|
83
|
-
server.run(endpoint)
|
data/examples/defer/worker.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'async'
|
4
|
-
require 'async/io/notification'
|
5
|
-
|
6
|
-
def defer(*args, &block)
|
7
|
-
Async do
|
8
|
-
notification = Async::IO::Notification.new
|
9
|
-
|
10
|
-
thread = Thread.new(*args) do
|
11
|
-
yield
|
12
|
-
ensure
|
13
|
-
notification.signal
|
14
|
-
end
|
15
|
-
|
16
|
-
notification.wait
|
17
|
-
thread.join
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
Async do
|
22
|
-
10.times do
|
23
|
-
defer do
|
24
|
-
puts "I'm going to sleep"
|
25
|
-
sleep 1
|
26
|
-
puts "I'm going to wake up"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
data/examples/echo/client.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
5
|
-
|
6
|
-
require 'async'
|
7
|
-
require 'async/io/trap'
|
8
|
-
require 'async/io/host_endpoint'
|
9
|
-
require 'async/io/stream'
|
10
|
-
|
11
|
-
endpoint = Async::IO::Endpoint.tcp('localhost', 4578)
|
12
|
-
|
13
|
-
Async do |task|
|
14
|
-
endpoint.connect do |peer|
|
15
|
-
stream = Async::IO::Stream.new(peer)
|
16
|
-
|
17
|
-
while true
|
18
|
-
task.sleep 1
|
19
|
-
stream.puts "Hello World!"
|
20
|
-
puts stream.gets.inspect
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/examples/echo/server.rb
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
5
|
-
|
6
|
-
require 'async'
|
7
|
-
require 'async/io/trap'
|
8
|
-
require 'async/io/host_endpoint'
|
9
|
-
require 'async/io/stream'
|
10
|
-
|
11
|
-
endpoint = Async::IO::Endpoint.tcp('localhost', 4578)
|
12
|
-
|
13
|
-
interrupt = Async::IO::Trap.new(:INT)
|
14
|
-
|
15
|
-
Async do |top|
|
16
|
-
interrupt.install!
|
17
|
-
|
18
|
-
endpoint.bind do |server, task|
|
19
|
-
Async.logger.info(server) {"Accepting connections on #{server.local_address.inspect}"}
|
20
|
-
|
21
|
-
task.async do |subtask|
|
22
|
-
interrupt.wait
|
23
|
-
|
24
|
-
Async.logger.info(server) {"Closing server socket..."}
|
25
|
-
server.close
|
26
|
-
|
27
|
-
interrupt.default!
|
28
|
-
|
29
|
-
Async.logger.info(server) {"Waiting for connections to close..."}
|
30
|
-
subtask.sleep(4)
|
31
|
-
|
32
|
-
Async.logger.info(server) do |buffer|
|
33
|
-
buffer.puts "Stopping all tasks..."
|
34
|
-
task.print_hierarchy(buffer)
|
35
|
-
buffer.puts "", "Reactor Hierarchy"
|
36
|
-
task.reactor.print_hierarchy(buffer)
|
37
|
-
end
|
38
|
-
|
39
|
-
task.stop
|
40
|
-
end
|
41
|
-
|
42
|
-
server.listen(128)
|
43
|
-
|
44
|
-
server.accept_each do |peer|
|
45
|
-
stream = Async::IO::Stream.new(peer)
|
46
|
-
|
47
|
-
while chunk = stream.read_partial
|
48
|
-
Async.logger.debug(self) {chunk.inspect}
|
49
|
-
stream.write(chunk)
|
50
|
-
stream.flush
|
51
|
-
|
52
|
-
Async.logger.info(server) do |buffer|
|
53
|
-
task.reactor.print_hierarchy(buffer)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require 'socket'
|
5
|
-
require 'openssl'
|
6
|
-
|
7
|
-
server = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
|
8
|
-
server.bind(Addrinfo.tcp('127.0.0.1', 4433))
|
9
|
-
server.listen(128)
|
10
|
-
|
11
|
-
ssl_server = OpenSSL::SSL::SSLServer.new(server, OpenSSL::SSL::SSLContext.new)
|
12
|
-
|
13
|
-
puts ssl_server.addr
|
14
|
-
|
15
|
-
# openssl/ssl.rb:234:in `addr': undefined method `addr' for #<Socket:fd 8> (NoMethodError)
|
data/examples/issues/pipes.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# wat.rb
|
4
|
-
require 'async'
|
5
|
-
require_relative '../../lib/async/io'
|
6
|
-
require 'digest/sha1'
|
7
|
-
require 'securerandom'
|
8
|
-
|
9
|
-
Async.run do |task|
|
10
|
-
r, w = IO.pipe.map { |io| Async::IO.try_convert(io) }
|
11
|
-
|
12
|
-
task.async do |subtask|
|
13
|
-
s = Digest::SHA1.new
|
14
|
-
l = 0
|
15
|
-
100.times do
|
16
|
-
bytes = SecureRandom.bytes(4000)
|
17
|
-
s << bytes
|
18
|
-
w << bytes
|
19
|
-
l += bytes.bytesize
|
20
|
-
end
|
21
|
-
w.close
|
22
|
-
p [:write, l, s.hexdigest]
|
23
|
-
end
|
24
|
-
|
25
|
-
task.async do |subtask|
|
26
|
-
s = Digest::SHA1.new
|
27
|
-
l = 0
|
28
|
-
while b = r.read(4096)
|
29
|
-
s << b
|
30
|
-
l += b.bytesize
|
31
|
-
end
|
32
|
-
p [:read, l, s.hexdigest]
|
33
|
-
end
|
34
|
-
end
|
data/examples/millions/client.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
$LOAD_PATH << File.expand_path("../../lib", __dir__)
|
5
|
-
|
6
|
-
require 'async/reactor'
|
7
|
-
require 'async/io/host_endpoint'
|
8
|
-
|
9
|
-
require 'async/container'
|
10
|
-
require 'async/container/forked'
|
11
|
-
|
12
|
-
endpoint = Async::IO::Endpoint.parse(ARGV.pop || "tcp://localhost:7234")
|
13
|
-
|
14
|
-
CONNECTIONS = 1_000_000
|
15
|
-
|
16
|
-
CONCURRENCY = Async::Container.hardware_concurrency
|
17
|
-
TASKS = 16
|
18
|
-
REPEATS = (CONNECTIONS.to_f / (TASKS * CONCURRENCY)).ceil
|
19
|
-
|
20
|
-
puts "Starting #{CONCURRENCY} processes, running #{TASKS} tasks, making #{REPEATS} connections."
|
21
|
-
puts "Total number of connections: #{CONCURRENCY * TASKS * REPEATS}!"
|
22
|
-
|
23
|
-
begin
|
24
|
-
container = Async::Container::Forked.new
|
25
|
-
|
26
|
-
container.run(count: CONCURRENCY) do
|
27
|
-
Async do |task|
|
28
|
-
connections = []
|
29
|
-
|
30
|
-
TASKS.times do
|
31
|
-
task.async do
|
32
|
-
REPEATS.times do
|
33
|
-
$stdout.write "."
|
34
|
-
connections << endpoint.connect
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
container.wait
|
42
|
-
ensure
|
43
|
-
container.stop if container
|
44
|
-
end
|