diff --git a/pp.A4-HandOverHandLocking/src/main/java/pp/ThreadsafeSimplifiedList.java b/pp.A4-HandOverHandLocking/src/main/java/pp/ThreadsafeSimplifiedList.java index ad7b277..2c53553 100644 --- a/pp.A4-HandOverHandLocking/src/main/java/pp/ThreadsafeSimplifiedList.java +++ b/pp.A4-HandOverHandLocking/src/main/java/pp/ThreadsafeSimplifiedList.java @@ -1,97 +1,143 @@ package pp; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import lombok.Synchronized; +import net.jcip.annotations.ThreadSafe; + +@ThreadSafe public class ThreadsafeSimplifiedList implements SimplifiedList { - private Node first; - private final Object headLock = new Object(); + private Node first; + private final Lock listLock = new ReentrantLock(); - private static class Node { - U element; - Node prev; - Node next; - final Object lock = new Object(); + private class Node { + private U element; + private Node prev; + private Node next; + private final Lock lock = new ReentrantLock(); - Node(U element, Node prev, Node next) { - this.element = element; - this.prev = prev; - this.next = next; - } - } + private Node(U element, Node prev, Node next) { + super(); + this.element = element; + this.prev = prev; + this.next = next; + } + } - public ThreadsafeSimplifiedList() { - this.first = null; - } + public ThreadsafeSimplifiedList() { + super(); + this.first = null; + } - @Override - public boolean add(T element) { - Node curr; + /** + * Returns the element at the specified position in this list. + * + * @param index index of the element to return + * @return the element at the specified position in this list + */ + @Override + @Synchronized + public T get(int index) { + this.listLock.lock(); + var ptr = this.first; + ptr.lock.lock(); + this.listLock.unlock(); + for (var j = 0; j < index; j++) { + if (ptr.next != null) { + ptr.next.lock.lock(); + var savePtr = ptr; + ptr = ptr.next; + savePtr.lock.unlock(); + } else { + return null; + } + ptr = ptr.next; + } + try { + return (ptr.element); + } + finally { + ptr.lock.unlock(); + } + } - synchronized (headLock) { - if (first == null) { - first = new Node<>(element, null, null); - return true; - } - curr = first; - } + /** + * Appends the specified element to the end of this list. There are no + * limitations on what elements may be added to this list. + * + * @param element element to be appended to this list + * @return true + * @see java.util.Collection#add(Object) + * + */ + @Override + @Synchronized + public boolean add(T element) { + this.listLock.lock(); + if (this.first != null) { + var ptr = this.first; + ptr.lock.lock(); + this.listLock.unlock(); + while (ptr.next != null) { + ptr.next.lock.lock(); + var saveNode = ptr; + ptr = ptr.next; + saveNode.lock.unlock(); + } + ptr.next = new Node<>(element, ptr, null); + } else { + this.first = new Node<>(element, null, null); + this.listLock.unlock(); + } + return true; + } - while (true) { - Node next; - synchronized (curr.lock) { - next = curr.next; - if (next == null) { - curr.next = new Node<>(element, curr, null); - return true; - } - } - curr = next; - } - } + /** + * Replaces the element at the specified position in this list with the + * specified element. + * + * @param index index of the element to replace + * @param element element to be stored at the specified position + * @return the element previously at the specified position + */ + @Override + @Synchronized + public T set(int index, T element) { + this.listLock.lock(); + var ptr = this.first; + ptr.lock.lock(); + this.listLock.lock(); + for (var j = 0; j < index; j++) { + if (ptr.next != null) { + ptr.next.lock.lock(); + var saveNode = ptr; + ptr = ptr.next; + saveNode.lock.unlock(); + } else { + return null; + } + } + var prevElement = ptr.element; + ptr.element = element; + ptr.lock.unlock(); + return prevElement; + } - @Override - public T get(int index) { - Node curr; - synchronized (headLock) { - if (first == null) throw new IndexOutOfBoundsException(); - curr = first; - } - - synchronized (curr.lock) { - for (int i = 0; i < index; i++) { - Node next = curr.next; - if (next == null) throw new IndexOutOfBoundsException(); - synchronized (next.lock) { - curr = next; - } - } - return delay(curr.element); - } - } - - @Override - public T set(int index, T element) { - Node curr; - synchronized (headLock) { - if (first == null) throw new IndexOutOfBoundsException(); - curr = first; - } - - synchronized (curr.lock) { - for (int i = 0; i < index; i++) { - Node next = curr.next; - if (next == null) throw new IndexOutOfBoundsException(); - synchronized (next.lock) { - curr = next; - } - } - T old = curr.element; - curr.element = element; - return old; - } - } - - @Override - public boolean isEmpty() { - synchronized (headLock) { - return first == null; - } - } + /** + * Returns true if this list contains no elements. + * + * @return true if this list contains no elements + */ + @Override + @Synchronized + public boolean isEmpty() { + this.listLock.lock(); + try { + return this.first == null; + } finally { + this.listLock.unlock(); + } + } } +