File tree Expand file tree Collapse file tree 1 file changed +11
-5
lines changed
src/main/java/org/truffleruby/language/control Expand file tree Collapse file tree 1 file changed +11
-5
lines changed Original file line number Diff line number Diff line change 16
16
import com .oracle .truffle .api .CompilerDirectives .CompilationFinal ;
17
17
import com .oracle .truffle .api .frame .VirtualFrame ;
18
18
19
+ import java .util .Objects ;
20
+
19
21
/** Executes a child node just once, and uses the same value each subsequent time the node is executed. */
20
22
public class OnceNode extends RubyContextSourceNode {
21
23
24
+ static class Holder { // Not NodeCloneable, on purpose
25
+ @ CompilationFinal private volatile Object cachedValue ;
26
+ }
27
+
22
28
@ Child private RubyNode child ;
23
29
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 ();
25
32
26
33
public OnceNode (RubyNode child ) {
27
34
this .child = child ;
28
35
}
29
36
30
37
@ Override
31
38
public Object execute (VirtualFrame frame ) {
32
- Object value = cachedValue ;
39
+ Object value = holder . cachedValue ;
33
40
34
41
if (value == null ) {
35
42
CompilerDirectives .transferToInterpreterAndInvalidate ();
36
43
synchronized (this ) {
37
44
// Read `cachedValue` again to check if the value was updated by another thread while this thread
38
45
// was waiting on the lock. If it's still null, this thread is the first one to get the lock and
39
46
// must update the cache.
40
- value = cachedValue ;
47
+ value = holder . cachedValue ;
41
48
if (value == null ) {
42
- value = cachedValue = child .execute (frame );
43
- assert value != null ;
49
+ value = holder .cachedValue = Objects .requireNonNull (child .execute (frame ));
44
50
}
45
51
}
46
52
}
You can’t perform that action at this time.
0 commit comments