Skip to content

Commit ea16721

Browse files
committed
[GR-17457] Implement poll for waiting on io.
PullRequest: truffleruby/3285
2 parents 64e17d9 + 5484d39 commit ea16721

File tree

10 files changed

+94
-3
lines changed

10 files changed

+94
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Performance:
3535
* Replace a call of `-"string"` with frozen string literal at parse time (@andrykonchin).
3636
* Report polymorphism inside `Hash#[]` to recover performance (@aardvark179).
3737
* Improved interpreter performance by optimizing for better host inlining (@eregon).
38+
* Use `poll` instead of `select` for simple IO waiting to reduce overheads (#1584, @aardvark179).
3839

3940
Changes:
4041

lib/truffle/io/wait.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,19 @@ def nread
1313
end
1414

1515
def ready?
16-
not Kernel.select([self], [], [], 0).nil?
16+
ensure_open_and_readable
17+
Truffle::IOOperations.poll(self, Truffle::IOOperations::POLLIN, 0)
1718
end
1819

1920
def wait(timeout = nil)
20-
Kernel.select([self], [], [], timeout).nil? ? nil : self
21+
ensure_open_and_readable
22+
Truffle::IOOperations.poll(self, Truffle::IOOperations::POLLIN, timeout) ? self : nil
2123
end
2224

2325
alias_method :wait_readable, :wait
2426

2527
def wait_writable(timeout = nil)
26-
Kernel.select([], [self], [], timeout).nil? ? nil : self
28+
ensure_open_and_writable
29+
Truffle::IOOperations.poll(self, Truffle::IOOperations::POLLOUT, timeout) ? self : nil
2730
end
2831
end

src/main/c/truffleposix/truffleposix.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ SUCH DAMAGE.
4646
#include <dirent.h>
4747
#include <errno.h>
4848
#include <fcntl.h>
49+
#include <poll.h>
4950
#include <pwd.h>
5051
#include <spawn.h>
5152
#include <stdint.h>
@@ -110,6 +111,15 @@ static void mark_ready_from_set(fd_set *set, int nfds, int *fds) {
110111
}
111112
}
112113

114+
int truffleposix_poll(int fd, int events, int timeout_ms) {
115+
struct pollfd fds;
116+
117+
fds.fd = fd;
118+
fds.events = events;
119+
120+
return poll(&fds, 1, timeout_ms);
121+
}
122+
113123
int truffleposix_select(int nread, int *readfds, int nwrite, int *writefds,
114124
int nexcept, int *exceptfds, long timeout_us) {
115125
struct timeval timeout;

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,9 @@ public static void load(NativeConfiguration configuration, RubyContext context)
402402
configuration.config("platform.fcntl.F_GLOBAL_NOCACHE", 55);
403403
configuration.config("platform.fcntl.F_ALLOCATECONTIG", 2);
404404
configuration.config("platform.fcntl.F_ALLOCATEALL", 4);
405+
configuration.config("platform.poll.POLLIN", 1);
406+
configuration.config("platform.poll.POLLPRI", 2);
407+
configuration.config("platform.poll.POLLOUT", 4);
405408
configuration.config("platform.socket.AF_APPLETALK", 16);
406409
configuration.config("platform.socket.PF_APPLETALK", 16);
407410
configuration.config("platform.socket.AF_INET", 2);

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,9 @@ public static void load(NativeConfiguration configuration, RubyContext context)
402402
configuration.config("platform.fcntl.F_GLOBAL_NOCACHE", 55);
403403
configuration.config("platform.fcntl.F_ALLOCATECONTIG", 2);
404404
configuration.config("platform.fcntl.F_ALLOCATEALL", 4);
405+
configuration.config("platform.poll.POLLIN", 1);
406+
configuration.config("platform.poll.POLLPRI", 2);
407+
configuration.config("platform.poll.POLLOUT", 4);
405408
configuration.config("platform.socket.AF_APPLETALK", 16);
406409
configuration.config("platform.socket.PF_APPLETALK", 16);
407410
configuration.config("platform.socket.AF_INET", 2);

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,9 @@ public static void load(NativeConfiguration configuration, RubyContext context)
413413
configuration.config("platform.fcntl.F_RDLCK", 0);
414414
configuration.config("platform.fcntl.F_UNLCK", 2);
415415
configuration.config("platform.fcntl.F_WRLCK", 1);
416+
configuration.config("platform.poll.POLLIN", 1);
417+
configuration.config("platform.poll.POLLPRI", 2);
418+
configuration.config("platform.poll.POLLOUT", 4);
416419
configuration.config("platform.socket.AF_APPLETALK", 5);
417420
configuration.config("platform.socket.PF_APPLETALK", 5);
418421
configuration.config("platform.socket.AF_AX25", 3);

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,9 @@ public static void load(NativeConfiguration configuration, RubyContext context)
413413
configuration.config("platform.fcntl.F_RDLCK", 0);
414414
configuration.config("platform.fcntl.F_UNLCK", 2);
415415
configuration.config("platform.fcntl.F_WRLCK", 1);
416+
configuration.config("platform.poll.POLLIN", 1);
417+
configuration.config("platform.poll.POLLPRI", 2);
418+
configuration.config("platform.poll.POLLOUT", 4);
416419
configuration.config("platform.socket.AF_APPLETALK", 5);
417420
configuration.config("platform.socket.PF_APPLETALK", 5);
418421
configuration.config("platform.socket.AF_AX25", 3);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ def self.attach_function_eagerly(native_name, argument_types, return_type,
214214
attach_function :open, [:string, :int, varargs(:mode_t)], :int
215215
attach_function :opendir, [:string], :pointer
216216
attach_function :pipe, [:pointer], :int
217+
attach_function :truffleposix_poll, [:pointer, :long, :int], :int, LIBTRUFFLEPOSIX, true
217218
attach_function :read, [:int, :pointer, :size_t], :ssize_t, LIBC, true
218219
attach_function :readlink, [:string, :pointer, :size_t], :ssize_t
219220
attach_function :realpath, [:string, :pointer], :pointer

src/main/ruby/truffleruby/core/truffle/io_operations.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010

1111
module Truffle
1212
module IOOperations
13+
14+
POLLIN = Truffle::Config['platform.poll.POLLIN']
15+
POLLPRI = Truffle::Config['platform.poll.POLLPRI']
16+
POLLOUT = Truffle::Config['platform.poll.POLLOUT']
17+
1318
def self.print(io, args, last_line_storage)
1419
if args.empty?
1520
raise 'last_line_binding is required' if Primitive.nil? last_line_storage
@@ -191,6 +196,58 @@ def self.select(readables, readable_ios, writables, writable_ios, errorables, er
191196

192197
end
193198

199+
# This method will return a true if poll returned without error
200+
# with an event within the timeout, false if the timeout expired,
201+
# or raises an exception for an errno.
202+
def self.poll(io, event_mask, timeout)
203+
if (event_mask & POLLIN) != 0
204+
return 1 unless io.__send__(:buffer_empty?)
205+
end
206+
207+
if timeout
208+
unless Primitive.object_kind_of? timeout, Numeric
209+
raise TypeError, 'Timeout must be numeric'
210+
end
211+
212+
raise ArgumentError, 'timeout must be positive' if timeout < 0
213+
214+
# Milliseconds, rounded down
215+
timeout = remaining_timeout = (timeout * 1_000).to_i
216+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
217+
deadline = start + timeout
218+
else
219+
remaining_timeout = -1
220+
end
221+
222+
begin
223+
primitive_result = Truffle::POSIX.truffleposix_poll(Primitive.io_fd(io), event_mask, remaining_timeout)
224+
result =
225+
if primitive_result < 0
226+
errno = Errno.errno
227+
if errno == Errno::EINTR::Errno
228+
if timeout
229+
# Update timeout
230+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
231+
if now >= deadline
232+
false # timeout
233+
else
234+
remaining_timeout = deadline - now
235+
:retry
236+
end
237+
else
238+
:retry
239+
end
240+
else
241+
Errno.handle_errno(errno)
242+
end
243+
else
244+
primitive_result > 0
245+
end
246+
end while result == :retry
247+
248+
result
249+
end
250+
194251
# The constants used to express a mode for the opening of files are
195252
# different to the fmode constants used to express the mode of an
196253
# opened file used by C extensions. Thus we will need to translate

tool/generate-native-config.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,13 @@ def constants(name)
636636
]
637637
end
638638

639+
constants 'poll' do |cg|
640+
cg.include 'poll.h'
641+
cg.consts %w[
642+
POLLIN POLLPRI POLLOUT
643+
]
644+
end
645+
639646
constants 'socket' do |cg|
640647
cg.include 'sys/types.h'
641648
cg.include 'sys/socket.h'

0 commit comments

Comments
 (0)