package linkedlists.lockbased; import contention.abstractions.AbstractCompositionalIntSet; /** * The original Versioned Linked List. * * This algorithm exploits a versioned try-lock to achieve optimal concurrency * as explained in: * * A Concurrency-Optimal List-Based Set. Gramoli, Kuznetsov, Ravi, Shang. * arXiv:1502.01633, February 2015 and DISC 2015 * * This version does not need Java 8+ and uses a custom versioned try-lock * but consider using the improved versioned based on the built-in * StampedLock when using Java 8+: * {@link linkedlists.lockbased.VersionListSetStampLock} * * @author Vincent Gramoli * @author Di Shang */ public class VersionedListSet extends AbstractCompositionalIntSet { // sentinel nodes final private Node head; final private Node tail; static final int ABORT = 1; static final int OK = 0; public VersionedListSet() { // init sentinel values tail = new Node(Integer.MAX_VALUE, null); head = new Node(Integer.MIN_VALUE, tail); } public class Window { public Node prev = head; public Node curr; public long prevVersion; public Window() { } public void setValues(Node prev, Node curr) { this.prev = prev; this.curr = curr; } } private int validate(Window win) { win.prevVersion = win.prev.getVersion(); if (win.prev.isDeleted || win.prev.next != win.curr) { return ABORT; } return OK; } /** * Traverse the linked list up to a node with value >= the value being searched for * * @param value * the value being searched for * @return a window of focus around the located node */ public Window traverse(final int value, final Window win) { Node prev = win.prev, curr; if (prev.isDeleted) { prev = head; } curr = prev.next; while (curr.value < value) { prev = curr; curr = curr.next; } win.setValues(prev, curr); return win; } /* * Insert * * @see contention.abstractions.CompositionalIntSet#addInt(int) */ @Override public boolean addInt(int v) { Window window = new Window(); Node newNode = null; // keep restarting upon abort while (true) { window = traverse(v, window); if (window.curr.isDeleted) { continue; // abort } // value already exist, operation fail if (window.curr.value == v) { return false; } if (validate(window) == ABORT) { continue; // abort } if (newNode == null) { newNode = new Node(v); } newNode.next = window.curr; // ------------------------ critical section --------------------- // try to lock prev at the versions we just read if (!window.prev.tryLockAtVersion(window.prevVersion)) { continue; // abort } // update prev to point to the new node window.prev.next = newNode; window.prev.unlockAndIncrementVersion(); // ------------------------ critical section --------------------- return true; } } /* * Remove * * @see contention.abstractions.CompositionalIntSet#removeInt(int) */ @Override public boolean removeInt(int v) { Window window = new Window(); // keep restarting upon abort while (true) { window = traverse(v, window); // value not exist or already deleted, operation fail if (window.curr.value != v || window.curr.isDeleted) { return false; } if (validate(window) == ABORT) { continue; // abort } // ------------------------ critical section --------------------- // try to lock both prev and curr at the versions we just read if (!window.prev.tryLockAtVersion(window.prevVersion)) { continue; // abort } window.curr.lock(); // mark curr as deleted window.curr.isDeleted = true; // update prev to point to next window.prev.next = window.curr.next; window.curr.unlockAndIncrementVersion(); window.prev.unlockAndIncrementVersion(); // ------------------------ critical section --------------------- return true; } } @Override public boolean containsInt(int v) { Node curr = head; while (curr.value < v) { curr = curr.next; } return (curr.value == v) && (!curr.isDeleted); } /** * Non atomic and thread-unsafe */ @Override public int size() { int count = 0; Node curr = head.next; while (curr != tail) { curr = curr.next; count++; } return count; } @Override public void clear() { head.next = tail; head.resetLock(); tail.resetLock(); } }