Skip to content

Commit f1f727e

Browse files
committed
Implement poll for waiting on io.
1 parent 327ff20 commit f1f727e

File tree

8 files changed

+119
-3
lines changed

8 files changed

+119
-3
lines changed

CHANGELOG.md

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

3132
Changes:
3233

lib/truffle/io/wait.rb

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

1515
def ready?
16-
not Kernel.select([self], [], [], 0).nil?
16+
Truffle::IOOperations.poll(self, Truffle::IOOperations::POLLIN, 0) > 0
1717
end
1818

1919
def wait(timeout = nil)
20-
Kernel.select([self], [], [], timeout).nil? ? nil : self
20+
Truffle::IOOperations.poll(self, Truffle::IOOperations::POLLIN, timeout) > 0 ? self : nil
2121
end
2222

2323
alias_method :wait_readable, :wait
2424

2525
def wait_writable(timeout = nil)
26-
Kernel.select([], [self], [], timeout).nil? ? nil : self
26+
Truffle::IOOperations.poll(self, Truffle::IOOperations::POLLOUT, timeout) > 0 ? self : nil
2727
end
2828
end

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,16 @@ public static void load(NativeConfiguration configuration, RubyContext context)
196196
configuration.config("platform.msghdr.msg_flags.offset", 44);
197197
configuration.config("platform.msghdr.msg_flags.size", 4);
198198
configuration.config("platform.msghdr.msg_flags.type", string(context, "int"));
199+
configuration.config("platform.pollfd.sizeof", 8);
200+
configuration.config("platform.pollfd.fd.offset", 0);
201+
configuration.config("platform.pollfd.fd.size", 4);
202+
configuration.config("platform.pollfd.fd.type", string(context, "int"));
203+
configuration.config("platform.pollfd.events.offset", 4);
204+
configuration.config("platform.pollfd.events.size", 2);
205+
configuration.config("platform.pollfd.events.type", string(context, "short"));
206+
configuration.config("platform.pollfd.revents.offset", 6);
207+
configuration.config("platform.pollfd.revents.size", 2);
208+
configuration.config("platform.pollfd.revents.type", string(context, "short"));
199209
configuration.config("platform.servent.sizeof", 32);
200210
configuration.config("platform.servent.s_name.offset", 0);
201211
configuration.config("platform.servent.s_name.size", 8);
@@ -402,6 +412,8 @@ public static void load(NativeConfiguration configuration, RubyContext context)
402412
configuration.config("platform.fcntl.F_GLOBAL_NOCACHE", 55);
403413
configuration.config("platform.fcntl.F_ALLOCATECONTIG", 2);
404414
configuration.config("platform.fcntl.F_ALLOCATEALL", 4);
415+
configuration.config("platform.poll.POLLIN", 1);
416+
configuration.config("platform.poll.POLLOUT", 4);
405417
configuration.config("platform.socket.AF_APPLETALK", 16);
406418
configuration.config("platform.socket.PF_APPLETALK", 16);
407419
configuration.config("platform.socket.AF_INET", 2);

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,16 @@ public static void load(NativeConfiguration configuration, RubyContext context)
196196
configuration.config("platform.msghdr.msg_flags.offset", 48);
197197
configuration.config("platform.msghdr.msg_flags.size", 4);
198198
configuration.config("platform.msghdr.msg_flags.type", string(context, "int"));
199+
configuration.config("platform.pollfd.sizeof", 8);
200+
configuration.config("platform.pollfd.fd.offset", 0);
201+
configuration.config("platform.pollfd.fd.size", 4);
202+
configuration.config("platform.pollfd.fd.type", string(context, "int"));
203+
configuration.config("platform.pollfd.events.offset", 4);
204+
configuration.config("platform.pollfd.events.size", 2);
205+
configuration.config("platform.pollfd.events.type", string(context, "short"));
206+
configuration.config("platform.pollfd.revents.offset", 6);
207+
configuration.config("platform.pollfd.revents.size", 2);
208+
configuration.config("platform.pollfd.revents.type", string(context, "short"));
199209
configuration.config("platform.servent.sizeof", 32);
200210
configuration.config("platform.servent.s_name.offset", 0);
201211
configuration.config("platform.servent.s_name.size", 8);
@@ -413,6 +423,8 @@ public static void load(NativeConfiguration configuration, RubyContext context)
413423
configuration.config("platform.fcntl.F_RDLCK", 0);
414424
configuration.config("platform.fcntl.F_UNLCK", 2);
415425
configuration.config("platform.fcntl.F_WRLCK", 1);
426+
configuration.config("platform.poll.POLLIN", 1);
427+
configuration.config("platform.poll.POLLOUT", 4);
416428
configuration.config("platform.socket.AF_APPLETALK", 5);
417429
configuration.config("platform.socket.PF_APPLETALK", 5);
418430
configuration.config("platform.socket.AF_AX25", 3);

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,16 @@ public static void load(NativeConfiguration configuration, RubyContext context)
196196
configuration.config("platform.msghdr.msg_flags.offset", 48);
197197
configuration.config("platform.msghdr.msg_flags.size", 4);
198198
configuration.config("platform.msghdr.msg_flags.type", string(context, "int"));
199+
configuration.config("platform.pollfd.sizeof", 8);
200+
configuration.config("platform.pollfd.fd.offset", 0);
201+
configuration.config("platform.pollfd.fd.size", 4);
202+
configuration.config("platform.pollfd.fd.type", string(context, "int"));
203+
configuration.config("platform.pollfd.events.offset", 4);
204+
configuration.config("platform.pollfd.events.size", 2);
205+
configuration.config("platform.pollfd.events.type", string(context, "short"));
206+
configuration.config("platform.pollfd.revents.offset", 6);
207+
configuration.config("platform.pollfd.revents.size", 2);
208+
configuration.config("platform.pollfd.revents.type", string(context, "short"));
199209
configuration.config("platform.servent.sizeof", 32);
200210
configuration.config("platform.servent.s_name.offset", 0);
201211
configuration.config("platform.servent.s_name.size", 8);
@@ -413,6 +423,8 @@ public static void load(NativeConfiguration configuration, RubyContext context)
413423
configuration.config("platform.fcntl.F_RDLCK", 0);
414424
configuration.config("platform.fcntl.F_UNLCK", 2);
415425
configuration.config("platform.fcntl.F_WRLCK", 1);
426+
configuration.config("platform.poll.POLLIN", 1);
427+
configuration.config("platform.poll.POLLOUT", 4);
416428
configuration.config("platform.socket.AF_APPLETALK", 5);
417429
configuration.config("platform.socket.PF_APPLETALK", 5);
418430
configuration.config("platform.socket.AF_AX25", 3);

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ 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+
poll_args = [:pointer, :long, :int]
218+
attach_function :poll, poll_args, :int, LIBC, true
217219
attach_function :read, [:int, :pointer, :size_t], :ssize_t, LIBC, true
218220
attach_function :readlink, [:string, :pointer, :size_t], :ssize_t
219221
attach_function :realpath, [:string, :pointer], :pointer
@@ -241,6 +243,9 @@ def self.attach_function_eagerly(native_name, argument_types, return_type,
241243
# We should capture the non-lazy method
242244
attach_function_eagerly :truffleposix_select, select_args, :int, LIBTRUFFLEPOSIX, false, :truffleposix_select, self
243245
SELECT = method(:truffleposix_select)
246+
247+
attach_function_eagerly :poll, poll_args, :int, LIBC, true, :poll, self
248+
POLL = method(:poll)
244249
end
245250
end
246251

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

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

1111
module Truffle
1212
module IOOperations
13+
14+
POLLIN = Truffle::Config['platform.poll.POLLIN']
15+
POLLOUT = Truffle::Config['platform.poll.POLLOUT']
16+
17+
POLLFD_SIZE = Truffle::Config['platform.pollfd.sizeof']
18+
POLLFD_FD_OFFSET = Truffle::Config['platform.pollfd.fd.offset']
19+
POLLFD_EVENTS_OFFSET = Truffle::Config['platform.pollfd.events.offset']
20+
1321
def self.print(io, args, last_line_storage)
1422
if args.empty?
1523
raise 'last_line_binding is required' if Primitive.nil? last_line_storage
@@ -191,6 +199,58 @@ def self.select(readables, readable_ios, writables, writable_ios, errorables, er
191199

192200
end
193201

202+
def self.poll(io, events, timeout)
203+
if timeout
204+
unless Primitive.object_kind_of? timeout, Numeric
205+
raise TypeError, 'Timeout must be numeric'
206+
end
207+
208+
raise ArgumentError, 'timeout must be positive' if timeout < 0
209+
210+
# Microseconds, rounded down
211+
timeout = remaining_timeout = Integer(timeout * 1_000)
212+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
213+
else
214+
remaining_timeout = -1
215+
end
216+
217+
pollfd = Primitive.io_thread_buffer_allocate(POLLFD_SIZE)
218+
begin
219+
pollfd.put_int(POLLFD_FD_OFFSET, Primitive.io_fd(io))
220+
pollfd.put_uint16(POLLFD_EVENTS_OFFSET, events)
221+
begin
222+
primitive_result = Truffle::POSIX.poll(pollfd, 1, remaining_timeout)
223+
result =
224+
if primitive_result < 0
225+
errno = Errno.errno
226+
if errno == Errno::EINTR::Errno
227+
if timeout
228+
# Update timeout
229+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
230+
waited = now - start
231+
if waited >= timeout
232+
nil # timeout
233+
else
234+
remaining_timeout = timeout - waited
235+
:retry
236+
end
237+
else
238+
:retry
239+
end
240+
else
241+
Errno.handle_errno(errno)
242+
end
243+
else
244+
primitive_result
245+
end
246+
end while result == :retry
247+
ensure
248+
Primitive.io_thread_buffer_free(pollfd)
249+
end
250+
251+
result
252+
end
253+
194254
# The constants used to express a mode for the opening of files are
195255
# different to the fmode constants used to express the mode of an
196256
# opened file used by C extensions. Thus we will need to translate

tool/generate-native-config.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,13 @@ def constants(name)
563563
s.field :msg_flags
564564
end
565565

566+
struct 'pollfd' do |s|
567+
s.include 'poll.h'
568+
s.field 'fd'
569+
s.field 'events'
570+
s.field 'revents'
571+
end
572+
566573
struct 'servent' do |s|
567574
s.include 'netdb.h'
568575
s.field :s_name
@@ -636,6 +643,13 @@ def constants(name)
636643
]
637644
end
638645

646+
constants 'poll' do |cg|
647+
cg.include 'poll.h'
648+
cg.consts %w[
649+
POLLIN POLLOUT
650+
]
651+
end
652+
639653
constants 'socket' do |cg|
640654
cg.include 'sys/types.h'
641655
cg.include 'sys/socket.h'

0 commit comments

Comments
 (0)