Can you handle the lifetime by hiding the threads behind a closable external iterator, e.g. the tree comparing example given in the Google Code project Coroutine could be written:
package googlecodecoroutines; import java.io.Closeable; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.Callable; import java.util.concurrent.Exchanger; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * Thread version of Google Code Coroutines: * http://code.google.com/p/coroutines/source/browse/trunk/src/mr/go/coroutines/tests/classes/Tree.java * * @author Howard Lovatt */ final class Tree<E extends Comparable<E>> { private final TreeNode root; public Tree( final E root ) { this.root = new TreeNode( root ); } public static <T extends Comparable<T>> boolean sameFringe( final Tree<T> t1, final Tree<T> t2 ) { final SafeCloseableIterator<T> t1Leaves = t1.leaves ( t1.root ).iterator(); final SafeCloseableIterator<T> t2Leaves = t2.leaves ( t2.root ).iterator(); try { while ( t1Leaves.hasNext() && t2Leaves.hasNext() ) { if ( t1Leaves.next().compareTo( t2Leaves.next() ) != 0 ) { return false; } } return !t1Leaves.hasNext() && !t2Leaves.hasNext(); } finally { t1Leaves.close(); t2Leaves.close(); } } public void insert( final E value ) { insert( root, value ); } private void insert( final TreeNode node, E value ) { if ( value.compareTo( node.key ) < 0 ) { if ( node.left != null ) { insert( node.left, value ); } else { node.left = new TreeNode( value ); } } else { if ( node.right != null ) { insert( node.right, value ); } else { node.right = new TreeNode( value ); } } } private Coroutine<E> leaves( final TreeNode tn ) { return new Coroutine<E>( new LeavesYieldable( tn ) ); } @Override public String toString() { return root.toString(); } private final class LeavesYieldable extends Yieldable<E> { final TreeNode tn; LeavesYieldable( final TreeNode tn ) { this.tn = tn; } @Override public void run() { if ( tn.isLeaf() ) { yield( tn.key ); } else { if ( tn.left != null ) { final SafeCloseableIterator<E> i = leaves( tn.left ).iterator (); try { while ( i.hasNext() ) { yield( i.next() ); } } finally { i.close(); } } if ( tn.right != null ) { final SafeCloseableIterator<E> i = leaves ( tn.right ).iterator(); try { while ( i.hasNext() ) { yield( i.next() ); } } finally { i.close(); } } } close(); } } private class TreeNode { final E key; TreeNode left; TreeNode right; TreeNode( final E key ) { this.key = key; } boolean isLeaf() { return ( left == null ) && ( right == null ); } @Override public String toString() { return "[" + String.valueOf( left ) + ", " + String.valueOf ( key ) + ", " + String.valueOf( right ) + "]"; } } } // Infrastructure common to all coroutines class Coroutine<E> implements SafeCloseableIterable<E> { private static final Exception DONE = new Exception(); public static final ExecutorService pool = Executors.newCachedThreadPool(); private final Yieldable<E> yieldable; public Coroutine( final Yieldable<E> yieldable ) { this.yieldable = yieldable; } @Override public SafeCloseableIterator<E> iterator() { return new Iterator(); } final class Iterator extends AbstractSequentialSafeCloseableIterator<E> { final Exchanger<E> exchanger = new Exchanger<E>(); private final Future<?> future = pool.submit( yieldable ); private Iterator() { yieldable.iterator = this; } @Override public E call() throws Exception { if ( future.isDone() ) { throw DONE; } return exchanger.exchange( null ); } @Override public void close() { super.close(); future.cancel( true ); } } } abstract class Yieldable<E> implements Runnable, SafeCloseable { Coroutine<E>.Iterator iterator; public final void yield( final E e ) { try { iterator.exchanger.exchange( e ); } catch ( InterruptedException notUsed ) { iterator.close(); } } @Override public final void close() { iterator.close(); } } abstract class AbstractSequentialSafeCloseableIterator<E> implements SafeCloseableIterator<E>, Callable<E> { private boolean hasNextCalled = false; private boolean hasNext; protected E next; @Override public boolean hasNext() { if ( !hasNextCalled ) { try { next = call(); hasNext = true; } catch ( Exception notUsed ) { hasNext = false; } hasNextCalled = true; } return hasNext; } @Override public E next() { if ( !hasNextCalled ) { hasNext(); } if ( !hasNext ) { throw new NoSuchElementException(); } hasNextCalled = false; return next; } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public void close() { hasNextCalled = true; hasNext = false; } } interface SafeCloseable extends Closeable { @Override void close(); } interface SafeCloseableIterable<E> extends Iterable<E> { @Override public SafeCloseableIterator<E> iterator(); } interface SafeCloseableIterator<E> extends SafeCloseable, Iterator<E> {} // Test of Tree example public class Main { public static void main( final String[] notUsed ) { final Tree<Integer> t1 = new Tree<Integer>( -1 ); final Tree<Integer> t2 = new Tree<Integer>( -1 ); for ( int i = 0; i < 6; i++ ) { t1.insert( i ); t2.insert( i ); } System.out.println( "t1 = " + t1 ); System.out.println( "t2 = " + t2 ); System.out.println( "Equal? " + Tree.sameFringe( t1, t2 ) ); Coroutine.pool.shutdownNow(); } } Note how the iterators are within a try block that closes them and the act of closing them frees the thread. In the example above it isn't important that the try blocks close the iterators since the iterators terminate normally and hence are closed and the thread is freed in any case. I don't know Ruby; so not sure if this helps, just a quick suggestion. On Sep 26, 7:41 am, Charles Oliver Nutter <[email protected]> wrote: > Ok, here's a classic challenge for those of us on the JVM: coroutines. > > In JRuby, the issue has been forced upon us by the inclusion of > somewhat localized coroutines for external enumeration in Ruby 1.8.7 > and general coroutines in the form of fibers in 1.9. The two cases are > slightly different. > > For external enumerating coroutines, we can have options that simulate > coroutine state. For example, the Enumerator for a core Array class > could know how to "next" through the array elements without using a > coroutine to call "each" with a block. But for other cases that may > have arbitrarily-complex iteration logic, we need a full-on coroutine > to hop in and out of that logic. And for these cases we must spin up a > thread to do the iteration. > > For fibers, the logic is almost always going to be arbitrarily > complex, so they'll be a fiber in every case. We'll use pooling tricks > to reduce the overhead of spinning up fibers, but we can't avoid using > threads to implement them. > > Functionally, spinning up threads where needed and ping-ponging state > back and forth is not a serious functional challenge. The challenge is > in managing the *lifecycle* of those threads. > > The use of threads for simulating coroutines has a few immediate problems: > > 1. Threads are more expensive to spin up > 2. Thread counts may be limited by the host platform > 3. Threads root objects, for GC purposes > 4. Threads will not GC until terminated > > The latter two issues have been keeping me up nights. I'm looking for > suggestions. > > Because Threads can root objects, and because we may want a fiber or > enumerator to GC before they've reached a terminal state (finished > enumerating/fiber exits), two major complexities arrive. > > * The fiber/enum object can hold a hard reference to the thread, but > must define a finalizer so that when they are ready for collection > they can terminate the coroutine thread > * The thread must have a hard reference to the fiber/enum object, so > as not to interfere with it coming eligible for collection > > Both of these stem from the realization that an incomplete > enumeration/fiber/generator can ultimately hold state forever, if the > thread doing that enumeration can't be killed once it would be > eligible for GC. > > If it were possible to create "unrooting" threads, or perhaps threads > that only reference but do not root objects in their stacks, this all > become much easier...but I don't think there's a way currently to > create such threads, is there? > > Are there other ways to implement this I've missed? Of course I know > about the approach Rife and Scala take for continuations in very > localized conditions, but they can't work across aribitrary libraries > that have not been similarly manipulated. > > I'm now getting more desperate to see coroutines added to *any* > production-class JVM, even if I have to sign a waiver and pay a pound > of flesh to get at it. > > - Charlie --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "JVM Languages" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/jvm-languages?hl=en -~----------~----~----~----~------~----~------~--~---
