callcc.patch
author lstadler
Thu Nov 12 17:13:06 2009 +0100 (2 months ago)
changeset 70 c44366606742
parent 609c5f72a3578d
permissions -rw-r--r--
continuation bugfixes, coroutine prototype
        1 diff --git a/src/share/classes/javax/stack/CoRunnable.java b/src/share/classes/javax/stack/CoRunnable.java
        2 new file mode 100644
        3 --- /dev/null
        4 +++ b/src/share/classes/javax/stack/CoRunnable.java
        5 @@ -0,0 +1,4 @@
        6 +package javax.stack;
        7 +public interface CoRunnable {
        8 +	public Object run();
        9 +}
       10 \ No newline at end of file
       11 diff --git a/src/share/classes/javax/stack/Continuable.java b/src/share/classes/javax/stack/Continuable.java
       12 new file mode 100644
       13 --- /dev/null
       14 +++ b/src/share/classes/javax/stack/Continuable.java
       15 @@ -0,0 +1,32 @@
       16 +/*
       17 + * Copyright 2007-2009 Sun Microsystems, Inc.  All Rights Reserved.
       18 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       19 + *
       20 + * This code is free software; you can redistribute it and/or modify it
       21 + * under the terms of the GNU General Public License version 2 only, as
       22 + * published by the Free Software Foundation.
       23 + *
       24 + * This code is distributed in the hope that it will be useful, but WITHOUT
       25 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       26 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       27 + * version 2 for more details (a copy is included in the LICENSE file that
       28 + * accompanied this code).
       29 + *
       30 + * You should have received a copy of the GNU General Public License version
       31 + * 2 along with this work; if not, write to the Free Software Foundation,
       32 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       33 + *
       34 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       35 + * CA 95054 USA or visit www.sun.com if you need additional information or
       36 + * have any questions.
       37 + */
       38 +package javax.stack;
       39 +
       40 +import java.lang.annotation.*;
       41 +
       42 +@Documented
       43 +@Retention(RetentionPolicy.RUNTIME)
       44 +@Target(ElementType.METHOD)
       45 +public @interface Continuable {
       46 +	ContinuableAccess value() default ContinuableAccess.HIDDEN;
       47 +}
       48 diff --git a/src/share/classes/javax/stack/ContinuableAccess.java b/src/share/classes/javax/stack/ContinuableAccess.java
       49 new file mode 100644
       50 --- /dev/null
       51 +++ b/src/share/classes/javax/stack/ContinuableAccess.java
       52 @@ -0,0 +1,27 @@
       53 +/*
       54 + * Copyright 2007-2009 Sun Microsystems, Inc.  All Rights Reserved.
       55 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       56 + *
       57 + * This code is free software; you can redistribute it and/or modify it
       58 + * under the terms of the GNU General Public License version 2 only, as
       59 + * published by the Free Software Foundation.
       60 + *
       61 + * This code is distributed in the hope that it will be useful, but WITHOUT
       62 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       63 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       64 + * version 2 for more details (a copy is included in the LICENSE file that
       65 + * accompanied this code).
       66 + *
       67 + * You should have received a copy of the GNU General Public License version
       68 + * 2 along with this work; if not, write to the Free Software Foundation,
       69 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       70 + *
       71 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       72 + * CA 95054 USA or visit www.sun.com if you need additional information or
       73 + * have any questions.
       74 + */
       75 +package javax.stack;
       76 +
       77 +public enum ContinuableAccess {
       78 +		HIDDEN, READONLY, READWRITE
       79 +}
       80 diff --git a/src/share/classes/javax/stack/Continuation.java b/src/share/classes/javax/stack/Continuation.java
       81 new file mode 100644
       82 --- /dev/null
       83 +++ b/src/share/classes/javax/stack/Continuation.java
       84 @@ -0,0 +1,115 @@
       85 +/*
       86 + * Copyright 2007-2009 Sun Microsystems, Inc.  All Rights Reserved.
       87 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       88 + *
       89 + * This code is free software; you can redistribute it and/or modify it
       90 + * under the terms of the GNU General Public License version 2 only, as
       91 + * published by the Free Software Foundation.
       92 + *
       93 + * This code is distributed in the hope that it will be useful, but WITHOUT
       94 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       95 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       96 + * version 2 for more details (a copy is included in the LICENSE file that
       97 + * accompanied this code).
       98 + *
       99 + * You should have received a copy of the GNU General Public License version
      100 + * 2 along with this work; if not, write to the Free Software Foundation,
      101 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      102 + *
      103 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
      104 + * CA 95054 USA or visit www.sun.com if you need additional information or
      105 + * have any questions.
      106 + */
      107 +package javax.stack;
      108 +
      109 +/**
      110 + * @author lukas.stadler@jku.at
      111 + */
      112 +@SuppressWarnings("unused")
      113 +public class Continuation {
      114 +	private static class CONTINUATION_CAPTURED {
      115 +	}
      116 +
      117 +	public static final Object CAPTURED = CONTINUATION_CAPTURED.class;
      118 +	private Object data;
      119 +	private Thread thread;
      120 +
      121 +	private static native void registerNatives();
      122 +
      123 +	static {
      124 +		registerNatives();
      125 +	}
      126 +
      127 +	/**
      128 +	 * Saves the current thread state into the continuation object
      129 +	 * @return null if the continuation was saved or the value given to resume if it was resumed
      130 +	 */
      131 +	public final native Object save();
      132 +
      133 +	/**
      134 +	 * Restores the stack to the state that it had when the continuation was saved.
      135 +	 * @param retValue the value that the save method should return
      136 +	 */
      137 +	public final native void resume(Object retValue);
      138 +
      139 +	/**
      140 +	 * Yields to the given target continuation, passing along the current continuation
      141 +	 * @param target the continuation that will be reinstated
      142 +	 */
      143 +	@Continuable
      144 +	public static final void yield(Continuation target) {
      145 +		Continuation c = new Continuation();
      146 +		if (c.save() == null)
      147 +			target.resume(c);
      148 +	}
      149 +
      150 +	/*
      151 +	 * This method will be called by startDelimited
      152 +	 */
      153 +	@Continuable
      154 +	private static final Object startDelimitedInternal(DelimitedRunnable runnable, Object firstValue) {
      155 +		Continuation cont = new Continuation();
      156 +		Object ret = cont.save();
      157 +		if (ret == CAPTURED)
      158 +			runnable.run(cont, firstValue);
      159 +		return ret;
      160 +	}
      161 +
      162 +	public static final native Object startDelimited(DelimitedRunnable runnable, Object firstValue);
      163 +
      164 +	/*
      165 +	 * This method will be called by continueDelimited
      166 +	 */
      167 +	@Continuable
      168 +	private static final Object continueDelimitedInternal(Continuation cont, Object value) {
      169 +		cont.resume(value);
      170 +		return null;
      171 +	}
      172 +
      173 +	public static final native Object continueDelimited(Continuation continuation, Object value);
      174 +
      175 +	public final native Object dump();
      176 +
      177 +	private static final native Object storeFrameObject(Object retValue);
      178 +
      179 +	private static final native void storeFrameVoid();
      180 +
      181 +	private static final native boolean storeFrameBoolean(boolean retValue);
      182 +
      183 +	private static final native byte storeFrameByte(byte retValue);
      184 +
      185 +	private static final native char storeFrameChar(char retValue);
      186 +
      187 +	private static final native short storeFrameShort(short retValue);
      188 +
      189 +	private static final native int storeFrameInt(int retValue);
      190 +
      191 +	private static final native long storeFrameLong(long retValue);
      192 +
      193 +	private static final native float storeFrameFloat(float retValue);
      194 +
      195 +	private static final native double storeFrameDouble(double retValue);
      196 +
      197 +	private static final native Throwable unwind(Throwable e);
      198 +
      199 +}
      200 diff --git a/src/share/classes/javax/stack/ContinuationPermission.java b/src/share/classes/javax/stack/ContinuationPermission.java
      201 new file mode 100644
      202 --- /dev/null
      203 +++ b/src/share/classes/javax/stack/ContinuationPermission.java
      204 @@ -0,0 +1,34 @@
      205 +/*
      206 + * Copyright 2007-2009 Sun Microsystems, Inc.  All Rights Reserved.
      207 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      208 + *
      209 + * This code is free software; you can redistribute it and/or modify it
      210 + * under the terms of the GNU General Public License version 2 only, as
      211 + * published by the Free Software Foundation.
      212 + *
      213 + * This code is distributed in the hope that it will be useful, but WITHOUT
      214 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      215 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      216 + * version 2 for more details (a copy is included in the LICENSE file that
      217 + * accompanied this code).
      218 + *
      219 + * You should have received a copy of the GNU General Public License version
      220 + * 2 along with this work; if not, write to the Free Software Foundation,
      221 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      222 + *
      223 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
      224 + * CA 95054 USA or visit www.sun.com if you need additional information or
      225 + * have any questions.
      226 + */
      227 +package javax.stack;
      228 +
      229 +import java.security.BasicPermission;
      230 +
      231 +/* name is either "resumeSecure" or "resumeUnsecure" */
      232 +public class ContinuationPermission extends BasicPermission {
      233 +	private static final long serialVersionUID = -3608714945512368738L;
      234 +
      235 +	public ContinuationPermission(String name) {
      236 +		super(name);
      237 +	}
      238 +}
      239 \ No newline at end of file
      240 diff --git a/src/share/classes/javax/stack/Coroutine.java b/src/share/classes/javax/stack/Coroutine.java
      241 new file mode 100644
      242 --- /dev/null
      243 +++ b/src/share/classes/javax/stack/Coroutine.java
      244 @@ -0,0 +1,305 @@
      245 +package javax.stack;
      246 +import javax.stack.Continuable;
      247 +import javax.stack.Continuation;
      248 +
      249 +/**
      250 + * Experimental coroutine implementation using the continuation framework.
      251 + * 
      252 + * This class works very much like the {@link Thread} class, it can either be supplied with a {@link CoRunnable} upon creation or
      253 + * the run method can be overridden.<br/>
      254 + * All coroutines belonging to a context are kept in a doubly-linked ring data structure. Due to this there is an ordering of
      255 + * coroutines, and the next and previous coroutines are known.
      256 + * 
      257 + * @author lukas.stadler@jku.at
      258 + */
      259 +public class Coroutine {
      260 +	// exception used to terminate/close coroutines, created only once because exception creation is expensive
      261 +	private static final CoroutineExitException exitException = new CoroutineExitException();
      262 +
      263 +	// contains the actual stack frames, local variables, etc.
      264 +	private Continuation data;
      265 +
      266 +	// the context this coroutine belongs to
      267 +	private CoroutineContext context;
      268 +
      269 +	// the code that will be executed if run isn't overridden
      270 +	private CoRunnable runnable;
      271 +
      272 +	// the pointers for the doubly-linked ring data structure
      273 +	private Coroutine last;
      274 +	private Coroutine next;
      275 +
      276 +	// if this is non-null then an exception will be raised when this coroutine is resumed
      277 +	private RuntimeException exception;
      278 +
      279 +	/**
      280 +	 * Creates a new coroutine that belongs to the currently active {@link CoroutineContext}. This constructor is only useful if
      281 +	 * the run method is overridden. The coroutine will be inserted after the current coroutine of the context, or as the first
      282 +	 * coroutine if it is the first one added to the context.
      283 +	 * @throws IllegalThreadStateException if there is no current context
      284 +	 */
      285 +	public Coroutine() {
      286 +		initialize(CoroutineContext.getCurrent());
      287 +	}
      288 +
      289 +	/**
      290 +	 * Creates a new coroutine that belongs to the currently active {@link CoroutineContext}. The coroutine will execute the given
      291 +	 * CoRunnable (if it is non-null). The coroutine will be inserted after the current coroutine of the context, or as the first
      292 +	 * coroutine if it is the first one added to the context.
      293 +	 * @throws IllegalThreadStateException if there is no current context
      294 +	 */
      295 +	public Coroutine(CoRunnable runnable) {
      296 +		this.runnable = runnable;
      297 +		initialize(CoroutineContext.getCurrent());
      298 +	}
      299 +
      300 +	/**
      301 +	 * Creates a new coroutine that belongs to the given {@link CoroutineContext}. This constructor is only useful if the run
      302 +	 * method is overridden. The coroutine will be inserted after the current coroutine of the context, or as the first coroutine
      303 +	 * if it is the first one added to the context.
      304 +	 * @throws IllegalArgumentException if context is null
      305 +	 */
      306 +	public Coroutine(CoroutineContext context) {
      307 +		if (context == null)
      308 +			throw new IllegalArgumentException("context cannot be null");
      309 +		initialize(context);
      310 +	}
      311 +
      312 +	/**
      313 +	 * Creates a new coroutine that belongs to the given {@link CoroutineContext}. The coroutine will execute the given CoRunnable
      314 +	 * (if it is non-null). The coroutine will be inserted after the current coroutine of the context, or as the first coroutine
      315 +	 * if it is the first one added to the context.
      316 +	 * @throws IllegalArgumentException if context is null
      317 +	 */
      318 +	public Coroutine(CoroutineContext context, CoRunnable runnable) {
      319 +		if (context == null)
      320 +			throw new IllegalArgumentException("context cannot be null");
      321 +		this.runnable = runnable;
      322 +		initialize(context);
      323 +	}
      324 +
      325 +	/*
      326 +	 * Adds this coroutine into the doubly-linked ring of the given context.
      327 +	 */
      328 +	private void initialize(CoroutineContext context) {
      329 +		if (context == null)
      330 +			throw new IllegalThreadStateException("Cannot create Coroutine without CoroutineContext");
      331 +		context.coroutineAdded(this);
      332 +		this.context = context;
      333 +		if (context.getCurrentCoroutine() == null) {
      334 +			last = this;
      335 +			next = this;
      336 +			context.setCurrentCoroutine(this);
      337 +		}
      338 +		else {
      339 +			Coroutine current = context.getCurrentCoroutine();
      340 +			last = current.last;
      341 +			next = current;
      342 +			current.last = this;
      343 +			last.next = this;
      344 +		}
      345 +	}
      346 +
      347 +	/**
      348 +	 * Yields to the target coroutine, without changing the order of the coroutines
      349 +	 * @param target the coroutine that will be current after this call
      350 +	 * @param value the value that is passed to the target coroutine
      351 +	 * @return the value passed to the yield that makes this coroutine active again
      352 +	 */
      353 +	@Continuable
      354 +	private Object yieldToInternal(Coroutine target, Object value) {
      355 +		assert target != null;
      356 +		// System.out.println("yield to " + target + ", value="+value);
      357 +		context.setCurrentCoroutine(target);
      358 +		if (data == null)
      359 +			data = new Continuation();
      360 +		Object ret = data.save();
      361 +		try {
      362 +			// System.out.println("ret="+ret);
      363 +			if (ret == Continuation.CAPTURED) {
      364 +				if (target.data == null)
      365 +					context.startCoroutine.resume(value);
      366 +				else {
      367 +					// System.out.println("target resume");
      368 +					target.data.resume(value);
      369 +				}
      370 +				return null;
      371 +			}
      372 +			else
      373 +				return ret;
      374 +		}
      375 +		finally {
      376 +			if (exception != null) {
      377 +				// System.out.println("throwing exception in coroutine " + this + ": " + exception);
      378 +				throw exception;
      379 +			}
      380 +		}
      381 +	}
      382 +
      383 +	/**
      384 +	 * Closes the coroutine. This will resume the coroutine and throw a {@link CoroutineExitException}. If it propagates down to
      385 +	 * the starting method the coroutine will end. Thus coroutines can veto the close operation if they catch the
      386 +	 * CoroutineExitException.
      387 +	 */
      388 +	@Continuable
      389 +	public final void close() {
      390 +		if (isActive()) {
      391 +			exception = exitException;
      392 +			if (context == CoroutineContext.getCurrent()) {
      393 +				if (this == Coroutine.getCurrent())
      394 +					throw exception;
      395 +				else
      396 +					try {
      397 +						yieldTo(this, null);
      398 +					}
      399 +					catch (CoroutineExitException e) {
      400 +						// ignore
      401 +					}
      402 +			}
      403 +		}
      404 +	}
      405 +
      406 +	/**
      407 +	 * @return the currently active coroutine, or null if there is no current coroutine context
      408 +	 */
      409 +	public static Coroutine getCurrent() {
      410 +		CoroutineContext currentContext = CoroutineContext.getCurrent();
      411 +		return currentContext == null ? null : currentContext.getCurrentCoroutine();
      412 +	}
      413 +
      414 +	/**
      415 +	 * @return true if this coroutine hasn't ended yet
      416 +	 */
      417 +	public final boolean isActive() {
      418 +		assert (next != null) == (last != null);
      419 +		return next != null;
      420 +	}
      421 +
      422 +	/**
      423 +	 * @return the coroutine that is scheduled to be executed after this one
      424 +	 */
      425 +	public final Coroutine getNext() {
      426 +		return next;
      427 +	}
      428 +
      429 +	/**
      430 +	 * @return the coroutine that was executed before this one
      431 +	 */
      432 +	public final Coroutine getLast() {
      433 +		return last;
      434 +	}
      435 +
      436 +	/**
      437 +	 * @return the context this coroutine belongs to
      438 +	 */
      439 +	public final CoroutineContext getContext() {
      440 +		return context;
      441 +	}
      442 +
      443 +	/**
      444 +	 * Yields to the next scheduled coroutine (as determined by the scheduling behavior of the coroutine context)
      445 +	 * @param value the value to be passed to the next coroutine
      446 +	 * @return the value passed to this coroutine when it is resumed
      447 +	 */
      448 +	@Continuable
      449 +	public static Object yield(Object value) {
      450 +		Coroutine current = getCurrent();
      451 +		return yieldTo(current.context.scheduleNext(current), value);
      452 +	}
      453 +
      454 +	/**
      455 +	 * Yields to the last coroutine. This can be used to implement coroutines that generate values for a caller.
      456 +	 * @param value the value to be passed to the next coroutine
      457 +	 * @return the value passed to this coroutine when it is resumed
      458 +	 */
      459 +	@Continuable
      460 +	public static Object yieldReturn(Object value) {
      461 +		Coroutine current = getCurrent();
      462 +		// System.out.println("current=" + current);
      463 +		// System.out.println("last=" + current.last);
      464 +		return current.yieldToInternal(current.last, value);
      465 +	}
      466 +
      467 +	/**
      468 +	 * Yields to the next coroutine (ignoring the scheduling behavior of the coroutine context). This does not change the
      469 +	 * coroutine order.
      470 +	 * @param value the value to be passed to the next coroutine
      471 +	 * @return the value passed to this coroutine when it is resumed
      472 +	 */
      473 +	@Continuable
      474 +	public static Object yieldNext(Object value) {
      475 +		Coroutine current = getCurrent();
      476 +		return current.yieldToInternal(current.next, value);
      477 +	}
      478 +
      479 +	/**
      480 +	 * Yields to the given coroutine (ignoring the scheduling behavior of the coroutine context). The coroutine order will be
      481 +	 * changed so that the given coroutine is the next one.
      482 +	 * @param target the coroutine to be executed next
      483 +	 * @param value the value to be passed to the next coroutine
      484 +	 * @return the value passed to this coroutine when it is resumed
      485 +	 */
      486 +	@Continuable
      487 +	public static Object yieldTo(Coroutine target, Object value) {
      488 +		Coroutine current = getCurrent();
      489 +		if (target != current.next) {
      490 +			// remove target from its current position
      491 +			target.last.next = target.next;
      492 +			target.next.last = target.last;
      493 +			// add target at the current position
      494 +			target.last = current;
      495 +			target.next = current.next;
      496 +			target.last.next = target;
      497 +			target.next.last = target;
      498 +		}
      499 +		return current.yieldToInternal(target, value);
      500 +	}
      501 +
      502 +	/*
      503 +	 * This method starts the coroutine execution. It catches exception and handles a return from the run method.
      504 +	 */
      505 +	@Continuable
      506 +	final void start(Object value) {
      507 +		Object ret = null;
      508 +		RuntimeException e = null;
      509 +		try {
      510 +			ret = run(value);
      511 +		}
      512 +		catch (Throwable t) {
      513 +			if (t != exitException) {
      514 +				t.printStackTrace();
      515 +				e = new RuntimeException("coroutine terminated by exception", t);
      516 +			}
      517 +		}
      518 +		finally {
      519 +			try {
      520 +				context.coroutineRemoved(this);
      521 +			}
      522 +			catch (Throwable t) {
      523 +				t.printStackTrace();
      524 +			}
      525 +			if (last != this) {
      526 +				last.next = next;
      527 +				next.last = last;
      528 +				Coroutine temp = last;
      529 +				last = null;
      530 +				next = null;
      531 +				if (e != null)
      532 +					temp.exception = e;
      533 +				yieldToInternal(temp, ret);
      534 +			}
      535 +		}
      536 +	}
      537 +
      538 +	/**
      539 +	 * This implementation of the run method calls the CoRunnable.run method.
      540 +	 * @param value the value passed via yield
      541 +	 * @return the value that will be passed to the next coroutine
      542 +	 */
      543 +	protected Object run(Object value) {
      544 +		if (runnable != null)
      545 +			return runnable.run();
      546 +		else
      547 +			return null;
      548 +	}
      549 +}
      550 diff --git a/src/share/classes/javax/stack/CoroutineContext.java b/src/share/classes/javax/stack/CoroutineContext.java
      551 new file mode 100644
      552 --- /dev/null
      553 +++ b/src/share/classes/javax/stack/CoroutineContext.java
      554 @@ -0,0 +1,85 @@
      555 +package javax.stack;
      556 +import javax.stack.Continuable;
      557 +import javax.stack.Continuation;
      558 +
      559 +/**
      560 + * This class provides a context in which coroutines can be executed. Coroutines are added to a context either explicitly by
      561 + * passing the context to the coroutine's constructor or implicitly by creating a coroutine while another coroutine of the context
      562 + * is running.
      563 + * 
      564 + * @author lukas.stadler@jku.at
      565 + */
      566 +public class CoroutineContext {
      567 +	private static ThreadLocal<CoroutineContext> current = new ThreadLocal<CoroutineContext>();
      568 +
      569 +	private CoroutineContext last;
      570 +	private Coroutine currentCoroutine;
      571 +
      572 +	Continuation startCoroutine = new Continuation();
      573 +
      574 +	@Continuable
      575 +	private void runInternal(Object value) {
      576 +		Object ret = startCoroutine.save();
      577 +		if (ret == Continuation.CAPTURED) {
      578 +			// System.out.println("start initial coroutine: " + value);
      579 +			currentCoroutine.start(value);
      580 +		}
      581 +		else {
      582 +			// System.out.println("start subsequent coroutine: " + ret);
      583 +			currentCoroutine.start(ret);
      584 +		}
      585 +	}
      586 +
      587 +	/**
      588 +	 * Starts the coroutine execution. The first coroutine that was created for this context will be the first one to run.
      589 +	 */
      590 +	public void start(Object value) {
      591 +		if (currentCoroutine == null)
      592 +			throw new IllegalStateException("Cannot start a context with no coroutines");
      593 +		last = current.get();
      594 +		current.set(this);
      595 +		runInternal(value);
      596 +		current.set(last);
      597 +		this.last = null;
      598 +	}
      599 +
      600 +	/**
      601 +	 * This method will be called every time a coroutine is added to this context
      602 +	 */
      603 +	void coroutineAdded(Coroutine coroutine) {
      604 +	}
      605 +
      606 +	/**
      607 +	 * This method will be called every time a coroutine ends (and is thus removed)
      608 +	 */
      609 +	void coroutineRemoved(Coroutine coroutine) {
      610 +	}
      611 +
      612 +	/**
      613 +	 * This method can be overridden to change the default scheduling behavior of a coroutine context.
      614 +	 * @param current the coroutine that is currently executed
      615 +	 * @return the coroutine to be executed next
      616 +	 */
      617 +	Coroutine scheduleNext(Coroutine current) {
      618 +		return current.getNext();
      619 +	}
      620 +
      621 +	/**
      622 +	 * @return the current context of the current thread, or null if there is no active context
      623 +	 */
      624 +	public static CoroutineContext getCurrent() {
      625 +		return current.get();
      626 +	}
      627 +
      628 +	/**
      629 +	 * @return the currently executing coroutine of this context (or the coroutine that will be executed first, if the context
      630 +	 *         hasn't started yet)
      631 +	 */
      632 +	public Coroutine getCurrentCoroutine() {
      633 +		return currentCoroutine;
      634 +	}
      635 +
      636 +	void setCurrentCoroutine(Coroutine coroutine) {
      637 +		currentCoroutine = coroutine;
      638 +	}
      639 +}
      640 diff --git a/src/share/classes/javax/stack/CoroutineExitException.java b/src/share/classes/javax/stack/CoroutineExitException.java
      641 new file mode 100644
      642 --- /dev/null
      643 +++ b/src/share/classes/javax/stack/CoroutineExitException.java
      644 @@ -0,0 +1,5 @@
      645 +package javax.stack;
      646 +public class CoroutineExitException extends RuntimeException {
      647 +	private static final long serialVersionUID = -9155842264591995913L;
      648 +
      649 +}
      650 diff --git a/src/share/classes/javax/stack/DelimitedRunnable.java b/src/share/classes/javax/stack/DelimitedRunnable.java
      651 new file mode 100644
      652 --- /dev/null
      653 +++ b/src/share/classes/javax/stack/DelimitedRunnable.java
      654 @@ -0,0 +1,27 @@
      655 +/*
      656 + * Copyright 2007-2009 Sun Microsystems, Inc.  All Rights Reserved.
      657 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      658 + *
      659 + * This code is free software; you can redistribute it and/or modify it
      660 + * under the terms of the GNU General Public License version 2 only, as
      661 + * published by the Free Software Foundation.
      662 + *
      663 + * This code is distributed in the hope that it will be useful, but WITHOUT
      664 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      665 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      666 + * version 2 for more details (a copy is included in the LICENSE file that
      667 + * accompanied this code).
      668 + *
      669 + * You should have received a copy of the GNU General Public License version
      670 + * 2 along with this work; if not, write to the Free Software Foundation,
      671 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      672 + *
      673 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
      674 + * CA 95054 USA or visit www.sun.com if you need additional information or
      675 + * have any questions.
      676 + */
      677 +package javax.stack;
      678 +
      679 +public interface DelimitedRunnable {
      680 +	public void run(Continuation start, Object firstValue);
      681 +}
      682 diff --git a/src/share/classes/javax/stack/Fiber.java b/src/share/classes/javax/stack/Fiber.java
      683 new file mode 100644
      684 --- /dev/null
      685 +++ b/src/share/classes/javax/stack/Fiber.java
      686 @@ -0,0 +1,69 @@
      687 +/*
      688 + * Copyright 2007-2009 Sun Microsystems, Inc.  All Rights Reserved.
      689 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      690 + *
      691 + * This code is free software; you can redistribute it and/or modify it
      692 + * under the terms of the GNU General Public License version 2 only, as
      693 + * published by the Free Software Foundation.
      694 + *
      695 + * This code is distributed in the hope that it will be useful, but WITHOUT
      696 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      697 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      698 + * version 2 for more details (a copy is included in the LICENSE file that
      699 + * accompanied this code).
      700 + *
      701 + * You should have received a copy of the GNU General Public License version
      702 + * 2 along with this work; if not, write to the Free Software Foundation,
      703 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      704 + *
      705 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
      706 + * CA 95054 USA or visit www.sun.com if you need additional information or
      707 + * have any questions.
      708 + */
      709 +package javax.stack;
      710 +
      711 +public abstract class Fiber {
      712 +	private Continuation returnContinuation;
      713 +	private Continuation continuation = new Continuation();
      714 +
      715 +	private class FiberRunnable implements DelimitedRunnable {
      716 +		@Continuable
      717 +		public final void run(Continuation start, Object firstValue) {
      718 +			returnContinuation = start;
      719 +			Object lastRetValue;
      720 +			try {
      721 +				lastRetValue = generate(firstValue);
      722 +			}
      723 +			catch (RuntimeException e) {
      724 +				e.printStackTrace();
      725 +				lastRetValue = null;
      726 +			}
      727 +			returnContinuation = null;
      728 +			start.resume(lastRetValue);
      729 +		}
      730 +	}
      731 +
      732 +	@Continuable
      733 +	protected abstract Object generate(Object firstIn);
      734 +
      735 +	@Continuable
      736 +	protected Object yield(Object returnValue) {
      737 +		Object o = continuation.save();
      738 +		if (o == Continuation.CAPTURED)
      739 +			returnContinuation.resume(returnValue);
      740 +		return o;
      741 +	}
      742 +
      743 +	@Continuable
      744 +	public Object resume(Object value) {
      745 +		if (returnContinuation == null)
      746 +			return Continuation.startDelimited(new FiberRunnable(), value);
      747 +		else
      748 +			return Continuation.continueDelimited(continuation, value);
      749 +	}
      750 +
      751 +	public void dump() {
      752 +		continuation.dump();
      753 +	}
      754 +
      755 +}
      756 diff --git a/test/javax/stack/ContinuationTest1.java b/test/javax/stack/ContinuationTest1.java
      757 new file mode 100644
      758 --- /dev/null
      759 +++ b/test/javax/stack/ContinuationTest1.java
      760 @@ -0,0 +1,229 @@
      761 +package javax.stack;
      762 +
      763 +import static org.junit.Assert.assertEquals;
      764 +
      765 +import org.junit.Before;
      766 +import org.junit.Test;
      767 +
      768 +public class ContinuationTest1 extends ContinuationTestTools {
      769 +
      770 +	public ContinuationTest1() {
      771 +		interpreted = this;
      772 +		if (compiled == null)
      773 +			compiled = new ContinuationTest1Compiled();
      774 +	}
      775 +
      776 +	@Before
      777 +	public void setUp() throws Exception {
      778 +		clearActions();
      779 +	}
      780 +
      781 +	@Continuable(ContinuableAccess.HIDDEN)
      782 +	public Object subSave(Continuation cont, int a, int b, int c, int d, int e) {
      783 +		int i;
      784 +		cont.save();
      785 +		i = 0;
      786 +		i += a;
      787 +		i += b;
      788 +		i += c;
      789 +		i += d;
      790 +		i += e;
      791 +		assertEquals(15, i);
      792 +		assertEquals(ContinuationTest1.class, this.getClass());
      793 +		assertEquals(Continuation.class, cont.getClass());
      794 +		return 10102;
      795 +	}
      796 +
      797 +	private int cnt = 0;
      798 +
      799 +	@Test
      800 +	@Continuable(ContinuableAccess.HIDDEN)
      801 +	public void testRec() {
      802 +		Integer i = 100000;
      803 +		Continuation cont = new Continuation();
      804 +		assertEquals(10102, compiled.subSave(cont, 1, 2, 3, 4, 5));
      805 +		assertEquals(ContinuationTest1.class, this.getClass());
      806 +		if (cnt++ == 0) {
      807 +			cont.resume(null);
      808 +		}
      809 +		assertEquals(100000, i);
      810 +	}
      811 +
      812 +	@Test
      813 +	@Continuable(ContinuableAccess.HIDDEN)
      814 +	public void testSimple() {
      815 +		Continuation cont = new Continuation();
      816 +		action(1);
      817 +		if (cont.save() == Continuation.CAPTURED) {
      818 +			action(2);
      819 +			cont.resume(null);
      820 +			action(100);
      821 +		}
      822 +		else
      823 +			action(3);
      824 +
      825 +		action(4);
      826 +		assertActions(new int[] { 1, 2, 3, 4 });
      827 +	}
      828 +
      829 +	@Continuable(ContinuableAccess.HIDDEN)
      830 +	int intSub(Continuation c, int code1, Integer code2, int depth) {
      831 +		if (depth == 0) {
      832 +			action(code1);
      833 +			intTemp = code1;
      834 +			objTemp = code2;
      835 +			thisTemp = this;
      836 +			lastSaveReturn = c.save();
      837 +			action(code2);
      838 +			assertEquals(thisTemp, this);
      839 +			assertEquals(intTemp, code1);
      840 +			assertEquals(objTemp, code2);
      841 +		}
      842 +		else {
      843 +			if ((depth & 3) == 0)
      844 +				compiled.intSub(c, code1, code2, depth - 1);
      845 +			else
      846 +				intSub(c, code1, code2, depth - 1);
      847 +		}
      848 +		return 101;
      849 +	}
      850 +
      851 +	@Continuable(ContinuableAccess.HIDDEN)
      852 +	Integer objSub(Continuation c, int code1, Integer code2, int depth) {
      853 +		if (depth == 0) {
      854 +			action(code1);
      855 +			intTemp = code1;
      856 +			objTemp = code2;
      857 +			thisTemp = this;
      858 +			lastSaveReturn = c.save();
      859 +			assertEquals(thisTemp, this);
      860 +			assertEquals(intTemp, code1);
      861 +			assertEquals(objTemp, code2);
      862 +			action(code2);
      863 +		}
      864 +		else {
      865 +			if ((depth & 3) == 0)
      866 +				compiled.objSub(c, code1, code2, depth - 1);
      867 +			else
      868 +				objSub(c, code1, code2, depth - 1);
      869 +		}
      870 +		return 201;
      871 +	}
      872 +
      873 +	@Continuable(ContinuableAccess.HIDDEN)
      874 +	void resumeRec(Continuation c, Object retValue, int depth) {
      875 +		if (depth == 0) {
      876 +			c.resume(null);
      877 +		}
      878 +		else {
      879 +			if ((depth & 3) == 0)
      880 +				compiled.resumeRec(c, retValue, depth - 1);
      881 +			else
      882 +				resumeRec(c, retValue, depth - 1);
      883 +		}
      884 +	}
      885 +
      886 +	@Continuable(ContinuableAccess.HIDDEN)
      887 +	public void testParamsReturnInternal(int resumeDepth, int saveDepth) {
      888 +		Continuation c = new Continuation();
      889 +		action(1);
      890 +		action(intSub(c, 2, 3, saveDepth));
      891 +		if (lastSaveReturn == Continuation.CAPTURED) {
      892 +			action(4);
      893 +			if (resumeDepth == 0)
      894 +				c.resume(null);
      895 +			else
      896 +				resumeRec(c, null, resumeDepth);
      897 +			action(100);
      898 +		}
      899 +		action(5);
      900 +		action(compiled.intSub(c, 6, 7, saveDepth));
      901 +		if (lastSaveReturn == Continuation.CAPTURED) {
      902 +			action(8);
      903 +			if (resumeDepth == 0)
      904 +				c.resume(null);
      905 +			else
      906 +				resumeRec(c, null, resumeDepth);
      907 +			action(100);
      908 +		}
      909 +		action(9);
      910 +		action(objSub(c, 10, 11, saveDepth));
      911 +		if (lastSaveReturn == Continuation.CAPTURED) {
      912 +			action(12);
      913 +			if (resumeDepth == 0)
      914 +				c.resume(null);
      915 +			else
      916 +				resumeRec(c, null, resumeDepth);
      917 +			action(100);
      918 +		}
      919 +		action(13);
      920 +		action(compiled.objSub(c, 14, 15, saveDepth));
      921 +		if (lastSaveReturn == Continuation.CAPTURED) {
      922 +			action(16);
      923 +			if (resumeDepth == 0)
      924 +				c.resume(null);
      925 +			else
      926 +				resumeRec(c, null, resumeDepth);
      927 +			action(100);
      928 +		}
      929 +		action(17);
      930 +		assertActions(new int[] { 1, 2, 3, 101, 4, 3, 101, 5, 6, 7, 102, 8, 7, 102, 9, 10, 11, 201, 12, 11, 201, 13, 14, 15, 202,
      931 +				16, 15, 202, 17 });
      932 +	}
      933 +
      934 +	@Test
      935 +	public void testParamsReturn() {
      936 +		for (int i2 = 1; i2 < 50; i2++) {
      937 +			for (int i = 0; i < 50; i++) {
      938 +//				System.out.println("resume: " + i + ", save: " + i2);
      939 +				pushActions();
      940 +				testParamsReturnInternal(i, i2);
      941 +				popActions();
      942 +			}
      943 +		}
      944 +	}
      945 +
      946 +	@Test
      947 +	@Continuable(ContinuableAccess.HIDDEN)
      948 +	public void testManyParams() {
      949 +		Continuation cont = new Continuation();
      950 +		if (manyParamsSub(cont, 5, true, false, 1, (short) 2, (byte) 3, (char) 4, (long) 5, 6, (short) 7, (byte) 8, (char) 9,
      951 +				(long) 10, (float) 11, (double) 12, (float) 13, (double) 14, 15, 16, 17, 18, 19, 20) == Continuation.CAPTURED)
      952 +			cont.resume(null);
      953 +	}
      954 +
      955 +	@Continuable(ContinuableAccess.HIDDEN)
      956 +	private Object manyParamsSub(Continuation cont, int depth, boolean a, boolean b, int c, short d, byte e, char f,
      957 +			long g, Integer h, Short i, Byte j, Character k, Long l, float m, double n, Float o, Double p, Integer q, int r,
      958 +			int s, int t, int u, int v) {
      959 +		final Object ret;
      960 +		if (depth > 0)
      961 +			ret = manyParamsSub(cont, depth - 1, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v);
      962 +		else
      963 +			ret = cont.save();
      964 +		assertEquals(cont.getClass(), Continuation.class);
      965 +		assertEquals(a, true);
      966 +		assertEquals(b, false);
      967 +		assertEquals(c, 1);
      968 +		assertEquals(d, (short) 2);
      969 +		assertEquals(e, (byte) 3);
      970 +		assertEquals(f, (char) 4);
      971 +		assertEquals(g, (long) 5);
      972 +		assertEquals(h, 6);
      973 +		assertEquals(i, (short) 7);
      974 +		assertEquals(j, (byte) 8);
      975 +		assertEquals(k, (char) 9);
      976 +		assertEquals(l, (long) 10);
      977 +		assertEquals(m, (float) 11);
      978 +		assertEquals(n, (double) 12);
      979 +		assertEquals(o, (float) 13);
      980 +		assertEquals(p, (double) 14);
      981 +		assertEquals(q, 15);
      982 +		assertEquals(r, 16);
      983 +		assertEquals(s, 17);
      984 +		assertEquals(t, 18);
      985 +		assertEquals(u, 19);
      986 +		assertEquals(v, 20);
      987 +		return ret;
      988 +	}
      989 +}
      990 diff --git a/test/javax/stack/ContinuationTest1Compiled.java b/test/javax/stack/ContinuationTest1Compiled.java
      991 new file mode 100644
      992 --- /dev/null
      993 +++ b/test/javax/stack/ContinuationTest1Compiled.java
      994 @@ -0,0 +1,225 @@
      995 +package javax.stack;
      996 +
      997 +import static org.junit.Assert.assertEquals;
      998 +
      999 +import org.junit.Before;
     1000 +import org.junit.Test;
     1001 +
     1002 +public class ContinuationTest1Compiled extends ContinuationTestTools {
     1003 +
     1004 +	public ContinuationTest1Compiled() {
     1005 +		compiled = this;
     1006 +		if (interpreted == null)
     1007 +			interpreted = new ContinuationTest1();
     1008 +	}
     1009 +
     1010 +	@Before
     1011 +	public void setUp() throws Exception {
     1012 +		clearActions();
     1013 +	}
     1014 +
     1015 +	@Continuable(ContinuableAccess.HIDDEN)
     1016 +	public Object subSave(Continuation cont, int a, int b, int c, int d, int e) {
     1017 +		int i;
     1018 +		cont.save();
     1019 +		i = 0;
     1020 +		i += a;
     1021 +		i += b;
     1022 +		i += c;
     1023 +		i += d;
     1024 +		i += e;
     1025 +		assertEquals(15, i);
     1026 +		assertEquals(ContinuationTest1Compiled.class, this.getClass());
     1027 +		assertEquals(Continuation.class, cont.getClass());
     1028 +		return 10102;
     1029 +	}
     1030 +
     1031 +	private int cnt = 0;
     1032 +
     1033 +	@Test
     1034 +	@Continuable(ContinuableAccess.HIDDEN)
     1035 +	public void testRec() {
     1036 +		Integer i = 100000;
     1037 +		Continuation cont = new Continuation();
     1038 +		assertEquals(10102, interpreted.subSave(cont, 1, 2, 3, 4, 5));
     1039 +		assertEquals(ContinuationTest1Compiled.class, this.getClass());
     1040 +		if (cnt++ == 0)
     1041 +			cont.resume(null);
     1042 +		assertEquals(100000, i);
     1043 +	}
     1044 +
     1045 +	@Test
     1046 +	@Continuable(ContinuableAccess.HIDDEN)
     1047 +	public void testSimple() {
     1048 +		Continuation cont = new Continuation();
     1049 +		action(1);
     1050 +		if (cont.save() == Continuation.CAPTURED) {
     1051 +			action(2);
     1052 +			cont.resume(null);
     1053 +			action(100);
     1054 +		}
     1055 +		else
     1056 +			action(3);
     1057 +
     1058 +		action(4);
     1059 +		assertActions(new int[] { 1, 2, 3, 4 });
     1060 +	}
     1061 +
     1062 +	@Continuable(ContinuableAccess.HIDDEN)
     1063 +	int intSub(Continuation c, int code1, Integer code2, int depth) {
     1064 +		if (depth == 0) {
     1065 +			action(code1);
     1066 +			intTemp = code1;
     1067 +			objTemp = code2;
     1068 +			lastSaveReturn = c.save();
     1069 +			assertEquals(intTemp, code1);
     1070 +			assertEquals(objTemp, code2);
     1071 +			action(code2);
     1072 +		}
     1073 +		else {
     1074 +			if ((depth & 3) == 0)
     1075 +				interpreted.intSub(c, code1, code2, depth - 1);
     1076 +			else
     1077 +				intSub(c, code1, code2, depth - 1);
     1078 +		}
     1079 +		return 102;
     1080 +	}
     1081 +
     1082 +	@Continuable(ContinuableAccess.HIDDEN)
     1083 +	Integer objSub(Continuation c, int code1, Integer code2, int depth) {
     1084 +		if (depth == 0) {
     1085 +			action(code1);
     1086 +			intTemp = code1;
     1087 +			objTemp = code2;
     1088 +			lastSaveReturn = c.save();
     1089 +			assertEquals(intTemp, code1);
     1090 +			assertEquals(objTemp, code2);
     1091 +			action(code2);
     1092 +		}
     1093 +		else {
     1094 +			if ((depth & 3) == 0)
     1095 +				interpreted.objSub(c, code1, code2, depth - 1);
     1096 +			else
     1097 +				objSub(c, code1, code2, depth - 1);
     1098 +		}
     1099 +		return 202;
     1100 +	}
     1101 +
     1102 +	@Continuable(ContinuableAccess.HIDDEN)
     1103 +	void resumeRec(Continuation c, Object retValue, int depth) {
     1104 +		if (depth == 0) {
     1105 +			c.resume(null);
     1106 +		}
     1107 +		else {
     1108 +			if ((depth & 3) == 0)
     1109 +				interpreted.resumeRec(c, retValue, depth - 1);
     1110 +			else
     1111 +				resumeRec(c, retValue, depth - 1);
     1112 +		}
     1113 +	}
     1114 +
     1115 +	@Continuable(ContinuableAccess.HIDDEN)
     1116 +	public void testParamsReturnInternal(int resumeDepth, int saveDepth) {
     1117 +		Continuation c = new Continuation();
     1118 +		action(1);
     1119 +		action(interpreted.intSub(c, 2, 3, saveDepth));
     1120 +		if (lastSaveReturn == Continuation.CAPTURED) {
     1121 +			action(4);
     1122 +			if (resumeDepth == 0)
     1123 +				c.resume(null);
     1124 +			else
     1125 +				resumeRec(c, null, resumeDepth);
     1126 +			action(100);
     1127 +		}
     1128 +		action(5);
     1129 +		action(intSub(c, 6, 7, saveDepth));
     1130 +		if (lastSaveReturn == Continuation.CAPTURED) {
     1131 +			action(8);
     1132 +			if (resumeDepth == 0)
     1133 +				c.resume(null);
     1134 +			else
     1135 +				resumeRec(c, null, resumeDepth);
     1136 +			action(100);
     1137 +		}
     1138 +		action(9);
     1139 +		action(interpreted.objSub(c, 10, 11, saveDepth));
     1140 +		if (lastSaveReturn == Continuation.CAPTURED) {
     1141 +			action(12);
     1142 +			if (resumeDepth == 0)
     1143 +				c.resume(null);
     1144 +			else
     1145 +				resumeRec(c, null, resumeDepth);
     1146 +			action(100);
     1147 +		}
     1148 +		action(13);
     1149 +		action(objSub(c, 14, 15, saveDepth));
     1150 +		if (lastSaveReturn == Continuation.CAPTURED) {
     1151 +			action(16);
     1152 +			if (resumeDepth == 0)
     1153 +				c.resume(null);
     1154 +			else
     1155 +				resumeRec(c, null, resumeDepth);
     1156 +			action(100);
     1157 +		}
     1158 +		action(17);
     1159 +		assertActions(new int[] { 1, 2, 3, 101, 4, 3, 101, 5, 6, 7, 102, 8, 7, 102, 9, 10, 11, 201, 12, 11, 201, 13, 14, 15, 202,
     1160 +				16, 15, 202, 17 });
     1161 +	}
     1162 +
     1163 +	@Test
     1164 +	public void testParamsReturn() {
     1165 +		for (int i2 = 0; i2 < 50; i2++) {
     1166 +			for (int i = 0; i < 50; i++) {
     1167 +				// System.out.println("resume: " + i + ", save: " + i2);
     1168 +				pushActions();
     1169 +				testParamsReturnInternal(i, i2);
     1170 +				popActions();
     1171 +			}
     1172 +		}
     1173 +	}
     1174 +
     1175 +	@Test
     1176 +	@Continuable(ContinuableAccess.HIDDEN)
     1177 +	public void testManyParams() {
     1178 +		Continuation cont = new Continuation();
     1179 +		if (manyParamsSub(cont, 5, true, false, 1, (short) 2, (byte) 3, (char) 4, (long) 5, 6, (short) 7, (byte) 8, (char) 9,
     1180 +				(long) 10, (float) 11, (double) 12, (float) 13, (double) 14, 15, 16, 17, 18, 19, 20) == Continuation.CAPTURED)
     1181 +			cont.resume(null);
     1182 +	}
     1183 +
     1184 +	@Continuable(ContinuableAccess.HIDDEN)
     1185 +	private Object manyParamsSub(Continuation cont, int depth, boolean a, boolean b, int c, short d, byte e, char f, long g,
     1186 +			Integer h, Short i, Byte j, Character k, Long l, float m, double n, Float o, Double p, Integer q, int r, int s,
     1187 +			int t, int u, int v) {
     1188 +		final Object ret;
     1189 +		if (depth > 0)
     1190 +			ret = manyParamsSub(cont, depth - 1, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v);
     1191 +		else
     1192 +			ret = cont.save();
     1193 +		assertEquals(cont.getClass(), Continuation.class);
     1194 +		assertEquals(a, true);
     1195 +		assertEquals(b, false);
     1196 +		assertEquals(c, 1);
     1197 +		assertEquals(d, (short) 2);
     1198 +		assertEquals(e, (byte) 3);
     1199 +		assertEquals(f, (char) 4);
     1200 +		assertEquals(g, (long) 5);
     1201 +		assertEquals(h, 6);
     1202 +		assertEquals(i, (short) 7);
     1203 +		assertEquals(j, (byte) 8);
     1204 +		assertEquals(k, (char) 9);
     1205 +		assertEquals(l, (long) 10);
     1206 +		assertEquals(m, (float) 11);
     1207 +		assertEquals(n, (double) 12);
     1208 +		assertEquals(o, (float) 13);
     1209 +		assertEquals(p, (double) 14);
     1210 +		assertEquals(q, 15);
     1211 +		assertEquals(r, 16);
     1212 +		assertEquals(s, 17);
     1213 +		assertEquals(t, 18);
     1214 +		assertEquals(u, 19);
     1215 +		assertEquals(v, 20);
     1216 +		return ret;
     1217 +	}
     1218 +
     1219 +}
     1220 diff --git a/test/javax/stack/ContinuationTestTools.java b/test/javax/stack/ContinuationTestTools.java
     1221 new file mode 100644
     1222 --- /dev/null
     1223 +++ b/test/javax/stack/ContinuationTestTools.java
     1224 @@ -0,0 +1,53 @@
     1225 +package javax.stack;
     1226 +
     1227 +import static org.junit.Assert.assertEquals;
     1228 +
     1229 +import java.util.ArrayList;
     1230 +import java.util.Stack;
     1231 +
     1232 +public class ContinuationTestTools {
     1233 +	private static Stack<ArrayList<Integer>> actions = new Stack<ArrayList<Integer>>();
     1234 +
     1235 +	protected static Object thisTemp;
     1236 +	protected static Object objTemp;
     1237 +	protected static int intTemp;
     1238 +	protected static Object lastSaveReturn = null;
     1239 +
     1240 +	protected static ContinuationTest1Compiled compiled;
     1241 +	protected static ContinuationTest1 interpreted;
     1242 +
     1243 +	protected static void clearActions() {
     1244 +		actions.clear();
     1245 +		actions.add(new ArrayList<Integer>());
     1246 +	}
     1247 +
     1248 +	protected static void pushActions() {
     1249 +		actions.push(new ArrayList<Integer>());
     1250 +	}
     1251 +
     1252 +	protected static void popActions() {
     1253 +		actions.pop();
     1254 +	}
     1255 +
     1256 +	protected static void action(int code) {
     1257 +		actions.peek().add(code);
     1258 +	}
     1259 +
     1260 +	protected static void assertActions(int[] expectedActions) {
     1261 +		if (expectedActions.length == actions.peek().size()) {
     1262 +			for (int i = 0; i < expectedActions.length; i++)
     1263 +				assertEquals(expectedActions[i], actions.peek().get(i));
     1264 +		}
     1265 +		else {
     1266 +			System.out.print("expected: ");
     1267 +			for (int i = 0; i < expectedActions.length; i++)
     1268 +				System.out.print(expectedActions[i] + " ");
     1269 +			System.out.print("\nactual: ");
     1270 +			for (int i = 0; i < actions.peek().size(); i++)
     1271 +				System.out.print(actions.peek().get(i) + " ");
     1272 +
     1273 +			assertEquals(expectedActions.length, actions.peek().size());
     1274 +		}
     1275 +	}
     1276 +
     1277 +}
     1278 diff --git a/test/javax/stack/DelimitedTest.java b/test/javax/stack/DelimitedTest.java
     1279 new file mode 100644
     1280 --- /dev/null
     1281 +++ b/test/javax/stack/DelimitedTest.java
     1282 @@ -0,0 +1,47 @@
     1283 +package javax.stack;
     1284 +
     1285 +import java.io.IOException;
     1286 +
     1287 +import org.junit.Test;
     1288 +
     1289 +public class DelimitedTest {
     1290 +
     1291 +	class TestFiber extends Fiber {
     1292 +		@Continuable(ContinuableAccess.HIDDEN)
     1293 +		protected Object generate(Object value) {
     1294 +			int i = (Integer) value;
     1295 +			while (true) {
     1296 +				i += (Integer) yield(i);
     1297 +			}
     1298 +		}
     1299 +	}
     1300 +
     1301 +	private static final long COUNT = 5000;
     1302 +	private static final long COUNT2 = 100;
     1303 +
     1304 +	@Test
     1305 +	@Continuable(ContinuableAccess.HIDDEN)
     1306 +	public void testRec() throws IOException {
     1307 +		try {
     1308 +			Continuation cont = new Continuation();
     1309 +			cont.save();
     1310 +			do {
     1311 +				long time = System.nanoTime();
     1312 +				for (int i2 = 0; i2 < COUNT2; i2++) {
     1313 +					TestFiber test = new TestFiber();
     1314 +					for (int i = 0; i < COUNT; i++) {
     1315 +						Object ret = test.resume(i);
     1316 +						int result = (Integer) ret;
     1317 +					}
     1318 +				}
     1319 +				long tps = (long) (COUNT * COUNT2 * 1000000000d / (System.nanoTime() - time));
     1320 +				System.out.println(tps);
     1321 +			}
     1322 +			while (true);
     1323 +		}
     1324 +		catch (Throwable e) {
     1325 +			e.printStackTrace();
     1326 +		}
     1327 +	}
     1328 +
     1329 +}
     1330 \ No newline at end of file