< prev index next >

src/java.base/share/classes/jdk/internal/vm/Continuation.java

Print this page

 29 import jdk.internal.misc.Unsafe;
 30 import jdk.internal.vm.annotation.DontInline;
 31 import jdk.internal.vm.annotation.IntrinsicCandidate;
 32 import sun.security.action.GetPropertyAction;
 33 
 34 import java.lang.invoke.MethodHandles;
 35 import java.lang.invoke.VarHandle;
 36 import java.util.EnumSet;
 37 import java.util.Map;
 38 import java.util.Set;
 39 import java.util.concurrent.ConcurrentHashMap;
 40 import java.util.function.Supplier;
 41 import jdk.internal.access.JavaLangAccess;
 42 import jdk.internal.access.SharedSecrets;
 43 
 44 /**
 45  * A one-shot delimited continuation.
 46  */
 47 public class Continuation {
 48     private static final Unsafe U = Unsafe.getUnsafe();
 49     private static final boolean PRESERVE_EXTENT_LOCAL_CACHE;
 50     private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 51     static {
 52         ContinuationSupport.ensureSupported();
 53         PreviewFeatures.ensureEnabled();
 54 
 55         StackChunk.init(); // ensure StackChunk class is initialized
 56 
 57         String value = GetPropertyAction.privilegedGetProperty("jdk.preserveExtentLocalCache");
 58         PRESERVE_EXTENT_LOCAL_CACHE = (value == null) || Boolean.parseBoolean(value);
 59     }
 60 
 61     private static final VarHandle MOUNTED;
 62 
 63     /** Reason for pinning */
 64     public enum Pinned {
 65         /** Native frame on stack */ NATIVE,
 66         /** Monitor held */          MONITOR,
 67         /** In critical section */   CRITICAL_SECTION }
 68 
 69     /** Preemption attempt result */
 70     public enum PreemptStatus {
 71         /** Success */                                                      SUCCESS(null),
 72         /** Permanent failure */                                            PERM_FAIL_UNSUPPORTED(null),
 73         /** Permanent failure: continuation already yielding */             PERM_FAIL_YIELDING(null),
 74         /** Permanent failure: continuation not mounted on the thread */    PERM_FAIL_NOT_MOUNTED(null),
 75         /** Transient failure: continuation pinned due to a held CS */      TRANSIENT_FAIL_PINNED_CRITICAL_SECTION(Pinned.CRITICAL_SECTION),
 76         /** Transient failure: continuation pinned due to native frame */   TRANSIENT_FAIL_PINNED_NATIVE(Pinned.NATIVE),
 77         /** Transient failure: continuation pinned due to a held monitor */ TRANSIENT_FAIL_PINNED_MONITOR(Pinned.MONITOR);
 78 

112         }
113     }
114 
115     private final Runnable target;
116 
117     /* While the native JVM code is aware that every continuation has a scope, it is, for the most part,
118      * oblivious to the continuation hierarchy. The only time this hierarchy is traversed in native code
119      * is when a hierarchy of continuations is mounted on the native stack.
120      */
121     private final ContinuationScope scope;
122     private Continuation parent; // null for native stack
123     private Continuation child; // non-null when we're yielded in a child continuation
124 
125     private StackChunk tail;
126 
127     private boolean done;
128     private volatile boolean mounted = false;
129     private Object yieldInfo;
130     private boolean preempted;
131 
132     private Object[] extentLocalCache;
133 
134     /**
135      * Constructs a continuation
136      * @param scope the continuation's scope, used in yield
137      * @param target the continuation's body
138      */
139     public Continuation(ContinuationScope scope, Runnable target) {
140         this.scope = scope;
141         this.target = target;
142     }
143 
144     @Override
145     public String toString() {
146         return super.toString() + " scope: " + scope;
147     }
148 
149     public ContinuationScope getScope() {
150         return scope;
151     }
152 

221         while (c.child != null)
222             c = c.child;
223         return c;
224     }
225 
226     private void mount() {
227         if (!compareAndSetMounted(false, true))
228             throw new IllegalStateException("Mounted!!!!");
229     }
230 
231     private void unmount() {
232         setMounted(false);
233     }
234 
235     /**
236      * Mounts and runs the continuation body. If suspended, continues it from the last suspend point.
237      */
238     public final void run() {
239         while (true) {
240             mount();
241             JLA.setExtentLocalCache(extentLocalCache);
242 
243             if (done)
244                 throw new IllegalStateException("Continuation terminated");
245 
246             Thread t = currentCarrierThread();
247             if (parent != null) {
248                 if (parent != JLA.getContinuation(t))
249                     throw new IllegalStateException();
250             } else
251                 this.parent = JLA.getContinuation(t);
252             JLA.setContinuation(t, this);
253 
254             try {
255                 boolean isVirtualThread = (scope == JLA.virtualThreadContinuationScope());
256                 if (!isStarted()) { // is this the first run? (at this point we know !done)
257                     enterSpecial(this, false, isVirtualThread);
258                 } else {
259                     assert !isEmpty();
260                     enterSpecial(this, true, isVirtualThread);
261                 }
262             } finally {
263                 fence();
264                 try {
265                     assert isEmpty() == done : "empty: " + isEmpty() + " done: " + done + " cont: " + Integer.toHexString(System.identityHashCode(this));
266                     JLA.setContinuation(currentCarrierThread(), this.parent);
267                     if (parent != null)
268                         parent.child = null;
269 
270                     postYieldCleanup();
271 
272                     unmount();
273                     if (PRESERVE_EXTENT_LOCAL_CACHE) {
274                         extentLocalCache = JLA.extentLocalCache();
275                     } else {
276                         extentLocalCache = null;
277                     }
278                     JLA.setExtentLocalCache(null);
279                 } catch (Throwable e) { e.printStackTrace(); System.exit(1); }
280             }
281             // we're now in the parent continuation
282 
283             assert yieldInfo == null || yieldInfo instanceof ContinuationScope;
284             if (yieldInfo == null || yieldInfo == scope) {
285                 this.parent = null;
286                 this.yieldInfo = null;
287                 return;
288             } else {
289                 parent.child = this;
290                 parent.yield0((ContinuationScope)yieldInfo, this);
291                 parent.child = null;
292             }
293         }
294     }
295 
296     private void postYieldCleanup() {
297         if (done) {
298             this.tail = null;

 29 import jdk.internal.misc.Unsafe;
 30 import jdk.internal.vm.annotation.DontInline;
 31 import jdk.internal.vm.annotation.IntrinsicCandidate;
 32 import sun.security.action.GetPropertyAction;
 33 
 34 import java.lang.invoke.MethodHandles;
 35 import java.lang.invoke.VarHandle;
 36 import java.util.EnumSet;
 37 import java.util.Map;
 38 import java.util.Set;
 39 import java.util.concurrent.ConcurrentHashMap;
 40 import java.util.function.Supplier;
 41 import jdk.internal.access.JavaLangAccess;
 42 import jdk.internal.access.SharedSecrets;
 43 
 44 /**
 45  * A one-shot delimited continuation.
 46  */
 47 public class Continuation {
 48     private static final Unsafe U = Unsafe.getUnsafe();
 49     private static final boolean PRESERVE_SCOPED_VALUE_CACHE;
 50     private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 51     static {
 52         ContinuationSupport.ensureSupported();
 53         PreviewFeatures.ensureEnabled();
 54 
 55         StackChunk.init(); // ensure StackChunk class is initialized
 56 
 57         String value = GetPropertyAction.privilegedGetProperty("jdk.preserveScopedValueCache");
 58         PRESERVE_SCOPED_VALUE_CACHE = (value == null) || Boolean.parseBoolean(value);
 59     }
 60 
 61     private static final VarHandle MOUNTED;
 62 
 63     /** Reason for pinning */
 64     public enum Pinned {
 65         /** Native frame on stack */ NATIVE,
 66         /** Monitor held */          MONITOR,
 67         /** In critical section */   CRITICAL_SECTION }
 68 
 69     /** Preemption attempt result */
 70     public enum PreemptStatus {
 71         /** Success */                                                      SUCCESS(null),
 72         /** Permanent failure */                                            PERM_FAIL_UNSUPPORTED(null),
 73         /** Permanent failure: continuation already yielding */             PERM_FAIL_YIELDING(null),
 74         /** Permanent failure: continuation not mounted on the thread */    PERM_FAIL_NOT_MOUNTED(null),
 75         /** Transient failure: continuation pinned due to a held CS */      TRANSIENT_FAIL_PINNED_CRITICAL_SECTION(Pinned.CRITICAL_SECTION),
 76         /** Transient failure: continuation pinned due to native frame */   TRANSIENT_FAIL_PINNED_NATIVE(Pinned.NATIVE),
 77         /** Transient failure: continuation pinned due to a held monitor */ TRANSIENT_FAIL_PINNED_MONITOR(Pinned.MONITOR);
 78 

112         }
113     }
114 
115     private final Runnable target;
116 
117     /* While the native JVM code is aware that every continuation has a scope, it is, for the most part,
118      * oblivious to the continuation hierarchy. The only time this hierarchy is traversed in native code
119      * is when a hierarchy of continuations is mounted on the native stack.
120      */
121     private final ContinuationScope scope;
122     private Continuation parent; // null for native stack
123     private Continuation child; // non-null when we're yielded in a child continuation
124 
125     private StackChunk tail;
126 
127     private boolean done;
128     private volatile boolean mounted = false;
129     private Object yieldInfo;
130     private boolean preempted;
131 
132     private Object[] scopedValueCache;
133 
134     /**
135      * Constructs a continuation
136      * @param scope the continuation's scope, used in yield
137      * @param target the continuation's body
138      */
139     public Continuation(ContinuationScope scope, Runnable target) {
140         this.scope = scope;
141         this.target = target;
142     }
143 
144     @Override
145     public String toString() {
146         return super.toString() + " scope: " + scope;
147     }
148 
149     public ContinuationScope getScope() {
150         return scope;
151     }
152 

221         while (c.child != null)
222             c = c.child;
223         return c;
224     }
225 
226     private void mount() {
227         if (!compareAndSetMounted(false, true))
228             throw new IllegalStateException("Mounted!!!!");
229     }
230 
231     private void unmount() {
232         setMounted(false);
233     }
234 
235     /**
236      * Mounts and runs the continuation body. If suspended, continues it from the last suspend point.
237      */
238     public final void run() {
239         while (true) {
240             mount();
241             JLA.setScopedValueCache(scopedValueCache);
242 
243             if (done)
244                 throw new IllegalStateException("Continuation terminated");
245 
246             Thread t = currentCarrierThread();
247             if (parent != null) {
248                 if (parent != JLA.getContinuation(t))
249                     throw new IllegalStateException();
250             } else
251                 this.parent = JLA.getContinuation(t);
252             JLA.setContinuation(t, this);
253 
254             try {
255                 boolean isVirtualThread = (scope == JLA.virtualThreadContinuationScope());
256                 if (!isStarted()) { // is this the first run? (at this point we know !done)
257                     enterSpecial(this, false, isVirtualThread);
258                 } else {
259                     assert !isEmpty();
260                     enterSpecial(this, true, isVirtualThread);
261                 }
262             } finally {
263                 fence();
264                 try {
265                     assert isEmpty() == done : "empty: " + isEmpty() + " done: " + done + " cont: " + Integer.toHexString(System.identityHashCode(this));
266                     JLA.setContinuation(currentCarrierThread(), this.parent);
267                     if (parent != null)
268                         parent.child = null;
269 
270                     postYieldCleanup();
271 
272                     unmount();
273                     if (PRESERVE_SCOPED_VALUE_CACHE) {
274                         scopedValueCache = JLA.scopedValueCache();
275                     } else {
276                         scopedValueCache = null;
277                     }
278                     JLA.setScopedValueCache(null);
279                 } catch (Throwable e) { e.printStackTrace(); System.exit(1); }
280             }
281             // we're now in the parent continuation
282 
283             assert yieldInfo == null || yieldInfo instanceof ContinuationScope;
284             if (yieldInfo == null || yieldInfo == scope) {
285                 this.parent = null;
286                 this.yieldInfo = null;
287                 return;
288             } else {
289                 parent.child = this;
290                 parent.yield0((ContinuationScope)yieldInfo, this);
291                 parent.child = null;
292             }
293         }
294     }
295 
296     private void postYieldCleanup() {
297         if (done) {
298             this.tail = null;
< prev index next >