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 }