From af9164d3dc0062d58a75588552dfb3c459548ceb Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 23 Apr 2024 13:51:04 +1200 Subject: [PATCH 01/10] `Async::IO::Stream` can handle native IO. (#82) --- lib/async/io/stream.rb | 23 +++++++++++++++++++++-- spec/async/io/stream_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/lib/async/io/stream.rb b/lib/async/io/stream.rb index baeebc8..56a35e0 100644 --- a/lib/async/io/stream.rb +++ b/lib/async/io/stream.rb @@ -197,6 +197,10 @@ def connected? @io.connected? end + def readable? + @io.readable? + end + def closed? @io.closed? end @@ -246,6 +250,21 @@ def eof! private + def sysread(size, buffer) + while true + result = @io.read_nonblock(size, buffer, exception: false) + + case result + when :wait_readable + @io.wait_readable + when :wait_writable + @io.wait_writable + else + return result + end + end + end + # Fills the buffer from the underlying stream. def fill_read_buffer(size = @block_size) # We impose a limit because the underlying `read` system call can fail if we request too much data in one go. @@ -257,12 +276,12 @@ def fill_read_buffer(size = @block_size) flush if @read_buffer.empty? - if @io.read_nonblock(size, @read_buffer, exception: false) + if sysread(size, @read_buffer) # Console.logger.debug(self, name: "read") {@read_buffer.inspect} return true end else - if chunk = @io.read_nonblock(size, @input_buffer, exception: false) + if chunk = sysread(size, @input_buffer) @read_buffer << chunk # Console.logger.debug(self, name: "read") {@read_buffer.inspect} diff --git a/spec/async/io/stream_spec.rb b/spec/async/io/stream_spec.rb index 9b5f18c..b19bfdc 100644 --- a/spec/async/io/stream_spec.rb +++ b/spec/async/io/stream_spec.rb @@ -23,6 +23,26 @@ end end + context "native I/O", if: RUBY_VERSION >= "3.1" do + let(:sockets) do + @sockets = ::Socket.pair(::Socket::AF_UNIX, ::Socket::SOCK_STREAM) + end + + after do + @sockets.each(&:close) + end + + let(:io) {sockets.first} + subject {described_class.new(sockets.last)} + + it "can read data" do + io.write("Hello World") + io.close_write + + expect(subject.read).to be == "Hello World" + end + end + context "socket I/O" do let(:sockets) do @sockets = Async::IO::Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM) From a7c9b6584ae1a9330b41bc63131c2ee97f340d12 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 23 Apr 2024 16:10:00 +1200 Subject: [PATCH 02/10] More compatibility with native sockets. --- lib/async/io/generic.rb | 4 ++++ lib/async/io/ssl_socket.rb | 12 ++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/async/io/generic.rb b/lib/async/io/generic.rb index 8a20137..a4076f2 100644 --- a/lib/async/io/generic.rb +++ b/lib/async/io/generic.rb @@ -192,6 +192,10 @@ def connected? !@io.closed? end + def readable? + @io.readable? + end + attr_accessor :timeout protected diff --git a/lib/async/io/ssl_socket.rb b/lib/async/io/ssl_socket.rb index bba1840..3659d7a 100644 --- a/lib/async/io/ssl_socket.rb +++ b/lib/async/io/ssl_socket.rb @@ -51,10 +51,14 @@ def initialize(socket, context) super else io = self.class.wrapped_klass.new(socket.to_io, context) - super(io, socket.reactor) - - # We detach the socket from the reactor, otherwise it's possible to add the file descriptor to the selector twice, which is bad. - socket.reactor = nil + if socket.respond_to?(:reactor) + super(io, socket.reactor) + + # We detach the socket from the reactor, otherwise it's possible to add the file descriptor to the selector twice, which is bad. + socket.reactor = nil + else + super(io) + end # This ensures that when the internal IO is closed, it also closes the internal socket: io.sync_close = true From 91ef29e5f31b673a79c11b93388eba6a8e064cfa Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 23 Apr 2024 16:13:48 +1200 Subject: [PATCH 03/10] Bump minor version. --- lib/async/io/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/async/io/version.rb b/lib/async/io/version.rb index adaf433..330d687 100644 --- a/lib/async/io/version.rb +++ b/lib/async/io/version.rb @@ -5,6 +5,6 @@ module Async module IO - VERSION = "1.42.1" + VERSION = "1.43.0" end end From 446a1c77e82a72868e8ff0b6ba0ac3e6c60dc7e4 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 23 Apr 2024 16:19:35 +1200 Subject: [PATCH 04/10] `IO#timeout` compatibility fix. Ruby < 3.2 doesn't provide `IO#timeout`. --- lib/async/io/ssl_socket.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/async/io/ssl_socket.rb b/lib/async/io/ssl_socket.rb index 3659d7a..8b26dd1 100644 --- a/lib/async/io/ssl_socket.rb +++ b/lib/async/io/ssl_socket.rb @@ -63,7 +63,9 @@ def initialize(socket, context) # This ensures that when the internal IO is closed, it also closes the internal socket: io.sync_close = true - @timeout = socket.timeout + if socket.respond_to?(:timeout) + @timeout = socket.timeout + end end end From 6c063c1cccf5988e49fad48d623ef8a2d9b92363 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 23 Apr 2024 16:20:05 +1200 Subject: [PATCH 05/10] Bump patch version. --- lib/async/io/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/async/io/version.rb b/lib/async/io/version.rb index 330d687..a673a42 100644 --- a/lib/async/io/version.rb +++ b/lib/async/io/version.rb @@ -5,6 +5,6 @@ module Async module IO - VERSION = "1.43.0" + VERSION = "1.43.1" end end From 965ff6d34a5637e804a26eba4d8f39459fed8011 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 23 Apr 2024 22:41:42 +1200 Subject: [PATCH 06/10] Copy timeout if possible. --- lib/async/io/ssl_socket.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/async/io/ssl_socket.rb b/lib/async/io/ssl_socket.rb index 8b26dd1..00f5ba1 100644 --- a/lib/async/io/ssl_socket.rb +++ b/lib/async/io/ssl_socket.rb @@ -118,8 +118,12 @@ def listen(*args) @server.listen(*args) end - def accept(task: Task.current, **options) - peer, address = @server.accept(**options) + def accept(task: Task.current, timeout: nil) + peer, address = @server.accept + + if timeout and peer.respond_to?(:timeout=) + peer.timeout = timeout + end wrapper = SSLSocket.new(peer, @context) From 268c0b564e9904ad49a4d8d1435279003721c297 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 23 Apr 2024 23:01:17 +1200 Subject: [PATCH 07/10] Bump patch version. --- lib/async/io/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/async/io/version.rb b/lib/async/io/version.rb index a673a42..3dd45b0 100644 --- a/lib/async/io/version.rb +++ b/lib/async/io/version.rb @@ -5,6 +5,6 @@ module Async module IO - VERSION = "1.43.1" + VERSION = "1.43.2" end end From 7d5657bc78ba9704b33c943ed96453b96e2a2094 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 23 Apr 2024 23:55:53 +1200 Subject: [PATCH 08/10] May be native IO. Don't try to clear `reactor`. --- lib/async/io/shared_endpoint.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/async/io/shared_endpoint.rb b/lib/async/io/shared_endpoint.rb index a929459..a259359 100644 --- a/lib/async/io/shared_endpoint.rb +++ b/lib/async/io/shared_endpoint.rb @@ -24,7 +24,10 @@ def self.bound(endpoint, backlog: Socket::SOMAXCONN, close_on_exec: false, **opt end server.close_on_exec = close_on_exec - server.reactor = nil + + if server.respond_to?(:reactor=) + server.reactor = nil + end end return self.new(endpoint, wrappers) @@ -35,7 +38,10 @@ def self.connected(endpoint, close_on_exec: false) wrapper = endpoint.connect wrapper.close_on_exec = close_on_exec - wrapper.reactor = nil + + if wrapper.respond_to?(:reactor=) + wrapper.reactor = nil + end return self.new(endpoint, [wrapper]) end From 6b5dc269b4526460bbb4caa2d33cf324b686bf13 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 7 May 2024 22:03:53 +1200 Subject: [PATCH 09/10] Add tea.xyz constitution file. --- tea.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tea.yaml diff --git a/tea.yaml b/tea.yaml new file mode 100644 index 0000000..cfafe79 --- /dev/null +++ b/tea.yaml @@ -0,0 +1,6 @@ +# https://tea.xyz/what-is-this-file +--- +version: 1.0.0 +codeOwners: + - '0x03d7E2c0cf7813867DDb318674B66CC53B8497dA' +quorum: 1 From efad25529371b109fe008f84f4f1ed09963b5dd4 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Fri, 30 Aug 2024 09:02:16 +1200 Subject: [PATCH 10/10] Add deprecation warning. --- readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/readme.md b/readme.md index 4805fad..1b41e2c 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,8 @@ # Async::IO +> [!CAUTION] +> This library is deprecated and should not be used in new projects. Instead, you should use for the endpoint-related functionality, and for stream/buffering functionality. + Async::IO provides builds on [async](https://github.com/socketry/async) and provides asynchronous wrappers for `IO`, `Socket`, and related classes. [![Development Status](https://github.com/socketry/async-io/workflows/Test/badge.svg)](https://github.com/socketry/async-io/actions?workflow=Test)