Skip to content

Commit d99b810

Browse files
manefzrwstauner
authored andcommitted
Support connect_timeout keyword arg in TCPSocket
Co-authored-by: Manef Zahra <[email protected]> Co-authored-by: Patrick Lin <[email protected]> Co-authored-by: Kevin Menard <[email protected]> Co-authored-by: Randy Stauner <[email protected]> closes #3421
1 parent 9aed25b commit d99b810

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
@@ -102,6 +102,7 @@ Compatibility:
102102
* Show the pointer size information (if available) in `FFI::Pointer#inspect` (@nirvdrum).
103103
* Implement performance warnings (`Warning[:performance]`) like in CRuby 3.3 (@eregon).
104104
* The output of `Marshal.dump` is now compatible with CRuby for `Rational` and `Complex` instances (#3228, @eregon).
105+
* Support `connect_timeout` keyword argument to `TCPSocket.{new,open}` (#3421, @manefz, @patricklinpl, @nirvdrum, @rwstauner).
105106

106107
Performance:
107108

lib/truffle/socket/tcp_socket.rb

Lines changed: 4 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
class TCPSocket < IPSocket
3133
def self.gethostbyname(hostname)
3234
addrinfos = Socket
@@ -44,7 +46,7 @@ def self.gethostbyname(hostname)
4446
[hostname, alternatives, family, *addresses]
4547
end
4648

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

5052
if host
@@ -108,7 +110,7 @@ def initialize(host, service, local_host = nil, local_service = nil)
108110
end
109111

110112
connect_status = Truffle::Socket::Foreign
111-
.connect(descriptor, Socket.sockaddr_in(port, address))
113+
.connect(descriptor, Socket.sockaddr_in(port, address), connect_timeout)
112114

113115
break if connect_status >= 0
114116
end

lib/truffle/socket/truffle/foreign.rb

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,23 @@ def self.bind(descriptor, sockaddr)
107107
end
108108
end
109109

110-
def self.connect(descriptor, sockaddr)
110+
def self.connect(descriptor, sockaddr, connect_timeout = nil)
111111
sockaddr = Socket.coerce_to_string(sockaddr)
112112

113113
sockaddr_p = Primitive.io_thread_buffer_allocate(sockaddr.bytesize)
114114
begin
115115
sockaddr_p.write_bytes(sockaddr)
116116

117-
_connect(descriptor, sockaddr_p, sockaddr.bytesize)
117+
if Primitive.nil?(connect_timeout)
118+
_connect(descriptor, sockaddr_p, sockaddr.bytesize)
119+
else
120+
# In CRuby a connect_timeout of 0 fires immediately whereas a 0 for
121+
# Timeout.timeout means "no timeout", so we change 0 to 1ns
122+
# (the smallest time interval for clock_gettime).
123+
Timeout.timeout(connect_timeout == 0 ? 0.000000001 : connect_timeout, IO::TimeoutError) do
124+
_connect(descriptor, sockaddr_p, sockaddr.bytesize)
125+
end
126+
end
118127
ensure
119128
Primitive.io_thread_buffer_free(sockaddr_p)
120129
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)