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

java8.util.LBQSpliterator Maven / Gradle / Ivy

Go to download

streamsupport is a backport of the Java 8 java.util.function (functional interfaces) and java.util.stream (streams) API for Java 6 / 7 and Android developers

There is a newer version: 1.7.4
Show newest version
/*
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group and released to the public domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */
package java8.util;

import java.util.Comparator;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.ReentrantLock;

import java8.util.function.Consumer;

/**
 * A customized variant of Spliterators.IteratorSpliterator for
 * LinkedBlockingQueues.
 * Keep this class in sync with (very similar) LBDSpliterator.
 * 

* The returned spliterator is weakly consistent. *

* The {@code Spliterator} reports {@link Spliterator#CONCURRENT}, * {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}. *

* The {@code Spliterator} implements {@code trySplit} to permit limited * parallelism. * * @param the type of elements held in the LinkedBlockingQueue */ final class LBQSpliterator implements Spliterator { // CVS rev. 1.110 private static final int MAX_BATCH = 1 << 25; // max batch array size private final LinkedBlockingQueue queue; private final ReentrantLock putLock; private final ReentrantLock takeLock; private Object current; // current node; null until initialized private int batch; // batch size for splits private boolean exhausted; // true when no more nodes private long est; // size estimate private LBQSpliterator(LinkedBlockingQueue queue) { this.queue = queue; this.est = queue.size(); this.putLock = getPutLock(queue); this.takeLock = getTakeLock(queue); } static Spliterator spliterator(LinkedBlockingQueue queue) { return new LBQSpliterator(queue); } Object succ(Object p) { return (p == (p = getNextNode(p))) ? getHeadNext(queue) : p; } @Override public int characteristics() { return (Spliterator.ORDERED | Spliterator.NONNULL | Spliterator.CONCURRENT); } @Override public long estimateSize() { return est; } @Override public void forEachRemaining(Consumer action) { Objects.requireNonNull(action); if (!exhausted) { exhausted = true; Object p = current; current = null; forEachFrom(action, p); } } @Override public Comparator getComparator() { return Spliterators.getComparator(this); } @Override public long getExactSizeIfKnown() { return Spliterators.getExactSizeIfKnown(this); } @Override public boolean hasCharacteristics(int characteristics) { return Spliterators.hasCharacteristics(this, characteristics); } @Override public boolean tryAdvance(Consumer action) { Objects.requireNonNull(action); if (!exhausted) { E e = null; fullyLock(); try { Object p; if ((p = current) != null || (p = getHeadNext(queue)) != null) do { e = getNodeItem(p); p = succ(p); } while (e == null && p != null); if ((current = p) == null) exhausted = true; } finally { fullyUnlock(); } if (e != null) { action.accept(e); return true; } } return false; } @Override public Spliterator trySplit() { Object h; LinkedBlockingQueue q = queue; if (!exhausted && ((h = current) != null || (h = getHeadNext(q)) != null) && getNextNode(h) != null) { int n = batch = Math.min(batch + 1, MAX_BATCH); Object[] a = new Object[n]; int i = 0; Object p = current; fullyLock(); try { if (p != null || (p = getHeadNext(q)) != null) for (; p != null && i < n; p = succ(p)) if ((a[i] = getNodeItem(p)) != null) i++; } finally { fullyUnlock(); } if ((current = p) == null) { est = 0L; exhausted = true; } else if ((est -= i) < 0L) est = 0L; if (i > 0) return Spliterators.spliterator (a, 0, i, (Spliterator.ORDERED | Spliterator.NONNULL | Spliterator.CONCURRENT)); } return null; } /** * Runs action on each element found during a traversal starting at p. * If p is null, traversal starts at head. */ void forEachFrom(Consumer action, Object p) { // Extract batches of elements while holding the lock; then // run the action on the elements while not final int batchSize = 64; // max number of elements per batch Object[] es = null; // container for batch of elements int n, len = 0; do { fullyLock(); try { if (es == null) { if (p == null) p = getHeadNext(queue); for (Object q = p; q != null; q = succ(q)) if (getNodeItem(q) != null && ++len == batchSize) break; es = new Object[len]; } for (n = 0; p != null && n < len; p = succ(p)) if ((es[n] = getNodeItem(p)) != null) n++; } finally { fullyUnlock(); } for (int i = 0; i < n; i++) { @SuppressWarnings("unchecked") E e = (E) es[i]; action.accept(e); } } while (n > 0 && p != null); } /** * Lock to prevent both puts and takes. */ private void fullyLock() { putLock.lock(); takeLock.lock(); } /** * Unlock to allow both puts and takes. */ private void fullyUnlock() { takeLock.unlock(); putLock.unlock(); } private static ReentrantLock getPutLock(LinkedBlockingQueue queue) { return (ReentrantLock) U.getObject(queue, PUT_LOCK_OFF); } private static ReentrantLock getTakeLock(LinkedBlockingQueue queue) { return (ReentrantLock) U.getObject(queue, TAKE_LOCK_OFF); } /** * Returns queue.head.next as an Object */ private static Object getHeadNext(LinkedBlockingQueue queue) { return getNextNode(U.getObject(queue, HEAD_OFF)); } /** * Returns node.next as an Object */ private static Object getNextNode(Object node) { return U.getObject(node, NODE_NEXT_OFF); } /** * Returns node.item as a T */ private static T getNodeItem(Object node) { return (T) U.getObject(node, NODE_ITEM_OFF); } // Unsafe mechanics private static final sun.misc.Unsafe U = UnsafeAccess.unsafe; private static final long HEAD_OFF; private static final long NODE_ITEM_OFF; private static final long NODE_NEXT_OFF; private static final long PUT_LOCK_OFF; private static final long TAKE_LOCK_OFF; static { try { Class nc = Class .forName("java.util.concurrent.LinkedBlockingQueue$Node"); HEAD_OFF = U.objectFieldOffset(LinkedBlockingQueue.class .getDeclaredField("head")); NODE_ITEM_OFF = U.objectFieldOffset(nc.getDeclaredField("item")); NODE_NEXT_OFF = U.objectFieldOffset(nc.getDeclaredField("next")); PUT_LOCK_OFF = U.objectFieldOffset(LinkedBlockingQueue.class .getDeclaredField("putLock")); TAKE_LOCK_OFF = U.objectFieldOffset(LinkedBlockingQueue.class .getDeclaredField("takeLock")); } catch (Exception e) { throw new Error(e); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy