package bes.concurrent; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; public final class SimpleCollection<E> implements Iterable<E> { // dummy head pointer, tail pointer only useful for addition. // both are only <= their true position, and can be reached // from any node (deleted or not) prior to them via a chain // of next links. private volatile Node head, tail; public SimpleCollection() { head = tail = new Node(null); } public Node add(E item) { Node insert = new Node(item); Node tail = this.tail; while (true) { Node next = tail.next; if (next != null) { tail = next; } else { insert.prev = tail; if (nextUpdater.compareAndSet(tail, null, insert)) { // no need for atomicity; if updates are out of order, chain simply needs to be walked forwards this.tail = insert; return insert; } else { tail = tail.next; } } } } public Iterator<E> iterator() { return new Iterator<E>() { Node cur = head; Object curv = null; { setNext(true); } private boolean setNext(boolean isHead) { boolean adjacent = true; Node p = cur, n = p.next; while (n != null) { Object item = n.item; if (item != null) { if (!adjacent) { // edit out a chain of deleted items cur.next = n; n.prev = cur; if (isHead) head = p; } // stash value and position curv = item; cur = n; return true; } if (isHead) prevUpdater.lazySet(p, null); p = n; n = n.next; adjacent = false; } return false; } public boolean hasNext() { return curv != null || setNext(false); } public E next() { if (curv == null && !setNext(false)) throw new NoSuchElementException(); Object r = curv; curv = null; return (E) r; } public void remove() { cur.remove(); } }; } public final class Node { volatile Object item; volatile Node next, prev; public Node(Object item) { this.item = item; } public boolean isDeleted() { return item == null; } // mark ourselves deleted, and attempt to edit ourselves out of the list public void remove() { item = null; // in case the list has some phantom elements, we try to remove ourselves // and any contiguous adjacent range of deleted nodes // start by finding our live predecessor Node p = prev; while (true) { if (p == null) { // we have no live predecessor; hasWaiters() will remove us cleanupHead(); return; } if (!p.isDeleted()) break; p = p.prev; } // we walk forwards from our live predecessor to find the next live node, // since the forward chaining is our source of truth (prev is only a helping hand) Node n = p.next; // if we are the tail of the list, we cannot be removed if (n == null) return; Node n2; while (n.isDeleted() && (n2 = n.next) != null) n = n2; p.next = n; } } void cleanupHead() { Node ph = head, h = ph, n; while ((n = h.next) != null && n.isDeleted()) { prevUpdater.lazySet(n, null); h = n; } if (ph != h) head = h; } private static final AtomicReferenceFieldUpdater<SimpleCollection.Node, SimpleCollection.Node> nextUpdater = AtomicReferenceFieldUpdater.newUpdater(SimpleCollection.Node.class, SimpleCollection.Node.class, "next"); private static final AtomicReferenceFieldUpdater<SimpleCollection.Node, SimpleCollection.Node> prevUpdater = AtomicReferenceFieldUpdater.newUpdater(SimpleCollection.Node.class, SimpleCollection.Node.class, "prev"); }