Skip to content

Commit a4c93d0

Browse files
committed
[GR-45490] Fix IO#wait specs transient failure
PullRequest: truffleruby/4182
2 parents 7edd963 + dfb70da commit a4c93d0

File tree

5 files changed

+58
-16
lines changed

5 files changed

+58
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Bug fixes:
66

77
* Add missing thread-safe objects write barriers for `TruffleRuby::ConcurrentMap` (#3179, @eregon).
88
* Fix repeated calling of methods `Dir#{each,each_child,children}` (#3464, @andrykonchin).
9+
* Fix `IO#{wait,wait_readable,wait_writable}` methods and switch the current thread into a sleep state (@andrykonchin).
910

1011
Compatibility:
1112
* Move `IO#wait_readable`, `IO#wait_writable`, `IO#wait_priority` and `IO#wait` into core library (@andrykonchin).

spec/ruby/library/io-wait/wait_spec.rb

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,25 +48,18 @@
4848
end
4949

5050
it "waits for the READABLE event to be ready" do
51-
queue = Queue.new
52-
thread = Thread.new { queue.pop; sleep 1; @w.write('data to read') };
51+
@r.wait(IO::READABLE, 0).should == nil
5352

54-
queue.push('signal');
55-
@r.wait(IO::READABLE, 2).should_not == nil
56-
57-
thread.join
53+
@w.write('data to read')
54+
@r.wait(IO::READABLE, 0).should_not == nil
5855
end
5956

6057
it "waits for the WRITABLE event to be ready" do
6158
written_bytes = IOWaitSpec.exhaust_write_buffer(@w)
59+
@w.wait(IO::WRITABLE, 0).should == nil
6260

63-
queue = Queue.new
64-
thread = Thread.new { queue.pop; sleep 1; @r.read(written_bytes) };
65-
66-
queue.push('signal');
67-
@w.wait(IO::WRITABLE, 2).should_not == nil
68-
69-
thread.join
61+
@r.read(written_bytes)
62+
@w.wait(IO::WRITABLE, 0).should_not == nil
7063
end
7164

7265
it "returns nil when the READABLE event is not ready during the timeout" do
@@ -89,6 +82,24 @@
8982
-> { @w.wait(-1, 0) }.should raise_error(ArgumentError, "Events must be positive integer!")
9083
end
9184
end
85+
86+
it "changes thread status to 'sleep' when waits for READABLE event" do
87+
t = Thread.new { @r.wait(IO::READABLE, 10) }
88+
sleep 1
89+
t.status.should == 'sleep'
90+
t.kill
91+
t.join # Thread#kill doesn't wait for the thread to end
92+
end
93+
94+
it "changes thread status to 'sleep' when waits for WRITABLE event" do
95+
written_bytes = IOWaitSpec.exhaust_write_buffer(@w)
96+
97+
t = Thread.new { @w.wait(IO::WRITABLE, 10) }
98+
sleep 1
99+
t.status.should == 'sleep'
100+
t.kill
101+
t.join # Thread#kill doesn't wait for the thread to end
102+
end
92103
end
93104

94105
context "[timeout, mode] passed" do

spec/tags/library/io-wait/wait_tags.txt

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/main/java/org/truffleruby/core/thread/ThreadNodes.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,33 @@ Object status(RubyThread self) {
585585

586586
}
587587

588+
@Primitive(name = "thread_set_status")
589+
public abstract static class ThreadSetStatusPrimitiveNode extends PrimitiveArrayArgumentsNode {
590+
591+
@TruffleBoundary
592+
@Specialization
593+
Object threadSetStatus(RubyThread thread, RubySymbol status) {
594+
ThreadStatus current = thread.status;
595+
String newName = status.getString();
596+
597+
if (newName.equals("run")) {
598+
thread.status = ThreadStatus.RUN;
599+
} else if (newName.equals("sleep")) {
600+
thread.status = ThreadStatus.SLEEP;
601+
} else if (newName.equals("aborting")) {
602+
thread.status = ThreadStatus.ABORTING;
603+
} else if (newName.equals("dead")) {
604+
thread.status = ThreadStatus.DEAD;
605+
} else {
606+
throw new RaiseException(getContext(),
607+
coreExceptions().argumentError("Unknown thread status: " + newName, this));
608+
}
609+
610+
String currentName = StringUtils.toLowerCase(current.name());
611+
return getLanguage().getSymbol(currentName);
612+
}
613+
}
614+
588615
@CoreMethod(names = "native_thread_id")
589616
public abstract static class NativeThreadIdNode extends CoreMethodArrayArgumentsNode {
590617

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,13 @@ def self.poll(io, event_mask, timeout)
259259
end
260260

261261
begin
262+
# Change thread status manually.
263+
# Don't mark Truffle::POSIX.truffleposix_poll_single_fd as blocking because
264+
# then it would automatically retry on EINTR without considering/adjusting the timeout.
265+
status = Primitive.thread_set_status(Thread.current, :sleep)
262266
returned_events = Truffle::POSIX.truffleposix_poll_single_fd(Primitive.io_fd(io), event_mask, remaining_timeout)
267+
Primitive.thread_set_status(Thread.current, status)
268+
263269
result =
264270
if returned_events < 0
265271
errno = Errno.errno

0 commit comments

Comments
 (0)