Skip to content

Commit fa9863d

Browse files
committed
[GR-16422] Support exception: false in Socket and StringIO APIs.
PullRequest: truffleruby/891
2 parents e6e4d87 + 360878a commit fa9863d

21 files changed

+226
-73
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Bug fixes:
66
* Fixed `Array#to_h` so it doesn't set a default value (#1698).
77
* Removed extra `public` methods on `IO` (#1702).
88
* Fixed `Process.kill(signal, Process.pid)` when the signal is trapped as `:IGNORE` (#1702).
9+
* Fixed `Addrinfo.new(String)` to reliably find the address family (#1702).
910

1011
Compatibility:
1112

@@ -16,6 +17,7 @@ Compatibility:
1617
* Implemented more `Ripper` methods as no-ops (#1694).
1718
* Implemented `rb_enc_sprintf` (#1702).
1819
* Implemented `ENV#{filter,filter!}` aliases for `select` and `select!`.
20+
* Non-blocking `StringIO` and `Socket` APIs now support `exception: false` like MRI (#1702).
1921

2022
Changes:
2123

lib/truffle/socket/addrinfo.rb

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,14 @@ def initialize(sockaddr, pfamily = nil, socktype = 0, protocol = 0)
106106
pfamily = Socket::PF_INET6
107107
end
108108
else
109-
if sockaddr.bytesize == Truffle::Socket::Foreign::SockaddrUn.size
109+
@afamily = Truffle::Socket::Foreign::Sockaddr.family_of_string(sockaddr)
110+
case @afamily
111+
when Socket::AF_UNIX
110112
@unix_path = Socket.unpack_sockaddr_un(sockaddr)
111-
@afamily = Socket::AF_UNIX
112-
else
113+
when Socket::AF_INET
114+
@ip_port, @ip_address = Socket.unpack_sockaddr_in(sockaddr)
115+
when Socket::AF_INET6
113116
@ip_port, @ip_address = Socket.unpack_sockaddr_in(sockaddr)
114-
115-
if sockaddr.bytesize == Truffle::Socket::Foreign::SockaddrIn6.size
116-
@afamily = Socket::AF_INET6
117-
else
118-
@afamily = Socket::AF_INET
119-
end
120117
end
121118
end
122119

@@ -203,17 +200,13 @@ def ip?
203200
end
204201

205202
def ip_address
206-
unless ip?
207-
raise SocketError, 'An IPv4/IPv6 address is required'
208-
end
203+
raise SocketError, 'need IPv4 or IPv6 address' unless ip?
209204

210205
@ip_address
211206
end
212207

213208
def ip_port
214-
unless ip?
215-
raise SocketError, 'An IPv4/IPv6 address is required'
216-
end
209+
raise SocketError, 'need IPv4 or IPv6 address' unless ip?
217210

218211
@ip_port
219212
end

lib/truffle/socket/basic_socket.rb

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,37 @@ def send(message, flags, dest_sockaddr = nil)
155155
bytes_sent
156156
end
157157

158-
def recv(bytes_to_read, flags = 0)
158+
private def internal_recv(bytes_to_read, flags, buffer, exception)
159+
raise ArgumentError, 'buffer argument not yet supported' if buffer
160+
159161
Truffle::Socket::Foreign.memory_pointer(bytes_to_read) do |buf|
160162
n_bytes = Truffle::Socket::Foreign.recv(@descriptor, buf, bytes_to_read, flags)
161-
Errno.handle('recv(2)') if n_bytes == -1
163+
164+
if n_bytes == -1
165+
if exception
166+
Truffle::Socket::Error.read_error('recv(2)', self)
167+
else
168+
return :wait_readable
169+
end
170+
end
171+
162172
return buf.read_string(n_bytes)
163173
end
164174
end
165175

166-
def recvmsg(max_msg_len = nil, flags = 0, *_)
176+
def recv(bytes_to_read, flags = 0, buf = nil)
177+
internal_recv(bytes_to_read, flags, buf, true)
178+
end
179+
180+
def recv_nonblock(bytes_to_read, flags = 0, buf = nil, exception: true)
181+
fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
182+
183+
Truffle::Socket::Error.wrap_read_nonblock do
184+
internal_recv(bytes_to_read, flags, buf, exception)
185+
end
186+
end
187+
188+
private def internal_recvmsg(max_msg_len, flags, max_control_len, scm_rights, exception)
167189
socket_type = getsockopt(:SOCKET, :TYPE).int
168190

169191
if socket_type == Socket::SOCK_STREAM
@@ -188,7 +210,13 @@ def recvmsg(max_msg_len = nil, flags = 0, *_)
188210
msg_size = Truffle::Socket::Foreign
189211
.recvmsg(@descriptor, header.pointer, flags)
190212

191-
Truffle::Socket::Error.read_error('recvmsg(2)', self) if msg_size < 0
213+
if msg_size < 0
214+
if exception
215+
Truffle::Socket::Error.read_error('recvmsg(2)', self)
216+
else
217+
return :wait_readable
218+
end
219+
end
192220

193221
if grow_msg and header.message_truncated?
194222
need_more = true
@@ -216,13 +244,17 @@ def recvmsg(max_msg_len = nil, flags = 0, *_)
216244
nil
217245
end
218246

219-
def recvmsg_nonblock(max_msg_len = nil, flags = 0, *_)
247+
def recvmsg(max_msg_len = nil, flags = 0, max_control_len = nil, scm_rights: false)
248+
internal_recvmsg(max_msg_len, flags, max_control_len, scm_rights, true)
249+
end
250+
251+
def recvmsg_nonblock(max_msg_len = nil, flags = 0, max_control_len = nil, exception: true, scm_rights: false)
220252
fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
221253

222-
recvmsg(max_msg_len, flags | Socket::MSG_DONTWAIT)
254+
internal_recvmsg(max_msg_len, flags | Socket::MSG_DONTWAIT, max_control_len, scm_rights, exception)
223255
end
224256

225-
def sendmsg(message, flags = 0, dest_sockaddr = nil, *_)
257+
private def internal_sendmsg(message, flags, dest_sockaddr, exception)
226258
msg_buffer = Truffle::Socket::Foreign.char_pointer(message.bytesize)
227259
io_vec = Truffle::Socket::Foreign::Iovec.with_buffer(msg_buffer)
228260
header = Truffle::Socket::Foreign::Msghdr.new
@@ -247,7 +279,13 @@ def sendmsg(message, flags = 0, dest_sockaddr = nil, *_)
247279
num_bytes = Truffle::Socket::Foreign
248280
.sendmsg(@descriptor, header.pointer, flags)
249281

250-
Truffle::Socket::Error.read_error('sendmsg(2)', self) if num_bytes < 0
282+
if num_bytes < 0
283+
if exception
284+
Truffle::Socket::Error.read_error('sendmsg(2)', self)
285+
else
286+
return :wait_writable
287+
end
288+
end
251289

252290
num_bytes
253291
ensure
@@ -258,10 +296,14 @@ def sendmsg(message, flags = 0, dest_sockaddr = nil, *_)
258296
end
259297
end
260298

261-
def sendmsg_nonblock(message, flags = 0, dest_sockaddr = nil, *_)
299+
def sendmsg(message, flags = 0, dest_sockaddr = nil, *controls)
300+
internal_sendmsg(message, flags, dest_sockaddr, true)
301+
end
302+
303+
def sendmsg_nonblock(message, flags = 0, dest_sockaddr = nil, *controls, exception: true)
262304
fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
263305

264-
sendmsg(message, flags | Socket::MSG_DONTWAIT, dest_sockaddr)
306+
internal_sendmsg(message, flags | Socket::MSG_DONTWAIT, dest_sockaddr, exception)
265307
end
266308

267309
def close_read
@@ -292,14 +334,6 @@ def close_write
292334
nil
293335
end
294336

295-
def recv_nonblock(bytes_to_read, flags = 0)
296-
fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
297-
298-
Truffle::Socket::Error.wrap_read_nonblock do
299-
recv(bytes_to_read, flags)
300-
end
301-
end
302-
303337
def shutdown(how = Socket::SHUT_RDWR)
304338
how = Truffle::Socket.shutdown_option(how)
305339
err = Truffle::Socket::Foreign.shutdown(@descriptor, how)

lib/truffle/socket/ip_socket.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ def peeraddr(reverse_lookup=nil)
3939
Truffle::Socket.address_info(:getpeername, self, reverse_lookup)
4040
end
4141

42-
def recvfrom(maxlen, flags = 0)
43-
flags = 0 if flags.nil?
42+
private def internal_recvfrom(maxlen, flags, exception)
43+
message, addr = internal_recvmsg(maxlen, flags, nil, false, exception)
4444

45-
message, addr = recvmsg(maxlen, flags)
45+
return message if message == :wait_readable
4646

4747
aname = Truffle::Socket.address_family_name(addr.afamily)
4848
hostname = addr.ip_address
@@ -60,4 +60,9 @@ def recvfrom(maxlen, flags = 0)
6060

6161
[message, [aname, addr.ip_port, hostname, addr.ip_address]]
6262
end
63+
64+
def recvfrom(maxlen, flags = 0)
65+
flags = 0 if flags.nil?
66+
internal_recvfrom(maxlen, flags, true)
67+
end
6368
end

lib/truffle/socket/socket.rb

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,16 @@ def self.socketpair(family, type, protocol = 0)
251251
family = Truffle::Socket.address_family(family)
252252
type = Truffle::Socket.socket_type(type)
253253

254-
fd0, fd1 = Truffle::Socket::Foreign.socketpair(family, type, protocol)
254+
fd1, fd2 = Truffle::Socket::Foreign.socketpair(family, type, protocol)
255255

256-
[for_fd(fd0), for_fd(fd1)]
256+
s1 = for_fd(fd1)
257+
s2 = for_fd(fd2)
258+
259+
[s1, s2].map do |sock|
260+
sock.instance_variable_set(:@family, family)
261+
sock.instance_variable_set(:@socket_type, type)
262+
sock
263+
end
257264
end
258265

259266
class << self
@@ -377,10 +384,14 @@ def recvfrom(bytes, flags = 0)
377384
[message, addr]
378385
end
379386

380-
def recvfrom_nonblock(bytes, flags = 0)
381-
message, addr = recvmsg_nonblock(bytes, flags)
387+
def recvfrom_nonblock(bytes, flags = 0, exception: true)
388+
message, addr = recvmsg_nonblock(bytes, flags, exception: exception)
382389

383-
[message, addr]
390+
if message == :wait_readable
391+
message
392+
else
393+
[message, addr]
394+
end
384395
end
385396

386397
def listen(backlog)

lib/truffle/socket/truffle.rb

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def self.sockaddr_class_for_socket(socket)
6666
end
6767
end
6868

69-
def self.accept(source, new_class)
69+
def self.internal_accept(source, new_class, exception)
7070
raise IOError, 'socket has been closed' if source.closed?
7171

7272
sockaddr = sockaddr_class_for_socket(source).new
@@ -79,7 +79,13 @@ def self.accept(source, new_class)
7979
.accept(source.fileno, sockaddr.pointer, size_p)
8080
end
8181

82-
Error.read_error('accept(2)', source) if fd < 0
82+
if fd < 0
83+
if exception
84+
Error.read_error('accept(2)', source)
85+
else
86+
return :wait_readable
87+
end
88+
end
8389

8490
socket = new_class.for_fd(fd)
8591

@@ -91,19 +97,16 @@ def self.accept(source, new_class)
9197
sockaddr.free
9298
end
9399
end
100+
private_class_method :internal_accept
101+
102+
def self.accept(source, new_class)
103+
internal_accept(source, new_class, true)
104+
end
94105

95106
def self.accept_nonblock(source, new_class, exception)
96107
source.fcntl(::Fcntl::F_SETFL, ::Fcntl::O_NONBLOCK)
97108

98-
begin
99-
accept(source, new_class)
100-
rescue Errno::EAGAIN
101-
if exception
102-
raise ::IO::EAGAINWaitReadable
103-
else
104-
return :wait_readable
105-
end
106-
end
109+
internal_accept(source, new_class, exception)
107110
end
108111

109112
def self.listen(source, backlog)

lib/truffle/socket/truffle/foreign.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ def self.unpack_sockaddr_in(sockaddr, reverse_lookup)
304304

305305
# On some systems this doesn't fail for families other than AF_INET(6)
306306
# so we raise manually here.
307-
unless family =~ /AF_INET/
307+
unless family.include?('AF_INET')
308308
raise ArgumentError, 'not an AF_INET/AF_INET6 sockaddr'
309309
end
310310

lib/truffle/socket/truffle/foreign/sockaddr.rb

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,30 @@ module Truffle
2828
module Socket
2929
module Foreign
3030
class Sockaddr < Truffle::FFI::Struct
31-
config('platform.sockaddr', :sa_data, :sa_family)
31+
config('platform.sockaddr', :sa_family, :sa_data)
3232

33-
def data
34-
self[:sa_data]
33+
SA_FAMILY_T = Truffle::Config['platform.typedef.sa_family_t'].to_sym
34+
SA_FAMILY_OFFSET = Truffle::Config['platform.sockaddr.sa_family.offset']
35+
36+
def self.family_of_string(str)
37+
case SA_FAMILY_T
38+
when :ushort
39+
str[SA_FAMILY_OFFSET..-1].unpack1('S')
40+
when :uchar
41+
str[SA_FAMILY_OFFSET..-1].unpack1('C')
42+
else
43+
raise "Unexpected type for sa_family_t: #{SA_FAMILY_T}"
44+
end
3545
end
3646

3747
def family
3848
self[:sa_family]
3949
end
4050

51+
def data
52+
self[:sa_data]
53+
end
54+
4155
def to_s
4256
pointer.read_string(self.class.size)
4357
end

lib/truffle/socket/udp_socket.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,12 @@ def send(message, flags, host = nil, port = nil)
7171
super(message, flags, addr)
7272
end
7373

74-
def recvfrom_nonblock(maxlen, flags = 0)
74+
def recvfrom_nonblock(maxlen, flags = 0, exception: true)
7575
fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
7676

7777
flags = 0 if flags.nil?
7878

79-
flags |= Socket::MSG_DONTWAIT
80-
81-
recvfrom(maxlen, flags)
79+
internal_recvfrom(maxlen, flags | Socket::MSG_DONTWAIT, exception)
8280
end
8381

8482
def inspect

lib/truffle/socket/unix_server.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,15 @@ def listen(backlog)
4949
end
5050

5151
def accept
52-
Truffle::Socket.accept(self, UNIXSocket)[0]
52+
socket, _ = Truffle::Socket.accept(self, UNIXSocket)
53+
54+
socket
5355
end
5456

5557
def accept_nonblock(exception: true)
56-
Truffle::Socket.accept_nonblock(self, UNIXSocket, exception)[0]
58+
socket, _ = Truffle::Socket.accept_nonblock(self, UNIXSocket, exception)
59+
60+
socket
5761
end
5862

5963
def sysaccept

0 commit comments

Comments
 (0)