1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang;
  27 
  28 import jdk.internal.HotSpotIntrinsicCandidate;
  29 import jdk.internal.vm.annotation.DontInline;
  30 import jdk.internal.vm.annotation.ForceInline;
  31 
  32 import java.lang.invoke.MethodHandles;
  33 import java.lang.invoke.VarHandle;
  34 import java.util.Arrays;
  35 import java.util.EnumSet;
  36 import java.util.Map;
  37 import java.util.Objects;
  38 import java.util.Set;
  39 import java.util.concurrent.ConcurrentHashMap;
  40 import java.util.concurrent.atomic.AtomicBoolean;
  41 import java.util.function.Supplier;
  42 
  43 /**
  44  * TBD
  45  */
  46 public class Continuation {
  47     // private static final WhiteBox WB = sun.hotspot.WhiteBox.WhiteBox.getWhiteBox();
  48     private static final jdk.internal.misc.Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe();
  49 
  50     private static final boolean TRACE = isEmptyOrTrue("java.lang.Continuation.trace");
  51     private static final boolean DEBUG = TRACE | isEmptyOrTrue("java.lang.Continuation.debug");
  52 
  53     private static final byte FLAG_LAST_FRAME_INTERPRETED = 1;
  54     private static final byte FLAG_SAFEPOINT_YIELD = 1 << 1;
  55     private static final int WATERMARK_THRESHOLD = 10;
  56     private static final VarHandle MOUNTED;
  57 
  58     /** Reason for pinning */
  59     public enum Pinned { 
  60         /** Native frame on stack */ NATIVE,
  61         /** Monitor held */          MONITOR,
  62         /** In critical section */   CRITICAL_SECTION }
  63     /** Preemption attempt result */
  64     public enum PreemptStatus { 
  65         /** Success */                                                      SUCCESS(null), 
  66         /** Permanent failure */                                            PERM_FAIL_UNSUPPORTED(null), 
  67         /** Permanent failure: continuation alreay yielding */              PERM_FAIL_YIELDING(null), 
  68         /** Permanent failure: continuation not mounted on the thread */    PERM_FAIL_NOT_MOUNTED(null), 
  69         /** Transient failure: continuation pinned due to a held CS */      TRANSIENT_FAIL_PINNED_CRITICAL_SECTION(Pinned.CRITICAL_SECTION),
  70         /** Transient failure: continuation pinned due to native frame */   TRANSIENT_FAIL_PINNED_NATIVE(Pinned.NATIVE), 
  71         /** Transient failure: continuation pinned due to a held monitor */ TRANSIENT_FAIL_PINNED_MONITOR(Pinned.MONITOR);
  72 
  73         final Pinned pinned;
  74         private PreemptStatus(Pinned reason) { this.pinned = reason; }
  75         /** 
  76          * TBD
  77          * @return TBD
  78          **/
  79         public Pinned pinned() { return pinned; }
  80     }
  81 
  82     private static PreemptStatus preemptStatus(int status) {
  83         switch (status) {
  84             case -5: return PreemptStatus.PERM_FAIL_UNSUPPORTED;
  85             case  0: return PreemptStatus.SUCCESS;
  86             case -1: return PreemptStatus.PERM_FAIL_NOT_MOUNTED;
  87             case -2: return PreemptStatus.PERM_FAIL_YIELDING;
  88             case  1: return PreemptStatus.TRANSIENT_FAIL_PINNED_CRITICAL_SECTION;
  89             case  2: return PreemptStatus.TRANSIENT_FAIL_PINNED_NATIVE;
  90             case  3: return PreemptStatus.TRANSIENT_FAIL_PINNED_MONITOR;
  91             default: throw new AssertionError("Unknown status: " + status);
  92         }
  93     }
  94 
  95     private static Pinned pinnedReason(int reason) {
  96         switch (reason) {
  97             case 1: return Pinned.CRITICAL_SECTION;
  98             case 2: return Pinned.NATIVE;
  99             case 3: return Pinned.MONITOR;
 100             default:
 101                 throw new AssertionError("Unknown pinned reason: " + reason);
 102         }
 103     }
 104 
 105     private static Thread currentCarrierThread() {
 106         return Thread.currentCarrierThread();
 107     }
 108 
 109     static {
 110         try {
 111             registerNatives();
 112 
 113             MethodHandles.Lookup l = MethodHandles.lookup();
 114             MOUNTED = l.findVarHandle(Continuation.class, "mounted", boolean.class);
 115         } catch (Exception e) {
 116             throw new InternalError(e);
 117         }
 118     }
 119 
 120     private Runnable target;
 121 
 122     /* While the native JVM code is aware that every continuation has a scope, it is, for the most part,
 123      * oblivious to the continuation hierarchy. The only time this hierarchy is traversed in native code
 124      * is when a hierarchy of continuations is mounted on the native stack.
 125      */
 126     private final ContinuationScope scope;
 127     private Continuation parent; // null for native stack
 128     private Continuation child; // non-null when we're yielded in a child continuation
 129 
 130     // The content of the stack arrays is extremely security-sensitive. Writing can lead to arbitrary code execution, and reading can leak sensitive data
 131     private int[] stack = null; // grows down
 132     private Object[] refStack = null;
 133 
 134     private long fp = 0; // an index into the h-stack if the top frame is interpreted, otherwise, the value of rbp
 135     private int sp = -1; // index into the h-stack
 136     private long pc = 0;
 137     private int refSP;
 138     private int maxSize; // maximal stack size when unpacked
 139     private byte flags;
 140     private boolean done;
 141     private volatile boolean mounted = false;
 142     private Object yieldInfo;
 143 
 144     private short cs; // critical section semaphore
 145 
 146     private boolean reset = false; // perftest only
 147 
 148     // transient state
 149     // addresses into vstack. only valid when mounted
 150     private long entrySP = 0;
 151     private long entryFP = 0;
 152     private long entryPC = 0;
 153 
 154     // monitoring
 155     private short numFrames;
 156     private short numInterpretedFrames;
 157 
 158     private byte sizeCounter;
 159     private int stackWatermark;
 160     private int refStackWatermark;
 161 
 162     // private long[] nmethods = null; // grows up
 163     // private int numNmethods = 0;
 164 
 165     /**
 166      * TBD
 167      * @param scope TBD
 168      * @param target TBD
 169      */
 170     public Continuation(ContinuationScope scope, Runnable target) {
 171         this.scope = scope;
 172         this.target = target;
 173     }
 174 
 175     /**
 176      * TBD
 177      * @param scope TBD
 178      * @param target TBD
 179      * @param stackSize in bytes
 180      */
 181     public Continuation(ContinuationScope scope, int stackSize, Runnable target) {
 182         this(scope, target);
 183         getStacks(stackSize, stackSize, stackSize / 8);
 184     }
 185 
 186     @Override
 187     public String toString() {
 188         return super.toString() + " scope: " + scope;
 189     }
 190 
 191     ContinuationScope getScope() {
 192         return scope;
 193     }
 194 
 195     Continuation getParent() {
 196         return parent;
 197     }
 198 
 199     /**
 200      * TBD
 201      * @param scope TBD
 202      * @return TBD
 203      */
 204     public static Continuation getCurrentContinuation(ContinuationScope scope) {
 205         Continuation cont = currentCarrierThread().getContinuation();
 206         while (cont != null && cont.scope != scope)
 207             cont = cont.parent;
 208         return cont;
 209     }
 210 
 211     /**
 212      * TBD
 213      * @return TBD
 214      */
 215     public StackWalker stackWalker() {
 216         return stackWalker(EnumSet.noneOf(StackWalker.Option.class));
 217     }
 218 
 219     /**
 220      * TBD
 221      * @param option TBD
 222      * @return TBD
 223      */
 224     public StackWalker stackWalker(StackWalker.Option option) {
 225         return stackWalker(EnumSet.of(Objects.requireNonNull(option)));
 226     }
 227 
 228     /**
 229      * TBD
 230      * @param options TBD
 231      * @return TBD
 232      */
 233     public StackWalker stackWalker(Set<StackWalker.Option> options) {
 234         return stackWalker(options, this.scope);
 235     }
 236 
 237     /**
 238      * TBD
 239      * @param options TBD
 240      * @param scope TBD
 241      * @return TBD
 242      */
 243     public StackWalker stackWalker(Set<StackWalker.Option> options, ContinuationScope scope) {
 244         // if (scope != null) {
 245         //     // verify the given scope exists in this continuation
 246         //     Continuation c;
 247         //     for (c = innermost(); c != null; c = c.parent) {
 248         //         if (c.scope == scope)
 249         //             break;
 250         //     }
 251         //     if (c.scope != scope)
 252         //         scope = this.scope; // throw new IllegalArgumentException("Continuation " + this + " not in scope " + scope); -- don't throw exception to have the same behavior as no continuation
 253         // } else {
 254         //     scope = this.scope;
 255         // }
 256         return StackWalker.newInstance(options, null, scope, innermost());
 257     }
 258 
 259     /**
 260      * TBD
 261      * @return TBD
 262      * @throws IllegalStateException if the continuation is mounted
 263      */
 264     public StackTraceElement[] getStackTrace() {
 265         return stackWalker().walk(s -> s.map(StackWalker.StackFrame::toStackTraceElement).toArray(StackTraceElement[]::new));
 266     }
 267 
 268     /// Support for StackWalker
 269     static <R> R wrapWalk(Continuation inner, ContinuationScope scope, Supplier<R> walk) {
 270         try {
 271             for (Continuation c = inner; c != null && c.scope != scope; c = c.parent)
 272                 c.mount();
 273 
 274             // if (!inner.isStarted())
 275             //     throw new IllegalStateException("Continuation not started");
 276                 
 277             return walk.get();
 278         } finally {
 279             for (Continuation c = inner; c != null && c.scope != scope; c = c.parent)
 280                 c.unmount();
 281         }
 282     }
 283 
 284     private Continuation innermost() {
 285         Continuation c = this;
 286         while (c.child != null)
 287             c = c.child;
 288         return c;
 289     }
 290 
 291     private void mount() {
 292         if (!compareAndSetMounted(false, true))
 293             throw new IllegalStateException("Mounted!!!!");
 294     }
 295 
 296     private void unmount() {
 297         setMounted(false);
 298     }
 299     
 300     /**
 301      * TBD
 302      */
 303     public final void run() {
 304         while (true) {
 305             if (TRACE) System.out.println("\n++++++++++++++++++++++++++++++");
 306 
 307             mount();
 308 
 309             if (done)
 310                 throw new IllegalStateException("Continuation terminated");
 311 
 312             Thread t = currentCarrierThread();
 313             if (parent != null) {
 314                 if (parent != t.getContinuation())
 315                     throw new IllegalStateException();
 316             } else
 317                 this.parent = t.getContinuation();
 318             t.setContinuation(this);
 319 
 320             int origRefSP = refSP;
 321 
 322             int origSP = sp, origMaxSize = maxSize; long origFP = fp, origPC = pc; // perftest only (used only if reset is true)
 323 
 324             try {
 325                 if (!isStarted()) { // is this the first run? (at this point we know !done)
 326                     if (TRACE) System.out.println("ENTERING " + id());
 327                     this.entrySP = getSP(); // now getSP also resets fastpath; this is also done in thaw for the doContinue branch
 328                     enter(); // make this an invokevirtual rather than invokeinterface. Otherwise it freaks out the interpreter (currently solved by patching in native)
 329                 } else {
 330                     doContinue(); // intrinsic. Jumps into yield, as a return from doYield    
 331                 }
 332             } finally {
 333                 fence();
 334                 try {
 335                 if (TRACE) System.out.println("run (after) sp: " + sp + " refSP: " + refSP + " maxSize: " + maxSize);
 336 
 337                 currentCarrierThread().setContinuation(this.parent);
 338                 if (parent != null)
 339                     parent.child = null;
 340 
 341                 if (reset) { maxSize = origMaxSize; sp = origSP; fp = origFP; pc = origPC; refSP = origRefSP; } // perftest only
 342                 postYieldCleanup(origRefSP);
 343 
 344                 if (TRACE) System.out.println("clearing entryPC");
 345                 this.entryPC = 0; // cannot be done in native code, as a safpoint on the transition back to Java may want to walk the stack (with the still-mounted continuation)
 346 
 347                 unmount();
 348                 } catch (Throwable e) { e.printStackTrace(); System.exit(1); }
 349                 assert !hasLeak() : "hasLeak1 " + "refSP: " + refSP + " refStack: " + Arrays.toString(refStack);
 350             }
 351             // we're now in the parent continuation
 352 
 353             assert yieldInfo == null || yieldInfo instanceof ContinuationScope;
 354             if (yieldInfo == null || yieldInfo == scope) {
 355                 this.parent = null;
 356                 this.yieldInfo = null;
 357                 return;
 358             } else {
 359                 parent.child = this;
 360                 parent.yield0((ContinuationScope)yieldInfo, this);
 361                 parent.child = null;
 362             }
 363         }
 364     }
 365 
 366     @DontInline
 367     @HotSpotIntrinsicCandidate
 368     private void enter() {
 369         // This method runs in the "entry frame".
 370         // A yield jumps to this method's caller as if returning from this method.
 371         try {
 372             target.run();
 373         } finally {
 374             done = true;
 375             assert reset || fence() && isStackEmpty() : "sp: " + sp + " stack.length: " + (stack != null ? stack.length : "null");
 376             // assert doneX;
 377             // System.out.println("-- done!  " + id());
 378             if (TRACE) System.out.println(">>>>>>>> DONE <<<<<<<<<<<<< " + id());
 379         }
 380     }
 381 
 382     private boolean isStarted() {
 383         return stack != null && sp < stack.length;
 384     }
 385 
 386     /**
 387      * TBD
 388      * 
 389      * @param scope The {@link ContinuationScope} to yield
 390      * @return {@code true} for success; {@code false} for failure
 391      * @throws IllegalStateException if not currently in the given {@code scope},
 392      */
 393     // @DontInline
 394     public static boolean yield(ContinuationScope scope) {
 395         Continuation cont = currentCarrierThread().getContinuation();
 396         Continuation c;
 397         for (c = cont; c != null && c.scope != scope; c = c.parent)
 398             ;
 399         if (c == null)
 400             throw new IllegalStateException("Not in scope " + scope);
 401 
 402         return cont.yield0(scope, null);
 403     }
 404 
 405     private boolean yield0(ContinuationScope scope, Continuation child) {
 406         if (TRACE) System.out.println(this + " yielding on scope " + scope + ". child: " + child);
 407         if (scope != this.scope)
 408             this.yieldInfo = scope;
 409         int res = doYield(0);
 410         unsafe.storeFence(); // needed to prevent certain transformations by the compiler
 411         
 412         if (TRACE) System.out.println(this + " awake on scope " + scope + " child: " + child + " res: " + res + " yieldInfo: " + yieldInfo);
 413 
 414         try {
 415         assert scope != this.scope || yieldInfo == null : "scope: " + scope + " this.scope: " + this.scope + " yieldInfo: " + yieldInfo + " res: " + res;
 416         assert yieldInfo == null || scope == this.scope || yieldInfo instanceof Integer : "scope: " + scope + " this.scope: " + this.scope + " yieldInfo: " + yieldInfo + " res: " + res;
 417 
 418         if (child != null) { // TODO: ugly
 419             if (res != 0) {
 420                 child.yieldInfo = res;
 421             } else if (yieldInfo != null) {
 422                 assert yieldInfo instanceof Integer;
 423                 child.yieldInfo = yieldInfo;
 424             } else {
 425                 child.yieldInfo = res;
 426             }
 427             this.yieldInfo = null;
 428 
 429             if (TRACE) System.out.println(this + " child.yieldInfo = " + child.yieldInfo);
 430         } else {
 431             if (res == 0 && yieldInfo != null) {
 432                 res = (Integer)yieldInfo;
 433             }
 434             this.yieldInfo = null;
 435 
 436             if (res == 0)
 437                 onContinue();
 438             else
 439                 onPinned0(res);
 440 
 441             if (TRACE) System.out.println(this + " res: " + res);
 442         }
 443         assert yieldInfo == null;
 444         assert reset || !hasLeak() : "hasLeak2 " + "refSP: " + refSP + " refStack: " + Arrays.toString(refStack);
 445 
 446         return res == 0;
 447         } catch (Throwable t) {
 448             t.printStackTrace();
 449             throw t;
 450         }
 451     }
 452 
 453     private void postYieldCleanup(int origRefSP) {
 454         if (done) {
 455             // TODO: The following are disabled just for some testing
 456             // this.stack = null;
 457             // this.sp = -1;
 458             // this.refStack = null;
 459             // this.refSP = -1;
 460         } else {
 461             if (TRACE && origRefSP < refSP)
 462                 System.out.println("Nulling refs " + origRefSP + " (inclusive) - " + refSP + " (exclusive)");
 463             for (int i = origRefSP; i < refSP; i++)
 464                 refStack[i] = null;
 465 
 466             maybeShrink();
 467         }
 468     }
 469 
 470     private void onPinned0(int reason) {
 471         if (TRACE) System.out.println("PINNED " + this + " reason: " + reason);
 472         onPinned(pinnedReason(reason));
 473     }
 474 
 475     /**
 476      * TBD
 477      * @param reason TBD
 478      */
 479     protected void onPinned(Pinned reason) {
 480         if (DEBUG)
 481             System.out.println("PINNED! " + reason);
 482         throw new IllegalStateException("Pinned: " + reason);
 483     }
 484 
 485     /**
 486      * TBD
 487      */
 488     protected void onContinue() {
 489         if (TRACE)
 490             System.out.println("On continue");
 491     }
 492 
 493     /**
 494      * TBD
 495      * @return TBD
 496      */
 497     public boolean isDone() {
 498         return done;
 499     }
 500 
 501     /**
 502      * TBD
 503      * @return TBD
 504      */
 505     public boolean isPreempted() {
 506         return isFlag(FLAG_SAFEPOINT_YIELD);
 507     }
 508 
 509     private boolean isFlag(byte flag) {
 510         return (flags & flag) != 0;
 511     }
 512 
 513     /**
 514      * Pins the current continuation (enters a critical section).
 515      * This increments an internal semaphore that, when greater than 0, pins the continuation.
 516      */
 517     public static void pin() {
 518         Continuation cont = currentCarrierThread().getContinuation();
 519         if (cont != null) {
 520             assert cont.cs >= 0;
 521             if (cont.cs == Short.MAX_VALUE)
 522                 throw new IllegalStateException("Too many pins");
 523             cont.cs++;
 524         }
 525     }
 526 
 527     /**
 528      * Unpins the current continuation (exits a critical section).
 529      * This decrements an internal semaphore that, when equal 0, unpins the current continuation
 530      * if pinne with {@link #pin()}.
 531      */
 532     public static void unpin() {
 533         Continuation cont = currentCarrierThread().getContinuation();
 534         if (cont != null) {
 535             assert cont.cs >= 0;
 536             if (cont.cs == 0)
 537                 throw new IllegalStateException("Not pinned");
 538             cont.cs--;
 539         }
 540     }
 541 
 542     /**
 543      * Tests whether the given scope is pinned. 
 544      * This method is slow.
 545      * 
 546      * @param scope the continuation scope
 547      * @return {@code} true if we're in the give scope and are pinned; {@code false otherwise}
 548      */
 549     public static boolean isPinned(ContinuationScope scope) {
 550         int res = isPinned0(scope);
 551         return res != 0;
 552     }
 553 
 554     static private native int isPinned0(ContinuationScope scope);
 555 
 556     private void clean() {
 557         if (!isStackEmpty())
 558             clean0();
 559     }
 560 
 561     private boolean fence() {
 562         unsafe.storeFence(); // needed to prevent certain transformations by the compiler
 563         return true;
 564     }
 565 
 566     // /**
 567     //  * TBD
 568     //  */
 569     // public void doneX() {
 570     //     // System.out.println("DONEX");
 571     //     this.doneX = true;
 572     // }
 573 
 574     private long readLong(int[] array, int index) {
 575         return (long)array[index] << 32 + array[index+1];
 576     }
 577 
 578     private void getStacks(int size, int oops, int frames) {
 579         try {
 580             boolean allocated = false;
 581             allocated |= getStack(size);
 582             allocated |= getRefStack(oops);
 583             assert allocated : "getStacks called for no good reason";
 584         } catch (Throwable t) {
 585             t.printStackTrace();
 586             throw t;
 587         }
 588     }
 589 
 590     // size is the size in bytes needed for newly frozen frames
 591     private boolean getStack(int size) {
 592         size = size >> 2;
 593         if (DEBUG)
 594             System.out.println("-- getStack size: " + size + " cur: " + (stack != null ? stack.length : 0) + " sp: " + sp);
 595 
 596         if (this.stack == null) {
 597             this.stack = new int[size];
 598             this.sp = stack.length;
 599             if (DEBUG)
 600                 System.out.println("-- getStack: allocated: " + stack.length + " sp: " + sp );
 601         } else {
 602             int oldLength = stack.length;
 603             int offset = sp >= 0 ? sp : oldLength;
 604             int minLength = (oldLength - offset) + size;
 605             if (minLength > oldLength) {
 606                 int newLength = newCapacity(oldLength, minLength);
 607                 int[] newStack = new int[newLength];
 608                 int n = oldLength - offset;
 609                 if (n > 0)
 610                     System.arraycopy(stack, offset, newStack, newLength - n, n);
 611                 this.stack = newStack;
 612                 // we need to preserve the same offset from the array's _end_
 613                 this.sp = fixDecreasingIndexAfterResize(sp, oldLength, newLength);
 614                 if (isFlag(FLAG_LAST_FRAME_INTERPRETED)) {
 615                     if (DEBUG) System.out.println("-- getStack CHANGING FP");
 616                     this.fp = fixDecreasingIndexAfterResize((int) fp, oldLength, newLength);
 617                 }
 618                 if (DEBUG)
 619                     System.out.println("-- getStack: allocated: " + newLength + " old: " + oldLength + " sp: " + sp + " fp: " + fp);
 620             } else
 621                 return false;
 622         }
 623         if (TRACE) System.out.println("--- end of getStack sp: " + sp);
 624 
 625         return true;
 626     }
 627 
 628     private boolean getRefStack(int size) {
 629         if (DEBUG)
 630             System.out.println("-- getRefStack: " + size);
 631         if (refStack == null) {
 632             this.refStack = new Object[size]; // TODO: nearest power of 2
 633             this.refSP = refStack.length;
 634             if (DEBUG)
 635                 System.out.println("-- getRefStack: allocated: " + refStack.length + " refSP: " + refSP);
 636         } else if (refSP < size) {
 637             int oldLength = refStack.length;
 638             int newLength = newCapacity(oldLength, (oldLength - refSP) + size);
 639             Object[] newRefStack = new Object[newLength];
 640             int n = oldLength - refSP;
 641             System.arraycopy(refStack, refSP, newRefStack, newLength - n, n);
 642             this.refStack = newRefStack;
 643             this.refSP = fixDecreasingIndexAfterResize(refSP, oldLength, newLength);
 644             if (DEBUG)
 645                 System.out.println("-- getRefStack: allocated: " + newLength + " old: " + oldLength + " refSP: " + refSP);
 646         } else
 647             return false;
 648         if (TRACE) System.out.println("--- end of getRefStack: " + refStack.length + " refSP: " + refSP);
 649         
 650         return true;
 651     }
 652 
 653     void maybeShrink() {
 654         if (stack == null || refStack == null)
 655             return;
 656 
 657         int stackSize = stack.length - sp;
 658         int refStackSize = refStack.length - refSP;
 659         assert sp >= 0;
 660         assert stackSize >= 0;
 661 
 662         if (stackSize > stackWatermark || refStackSize > refStackWatermark) {
 663             this.stackWatermark    = Math.max(stackWatermark,    stackSize);
 664             this.refStackWatermark = Math.max(refStackWatermark, refStackSize);
 665             this.sizeCounter = 0;
 666             return;
 667         }
 668 
 669         sizeCounter++;
 670 
 671         if (sizeCounter >= WATERMARK_THRESHOLD) {
 672             if (stackWatermark < stack.length) resizeStack(stackWatermark);
 673             this.stackWatermark = 0;
 674 
 675             if (refStackWatermark < refStack.length) resizeRefStack(refStackWatermark);
 676             this.refStackWatermark = 0;
 677         }
 678     }
 679 
 680     void resizeStack(int newLength) {
 681         int oldLength = stack.length;
 682         int offset = sp;
 683         int n = oldLength - offset;
 684         assert newLength >= n;
 685         if (DEBUG)
 686             System.out.println("-- resizeStack0 length: " + stack.length + " sp: " + sp + " newLength: " + newLength + " used: " + n);
 687         int[] newStack = new int[newLength];
 688         System.arraycopy(stack, offset, newStack, newLength - n, n);
 689         this.stack = newStack;
 690 
 691         this.sp = fixDecreasingIndexAfterResize(sp, oldLength, newLength);
 692         if (isFlag(FLAG_LAST_FRAME_INTERPRETED)) {
 693             if (DEBUG) System.out.println("-- resizeStack CHANGING FP");
 694             this.fp = fixDecreasingIndexAfterResize((int) fp, oldLength, newLength);
 695         }
 696         if (DEBUG)
 697             System.out.println("-- resizeStack1 length: " + stack.length + " sp: " + sp + " fp: " + fp);
 698     }
 699 
 700     void resizeRefStack(int newLength) {
 701         if (DEBUG)
 702             System.out.println("-- resizeRefStack0 length: " + refStack.length + " refSP: " + refSP + " newLength: " + newLength);
 703         int oldLength = refStack.length;
 704         int n = oldLength - refSP;
 705         assert newLength >= n;
 706         Object[] newRefStack = new Object[newLength];
 707         System.arraycopy(refStack, refSP, newRefStack, newLength - n, n);
 708         this.refStack = newRefStack;
 709 
 710         this.refSP = fixDecreasingIndexAfterResize(refSP, oldLength, newLength);
 711         if (DEBUG)
 712             System.out.println("-- resizeRefStack1 length: " + refStack.length + " refSP: " + refSP);
 713     }
 714 
 715     private int fixDecreasingIndexAfterResize(int index, int oldLength, int newLength) {
 716         return newLength - (oldLength - index);
 717     }
 718 
 719     private int newCapacity(int oldCapacity, int minCapacity) {
 720         // overflow-conscious code
 721         int newCapacity = oldCapacity + (oldCapacity >> 1);
 722         if (newCapacity - minCapacity <= 0) {
 723             if (minCapacity < 0) // overflow
 724                 throw new OutOfMemoryError();
 725             return minCapacity;
 726         }
 727         return newCapacity;
 728     }
 729 
 730     /**
 731      * temporary testing
 732      */
 733     public void something_something_1() {
 734         this.sp = stack.length;
 735         this.refSP = refStack.length;
 736         this.done = false;
 737         this.flags = 0;
 738 
 739         this.fp = 0;
 740         this.pc = 0;
 741         this.entrySP = 0;
 742         this.entryFP = 0;
 743         this.entryPC = 0;
 744 
 745         this.numFrames = 0;
 746         this.numInterpretedFrames = 0;
 747     
 748         this.sizeCounter = 0;
 749         this.stackWatermark = 0;
 750         this.refStackWatermark = 0;
 751 
 752         setMounted(false);
 753     }
 754 
 755     /**
 756      * temporary testing
 757      */
 758     public void something_something_2() {
 759         reset = true;
 760     }
 761 
 762     /**
 763      * temporary testing
 764      */
 765     public void something_something_3() {
 766         this.done = false;
 767     }
 768 
 769     // private void pushNmethod(long nmethod) {
 770     //     if (nmethods == null) {
 771     //         nmethods = new long[8];
 772     //     } else {
 773     //         if (numNmethods == nmethods.length) {
 774     //             long[] newNmethods = new long[nmethods.length * 2];
 775     //             System.arraycopy(nmethods, 0, newNmethods, 0, numNmethods);
 776     //             this.nmethods = newNmethods;
 777     //         }
 778     //     }
 779     //     nmethods[numNmethods++] = nmethod;
 780     // }
 781 
 782     // private void popNmethod() {
 783     //     numNmethods--;
 784     // }
 785 
 786     private static Map<Long, Integer> liveNmethods = new ConcurrentHashMap<>();
 787 
 788     private void processNmethods(int before, int after) {
 789 
 790     }
 791 
 792 
 793     @HotSpotIntrinsicCandidate
 794     private static long getSP() { throw new Error("Intrinsic not installed"); };
 795 
 796     @HotSpotIntrinsicCandidate
 797     private static long getFP() { throw new Error("Intrinsic not installed"); };
 798 
 799     @HotSpotIntrinsicCandidate
 800     private static long getPC() { throw new Error("Intrinsic not installed"); };
 801 
 802     @HotSpotIntrinsicCandidate
 803     private void doContinue() { throw new Error("Intrinsic not installed"); };
 804 
 805     @HotSpotIntrinsicCandidate
 806     private static int doYield(int scopes) { throw new Error("Intrinsic not installed"); };
 807 
 808     @HotSpotIntrinsicCandidate
 809     private static void jump(long sp, long fp, long pc) { throw new Error("Intrinsic not installed"); };
 810 
 811     /**
 812      * TBD
 813      * @return TBD
 814      */
 815     public int getNumFrames() {
 816         return numFrames;
 817     }
 818 
 819     /**
 820      * TBD
 821      * @return TBD
 822      */
 823     public int getNumInterpretedFrames() {
 824         return numInterpretedFrames;
 825     }
 826 
 827     /**
 828      * TBD
 829      * @return TBD
 830      */
 831     public int getStackUsageInBytes() {
 832         return (stack != null ? stack.length - sp + 1 : 0) * 4;
 833     }
 834 
 835     /**
 836      * TBD
 837      * @return TBD
 838      */
 839     public int getNumRefs() {
 840         return (refStack != null ? refStack.length - refSP : 0);
 841     }
 842 
 843     /**
 844      * TBD
 845      * @return value
 846      */
 847     @HotSpotIntrinsicCandidate
 848     public static int runLevel() { return 0; }
 849 
 850     private boolean compareAndSetMounted(boolean expectedValue, boolean newValue) {
 851        boolean res = MOUNTED.compareAndSet(this, expectedValue, newValue);
 852     //    System.out.println("-- compareAndSetMounted:  ex: " + expectedValue + " -> " + newValue + " " + res + " " + id());
 853        return res;
 854      }
 855 
 856     private void setMounted(boolean newValue) {
 857         // System.out.println("-- setMounted:  " + newValue + " " + id());
 858         mounted = newValue;
 859         // MOUNTED.setVolatile(this, newValue);
 860     }
 861 
 862     private String id() {
 863         return Integer.toHexString(System.identityHashCode(this)) + " [" + currentCarrierThread().getId() + "]";
 864     }
 865 
 866     private native void clean0();
 867 
 868     /**
 869      * TBD
 870      * Subclasses may throw an {@link UnsupportedOperationException}, but this does not prevent
 871      * the continuation from being preempted on a parent scope.
 872      * 
 873      * @param thread TBD
 874      * @return TBD
 875      * @throws UnsupportedOperationException if this continuation does not support preemption
 876      */
 877     public PreemptStatus tryPreempt(Thread thread) {
 878         PreemptStatus res = preemptStatus(tryForceYield0(thread));
 879         if (res == PreemptStatus.PERM_FAIL_UNSUPPORTED) {
 880             throw new UnsupportedOperationException("Thread-local handshakes disabled");
 881         }
 882         return res;
 883     }
 884 
 885     private native int tryForceYield0(Thread thread);
 886 
 887     // native methods
 888     private static native void registerNatives();
 889 
 890     private boolean hasLeak() {
 891         assert refStack != null || refSP <= 0 : "refSP: " + refSP;
 892         for (int i = 0; i < refSP; i++) {
 893             if (refStack[i] != null)
 894                 return true;
 895         }
 896         return false;
 897     }
 898 
 899     private boolean isStackEmpty() {
 900         return (stack == null) || (sp < 0) || (sp >= stack.length);
 901     }
 902 
 903     private void dump() {
 904         System.out.println("Continuation@" + Long.toHexString(System.identityHashCode(this)));
 905         System.out.println("\tparent: " + parent);
 906         System.out.println("\tstack.length: " + stack.length);
 907         for (int i = 1; i <= 10; i++) {
 908             int j = stack.length - i;
 909             System.out.println("\tarray[ " + j + "] = " + stack[j]);
 910         }
 911     }
 912 
 913     private static boolean isEmptyOrTrue(String property) {
 914         final String value = System.getProperty(property);
 915         if (value == null)
 916             return false;
 917         return value.isEmpty() || Boolean.parseBoolean(value);
 918     }
 919 }