/* * The MIT License * * Copyright (c) 2009 The Broad Institute * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package picard.util; import htsjdk.samtools.util.CloseableIterator; import java.util.NoSuchElementException; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; /** * Wrapper around a CloseableIterator that reads in a separate thread, for cases in which that might be * efficient. */ public class AsyncIterator<T> implements CloseableIterator<T> { private static volatile int threadsCreated = 0; // Just used for thread naming. public static final int DEFAULT_QUEUE_SIZE = 2000; private final AtomicBoolean isClosed = new AtomicBoolean(false); private final BlockingQueue<T> queue; private final Thread reader; private final ReaderRunnable readerRunnable; private final AtomicReference<Throwable> ex = new AtomicReference<Throwable>(null); private T theNext = null; private final CloseableIterator<T> underlyingIterator; public AsyncIterator(final CloseableIterator<T> underlyingIterator, final int queueSize, final String threadNamePrefix) { this.underlyingIterator = underlyingIterator; this.queue = new ArrayBlockingQueue<T>(queueSize); this.readerRunnable = new ReaderRunnable(); this.reader = new Thread(readerRunnable, threadNamePrefix + threadsCreated++); this.reader.setDaemon(true); this.reader.start(); getNext(); } /** * Set theNext to the next item to be returned, or null if there are no more items. */ private void getNext() { assertOpen(); checkAndRethrow(); try { theNext = null; while (!this.queue.isEmpty() || !this.readerRunnable.isDone()) { theNext = this.queue.poll(5, TimeUnit.SECONDS); checkAndRethrow(); if (theNext != null) break; } } catch (InterruptedException ie) { throw new RuntimeException("Interrupted queueing item for writing.", ie); } checkAndRethrow(); } public boolean hasNext() { assertOpen(); return theNext != null; } public T next() { assertOpen(); if (!hasNext()) throw new NoSuchElementException(); final T ret = theNext; getNext(); return ret; } public void remove() { throw new UnsupportedOperationException(); } /** * Stops the thread and then calls synchronouslyClose() to allow implementation to do any one time clean up. */ public void close() { checkAndRethrow(); assertOpen(); this.isClosed.set(true); try { this.reader.join(); } catch (InterruptedException ie) { throw new RuntimeException("Interrupted waiting on reader thread.", ie); } underlyingIterator.close(); checkAndRethrow(); this.queue.clear(); } private void assertOpen() { if (this.isClosed.get()) { throw new RuntimeException("AsyncIterator already closed."); } } /** * Checks to see if an exception has been raised in the reader thread and if so rethrows it as an Error * or RuntimeException as appropriate. */ private void checkAndRethrow() { final Throwable t = this.ex.get(); if (t != null) { if (t instanceof Error) throw (Error) t; if (t instanceof RuntimeException) throw (RuntimeException) t; else throw new RuntimeException(t); } } /** * Small Runnable implementation that simply reads from underlying iterator and stores on the blocking queue. */ private class ReaderRunnable implements Runnable { private final AtomicBoolean readerDone = new AtomicBoolean(false); public boolean isDone() { return readerDone.get(); } public void run() { try { boolean isEof = false; while (!isClosed.get() && !isEof) { try { if (!underlyingIterator.hasNext()) { isEof = true; } else { final T item = underlyingIterator.next(); // Keep trying to put item on the queue unless close() has been called. while (!isClosed.get() && !queue.offer(item, 2, TimeUnit.SECONDS)) { } } } catch (InterruptedException ie) { /* Do Nothing */ } } } catch (Throwable t) { ex.compareAndSet(null, t); } finally { readerDone.set(true); } } } }