/**
* A "yield return" implementation for Java
* By Jim Blackler (jimblackler@gmail.com)
*
* http://jimblackler.net/blog/?p=61
* http://svn.jimblackler.net/jimblackler/trunk/IdeaProjects/YieldAdapter/
*/
package php.runtime.util.generator;
import java.util.NoSuchElementException;
import java.util.concurrent.SynchronousQueue;
/**
* A class to convert methods that implement the Collector<> class into a standard Iterable<>, using
* a new thread created for the collection process, and a SynchronousQueue<> object.
*/
public class ThreadedYieldAdapter<T> implements YieldAdapter<T> {
/**
* Message structure to pass values between threads.
*/
class Message {
}
abstract private class StopMessage extends Message {
}
private class EndMessage extends StopMessage {
}
private class AbortedMessage extends StopMessage {
}
private static class VarContainer<T> {
T var;
public T getVar() {
return var;
}
public void setVar(T var) {
this.var = var;
}
}
/**
* The vehicle to pass the actual values.
*/
class ValueMessage extends Message {
ValueMessage(T value) {
this.value = value;
}
final T value;
}
/**
* Convert a method that implements the Collector<> class with a standard Iterable<>. This means
* that the collecting method can use complex recursive logic, but still allows the calling code
* to handle the results with a standard iterator. Results are returned immediately and do not
* incur overhead of being stored in a list. Calculation overhead is only performed for the
* results that are requested through the iterator.
*
* This is implemented using a new thread created for the collection process, and a
* SynchronousQueue<> object.
*/
public YieldAdapterIterable<T> adapt(final Collector<T> client) {
return new YieldAdapterIterable<T>() {
public YieldAdapterIterator<T> iterator() {
final SynchronousQueue<Message> synchronousQueue = new SynchronousQueue<Message>();
// Mechanism to ensure both threads don't run at the same time
final SynchronousQueue<Object> returnQueue = new SynchronousQueue<Object>();
final VarContainer<Thread> collectThread = new VarContainer<Thread>();
final YieldAdapterIterator<T> iterator = new YieldAdapterIterator<T>() {
private Message messageWaiting = null;
public boolean hasNext() {
readNextMessage();
return !StopMessage.class.isAssignableFrom(messageWaiting.getClass());
// instanceof cannot be used because of generics restriction
}
public T next() {
readNextMessage();
if (StopMessage.class.isAssignableFrom(messageWaiting.getClass())) {
// instanceof cannot be used because of generics restriction
throw new NoSuchElementException();
}
final T value = currentValue = ((ValueMessage) messageWaiting).value;
messageWaiting = null; // for next time
return value;
}
private void readNextMessage() {
if (messageWaiting == null) { // do not run if value waiting to be put
try {
returnQueue.put(new Object()); // allow other thread to gather result
messageWaiting = synchronousQueue.take();
} catch (final InterruptedException e) {
messageWaiting = new EndMessage();
}
}
}
public void remove() {
throw new UnsupportedOperationException("Generators don't support remove()");
}
@Override
/**
* Iterator's finalize() can be used to tell when it is out of scope, and the
* collecting thread can be terminated.
*/
protected void finalize() throws Throwable {
close();
super.finalize();
}
/**
* This can be manually called by the calling code to force release of
* resources at the earliest opportunity.
*/
@Override
public void close() {
collectThread.getVar().interrupt();
}
};
// This thread is where the collecting logic is executed.
collectThread.setVar(new Thread() {
@Override
public void run() {
// Important .. handling thread (main thread) gets to run first.
// This is because the collecting process should be run on demand in response to
// iterator access. Each result should be dealt with by the handling process before
// the collecting process is able to modify any resources that may be requred by
// results.
try {
returnQueue.take();
} catch (final InterruptedException e) {
//throw new RuntimeException("Error with yield adapter", e);
return;
}
try {
try {
client.collect(new ResultHandler<T>() {
public T handleResult(final T value)
throws CollectionAbortedException {
try {
T oldValue = iterator.getCurrentValue();
synchronousQueue.put(new ValueMessage(value));
returnQueue.take(); // wait for permission to continue
return oldValue;
} catch (final InterruptedException e) {
// this thread has been aborted
throw new CollectionAbortedException(e);
}
}
});
synchronousQueue.put(new EndMessage());
// Signal no more results to come
} catch (final CollectionAbortedException collectionAborted) {
if (!(collectionAborted
.getCause() instanceof InterruptedException)) {
// Collect was aborted by client
// This is not sent on thread abort as there is nothing waiting
// to receive it, and the thread will block.
synchronousQueue.put(new AbortedMessage());
}
}
} catch (final InterruptedException e) {
// Operation was aborted internally (e.g. iterator out of scope)
}
}
});
collectThread.getVar().setDaemon(true);
collectThread.getVar().start();
return iterator;
}
};
}
}