Skip to content

Commit 58f96a4

Browse files
committed
[GR-18163] Fix IO.select([io], nil, [io]) on macOS
PullRequest: truffleruby/4090
2 parents e415105 + 4745d67 commit 58f96a4

9 files changed

+65
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Bug fixes:
1111
* Fix `Regexp.union` negotiating the wrong result encoding (#3287, @nirvdrum, @simonlevasseur).
1212
* Fix `Proc#parameters` and return all the numbered parameters lower than the used explicitly ones (@andrykonchin).
1313
* Fix some C API functions which were failing when called with Ruby values represented as Java primitives (#3352, @eregon).
14+
* Fix `IO.select([io], nil, [io])` on macOS, it was hanging due to a bug in macOS `poll(2)` (#3346, @eregon, @andrykonchin).
1415

1516
Compatibility:
1617

spec/ruby/core/io/select_spec.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,39 @@
114114
it "raises an ArgumentError when passed a negative timeout" do
115115
-> { IO.select(nil, nil, nil, -5)}.should raise_error(ArgumentError)
116116
end
117+
118+
describe "returns the available descriptors when the file descriptor" do
119+
it "is in both read and error arrays" do
120+
@wr.write("foobar")
121+
result = IO.select([@rd], nil, [@rd])
122+
result.should == [[@rd], [], []]
123+
end
124+
125+
it "is in both write and error arrays" do
126+
result = IO.select(nil, [@wr], [@wr])
127+
result.should == [[], [@wr], []]
128+
end
129+
130+
it "is in both read and write arrays" do
131+
filename = tmp("IO_select_read_write_file")
132+
w = File.open(filename, 'w+')
133+
begin
134+
IO.select([w], [w], []).should == [[w], [w], []]
135+
ensure
136+
w.close
137+
rm_r filename
138+
end
139+
140+
IO.select([@wr], [@wr], []).should == [[], [@wr], []]
141+
142+
@wr.write("foobar")
143+
# CRuby on macOS returns [[@rd], [@rd], []], weird but we accept it here, probably only for pipe read-end
144+
[
145+
[[@rd], [], []],
146+
[[@rd], [@rd], []]
147+
].should.include? IO.select([@rd], [@rd], [])
148+
end
149+
end
117150
end
118151

119152
describe "IO.select when passed nil for timeout" do

src/main/java/org/truffleruby/platform/DarwinAArch64NativeConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,7 @@ public static void load(NativeConfiguration configuration, RubyContext context)
750750
configuration.config("platform.typedef.sae_connid_t", string(context, "uint"));
751751
configuration.config("platform.typedef.rlim_t", string(context, "ulong_long"));
752752
configuration.config("platform.typedef.rusage_info_t", string(context, "pointer"));
753+
configuration.config("platform.typedef.nfds_t", string(context, "uint"));
753754
configuration.config("platform.typedef.pthread_t", string(context, "pointer"));
754755
}
755756

src/main/java/org/truffleruby/platform/DarwinAMD64NativeConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,7 @@ public static void load(NativeConfiguration configuration, RubyContext context)
749749
configuration.config("platform.typedef.sae_connid_t", string(context, "uint"));
750750
configuration.config("platform.typedef.rlim_t", string(context, "ulong_long"));
751751
configuration.config("platform.typedef.rusage_info_t", string(context, "pointer"));
752+
configuration.config("platform.typedef.nfds_t", string(context, "uint"));
752753
configuration.config("platform.typedef.pthread_t", string(context, "pointer"));
753754
}
754755

src/main/java/org/truffleruby/platform/LinuxAArch64NativeConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,7 @@ public static void load(NativeConfiguration configuration, RubyContext context)
863863
configuration.config("platform.typedef.sa_family_t", string(context, "ushort"));
864864
configuration.config("platform.typedef.rlim_t", string(context, "ulong"));
865865
configuration.config("platform.typedef.rlim64_t", string(context, "ulong"));
866+
configuration.config("platform.typedef.nfds_t", string(context, "ulong"));
866867
}
867868

868869
}

src/main/java/org/truffleruby/platform/LinuxAMD64NativeConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,7 @@ public static void load(NativeConfiguration configuration, RubyContext context)
863863
configuration.config("platform.typedef.sa_family_t", string(context, "ushort"));
864864
configuration.config("platform.typedef.rlim_t", string(context, "ulong"));
865865
configuration.config("platform.typedef.rlim64_t", string(context, "ulong"));
866+
configuration.config("platform.typedef.nfds_t", string(context, "ulong"));
866867
}
867868

868869
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,21 @@ def self.select(readables = nil, writables = nil, errorables = nil, timeout = ni
767767
raise IOError, 'closed stream' if io.closed?
768768
io
769769
end
770+
771+
if Truffle::Platform.darwin? and !errorables.empty?
772+
if errorables.all? { |obj| readables.include?(obj) or writables.include?(obj) }
773+
# On macOS, if all errorables are in readables or writables, we make errorables empty,
774+
# because if non-empty they cause a bug with macOS poll(2), detailed in
775+
# https://github.com/oracle/truffleruby/issues/3346#issuecomment-1847323741
776+
# Errors are still reported, POLLIN_SET and POLLOUT_SET both include POLLERR.
777+
# POLLPRI is only for TCP OOB data (rare, not portable, poll(2) hangs on macOS with TCP OOB, another macOS bug),
778+
# and other "high priority data" which seems to not exist or unused since there is no way to read that data.
779+
errorables = []
780+
errorable_ios = []
781+
else
782+
raise IOError, 'IO.select call on macOS with error IOs not in read IOs or write IOs, this is not supported due to poll(2) bugs on macOS'
783+
end
784+
end
770785
else
771786
errorables = []
772787
errorable_ios = []

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ def self.attach_function_eagerly(native_name, argument_types, return_type,
199199
attach_function :fsync, [:int], :int
200200
attach_function :ftruncate, [:int, :off_t], :int
201201
attach_function :getcwd, [:pointer, :size_t], :string
202-
attach_function :ioctl, [:int, :ulong, :long], :int
202+
attach_function :ioctl, [:int, :ulong, varargs(:pointer)], :int
203203
attach_function :isatty, [:int], :int
204204
attach_function :lchmod, [:string, :mode_t], :int
205205
attach_function :lchown, [:string, :uid_t, :gid_t], :int
@@ -215,8 +215,8 @@ def self.attach_function_eagerly(native_name, argument_types, return_type,
215215
attach_function :opendir, [:string], :pointer
216216
attach_function :pipe, [:pointer], :int
217217
# blocking=false for both poll because the timeout needs to be decreased on EINTR
218-
attach_function :truffleposix_poll_single_fd, [:pointer, :long, :int], :int, LIBTRUFFLEPOSIX
219-
attach_function :poll, [:pointer, :long, :int], :int
218+
attach_function :truffleposix_poll_single_fd, [:int, :int, :int], :int, LIBTRUFFLEPOSIX
219+
attach_function :poll, [:pointer, :nfds_t, :int], :int
220220
attach_function :read, [:int, :pointer, :size_t], :ssize_t, LIBC, true
221221
attach_function :readlink, [:string, :pointer, :size_t], :ssize_t
222222
attach_function :realpath, [:string, :pointer], :pointer
@@ -240,7 +240,7 @@ def self.attach_function_eagerly(native_name, argument_types, return_type,
240240
Truffle::Boot.delay do
241241
if NATIVE
242242
# We should capture the non-lazy method
243-
attach_function_eagerly :poll, [:pointer, :long, :int], :int, LIBC, false, :poll, self
243+
attach_function_eagerly :poll, [:pointer, :nfds_t, :int], :int, LIBC, false, :poll, self
244244
POLL = method(:poll)
245245
end
246246
end

tool/generate-native-config.rb

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -358,11 +358,14 @@ def initialize
358358
end
359359

360360
def source(io)
361-
io.puts '#include <stdint.h>'
362-
io.puts '#include <stddef.h>'
363-
io.puts '#include <sys/types.h>'
364-
io.puts '#include <sys/socket.h>'
365-
io.puts '#include <sys/resource.h>'
361+
io.puts <<~C
362+
#include <stdint.h>
363+
#include <stddef.h>
364+
#include <sys/types.h>
365+
#include <sys/socket.h>
366+
#include <sys/resource.h>
367+
#include <poll.h>
368+
C
366369
end
367370

368371
def compile_command(source, target)

0 commit comments

Comments
 (0)