diff --git a/src/share/classes/java/dyn/AsymCoroutine.java b/src/share/classes/java/dyn/AsymCoroutine.java
new file mode 100644
index 0000000000000000000000000000000000000000..3a34d026e764ea01da0c03ceea3c4e9a75c23ac9
--- /dev/null
+++ b/src/share/classes/java/dyn/AsymCoroutine.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.dyn;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Implementation of asymmetric coroutines. A AsymCoroutine can be called by any coroutine (Coroutine and AsymCoroutine) and will return to
+ * its caller upon {@link #ret()}.
+ *
+ * Similar to {@link Thread} there are two ways to implement a AsymCoroutine: either by implementing a subclass of AsymCoroutine (and
+ * overriding {@link #run()}) or by providing a {@link Runnable} to the AsymCoroutine constructor.
+ *
+ * An implementation of a simple AsymCoroutine that always returns the average of all its previous inputs might look like this:
+ *
+ *
+ *
+ *
+ *
+ * class Average extends AsymCoroutine<Integer, Integer> {
+ * public Integer run(Integer value) {
+ * int sum = value;
+ * int count = 1;
+ * while (true) {
+ * sum += ret(sum / count++);
+ * }
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ * This AsymCoroutine can be invoked either by reading and writing the {@link #output} and {@link #input} fields and invoking the
+ * {@link #call()} method:
+ *
+ *
+ *
+ *
+ * Average avg = new Average();
+ * avg.input = 10;
+ * avg.call();
+ * System.out.println(avg.output);
+ *
+ *
+ *
+ *
+ * Another way to invoke this AsymCoroutine is by using the shortcut {@link #call(Object)} methods:
+ *
+ *
+ *
+ *
+ * Average avg = new Average();
+ * System.out.println(avg.call(10));
+ *
+ *
+ *
+ *
+ *
+ * @author Lukas Stadler
+ *
+ * @param
+ * input type of this AsymCoroutine, Void if no input value is expected
+ * @param
+ * output type of this AsymCoroutine, Void if no output is produced
+ */
+public class AsymCoroutine extends CoroutineBase implements Iterable {
+ CoroutineBase caller;
+
+ private final AsymRunnable super InT, ? extends OutT> target;
+
+ private InT input;
+ private OutT output;
+
+ public AsymCoroutine() {
+ target = null;
+ threadSupport.addCoroutine(this, -1);
+ }
+
+ public AsymCoroutine(long stacksize) {
+ target = null;
+ threadSupport.addCoroutine(this, stacksize);
+ }
+
+ public AsymCoroutine(AsymRunnable super InT, ? extends OutT> target) {
+ this.target = target;
+ threadSupport.addCoroutine(this, -1);
+ }
+
+ public AsymCoroutine(AsymRunnable super InT, ? extends OutT> target, long stacksize) {
+ this.target = target;
+ threadSupport.addCoroutine(this, stacksize);
+ }
+
+ public final OutT call(final InT input) {
+ this.input = input;
+ Thread.currentThread().getCoroutineSupport().asymmetricCall(this);
+ return output;
+ }
+
+ public final OutT call() {
+ Thread.currentThread().getCoroutineSupport().asymmetricCall(this);
+ return output;
+ }
+
+ public final InT ret(final OutT value) {
+ output = value;
+ Thread.currentThread().getCoroutineSupport().asymmetricReturn(this);
+ return input;
+ }
+
+ public final InT ret() {
+ Thread.currentThread().getCoroutineSupport().asymmetricReturn(this);
+ return input;
+ }
+
+ protected OutT run(InT value) {
+ return target.run(this, value);
+ }
+
+ private static class Iter implements Iterator {
+ private final AsymCoroutine, OutT> fiber;
+
+ public Iter(final AsymCoroutine, OutT> fiber) {
+ this.fiber = fiber;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return !fiber.isFinished();
+ }
+
+ @Override
+ public OutT next() {
+ if (fiber.isFinished())
+ throw new NoSuchElementException();
+ return fiber.call();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+
+ @Override
+ public final Iterator iterator() {
+ return new Iter(this);
+ }
+
+ protected final void run() {
+ output = run(input);
+ }
+}
diff --git a/src/share/classes/java/dyn/AsymRunnable.java b/src/share/classes/java/dyn/AsymRunnable.java
new file mode 100644
index 0000000000000000000000000000000000000000..e28f3528439937de1e133cebef66e249b80637ac
--- /dev/null
+++ b/src/share/classes/java/dyn/AsymRunnable.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.dyn;
+
+public interface AsymRunnable {
+ public OutT run(AsymCoroutine extends InT, ? super OutT> coroutine, InT value);
+}
diff --git a/src/share/classes/java/dyn/Coroutine.java b/src/share/classes/java/dyn/Coroutine.java
new file mode 100644
index 0000000000000000000000000000000000000000..479d4335f6974618cc6fcdb292760ee98251816d
--- /dev/null
+++ b/src/share/classes/java/dyn/Coroutine.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.dyn;
+
+/**
+ * Implementation of symmetric coroutines. A Coroutine will take part in thread-wide scheduling of coroutines. It transfers control to
+ * the next coroutine whenever yield is called.
+ *
+ * Similar to {@link Thread} there are two ways to implement a Coroutine: either by implementing a subclass of Coroutine (and overriding
+ * {@link #run()}) or by providing a {@link Runnable} to the Coroutine constructor.
+ *
+ * An implementation of a simple Coroutine might look like this:
+ *
+ *
+ *
+ *
+ *
+ * class Numbers extends Coroutine {
+ * public void run() {
+ * for (int i = 0; i < 10; i++) {
+ * System.out.println(i);
+ * yield();
+ * }
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ * A Coroutine is active as soon as it is created, and will run as soon as control is transferred to it:
+ *
+ *
+ *
+ *
+ * new Numbers();
+ * for (int i = 0; i < 10; i++)
+ * yield();
+ *
+ *
+ *
+ *
+ * @author Lukas Stadler
+ */
+public class Coroutine extends CoroutineBase {
+ private final Runnable target;
+
+ Coroutine next;
+ Coroutine last;
+
+ public Coroutine() {
+ this.target = null;
+ threadSupport.addCoroutine(this, -1);
+ }
+
+ public Coroutine(Runnable target) {
+ this.target = target;
+ threadSupport.addCoroutine(this, -1);
+ }
+
+ public Coroutine(long stacksize) {
+ this.target = null;
+ threadSupport.addCoroutine(this, stacksize);
+ }
+
+ public Coroutine(Runnable target, long stacksize) {
+ this.target = target;
+ threadSupport.addCoroutine(this, stacksize);
+ }
+
+ // creates the initial coroutine for a new thread
+ Coroutine(CoroutineSupport threadSupport, long data) {
+ super(threadSupport, data);
+ this.target = null;
+ }
+
+ /**
+ * Yields execution to the next coroutine in the current threads coroutine queue.
+ */
+ public static void yield() {
+ Thread.currentThread().getCoroutineSupport().symmetricYield();
+ }
+
+ public static void yieldTo(Coroutine target) {
+ Thread.currentThread().getCoroutineSupport().symmetricYieldTo(target);
+ }
+
+ public void stop() {
+ Thread.currentThread().getCoroutineSupport().symmetricStopCoroutine(this);
+ }
+
+ protected void run() {
+ assert Thread.currentThread() == threadSupport.getThread();
+ if (target != null) {
+ target.run();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/share/classes/java/dyn/CoroutineBase.java b/src/share/classes/java/dyn/CoroutineBase.java
new file mode 100644
index 0000000000000000000000000000000000000000..aab2d59141d7d75c8b4da4f4975bd4e4d7ca2913
--- /dev/null
+++ b/src/share/classes/java/dyn/CoroutineBase.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.dyn;
+
+public abstract class CoroutineBase {
+ transient long data;
+
+ transient CoroutineLocal.CoroutineLocalMap coroutineLocals = null;
+
+ boolean finished = false;
+
+ transient CoroutineSupport threadSupport;
+
+ CoroutineBase() {
+ Thread thread = Thread.currentThread();
+ assert thread.getCoroutineSupport() != null;
+ this.threadSupport = thread.getCoroutineSupport();
+ }
+
+ // creates the initial coroutine for a new thread
+ CoroutineBase(CoroutineSupport threadSupport, long data) {
+ this.threadSupport = threadSupport;
+ this.data = data;
+ }
+
+ protected abstract void run();
+
+ @SuppressWarnings({ "unused" })
+ private final void startInternal() {
+ assert threadSupport.getThread() == Thread.currentThread();
+ try {
+ if (CoroutineSupport.DEBUG) {
+ System.out.println("starting coroutine " + this);
+ }
+ run();
+ } catch (Throwable t) {
+ if (!(t instanceof CoroutineExitException)) {
+ t.printStackTrace();
+ }
+ } finally {
+ finished = true;
+ // use Thread.currentThread().getCoroutineSupport() because we might have been migrated to another thread!
+ if (this instanceof Coroutine) {
+ Thread.currentThread().getCoroutineSupport().terminateCoroutine();
+ } else {
+ Thread.currentThread().getCoroutineSupport().terminateCallable();
+ }
+ }
+ assert threadSupport.getThread() == Thread.currentThread();
+ }
+
+ /**
+ * Returns true if this coroutine has reached its end. Under normal circumstances this happens when the {@link #run()} method returns.
+ */
+ public final boolean isFinished() {
+ return finished;
+ }
+
+ /**
+ * @return the thread that this coroutine is associated with
+ * @throws NullPointerException
+ * if the coroutine has terminated
+ */
+ public Thread getThread() {
+ return threadSupport.getThread();
+ }
+
+ public static CoroutineBase current() {
+ return Thread.currentThread().getCoroutineSupport().getCurrent();
+ }
+}
diff --git a/src/share/classes/java/dyn/CoroutineExitException.java b/src/share/classes/java/dyn/CoroutineExitException.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4a12c6ab5f56aed747deed643b74956673d2a9d
--- /dev/null
+++ b/src/share/classes/java/dyn/CoroutineExitException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.dyn;
+
+public class CoroutineExitException extends RuntimeException {
+ private static final long serialVersionUID = -2651365020938997924L;
+
+ public CoroutineExitException() {
+ }
+
+ public CoroutineExitException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public CoroutineExitException(String message) {
+ super(message);
+ }
+
+ public CoroutineExitException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/share/classes/java/dyn/CoroutineLocal.java b/src/share/classes/java/dyn/CoroutineLocal.java
new file mode 100644
index 0000000000000000000000000000000000000000..60468923762c4eba4b467f42825ec540f8f8a635
--- /dev/null
+++ b/src/share/classes/java/dyn/CoroutineLocal.java
@@ -0,0 +1,541 @@
+/*
+ * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.dyn;
+
+import java.lang.Object;
+import java.lang.Thread;
+import java.lang.UnsupportedOperationException;
+import java.lang.ref.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@SuppressWarnings({ "rawtypes", "unchecked" })
+public class CoroutineLocal {
+ private final int coroutineLocalHashCode = nextHashCode();
+ private static AtomicInteger nextHashCode = new AtomicInteger();
+ private static final int HASH_INCREMENT = 0x61c88647;
+
+ private static int nextHashCode() {
+ return nextHashCode.getAndAdd(HASH_INCREMENT);
+ }
+
+ protected T initialValue() {
+ return null;
+ }
+
+ public CoroutineLocal() {
+ }
+
+ public T get() {
+ assert Thread.currentThread().getCoroutineSupport() != null;
+ CoroutineBase t = Thread.currentThread().getCoroutineSupport().getCurrent();
+ CoroutineLocalMap map = getMap(t);
+ if (map != null) {
+ CoroutineLocalMap.Entry e = map.getEntry(this);
+ if (e != null)
+ return (T) e.value;
+ }
+ return setInitialValue();
+ }
+
+ /**
+ * Variant of set() to establish initialValue. Used instead of set() in case user has overridden the set() method.
+ *
+ * @return the initial value
+ */
+ private T setInitialValue() {
+ T value = initialValue();
+ assert Thread.currentThread().getCoroutineSupport() != null;
+ CoroutineBase t = Thread.currentThread().getCoroutineSupport().getCurrent();
+ CoroutineLocalMap map = getMap(t);
+ if (map != null)
+ map.set(this, value);
+ else
+ createMap(t, value);
+ return value;
+ }
+
+ /**
+ * Sets the current thread's copy of this thread-local variable to the specified value. Most subclasses will have no need to override
+ * this method, relying solely on the {@link #initialValue} method to set the values of thread-locals.
+ *
+ * @param value the value to be stored in the current thread's copy of this thread-local.
+ */
+ public void set(T value) {
+ assert Thread.currentThread().getCoroutineSupport() != null;
+ CoroutineBase t = Thread.currentThread().getCoroutineSupport().getCurrent();
+ CoroutineLocalMap map = getMap(t);
+ if (map != null)
+ map.set(this, value);
+ else
+ createMap(t, value);
+ }
+
+ /**
+ * Removes the current thread's value for this thread-local variable. If this thread-local variable is subsequently {@linkplain #get
+ * read} by the current thread, its value will be reinitialized by invoking its {@link #initialValue} method, unless its value is
+ * {@linkplain #set set} by the current thread in the interim. This may result in multiple invocations of the initialValue
+ * method in the current thread.
+ *
+ * @since 1.5
+ */
+ public void remove() {
+ assert Thread.currentThread().getCoroutineSupport() != null;
+ CoroutineLocalMap m = getMap(Thread.currentThread().getCoroutineSupport().getCurrent());
+ if (m != null)
+ m.remove(this);
+ }
+
+ CoroutineLocalMap getMap(CoroutineBase t) {
+ return t.coroutineLocals;
+ }
+
+ /**
+ * Create the map associated with a ThreadLocal. Overridden in InheritableThreadLocal.
+ *
+ * @param t the current thread
+ * @param firstValue value for the initial entry of the map
+ * @param map the map to store.
+ */
+ void createMap(CoroutineBase t, T firstValue) {
+ t.coroutineLocals = new CoroutineLocalMap(this, firstValue);
+ }
+
+ /**
+ * Factory method to create map of inherited thread locals. Designed to be called only from Thread constructor.
+ *
+ * @param parentMap the map associated with parent thread
+ * @return a map containing the parent's inheritable bindings
+ */
+ static CoroutineLocalMap createInheritedMap(CoroutineLocalMap parentMap) {
+ return new CoroutineLocalMap(parentMap);
+ }
+
+ /**
+ * Method childValue is visibly defined in subclass InheritableThreadLocal, but is internally defined here for the sake of providing
+ * createInheritedMap factory method without needing to subclass the map class in InheritableThreadLocal. This technique is preferable
+ * to the alternative of embedding instanceof tests in methods.
+ */
+ T childValue(T parentValue) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * ThreadLocalMap is a customized hash map suitable only for maintaining thread local values. No operations are exported outside of the
+ * ThreadLocal class. The class is package private to allow declaration of fields in class Thread. To help deal with very large and
+ * long-lived usages, the hash table entries use WeakReferences for keys. However, since reference queues are not used, stale entries
+ * are guaranteed to be removed only when the table starts running out of space.
+ */
+ static class CoroutineLocalMap {
+
+ /**
+ * The entries in this hash map extend WeakReference, using its main ref field as the key (which is always a ThreadLocal object).
+ * Note that null keys (i.e. entry.get() == null) mean that the key is no longer referenced, so the entry can be expunged from
+ * table. Such entries are referred to as "stale entries" in the code that follows.
+ */
+ static class Entry extends WeakReference {
+ /** The value associated with this ThreadLocal. */
+ Object value;
+
+ Entry(CoroutineLocal k, Object v) {
+ super(k);
+ value = v;
+ }
+ }
+
+ /**
+ * The initial capacity -- MUST be a power of two.
+ */
+ private static final int INITIAL_CAPACITY = 16;
+
+ /**
+ * The table, resized as necessary. table.length MUST always be a power of two.
+ */
+ private Entry[] table;
+
+ /**
+ * The number of entries in the table.
+ */
+ private int size = 0;
+
+ /**
+ * The next size value at which to resize.
+ */
+ private int threshold; // Default to 0
+
+ /**
+ * Set the resize threshold to maintain at worst a 2/3 load factor.
+ */
+ private void setThreshold(int len) {
+ threshold = len * 2 / 3;
+ }
+
+ /**
+ * Increment i modulo len.
+ */
+ private static int nextIndex(int i, int len) {
+ return ((i + 1 < len) ? i + 1 : 0);
+ }
+
+ /**
+ * Decrement i modulo len.
+ */
+ private static int prevIndex(int i, int len) {
+ return ((i - 1 >= 0) ? i - 1 : len - 1);
+ }
+
+ /**
+ * Construct a new map initially containing (firstKey, firstValue). ThreadLocalMaps are constructed lazily, so we only create one
+ * when we have at least one entry to put in it.
+ */
+ CoroutineLocalMap(CoroutineLocal firstKey, Object firstValue) {
+ table = new Entry[INITIAL_CAPACITY];
+ int i = firstKey.coroutineLocalHashCode & (INITIAL_CAPACITY - 1);
+ table[i] = new Entry(firstKey, firstValue);
+ size = 1;
+ setThreshold(INITIAL_CAPACITY);
+ }
+
+ /**
+ * Construct a new map including all Inheritable ThreadLocals from given parent map. Called only by createInheritedMap.
+ *
+ * @param parentMap the map associated with parent thread.
+ */
+ private CoroutineLocalMap(CoroutineLocalMap parentMap) {
+ Entry[] parentTable = parentMap.table;
+ int len = parentTable.length;
+ setThreshold(len);
+ table = new Entry[len];
+
+ for (int j = 0; j < len; j++) {
+ Entry e = parentTable[j];
+ if (e != null) {
+ CoroutineLocal key = e.get();
+ if (key != null) {
+ Object value = key.childValue(e.value);
+ Entry c = new Entry(key, value);
+ int h = key.coroutineLocalHashCode & (len - 1);
+ while (table[h] != null)
+ h = nextIndex(h, len);
+ table[h] = c;
+ size++;
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the entry associated with key. This method itself handles only the fast path: a direct hit of existing key. It otherwise
+ * relays to getEntryAfterMiss. This is designed to maximize performance for direct hits, in part by making this method readily
+ * inlinable.
+ *
+ * @param key the thread local object
+ * @return the entry associated with key, or null if no such
+ */
+ private Entry getEntry(CoroutineLocal key) {
+ int i = key.coroutineLocalHashCode & (table.length - 1);
+ Entry e = table[i];
+ if (e != null && e.get() == key)
+ return e;
+ else
+ return getEntryAfterMiss(key, i, e);
+ }
+
+ /**
+ * Version of getEntry method for use when key is not found in its direct hash slot.
+ *
+ * @param key the thread local object
+ * @param i the table index for key's hash code
+ * @param e the entry at table[i]
+ * @return the entry associated with key, or null if no such
+ */
+ private Entry getEntryAfterMiss(CoroutineLocal key, int i, Entry e) {
+ Entry[] tab = table;
+ int len = tab.length;
+
+ while (e != null) {
+ CoroutineLocal k = e.get();
+ if (k == key)
+ return e;
+ if (k == null)
+ expungeStaleEntry(i);
+ else
+ i = nextIndex(i, len);
+ e = tab[i];
+ }
+ return null;
+ }
+
+ /**
+ * Set the value associated with key.
+ *
+ * @param key the thread local object
+ * @param value the value to be set
+ */
+ private void set(CoroutineLocal key, Object value) {
+
+ // We don't use a fast path as with get() because it is at
+ // least as common to use set() to create new entries as
+ // it is to replace existing ones, in which case, a fast
+ // path would fail more often than not.
+
+ Entry[] tab = table;
+ int len = tab.length;
+ int i = key.coroutineLocalHashCode & (len - 1);
+
+ for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
+ CoroutineLocal k = e.get();
+
+ if (k == key) {
+ e.value = value;
+ return;
+ }
+
+ if (k == null) {
+ replaceStaleEntry(key, value, i);
+ return;
+ }
+ }
+
+ tab[i] = new Entry(key, value);
+ int sz = ++size;
+ if (!cleanSomeSlots(i, sz) && sz >= threshold)
+ rehash();
+ }
+
+ /**
+ * Remove the entry for key.
+ */
+ private void remove(CoroutineLocal key) {
+ Entry[] tab = table;
+ int len = tab.length;
+ int i = key.coroutineLocalHashCode & (len - 1);
+ for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
+ if (e.get() == key) {
+ e.clear();
+ expungeStaleEntry(i);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Replace a stale entry encountered during a set operation with an entry for the specified key. The value passed in the value
+ * parameter is stored in the entry, whether or not an entry already exists for the specified key.
+ *
+ * As a side effect, this method expunges all stale entries in the "run" containing the stale entry. (A run is a sequence of entries
+ * between two null slots.)
+ *
+ * @param key the key
+ * @param value the value to be associated with key
+ * @param staleSlot index of the first stale entry encountered while searching for key.
+ */
+ private void replaceStaleEntry(CoroutineLocal key, Object value, int staleSlot) {
+ Entry[] tab = table;
+ int len = tab.length;
+ Entry e;
+
+ // Back up to check for prior stale entry in current run.
+ // We clean out whole runs at a time to avoid continual
+ // incremental rehashing due to garbage collector freeing
+ // up refs in bunches (i.e., whenever the collector runs).
+ int slotToExpunge = staleSlot;
+ for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len))
+ if (e.get() == null)
+ slotToExpunge = i;
+
+ // Find either the key or trailing null slot of run, whichever
+ // occurs first
+ for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {
+ CoroutineLocal k = e.get();
+
+ // If we find key, then we need to swap it
+ // with the stale entry to maintain hash table order.
+ // The newly stale slot, or any other stale slot
+ // encountered above it, can then be sent to expungeStaleEntry
+ // to remove or rehash all of the other entries in run.
+ if (k == key) {
+ e.value = value;
+
+ tab[i] = tab[staleSlot];
+ tab[staleSlot] = e;
+
+ // Start expunge at preceding stale entry if it exists
+ if (slotToExpunge == staleSlot)
+ slotToExpunge = i;
+ cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
+ return;
+ }
+
+ // If we didn't find stale entry on backward scan, the
+ // first stale entry seen while scanning for key is the
+ // first still present in the run.
+ if (k == null && slotToExpunge == staleSlot)
+ slotToExpunge = i;
+ }
+
+ // If key not found, put new entry in stale slot
+ tab[staleSlot].value = null;
+ tab[staleSlot] = new Entry(key, value);
+
+ // If there are any other stale entries in run, expunge them
+ if (slotToExpunge != staleSlot)
+ cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
+ }
+
+ /**
+ * Expunge a stale entry by rehashing any possibly colliding entries lying between staleSlot and the next null slot. This also
+ * expunges any other stale entries encountered before the trailing null. See Knuth, Section 6.4
+ *
+ * @param staleSlot index of slot known to have null key
+ * @return the index of the next null slot after staleSlot (all between staleSlot and this slot will have been checked for
+ * expunging).
+ */
+ private int expungeStaleEntry(int staleSlot) {
+ Entry[] tab = table;
+ int len = tab.length;
+
+ // expunge entry at staleSlot
+ tab[staleSlot].value = null;
+ tab[staleSlot] = null;
+ size--;
+
+ // Rehash until we encounter null
+ Entry e;
+ int i;
+ for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {
+ CoroutineLocal k = e.get();
+ if (k == null) {
+ e.value = null;
+ tab[i] = null;
+ size--;
+ }
+ else {
+ int h = k.coroutineLocalHashCode & (len - 1);
+ if (h != i) {
+ tab[i] = null;
+
+ // Unlike Knuth 6.4 Algorithm R, we must scan until
+ // null because multiple entries could have been stale.
+ while (tab[h] != null)
+ h = nextIndex(h, len);
+ tab[h] = e;
+ }
+ }
+ }
+ return i;
+ }
+
+ /**
+ * Heuristically scan some cells looking for stale entries. This is invoked when either a new element is added, or another stale one
+ * has been expunged. It performs a logarithmic number of scans, as a balance between no scanning (fast but retains garbage) and a
+ * number of scans proportional to number of elements, that would find all garbage but would cause some insertions to take O(n)
+ * time.
+ *
+ * @param i a position known NOT to hold a stale entry. The scan starts at the element after i.
+ *
+ * @param n scan control: log2(n) cells are scanned, unless a stale entry is found, in which case
+ * log2(table.length)-1 additional cells are scanned. When called from insertions, this parameter is the number
+ * of elements, but when from replaceStaleEntry, it is the table length. (Note: all this could be changed to be either
+ * more or less aggressive by weighting n instead of just using straight log n. But this version is simple, fast, and
+ * seems to work well.)
+ *
+ * @return true if any stale entries have been removed.
+ */
+ private boolean cleanSomeSlots(int i, int n) {
+ boolean removed = false;
+ Entry[] tab = table;
+ int len = tab.length;
+ do {
+ i = nextIndex(i, len);
+ Entry e = tab[i];
+ if (e != null && e.get() == null) {
+ n = len;
+ removed = true;
+ i = expungeStaleEntry(i);
+ }
+ }
+ while ((n >>>= 1) != 0);
+ return removed;
+ }
+
+ /**
+ * Re-pack and/or re-size the table. First scan the entire table removing stale entries. If this doesn't sufficiently shrink the
+ * size of the table, double the table size.
+ */
+ private void rehash() {
+ expungeStaleEntries();
+
+ // Use lower threshold for doubling to avoid hysteresis
+ if (size >= threshold - threshold / 4)
+ resize();
+ }
+
+ /**
+ * Double the capacity of the table.
+ */
+ private void resize() {
+ Entry[] oldTab = table;
+ int oldLen = oldTab.length;
+ int newLen = oldLen * 2;
+ Entry[] newTab = new Entry[newLen];
+ int count = 0;
+
+ for (int j = 0; j < oldLen; ++j) {
+ Entry e = oldTab[j];
+ if (e != null) {
+ CoroutineLocal k = e.get();
+ if (k == null) {
+ e.value = null; // Help the GC
+ }
+ else {
+ int h = k.coroutineLocalHashCode & (newLen - 1);
+ while (newTab[h] != null)
+ h = nextIndex(h, newLen);
+ newTab[h] = e;
+ count++;
+ }
+ }
+ }
+
+ setThreshold(newLen);
+ size = count;
+ table = newTab;
+ }
+
+ /**
+ * Expunge all stale entries in the table.
+ */
+ private void expungeStaleEntries() {
+ Entry[] tab = table;
+ int len = tab.length;
+ for (int j = 0; j < len; j++) {
+ Entry e = tab[j];
+ if (e != null && e.get() == null)
+ expungeStaleEntry(j);
+ }
+ }
+ }
+}
diff --git a/src/share/classes/java/dyn/CoroutineSupport.java b/src/share/classes/java/dyn/CoroutineSupport.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc245cd4acac29d8e654a6b0a3bea1ffb4dc975e
--- /dev/null
+++ b/src/share/classes/java/dyn/CoroutineSupport.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.dyn;
+
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+public class CoroutineSupport {
+ // Controls debugging and tracing, for maximum performance the actual if(DEBUG/TRACE) code needs to be commented out
+ static final boolean DEBUG = false;
+ static final boolean TRACE = false;
+
+ static final Object TERMINATED = new Object();
+
+ // The thread that this CoroutineSupport belongs to. There's only one CoroutineSupport per Thread
+ private final Thread thread;
+ // The initial coroutine of the Thread
+ private final Coroutine threadCoroutine;
+
+ // The currently executing, symmetric or asymmetric coroutine
+ CoroutineBase currentCoroutine;
+ // The anchor of the doubly-linked ring of coroutines
+ Coroutine scheduledCoroutines;
+
+ static {
+ registerNatives();
+ }
+
+ public CoroutineSupport(Thread thread) {
+ if (thread.getCoroutineSupport() != null) {
+ throw new IllegalArgumentException("Cannot instantiate CoroutineThreadSupport for existing Thread");
+ }
+ this.thread = thread;
+ threadCoroutine = new Coroutine(this, getThreadCoroutine());
+ threadCoroutine.next = threadCoroutine;
+ threadCoroutine.last = threadCoroutine;
+ currentCoroutine = threadCoroutine;
+ scheduledCoroutines = threadCoroutine;
+ }
+
+ public Coroutine threadCoroutine() {
+ return threadCoroutine;
+ }
+
+ void addCoroutine(Coroutine coroutine, long stacksize) {
+ assert scheduledCoroutines != null;
+ assert currentCoroutine != null;
+
+ coroutine.data = createCoroutine(coroutine, stacksize);
+ if (DEBUG) {
+ System.out.println("add Coroutine " + coroutine + ", data" + coroutine.data);
+ }
+
+ // add the coroutine into the doubly linked ring
+ coroutine.next = scheduledCoroutines.next;
+ coroutine.last = scheduledCoroutines;
+ scheduledCoroutines.next = coroutine;
+ coroutine.next.last = coroutine;
+ }
+
+ void addCoroutine(AsymCoroutine, ?> coroutine, long stacksize) {
+ coroutine.data = createCoroutine(coroutine, stacksize);
+ if (DEBUG) {
+ System.out.println("add AsymCoroutine " + coroutine + ", data" + coroutine.data);
+ }
+
+ coroutine.caller = null;
+ }
+
+ Thread getThread() {
+ return thread;
+ }
+
+ public void drain() {
+ if (Thread.currentThread() != thread) {
+ throw new IllegalArgumentException("Cannot drain another threads CoroutineThreadSupport");
+ }
+
+ if (DEBUG) {
+ System.out.println("draining");
+ }
+ try {
+ // drain all scheduled coroutines
+ while (scheduledCoroutines.next != scheduledCoroutines) {
+ symmetricExitInternal(scheduledCoroutines.next);
+ }
+
+ CoroutineBase coro;
+ while ((coro = cleanupCoroutine()) != null) {
+ System.out.println(coro);
+ throw new NotImplementedException();
+ }
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ void symmetricYield() {
+ if (scheduledCoroutines != currentCoroutine) {
+ throw new IllegalThreadStateException("Cannot call yield from within an asymmetric coroutine");
+ }
+ assert currentCoroutine instanceof Coroutine;
+
+ if (TRACE) {
+ System.out.println("locking for symmetric yield...");
+ }
+
+ Coroutine next = scheduledCoroutines.next;
+ if (next == scheduledCoroutines) {
+ return;
+ }
+
+ if (TRACE) {
+ System.out.println("symmetric yield to " + next);
+ }
+
+ final Coroutine current = scheduledCoroutines;
+ scheduledCoroutines = next;
+ currentCoroutine = next;
+
+ switchTo(current, next);
+ }
+
+ public void symmetricYieldTo(Coroutine target) {
+ if (scheduledCoroutines != currentCoroutine) {
+ throw new IllegalThreadStateException("Cannot call yield from within an asymmetric coroutine");
+ }
+ assert currentCoroutine instanceof Coroutine;
+
+ moveCoroutine(scheduledCoroutines, target);
+
+ final Coroutine current = scheduledCoroutines;
+ scheduledCoroutines = target;
+ currentCoroutine = target;
+
+ switchTo(current, target);
+ }
+
+ private void moveCoroutine(Coroutine a, Coroutine position) {
+ // remove a from the ring
+ a.last.next = a.next;
+ a.next.last = a.last;
+
+ // ... and insert at the new position
+ a.next = position.next;
+ a.last = position;
+ a.next.last = a;
+ position.next = a;
+ }
+
+ public void symmetricStopCoroutine(Coroutine target) {
+ if (scheduledCoroutines != currentCoroutine) {
+ throw new IllegalThreadStateException("Cannot call yield from within an asymmetric coroutine");
+ }
+ assert currentCoroutine instanceof Coroutine;
+
+ moveCoroutine(scheduledCoroutines, target);
+
+ final Coroutine current = scheduledCoroutines;
+ scheduledCoroutines = target;
+ currentCoroutine = target;
+
+ switchToAndExit(current, target);
+ }
+
+ void symmetricExitInternal(Coroutine coroutine) {
+ if (scheduledCoroutines != currentCoroutine) {
+ throw new IllegalThreadStateException("Cannot call exitNext from within an unscheduled coroutine");
+ }
+ assert currentCoroutine instanceof Coroutine;
+ assert currentCoroutine != coroutine;
+
+ // remove the coroutine from the ring
+ coroutine.last.next = coroutine.next;
+ coroutine.next.last = coroutine.last;
+
+ if (!isDisposable(coroutine.data)) {
+ // and insert it before the current coroutine
+ coroutine.last = scheduledCoroutines.last;
+ coroutine.next = scheduledCoroutines;
+ coroutine.last.next = coroutine;
+ scheduledCoroutines.last = coroutine;
+
+ final Coroutine current = scheduledCoroutines;
+ scheduledCoroutines = coroutine;
+ currentCoroutine = coroutine;
+ switchToAndExit(current, coroutine);
+ }
+ }
+
+ void asymmetricCall(AsymCoroutine, ?> target) {
+ if (target.threadSupport != this) {
+ throw new IllegalArgumentException("Cannot activate a coroutine that belongs to another thread");
+ }
+ if (target.caller != null) {
+ throw new IllegalArgumentException("Coroutine already in use");
+ }
+ if (target.data == 0) {
+ throw new IllegalArgumentException("Target coroutine has already finished");
+ }
+ if (TRACE) {
+ System.out.println("yieldCall " + target + " (" + target.data + ")");
+ }
+
+ final CoroutineBase current = currentCoroutine;
+ target.caller = current;
+ currentCoroutine = target;
+ switchTo(target.caller, target);
+ }
+
+ void asymmetricReturn(final AsymCoroutine, ?> current) {
+ if (current != currentCoroutine) {
+ throw new IllegalThreadStateException("cannot return from non-current fiber");
+ }
+ final CoroutineBase caller = current.caller;
+ if (TRACE) {
+ System.out.println("yieldReturn " + caller + " (" + caller.data + ")");
+ }
+
+ current.caller = null;
+ currentCoroutine = caller;
+ switchTo(current, currentCoroutine);
+ }
+
+ void asymmetricReturnAndTerminate(final AsymCoroutine, ?> current) {
+ if (current != currentCoroutine) {
+ throw new IllegalThreadStateException("cannot return from non-current fiber");
+ }
+ final CoroutineBase caller = current.caller;
+ if (TRACE) {
+ System.out.println("yieldReturn " + caller + " (" + caller.data + ")");
+ }
+
+ current.caller = null;
+ currentCoroutine = caller;
+ switchToAndTerminate(current, currentCoroutine);
+ }
+
+ void terminateCoroutine() {
+ assert currentCoroutine == scheduledCoroutines;
+ assert currentCoroutine != threadCoroutine : "cannot exit thread coroutine";
+ assert scheduledCoroutines != scheduledCoroutines.next : "last coroutine shouldn't call coroutineexit";
+
+ Coroutine old = scheduledCoroutines;
+ Coroutine forward = old.next;
+ currentCoroutine = forward;
+ scheduledCoroutines = forward;
+ old.last.next = old.next;
+ old.next.last = old.last;
+
+ if (DEBUG) {
+ System.out.println("to be terminated: " + old);
+ }
+ switchToAndTerminate(old, forward);
+ }
+
+ void terminateCallable() {
+ assert currentCoroutine != scheduledCoroutines;
+ assert currentCoroutine instanceof AsymCoroutine, ?>;
+
+ if (DEBUG) {
+ System.out.println("to be terminated: " + currentCoroutine);
+ }
+ asymmetricReturnAndTerminate((AsymCoroutine, ?>) currentCoroutine);
+ }
+
+ public boolean isCurrent(CoroutineBase coroutine) {
+ return coroutine == currentCoroutine;
+ }
+
+ public CoroutineBase getCurrent() {
+ return currentCoroutine;
+ }
+
+ private static native void registerNatives();
+
+ private static native long getThreadCoroutine();
+
+ private static native long createCoroutine(CoroutineBase coroutine, long stacksize);
+
+ private static native void switchTo(CoroutineBase current, CoroutineBase target);
+
+ private static native void switchToAndTerminate(CoroutineBase current, CoroutineBase target);
+
+ private static native void switchToAndExit(CoroutineBase current, CoroutineBase target);
+
+ private static native boolean isDisposable(long coroutine);
+
+ private static native CoroutineBase cleanupCoroutine();
+
+}
diff --git a/src/share/classes/java/lang/Thread.java b/src/share/classes/java/lang/Thread.java
index 1592d50e9c4eb957ec44b9094771df049e6c3a60..005a47e3817226faf3595356b9ebf5420da3cf1a 100644
--- a/src/share/classes/java/lang/Thread.java
+++ b/src/share/classes/java/lang/Thread.java
@@ -25,6 +25,7 @@
package java.lang;
+import java.dyn.CoroutineSupport;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
@@ -274,6 +275,18 @@ class Thread implements Runnable {
*/
public final static int MAX_PRIORITY = 10;
+ private CoroutineSupport coroutineSupport;
+
+ public CoroutineSupport getCoroutineSupport() {
+ return coroutineSupport;
+ }
+
+ private void initializeCoroutineSupport() {
+ if (sun.misc.VM.isEnableCoroutine()) {
+ coroutineSupport = new CoroutineSupport(this);
+ }
+ }
+
/**
* Returns a reference to the currently executing thread object.
*
@@ -781,6 +794,10 @@ class Thread implements Runnable {
* a chance to clean up before it actually exits.
*/
private void exit() {
+ if (sun.misc.VM.isEnableCoroutine() && (coroutineSupport != null)) {
+ coroutineSupport.drain();
+ }
+
if (group != null) {
group.threadTerminated(this);
group = null;
diff --git a/src/share/classes/sun/misc/VM.java b/src/share/classes/sun/misc/VM.java
index 3e64628c6fbd0e5ecc94d12b08f29ba22c84c3b9..2e2c1cc492f86cdfbef90fa545c8f970fe98c2f1 100644
--- a/src/share/classes/sun/misc/VM.java
+++ b/src/share/classes/sun/misc/VM.java
@@ -179,6 +179,12 @@ public class VM {
}
}
+ private static boolean enableCoroutine;
+
+ public static boolean isEnableCoroutine() {
+ return enableCoroutine;
+ }
+
// A user-settable upper limit on the maximum amount of allocatable direct
// buffer memory. This value may be changed during VM initialization if
// "java" is launched with "-XX:MaxDirectMemorySize=".
@@ -276,6 +282,10 @@ public class VM {
savedProps.putAll(props);
+ //Set the enableCoroutine flag, This value is controlled
+ //by the vm option -XX:+EnableCoroutine
+ enableCoroutine = Boolean.valueOf((String) props.remove("com.alibaba.coroutine.enableCoroutine"));
+
// Set the maximum amount of direct memory. This value is controlled
// by the vm option -XX:MaxDirectMemorySize=.
// The maximum amount of allocatable direct buffer memory (in bytes)
diff --git a/test/java/dyn/CoroutineTest.java b/test/java/dyn/CoroutineTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..968fc9ac1e1281f242c514a63df97c13657962f3
--- /dev/null
+++ b/test/java/dyn/CoroutineTest.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @summary unit tests for coroutines
+ * @run junit/othervm -XX:+EnableCoroutine test.java.dyn.CoroutineTest
+ */
+
+package test.java.dyn;
+
+import java.dyn.Coroutine;
+import java.dyn.AsymCoroutine;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class CoroutineTest {
+ private StringBuilder seq;
+
+ @Before
+ public void before() {
+ seq = new StringBuilder();
+ seq.append("a");
+ }
+
+ @Test
+ public void symSequence() {
+ Coroutine coro = new Coroutine() {
+ protected void run() {
+ seq.append("c");
+ for (int i = 0; i < 3; i++) {
+ yield();
+ seq.append("e");
+ }
+ }
+ };
+ seq.append("b");
+ assertFalse(coro.isFinished());
+ Coroutine.yield();
+ for (int i = 0; i < 3; i++) {
+ seq.append("d");
+ assertFalse(coro.isFinished());
+ Coroutine.yield();
+ }
+ seq.append("f");
+ assertTrue(coro.isFinished());
+ Coroutine.yield();
+ seq.append("g");
+ assertEquals("abcdededefg", seq.toString());
+ }
+
+ @Test
+ public void symMultiSequence() {
+ for (int i = 0; i < 10; i++)
+ new Coroutine() {
+ protected void run() {
+ seq.append("c");
+ yield();
+ seq.append("e");
+ }
+ };
+ seq.append("b");
+ Coroutine.yield();
+ seq.append("d");
+ Coroutine.yield();
+ seq.append("f");
+ Coroutine.yield();
+ seq.append("g");
+ assertEquals("abccccccccccdeeeeeeeeeefg", seq.toString());
+ }
+
+ @Test
+ public void asymSequence() {
+ AsymCoroutine coro = new AsymCoroutine() {
+ protected Void run(Void value) {
+ seq.append(value + "b");
+ Object o = ret();
+ seq.append(o + "d");
+ return null;
+ }
+ };
+ assertFalse(coro.isFinished());
+ coro.call();
+ assertFalse(coro.isFinished());
+ seq.append("c");
+ coro.call();
+ seq.append("e");
+ assertTrue(coro.isFinished());
+
+ RuntimeException exception = null;
+ try {
+ coro.call();
+ } catch (RuntimeException e) {
+ exception = e;
+ }
+ assertNotNull(exception);
+ assertEquals("anullbcnullde", seq.toString());
+ }
+
+ @Test
+ public void asymMultiSequence() {
+ AsymCoroutine coro = null;
+ for (int j = 4; j >= 0; j--) {
+ final AsymCoroutine last = coro;
+ final int i = j;
+ coro = new AsymCoroutine() {
+ protected Void run(Void value) {
+ seq.append("b" + i);
+ if (last != null)
+ last.call();
+ seq.append("c" + i);
+ ret();
+ seq.append("e" + i);
+ if (last != null)
+ last.call();
+ seq.append("f" + i);
+ return null;
+ }
+ };
+ }
+ seq.append("_");
+ assertFalse(coro.isFinished());
+ coro.call();
+ assertFalse(coro.isFinished());
+ seq.append("d");
+ coro.call();
+ seq.append("g");
+ assertTrue(coro.isFinished());
+
+ RuntimeException exception = null;
+ try {
+ coro.call();
+ } catch (RuntimeException e) {
+ exception = e;
+ }
+ assertNotNull(exception);
+ assertEquals("a_b0b1b2b3b4c4c3c2c1c0de0e1e2e3e4f4f3f2f1f0g", seq.toString());
+ }
+
+ @Test
+ public void asymReturnValue() {
+ AsymCoroutine coro = new AsymCoroutine() {
+ protected Integer run(Integer value) {
+ value = ret(value * 2 + 1);
+ value = ret(value * 2 + 2);
+ value = ret(value * 2 + 3);
+ value = ret(value * 2 + 4);
+ value = ret(value * 2 + 5);
+ return value * 2 + 6;
+ }
+ };
+ assertFalse(coro.isFinished());
+ assertEquals(2001, (int) coro.call(1000));
+ assertEquals(4002, (int) coro.call(2000));
+ assertEquals(6003, (int) coro.call(3000));
+ assertEquals(8004, (int) coro.call(4000));
+ assertEquals(10005, (int) coro.call(5000));
+ assertEquals(12006, (int) coro.call(6000));
+ assertTrue(coro.isFinished());
+ }
+
+ @Test
+ public void gcTest1() {
+ new Coroutine() {
+ protected void run() {
+ seq.append("c");
+ Integer v1 = 1;
+ Integer v2 = 14555668;
+ yield();
+ seq.append("e");
+ seq.append("(" + v1 + "," + v2 + ")");
+ }
+ };
+ seq.append("b");
+ System.gc();
+ Coroutine.yield();
+ System.gc();
+ seq.append("d");
+ Coroutine.yield();
+ seq.append("f");
+ Coroutine.yield();
+ seq.append("g");
+ assertEquals("abcde(1,14555668)fg", seq.toString());
+ }
+
+ @Test
+ public void exceptionTest1() {
+ Coroutine coro = new Coroutine() {
+ protected void run() {
+ seq.append("c");
+ long temp = System.nanoTime();
+ if (temp != 0)
+ throw new RuntimeException();
+ yield();
+ seq.append("e");
+ }
+ };
+ seq.append("b");
+ assertFalse(coro.isFinished());
+ Coroutine.yield();
+ seq.append("d");
+ Coroutine.yield();
+ seq.append("f");
+ assertEquals("abcdf", seq.toString());
+ }
+
+ @Test
+ public void largeStackframeTest() {
+ new Coroutine() {
+ protected void run() {
+ seq.append("c");
+ Integer v0 = 10000;
+ Integer v1 = 10001;
+ Integer v2 = 10002;
+ Integer v3 = 10003;
+ Integer v4 = 10004;
+ Integer v5 = 10005;
+ Integer v6 = 10006;
+ Integer v7 = 10007;
+ Integer v8 = 10008;
+ Integer v9 = 10009;
+ Integer v10 = 10010;
+ Integer v11 = 10011;
+ Integer v12 = 10012;
+ Integer v13 = 10013;
+ Integer v14 = 10014;
+ Integer v15 = 10015;
+ Integer v16 = 10016;
+ Integer v17 = 10017;
+ Integer v18 = 10018;
+ Integer v19 = 10019;
+ yield();
+ int sum = v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16 + v17 + v18 + v19;
+ seq.append("e" + sum);
+ }
+ };
+ seq.append("b");
+ System.gc();
+ Coroutine.yield();
+ System.gc();
+ seq.append("d");
+ Coroutine.yield();
+ seq.append("f");
+ assertEquals("abcde200190f", seq.toString());
+ }
+
+ @Test
+ public void shaTest() {
+ Coroutine coro = new Coroutine(65536) {
+ protected void run() {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA");
+ digest.update("TestMessage".getBytes());
+ seq.append("b");
+ yield();
+ seq.append(digest.digest()[0]);
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ }
+ };
+ Coroutine.yield();
+ seq.append("c");
+ assertFalse(coro.isFinished());
+ Coroutine.yield();
+ assertTrue(coro.isFinished());
+ assertEquals("abc72", seq.toString());
+ }
+
+ public void stackoverflowTest() {
+ for (int i = 0; i < 10; i++) {
+ new Coroutine(65536) {
+ int i = 0;
+
+ protected void run() {
+ System.out.println("start");
+ try {
+ iter();
+ } catch (StackOverflowError e) {
+ System.out.println("i: " + i);
+ }
+ System.out.println("asdf");
+ }
+
+ private void iter() {
+ System.out.print(".");
+ i++;
+ iter();
+ }
+ };
+ }
+ Coroutine.yield();
+ }
+}