/*
* Copyright (c) 2012-2014, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.common.collection;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Implements a concurrent blocking multiset −− bag
* Transforms existing blocking multisets into multilane forms Exposes take() and put() accessor methods.
* Lanes: Array of underlying blocking concurrent collections like: ArrayBlockingQueue, LinkedBlockingQueue, LinkedTransferQueue, SynchronousQueue etc
*
* @see http://blogs.oracle.com/dave/resource/spaa11-multiset.pdf
* @author Dave Dice dave.dice@oracle.com
* @author Oleksandr Otenko oleksandr.otenko@oracle.com
*/
public class MultiLane<T> {
// putCursor and takeCursor are write and read "cursors" that chase each other.
// These are the only central read−write variables in our algorithm.
// Invariant: the generated indices must follow the same trajectory
// The stream of indexes generated by putCursor and takeCursor does _not_ need to be strictly cyclic, and in fact will not be when the putCursor
// and takeCursor overflow and change sign. That’s benign.
// Progress property : obstruction within a lane impacts only that lane.
// Invariant: if there are any "written" lanes in the MultiLane collection then the lane identified by takeCursor is written.
// "Written" means that the sub−collection at that specified lane has at least one available element, or that some arriving put() has advanced putCursor and is poised to put() into that lane.
// take() may thus pair−up promptly if put messages are available. Complementary invariant follow the same trajectory.
// The general approach is similar to that of a ring buffer, except that
// there are no locks at the top level but only atomically updated cursors, and
// the ring elements are themselves blocking concurrent collections
private final LinkedBlockingQueue<T>[] lanes;
private final AtomicInteger putCursor = new AtomicInteger();
private final AtomicInteger takeCursor = new AtomicInteger();
public MultiLane(int width) {
if (width <= 0)
throw new IllegalArgumentException("width must be positive but is " + width);
if ((width & (width - 1)) != 0) // For brevity of explication require power−of−two width value that allows efficient indexing of the form : lanes[i & (lanes.length−1)]
throw new IllegalArgumentException("width must be a power of 2 but is " + width);
lanes = (LinkedBlockingQueue<T>[]) new LinkedBlockingQueue[width];
for (int i = 0; i < width; i++)
lanes[i] = new LinkedBlockingQueue<T>();
}
public void put(T v) throws InterruptedException {
final int curs = putCursor.getAndIncrement();
lanes[curs & (lanes.length - 1)].put(v); // put() is blocking
}
public T take() throws InterruptedException {
final int curs = takeCursor.getAndIncrement();
return lanes[curs & (lanes.length - 1)].take(); // take() is blocking
}
}