All Downloads are FREE. Search and download functionalities are using the official Maven repository.

Alachisoft.NCache.Common.yield.ThreadedYieldAdapter Maven / Gradle / Ivy

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Alachisoft.NCache.Common.yield;

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 implements YieldAdapter {

    /**
     * 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 adapt(final Collector client) { return new YieldAdapterIterable() { public YieldAdapterIterator iterator() { final SynchronousQueue synchronousQueue = new SynchronousQueue(); // Mechanism to ensure both threads don't run at the same time final SynchronousQueue returnQueue = new SynchronousQueue(); // This thread is where the collecting logic is executed. final Thread collectThread = 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 (InterruptedException e) { throw new RuntimeException("Error with yield adapter", e); } try { try { client.collect(new ResultHandler() { public void handleResult(T value) throws CollectionAbortedException { try { synchronousQueue.put(new ValueMessage(value)); returnQueue.take(); // wait for permission to continue } catch (InterruptedException e) { // this thread has been aborted throw new CollectionAbortedException(e); } } }); synchronousQueue.put(new EndMessage()); // Signal no more results to come } catch (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 (InterruptedException e) { // Operation was aborted internally (e.g. iterator out of scope) } } }; collectThread.setDaemon(true); collectThread.start(); return new YieldAdapterIterator() { 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 = ((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 (InterruptedException e) { messageWaiting = new EndMessage(); } } } public void 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 { dispose(); super.finalize(); } /** * This can be manually called by the calling code to force release of resources at the earliest opportunity. */ public void dispose() { collectThread.interrupt(); } }; } }; } /** * 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 { } /** * The vehicle to pass the actual values. */ class ValueMessage extends Message { final T value; ValueMessage(T value) { this.value = value; } } }