Skip to content

Commit 387fa10

Browse files
committed
[GR-14575] Fix race in Thread.list.
PullRequest: truffleruby/713
2 parents 506d28e + 8ae1f84 commit 387fa10

File tree

3 files changed

+44
-29
lines changed

3 files changed

+44
-29
lines changed

spec/ruby/core/thread/list_spec.rb

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,21 @@
3535
t.join
3636
end
3737
end
38-
end
3938

40-
describe "Thread.list" do
41-
it "needs to be reviewed for spec completeness"
39+
it "returns instances of Thread and not null or nil values" do
40+
spawner = Thread.new do
41+
Array.new(100) do
42+
Thread.new {}
43+
end
44+
end
45+
46+
while spawner.alive?
47+
Thread.list.each { |th|
48+
th.should be_kind_of(Thread)
49+
}
50+
end
51+
52+
threads = spawner.value
53+
threads.each(&:join)
54+
end
4255
end

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ public DynamicObject createBootThread(String info) {
148148

149149
public DynamicObject createThread(DynamicObject rubyClass, AllocateObjectNode allocateObjectNode) {
150150
final DynamicObject currentGroup = Layouts.THREAD.getThreadGroup(getCurrentThread());
151+
assert currentGroup != null;
151152
final DynamicObject thread = allocateObjectNode.allocate(rubyClass,
152153
packThreadFields(currentGroup, "<uninitialized>"));
153154
setFiberManager(thread);
@@ -156,6 +157,7 @@ public DynamicObject createThread(DynamicObject rubyClass, AllocateObjectNode al
156157

157158
public DynamicObject createForeignThread() {
158159
final DynamicObject currentGroup = Layouts.THREAD.getThreadGroup(rootThread);
160+
assert currentGroup != null;
159161
final DynamicObject thread = context.getCoreLibrary().getThreadFactory().newInstance(
160162
packThreadFields(currentGroup, "<foreign thread>"));
161163
setFiberManager(thread);
@@ -624,7 +626,8 @@ private void doKillOtherThreads() {
624626

625627
@TruffleBoundary
626628
public Object[] getThreadList() {
627-
return runningRubyThreads.toArray(new Object[runningRubyThreads.size()]);
629+
// This must not pre-allocate an array, or it could contain null's.
630+
return runningRubyThreads.toArray();
628631
}
629632

630633
@TruffleBoundary

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

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,16 @@
4040
*/
4141
package org.truffleruby.core.thread;
4242

43-
import java.util.concurrent.CountDownLatch;
44-
import java.util.concurrent.TimeUnit;
45-
43+
import com.oracle.truffle.api.CompilerDirectives;
44+
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
45+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
46+
import com.oracle.truffle.api.dsl.Cached;
47+
import com.oracle.truffle.api.dsl.Specialization;
48+
import com.oracle.truffle.api.frame.VirtualFrame;
49+
import com.oracle.truffle.api.nodes.Node;
50+
import com.oracle.truffle.api.object.DynamicObject;
51+
import com.oracle.truffle.api.profiles.BranchProfile;
52+
import com.oracle.truffle.api.source.SourceSection;
4653
import org.jcodings.specific.USASCIIEncoding;
4754
import org.jcodings.specific.UTF8Encoding;
4855
import org.truffleruby.Layouts;
@@ -76,16 +83,8 @@
7683
import org.truffleruby.language.objects.shared.SharedObjects;
7784
import org.truffleruby.language.yield.YieldNode;
7885

79-
import com.oracle.truffle.api.CompilerDirectives;
80-
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
81-
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
82-
import com.oracle.truffle.api.dsl.Cached;
83-
import com.oracle.truffle.api.dsl.Specialization;
84-
import com.oracle.truffle.api.frame.VirtualFrame;
85-
import com.oracle.truffle.api.nodes.Node;
86-
import com.oracle.truffle.api.object.DynamicObject;
87-
import com.oracle.truffle.api.profiles.BranchProfile;
88-
import com.oracle.truffle.api.source.SourceSection;
86+
import java.util.concurrent.CountDownLatch;
87+
import java.util.concurrent.TimeUnit;
8988

9089
@CoreClass("Thread")
9190
public abstract class ThreadNodes {
@@ -244,6 +243,18 @@ private DynamicObject getNeverSymbol() {
244243

245244
}
246245

246+
@Primitive(name = "thread_allocate")
247+
public abstract static class ThreadAllocateNode extends PrimitiveArrayArgumentsNode {
248+
249+
@Specialization
250+
public DynamicObject allocate(
251+
DynamicObject rubyClass,
252+
@Cached("create()") AllocateObjectNode allocateObjectNode) {
253+
return getContext().getThreadManager().createThread(rubyClass, allocateObjectNode);
254+
}
255+
256+
}
257+
247258
@Primitive(name = "thread_initialized?")
248259
public abstract static class ThreadIsInitializedNode extends PrimitiveArrayArgumentsNode {
249260

@@ -487,18 +498,6 @@ public DynamicObject setAbortOnException(DynamicObject self, boolean abortOnExce
487498

488499
}
489500

490-
@Primitive(name = "thread_allocate")
491-
public abstract static class ThreadAllocateNode extends PrimitiveArrayArgumentsNode {
492-
493-
@Specialization
494-
public DynamicObject allocate(
495-
DynamicObject rubyClass,
496-
@Cached("create()") AllocateObjectNode allocateObjectNode) {
497-
return getContext().getThreadManager().createThread(rubyClass, allocateObjectNode);
498-
}
499-
500-
}
501-
502501
@CoreMethod(names = "list", onSingleton = true)
503502
public abstract static class ListNode extends CoreMethodArrayArgumentsNode {
504503

0 commit comments

Comments
 (0)