Skip to content

Commit e1d131d

Browse files
manefzrwstauner
authored andcommitted
Support connect_timeout keyword arg in TCPSocket
Co-authored-by: Manef Zahra <manef.zahra@shopify.com> Co-authored-by: Patrick Lin <patrick.lin@shopify.com> Co-authored-by: Kevin Menard <kevin.menard@shopify.com> Co-authored-by: Randy Stauner <randy.stauner@shopify.com> closes #3421
1 parent 9aed25b commit e1d131d

File tree

7 files changed

+22
-11
lines changed

7 files changed

+22
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Compatibility:
4343
* Implement `Time#deconstruct_keys` from Ruby 3.2 (#3039, @rwstauner).
4444
* Do not autosplat a proc that accepts a single positional argument and keywords (#3039, @andrykonchin).
4545
* Support passing anonymous * and ** parameters as method call arguments (#3039, @andrykonchin).
46+
* Support `connect_timeout` keyword argument to `TCPSocket.{new,open}` (#3421, @manefz, @patricklinpl, @nirvdrum, @rwstauner).
4647

4748
Performance:
4849

lib/truffle/socket/tcp_socket.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def self.gethostbyname(hostname)
4444
[hostname, alternatives, family, *addresses]
4545
end
4646

47-
def initialize(host, service, local_host = nil, local_service = nil)
47+
def initialize(host, service, local_host = nil, local_service = nil, connect_timeout: nil)
4848
@no_reverse_lookup = Primitive.class(self).do_not_reverse_lookup
4949

5050
if host
@@ -108,7 +108,7 @@ def initialize(host, service, local_host = nil, local_service = nil)
108108
end
109109

110110
connect_status = Truffle::Socket::Foreign
111-
.connect(descriptor, Socket.sockaddr_in(port, address))
111+
.connect(descriptor, Socket.sockaddr_in(port, address), connect_timeout)
112112

113113
break if connect_status >= 0
114114
end

lib/truffle/socket/truffle/foreign.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
2828
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2929

30+
require 'timeout'
31+
3032
module Truffle
3133
module Socket
3234
module Foreign
@@ -107,14 +109,23 @@ def self.bind(descriptor, sockaddr)
107109
end
108110
end
109111

110-
def self.connect(descriptor, sockaddr)
112+
def self.connect(descriptor, sockaddr, connect_timeout = nil)
111113
sockaddr = Socket.coerce_to_string(sockaddr)
112114

113115
sockaddr_p = Primitive.io_thread_buffer_allocate(sockaddr.bytesize)
114116
begin
115117
sockaddr_p.write_bytes(sockaddr)
116118

117-
_connect(descriptor, sockaddr_p, sockaddr.bytesize)
119+
if Primitive.nil?(connect_timeout)
120+
_connect(descriptor, sockaddr_p, sockaddr.bytesize)
121+
else
122+
# In CRuby a connect_timeout of 0 fires immediately whereas a 0 for
123+
# Timeout.timeout means "no timeout", so we change 0 to 1ns
124+
# (the smallest time interval for clock_gettime).
125+
Timeout.timeout(connect_timeout == 0 ? 0.000000001 : connect_timeout, IO::TimeoutError) do
126+
_connect(descriptor, sockaddr_p, sockaddr.bytesize)
127+
end
128+
end
118129
ensure
119130
Primitive.io_thread_buffer_free(sockaddr_p)
120131
end
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
fails:TCPSocket#initialize raises Errno::ETIMEDOUT with :connect_timeout when no server is listening on the given address
2-
fails:TCPSocket#initialize with a running server connects to a server when passed connect_timeout argument
3-
fails:TCPSocket#initialize raises IO::TimeoutError with :connect_timeout when no server is listening on the given address
41
fails:TCPSocket#initialize with a running server does not use the given block and warns to use TCPSocket::open
52
fails:TCPSocket#initialize using IPv4 when a server is listening on the given address creates a socket which is set to nonblocking
63
fails:TCPSocket#initialize using IPv4 when a server is listening on the given address creates a socket which is set to close on exec
74
fails:TCPSocket#initialize using IPv6 when a server is listening on the given address creates a socket which is set to nonblocking
85
fails:TCPSocket#initialize using IPv6 when a server is listening on the given address creates a socket which is set to close on exec
6+
slow:TCPSocket#initialize raises IO::TimeoutError with :connect_timeout when no server is listening on the given address
7+
slow:TCPSocket#initialize with a running server connects to a server when passed connect_timeout argument
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
fails:TCPSocket.open raises Errno::ETIMEDOUT with :connect_timeout when no server is listening on the given address
2-
fails:TCPSocket.open with a running server connects to a server when passed connect_timeout argument
3-
fails:TCPSocket.open raises IO::TimeoutError with :connect_timeout when no server is listening on the given address
1+
slow:TCPSocket.open raises IO::TimeoutError with :connect_timeout when no server is listening on the given address
2+
slow:TCPSocket.open with a running server connects to a server when passed connect_timeout argument

src/main/ruby/truffleruby/core/io.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class IO
3838

3939
include Enumerable
4040

41+
class TimeoutError < IOError; end
42+
4143
module WaitReadable; end
4244
module WaitWritable; end
4345

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
exclude :test_initialize_failure, "needs investigation"
22
exclude :test_inspect, "needs investigation"
33
exclude :test_recvfrom, "needs investigation"
4-
exclude :test_initialize_connect_timeout, "needs investigation"
54
exclude :test_initialize_resolv_timeout, "needs investigation"

0 commit comments

Comments
 (0)