Skip to content

Commit 0b639c0

Browse files
committed
Fix OnceNode to compute the value only once with splitting
1 parent 28d5206 commit 0b639c0

File tree

1 file changed

+11
-5
lines changed

1 file changed

+11
-5
lines changed

src/main/java/org/truffleruby/language/control/OnceNode.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,37 @@
1616
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
1717
import com.oracle.truffle.api.frame.VirtualFrame;
1818

19+
import java.util.Objects;
20+
1921
/** Executes a child node just once, and uses the same value each subsequent time the node is executed. */
2022
public class OnceNode extends RubyContextSourceNode {
2123

24+
static class Holder { // Not NodeCloneable, on purpose
25+
@CompilationFinal private volatile Object cachedValue;
26+
}
27+
2228
@Child private RubyNode child;
2329

24-
@CompilationFinal private volatile Object cachedValue;
30+
// An extra indirection so with splitting we compute the value only once, and the Holder is shared across splits.
31+
private final Holder holder = new Holder();
2532

2633
public OnceNode(RubyNode child) {
2734
this.child = child;
2835
}
2936

3037
@Override
3138
public Object execute(VirtualFrame frame) {
32-
Object value = cachedValue;
39+
Object value = holder.cachedValue;
3340

3441
if (value == null) {
3542
CompilerDirectives.transferToInterpreterAndInvalidate();
3643
synchronized (this) {
3744
// Read `cachedValue` again to check if the value was updated by another thread while this thread
3845
// was waiting on the lock. If it's still null, this thread is the first one to get the lock and
3946
// must update the cache.
40-
value = cachedValue;
47+
value = holder.cachedValue;
4148
if (value == null) {
42-
value = cachedValue = child.execute(frame);
43-
assert value != null;
49+
value = holder.cachedValue = Objects.requireNonNull(child.execute(frame));
4450
}
4551
}
4652
}

0 commit comments

Comments
 (0)