/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.backend.impl.lucene;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* A custom structure similar to a concurrent linked list.
*
* This could be functionally replaced by a LinkedBlockingDeque, but we only
* need a specific subset of its functionality.
* Specifically, we need to maintain the order of elements being added, but on
* a drain we'll only ever need to iterate the list sequentially, and the
* drain needs to atomically reset the queue.
*
* @author Sanne Grinovero (C) 2014 Red Hat Inc.
* @since 5.0
*/
public final class MultiWriteDrainableLinkedList<T> {
//Guarded by synchronization on this
private Node<T> first = null;
//Guarded by synchronization on this
private Node<T> last = null;
/**
* Adds a new entry to this list.
*
* @param element The element to add.
*/
public void add(T element) {
final Node<T> newnode = new Node<T>( element );
addNode( newnode );
}
private synchronized void addNode(Node<T> newnode) {
if ( first == null ) {
first = newnode;
last = newnode;
}
else {
last.next = newnode;
last = newnode;
}
}
/**
* Returns an Iterable over all results added so far, but
* atomically clears the structure as well.
* The returned iterable will be the only entry point to
* read the previously appended data.
* @return an Iterable, or null if there is no data.
*/
public Iterable<T> drainToDetachedIterable() {
final Node<T> head = drainHead();
if ( head != null ) {
return new DetachedNodeIterable<T>( head );
}
else {
//The choice to return null rather than an empty iterator
//allows the client to not need an isEmpty() method, which would
//need a different level of lock granularity.
return null;
}
}
private synchronized Node<T> drainHead() {
final Node<T> head = first;
first = null;
last = null;
return head;
}
static final class Node<T> {
final T value;
Node<T> next;
Node(T x) {
value = x;
}
}
static final class DetachedNodeIterable<T> implements Iterable<T> {
private final Node<T> head;
public DetachedNodeIterable(Node<T> head) {
this.head = head;
}
@Override
public Iterator<T> iterator() {
return new DetachedNodeIterator<T>( head );
}
}
static final class DetachedNodeIterator<T> implements Iterator<T> {
private Node<T> current;
DetachedNodeIterator(Node<T> head) {
this.current = head;
}
@Override
public boolean hasNext() {
return current != null;
}
@Override
public T next() {
if ( current == null ) {
throw new NoSuchElementException();
}
T v = current.value;
current = current.next;
return v;
}
@Override
public void remove() {
throw new UnsupportedOperationException( "This iterator is unable to remove elements" );
}
}
}