summaryrefslogtreecommitdiffstats
path: root/yolean/src/main/java/com/yahoo/yolean/concurrent/ThreadRobustList.java
diff options
context:
space:
mode:
Diffstat (limited to 'yolean/src/main/java/com/yahoo/yolean/concurrent/ThreadRobustList.java')
-rw-r--r--yolean/src/main/java/com/yahoo/yolean/concurrent/ThreadRobustList.java122
1 files changed, 122 insertions, 0 deletions
diff --git a/yolean/src/main/java/com/yahoo/yolean/concurrent/ThreadRobustList.java b/yolean/src/main/java/com/yahoo/yolean/concurrent/ThreadRobustList.java
new file mode 100644
index 00000000000..5060ed8ef6a
--- /dev/null
+++ b/yolean/src/main/java/com/yahoo/yolean/concurrent/ThreadRobustList.java
@@ -0,0 +1,122 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.yolean.concurrent;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * <p>This class implements a thread-safe, lock-free list of Objects that supports multiple readers and a single writer.
+ * Because there are no locks or other memory barriers involved, there exists no <i>happens-before</i> relationship
+ * among calls to either methods of the <tt>ThreadRobustList</tt>. This means that there are no guarantees as to when
+ * (or even if) an item {@link #add(Object)}ed becomes visible through {@link #iterator()}. If visibility is required,
+ * either use explicit synchronization between reader and writer thread, or move to a different concurrent collection
+ * (e.g. <tt>CopyOnWriteArrayList</tt>).</p>
+ * <p>Because it is lock-free, the <tt>ThreadRobustList</tt> has minimal overhead to both reading and writing. The
+ * iterator offered by this class always observes the list in a consistent state, and it never throws a
+ * <tt>ConcurrentModificationException</tt>.</p>
+ * <p>The <tt>ThreadRobustList</tt> does not permit adding <tt>null</tt> items.</p>
+ * <p>The usage of <tt>ThreadRobustList</tt> has no memory consistency effects. </p>
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ * @since 5.1.15
+ */
+public class ThreadRobustList<T> implements Iterable<T> {
+
+ private Object[] items;
+ private int next = 0;
+
+ /**
+ * <p>Constructs a new instance of this class with an initial capacity of <tt>10</tt>.</p>
+ */
+ public ThreadRobustList() {
+ this(10);
+ }
+
+ /**
+ * <p>Constructs a new instance of this class with a given initial capacity.</p>
+ *
+ * @param initialCapacity the initial capacity of this list
+ */
+ public ThreadRobustList(int initialCapacity) {
+ items = new Object[initialCapacity];
+ }
+
+ /**
+ * <p>Returns whether or not this list is empty.</p>
+ *
+ * @return <tt>true</tt> if this list has zero items
+ */
+ public boolean isEmpty() {
+ return next == 0;
+ }
+
+ /**
+ * <p>Adds an item to this list. As opposed to <tt>CopyOnWriteArrayList</tt>, items added to this list may become
+ * visible to iterators created <em>before</em> a call to this method.</p>
+ *
+ * @param item the item to add
+ * @throws NullPointerException if <tt>item</tt> is <tt>null</tt>
+ */
+ public void add(T item) {
+ if (item == null) {
+ throw new NullPointerException();
+ }
+ Object[] workItems = items;
+ if (next >= items.length) {
+ workItems = Arrays.copyOf(workItems, 20 + items.length * 2);
+ workItems[next++] = item;
+ items = workItems;
+ } else {
+ workItems[next++] = item;
+ }
+ }
+
+ /**
+ * <p>Returns an iterator over the items in this list. As opposed to <tt>CopyOnWriteArrayList</tt>, this iterator
+ * may see items added to the <tt>ThreadRobustList</tt> even if they occur <em>after</em> a call to this method.</p>
+ * <p>The returned iterator does not support <tt>remove()</tt>.</p>
+ *
+ * @return an iterator over this list
+ */
+ @Override
+ public Iterator<T> iterator() {
+ return new ThreadRobustIterator<>(items);
+ }
+
+ private static class ThreadRobustIterator<T> implements Iterator<T> {
+
+ final Object[] items;
+ int nextIndex = 0;
+
+ ThreadRobustIterator(Object[] items) {
+ this.items = items;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ return (T)items[nextIndex++];
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (nextIndex >= items.length) {
+ return false;
+ }
+ if (items[nextIndex] == null) {
+ return false;
+ }
+ return true;
+ }
+ }
+}