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 
  31 import java.util.Arrays;
  32 import java.util.Map;
  33 import java.util.concurrent.ConcurrentHashMap;
  34 import java.util.concurrent.atomic.AtomicBoolean;
  35 
  36 
  37 /*
  38 *
  39 *
  40  */
  41 /**
  42  * TBD
  43  */
  44 public class Continuation {
  45     private static final boolean DEBUG = isEmptyOrTrue("java.lang.Continuation.debug");
  46 
  47     private static final byte FLAG_LAST_FRAME_INTERPRETED = 1;
  48 
  49     private static Thread currentKernelThread() {
  50         return Thread.currentKernelThread();
  51     }
  52 
  53     private static final int METADATA_SIZE = 2;
  54     static {
  55         // registerNatives();
  56     }
  57 
  58     private final ContinuationScope scope;
  59     private Runnable target;
  60     private Continuation parent; // null for native stack
  61     // addresses into vstack. only valid when mounted
  62     private long entrySP = 0;
  63     private long entryFP = 0;
  64     private long entryPC = 0;
  65 
  66     private int[] stack = null; // grows down
  67     private Object[] refStack = null;
  68 
  69     private long fp = 0; // an index into the h-stack if the top frame is interpreted, otherwise, the value of rbp
  70     private int sp = -1; // index into the h-stack
  71     private long pc = 0;
  72     private int refSP;
  73     private int maxSize; // maximal stack size when unpacked
  74 
  75     private short numFrames;
  76     private short numInterpretedFrames;
  77     
  78     private byte flags;
  79 
  80     private final AtomicBoolean mounted = new AtomicBoolean();
  81 
  82 
  83     private Continuation captor;
  84 
  85     private long[] nmethods = null; // grows up
  86     private int numNmethods = 0;
  87 
  88     private boolean done;
  89     // private boolean doneX;
  90 
  91     /**
  92      * TBD
  93      * @param scope TBD
  94      * @param target TBD
  95      */
  96     public Continuation(ContinuationScope scope, Runnable target) {
  97         this.scope = scope;
  98         this.target = target;
  99     }
 100 
 101     /**
 102      * TBD
 103      * @param scope TBD
 104      * @param target TBD
 105      * @param stackSize in bytes
 106      */
 107     public Continuation(ContinuationScope scope, int stackSize, Runnable target) {
 108         this(scope, target);
 109         getStacks(stackSize, stackSize, stackSize / 8);
 110     }
 111 
 112     /**
 113      * TBD
 114      * @param scope TBD
 115      * @return TBD
 116      */
 117     public static Continuation getCurrentContinuation(ContinuationScope scope) {
 118         Continuation cont = currentKernelThread().getContinuation();
 119         while (cont != null && cont.scope != scope)
 120             cont = cont.parent;
 121         return cont;
 122     }
 123 
 124     /**
 125      * TBD
 126      */
 127     @DontInline
 128     public final void run() {
 129         if (DEBUG) {
 130             System.out.println();
 131             System.out.println("++++++++++++++++++++++++++++++");
 132         }
 133 
 134         if (!mounted.compareAndSet(false, true))
 135             throw new IllegalStateException("Mounted!!!!");
 136 
 137         if (done)
 138             throw new IllegalStateException("Continuation terminated");
 139 
 140         Thread t = currentKernelThread();
 141         this.parent = t.getContinuation();
 142         t.setContinuation(this);
 143 
 144         if (DEBUG)
 145             walkFrames();
 146 
 147         if (captor != null && captor != parent)
 148             throw new IllegalStateException();
 149 
 150         int origRefSP = refSP;
 151         try {
 152             enter();
 153         } finally {
 154             try {
 155                 // assert done == doneX : "done: " + done + " doneX: " + doneX;
 156                 if (DEBUG)
 157                     System.out.println("run (after) sp: " + sp + " refSP: " + refSP + " maxSize: " + maxSize);
 158 
 159                 entrySP = 0;
 160                 entryFP = 0;
 161                 entryPC = 0;
 162                 if (DEBUG) {
 163                     if (origRefSP < refSP)
 164                         System.out.println("Nulling refs " + origRefSP + " (inclusive) - " + refSP + " (exclusive)");
 165                 }
 166                 for (int i = origRefSP; i < refSP; i++)
 167                     refStack[i] = null;
 168 
 169                 currentKernelThread().setContinuation(this.parent);
 170                 this.parent = null; // set captor
 171 
 172                 mounted.set(false);
 173 
 174                 if (DEBUG)
 175                     walkFrames();
 176             } catch (Exception e) {
 177                 e.printStackTrace();
 178                 System.exit(1);
 179             }
 180         }
 181     }
 182 
 183     /**
 184      * TBD
 185      */
 186     @DontInline
 187     private void enter() {
 188         // This method runs in the "entry frame".
 189         // A yield jumps to this method's caller as if returning from this method.
 190         try {
 191             if (stack == null || sp >= stack.length) { // is this the first run? (at this point we know !done)
 192                 this.entrySP = getSP();
 193                 enter0(); // make this an invokevirtual rather than invokeinterface. Otherwise it freaks the interpreter (currently solved by patching in native)
 194             } else
 195                 doContinue(); // intrinsic. Jumps into yield, as a return from doYield
 196         } finally {
 197             done = true;
 198             // assert doneX;
 199             if (DEBUG)
 200                 System.out.println(">>>>>>>> DONE <<<<<<<<<<<<<");
 201         }
 202     }
 203 
 204     @DontInline
 205     private void enter0() {
 206         target.run();
 207     }
 208 
 209     /**
 210      * TBD
 211      * @param scope TBD
 212      */
 213     @DontInline
 214     public static void yield(ContinuationScope scope) {
 215         Continuation cont = currentKernelThread().getContinuation();
 216         int scopes = 0;
 217         while (cont != null && cont.scope != scope) {
 218             cont = cont.parent;
 219             scopes++;
 220         }
 221         if (cont == null)
 222             throw new IllegalStateException("Not in scope " + scope);
 223         // System.out.println("SLEEP!");
 224         assert scopes == 0;
 225         doYield(scopes); // intrinsic
 226         cont.onContinue();
 227         // System.out.println("AWAKE!");
 228     }
 229 
 230     private void onPinned0(int reason) {
 231         onPinned(reason);
 232     }
 233 
 234     /**
 235      * TBD
 236      * @param reason TBD
 237      */
 238     protected void onPinned(int reason) {
 239         if (DEBUG)
 240             System.out.println("PINNED! " + reason);
 241         throw new IllegalStateException("Pinned: " + reason);
 242     }
 243 
 244     /**
 245      * TBD
 246      */
 247     protected void onContinue() {
 248         if (DEBUG)
 249             System.out.println("On continue");
 250     }
 251 
 252     /**
 253      * TBD
 254      * @return TBD
 255      */
 256     public boolean isDone() {
 257         return done;
 258     }
 259 
 260     private boolean isFlag(byte flag) {
 261         return (flags & flag) != 0;
 262     }
 263 
 264     // /**
 265     //  * TBD
 266     //  */
 267     // public void doneX() {
 268     //     // System.out.println("DONEX");
 269     //     this.doneX = true;
 270     // }
 271 
 272     private long readLong(int[] array, int index) {
 273         return (long)array[index] << 32 + array[index+1];
 274     }
 275 
 276     private void getStacks(int size, int oops, int frames) {
 277         try {
 278             getStack(size);
 279             getRefStack(oops);
 280         } catch (Throwable t) {
 281             t.printStackTrace();
 282             throw t;
 283         } 
 284     }
 285 
 286     // size is the size in bytes needed for newly frozen frames
 287     private void getStack(int size) {
 288         if (DEBUG)
 289             System.out.println("-- getStack size: " + size);
 290         size = size >> 2;
 291 
 292         if (this.stack == null) {
 293             this.stack = new int[size];
 294             // we let the native code update sp and fp accordingly
 295             // this.sp = stack.length + METADATA_SIZE;
 296         } else {
 297             int oldLength = stack.length;
 298             int offset = sp >= 0 ? sp - METADATA_SIZE : oldLength;
 299             int minLength = (oldLength - offset) + size;
 300             if (minLength > oldLength) {
 301                 int newLength = newCapacity(oldLength, minLength);
 302                 int[] newStack = new int[newLength];
 303                 int n = oldLength - offset;
 304                 if (n > 0)
 305                     System.arraycopy(stack, offset, newStack, newLength - n, n);
 306                 this.stack = newStack;
 307 
 308                 // we let the native code update sp and fp accordingly
 309                 // we need to preserve the same offset from the array's _end_
 310                 // this.sp = newLength - (oldLength - sp);
 311             }
 312         }
 313         if (DEBUG) {
 314             walkFrames();
 315             System.out.println("--- end of getStack");
 316         }
 317     }
 318 
 319     private void getRefStack(int size) {
 320         if (DEBUG)
 321             System.out.println("-- getRefStack: " + size);
 322         if (refStack == null) {
 323             this.refStack = new Object[size]; // TODO: nearest power of 2
 324             this.refSP = refStack.length;
 325         } else if (refSP < size) {
 326             int oldLength = refStack.length;
 327             int newLength = newCapacity(oldLength, (oldLength - refSP) + size);
 328             Object[] newRefStack = new Object[newLength];
 329             int n = oldLength - refSP;
 330             System.arraycopy(refStack, refSP, newRefStack, newLength - n, n);
 331             this.refStack = newRefStack;
 332             this.refSP = newLength - (oldLength - refSP);
 333         }
 334         if (DEBUG) {
 335             walkFrames();
 336             System.out.println("--- end of getRefStack: " + refStack.length);
 337         }
 338     }
 339 
 340     void resizeStack(int newLength) {
 341         if (DEBUG)
 342             System.out.println("-- resizeStack0 length: " + stack.length + " sp: " + sp + " fp: " + fp);
 343         int oldLength = stack.length;
 344         int offset = sp - METADATA_SIZE;
 345         int n = oldLength - offset;
 346         assert newLength >= n;
 347         int[] newStack = new int[newLength];
 348         System.arraycopy(stack, offset, newStack, newLength - n, n);
 349         this.stack = newStack;
 350 
 351         this.sp = fixDecreasingIndexAfterResize(sp, oldLength, newLength);
 352         if (isFlag(FLAG_LAST_FRAME_INTERPRETED))
 353             this.fp = fixDecreasingIndexAfterResize((int) fp, oldLength, newLength);
 354         if (DEBUG)
 355             System.out.println("-- resizeStack1 length: " + stack.length + " sp: " + sp + " fp: " + fp);
 356     }
 357 
 358     void resizeRefStack(int newLength) {
 359         if (DEBUG)
 360             System.out.println("-- resizeRefStack0 length: " + refStack.length + " refSP: " + refSP);
 361         int oldLength = refStack.length;
 362         int n = oldLength - refSP;
 363         assert newLength >= n;
 364         Object[] newRefStack = new Object[newLength];
 365         System.arraycopy(refStack, refSP, newRefStack, newLength - n, n);
 366         this.refStack = newRefStack;
 367 
 368         this.refSP = fixDecreasingIndexAfterResize(refSP, oldLength, newLength);
 369         if (DEBUG)
 370             System.out.println("-- resizeRefStack1 length: " + refStack.length + " refSP: " + refSP);
 371     }
 372 
 373     private int fixDecreasingIndexAfterResize(int index, int oldLength, int newLength) {
 374         return newLength - (oldLength - index);
 375     }
 376 
 377     private int newCapacity(int oldCapacity, int minCapacity) {
 378         // overflow-conscious code
 379         int newCapacity = oldCapacity + (oldCapacity >> 1);
 380         if (newCapacity - minCapacity <= 0) {
 381             if (minCapacity < 0) // overflow
 382                 throw new OutOfMemoryError();
 383             return minCapacity;
 384         }
 385         return newCapacity;
 386     }
 387 
 388     private void pushNmethod(long nmethod) {
 389         if (nmethods == null) {
 390             nmethods = new long[8];
 391         } else {
 392             if (numNmethods == nmethods.length) {
 393                 long[] newNmethods = new long[nmethods.length * 2];
 394                 System.arraycopy(nmethods, 0, newNmethods, 0, numNmethods);
 395                 this.nmethods = newNmethods;
 396             }
 397         }
 398         nmethods[numNmethods++] = nmethod;
 399     }
 400 
 401     private void popNmethod() {
 402         numNmethods--;
 403     }
 404 
 405     private static Map<Long, Integer> liveNmethods = new ConcurrentHashMap<>();
 406 
 407     private void processNmethods(int before, int after) {
 408 
 409     }
 410 
 411 
 412     @HotSpotIntrinsicCandidate
 413     private static long getSP() { throw new Error("Intrinsic not installed"); };
 414 
 415     @HotSpotIntrinsicCandidate
 416     private static long getFP() { throw new Error("Intrinsic not installed"); };
 417 
 418     @HotSpotIntrinsicCandidate
 419     private static long getPC() { throw new Error("Intrinsic not installed"); };
 420 
 421     @HotSpotIntrinsicCandidate
 422     private void doContinue() { throw new Error("Intrinsic not installed"); };
 423 
 424     @HotSpotIntrinsicCandidate
 425     private static void doYield(int scopes) { throw new Error("Intrinsic not installed"); };
 426 
 427     /**
 428      * TBD
 429      * @return TBD
 430      */
 431     public int getNumFrames() {
 432         return numFrames;
 433     }
 434 
 435     /**
 436      * TBD
 437      * @return TBD
 438      */
 439     public int getNumInterpretedFrames() {
 440         return numInterpretedFrames;
 441     }
 442 
 443     /**
 444      * TBD
 445      * @return TBD
 446      */
 447     public int getStackUsageInBytes() {
 448         return (stack != null ? stack.length - sp + 1 : 0) * 4;
 449     }
 450 
 451     /**
 452      * TBD
 453      * @return TBD
 454      */
 455     public int getNumRefs() {
 456         return (refStack != null ? refStack.length - refSP : 0);
 457     }
 458 
 459     /**
 460      * TBD
 461      * @return value
 462      */
 463     @HotSpotIntrinsicCandidate
 464     public static int runLevel() { return 0; }
 465 
 466     /**
 467      * TBD
 468      */
 469     public native void foo();
 470 
 471     // native methods
 472     private static native void registerNatives();
 473 
 474     private void walkFrames() {
 475         System.out.println("--------------");
 476         System.out.println("walkFrames:");
 477         if (stack == null) {
 478             System.out.println("Empty stack.");
 479             return;
 480         }
 481         int sp = this.sp;
 482         System.out.println("stack.length = " + stack.length + " sp: " + sp);
 483         System.out.println("++++++++++++");
 484         if (refStack != null) {
 485             System.out.println("refStack.length : " + refStack.length);
 486             for (int i = refStack.length - 1; i >= refSP; i--) {
 487                 Object obj = refStack[i];
 488                 System.out.println(i + ": " + (obj == this ? "this" : obj));
 489             }
 490         }
 491         System.out.println("##############");
 492     }
 493 
 494     private void dump() {
 495         System.out.println("Continuation@" + Long.toHexString(System.identityHashCode(this)));
 496         System.out.println("\tparent: " + parent);
 497         System.out.println("\tstack.length: " + stack.length);
 498         for (int i = 1; i <= 10; i++) {
 499             int j = stack.length - i;
 500             System.out.println("\tarray[ " + j + "] = " + stack[j]);
 501         }
 502     }
 503 
 504     private static boolean isEmptyOrTrue(String property) {
 505         final String value = System.getProperty(property);
 506         if (value == null)
 507             return false;
 508         return value.isEmpty() || Boolean.parseBoolean(value);
 509     }
 510 }