diff --git a/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImpl.java b/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImpl.java new file mode 100644 index 000000000..f096281f1 --- /dev/null +++ b/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImpl.java @@ -0,0 +1,126 @@ +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicMarkableReference; + +public class LockFreeSetImpl> implements LockFreeSet { + + private final Node head; + + public LockFreeSetImpl() { + this.head = new Node(); + } + + @Override + public boolean add(T value) { + while (true) { + Window window = find(value); + if (window.current != null && window.current.value != null && window.current.value.compareTo(value) == 0) { + return false; + } else { + Node node = new Node(value, window.current); + if (window.previous.next.compareAndSet(window.current, node, false, false)) { + return true; + } + } + } + } + + @Override + public boolean remove(T value) { + while (true) { + Window window = find(value); + if (window.current == null || window.current.value.compareTo(value) != 0) { + return false; + } else { + Node successor = window.current.next.getReference(); + if (!window.current.next.attemptMark(successor, true)) { + continue; + } else { + window.previous.next.compareAndSet(window.current, successor, false, false); + return true; + } + } + } + } + + @Override + public boolean contains(T value) { + Node current = head.next.getReference(); + + while (current != null + && current.value.compareTo(value) < 0) { + current = current.next.getReference(); + } + + return current != null + && !current.next.isMarked() + && current.value.compareTo(value) == 0; + } + + @Override + public boolean isEmpty() { + Node current = head.next.getReference(); + while (current != null && current.next.isMarked()) { + current = current.next.getReference(); + } + return current == null; + } + + @Override + public Iterator iterator() { + return null; + } + + private Window find(T value) { + while (true) { + Node previous = head; + Node current = previous.next.getReference(); + Node successor; + + while (true) { + if (current == null) { + return new Window(previous, null); + } + successor = current.next.getReference(); + if (current.next.isMarked()) { + if (!previous.next.compareAndSet(current, successor, false, false)) { + break; + } + current = successor; + + } else { + if (current.value.compareTo(value) >= 0) { + return new Window(previous, current); + } + previous = current; + current = successor; + } + } + } + } + + private final class Window { + private final Node previous; + private final Node current; + + Window(Node previous, Node current) { + this.previous = previous; + this.current = current; + + } + } + + private final class Node { + private final T value; + private final AtomicMarkableReference next; + + Node() { + this.value = null; + this.next = new AtomicMarkableReference<>(null, false); + } + + Node(T value, Node next) { + this.value = value; + this.next = new AtomicMarkableReference<>(next, false); + } + } +} \ No newline at end of file diff --git a/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImplTest.java b/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImplTest.java new file mode 100644 index 000000000..729c8a2a2 --- /dev/null +++ b/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImplTest.java @@ -0,0 +1,93 @@ +import org.junit.Assert; +import org.junit.Test; + +public class LockFreeSetImplTest { + + @Test + public void remove() { + LockFreeSetImpl lockFreeSet = new LockFreeSetImpl(); + Assert.assertTrue(lockFreeSet.isEmpty()); + Assert.assertFalse(lockFreeSet.remove(1)); + Assert.assertFalse(lockFreeSet.remove(2)); + + lockFreeSet.add(1); + Assert.assertFalse(lockFreeSet.remove(2)); + Assert.assertTrue(lockFreeSet.remove(1)); + + lockFreeSet.add(1); + lockFreeSet.add(2); + Assert.assertTrue(lockFreeSet.remove(2)); + Assert.assertTrue(lockFreeSet.remove(1)); + + Assert.assertTrue(lockFreeSet.isEmpty()); + + for(int i =0; i < 10; i++){ + lockFreeSet.add(i); + } + + Assert.assertTrue(lockFreeSet.remove(0)); + Assert.assertTrue(lockFreeSet.remove(9)); + Assert.assertTrue(lockFreeSet.remove(5)); + } + + @Test + public void isEmpty() { + LockFreeSetImpl lockFreeSet = new LockFreeSetImpl(); + Assert.assertTrue(lockFreeSet.isEmpty()); + + lockFreeSet.add(1); + Assert.assertFalse(lockFreeSet.isEmpty()); + + lockFreeSet.add(2); + Assert.assertFalse(lockFreeSet.isEmpty()); + + lockFreeSet.remove(1); + Assert.assertFalse(lockFreeSet.isEmpty()); + + lockFreeSet.remove(2); + Assert.assertTrue(lockFreeSet.isEmpty()); + } + + @Test + public void add() { + LockFreeSetImpl lockFreeSet = new LockFreeSetImpl(); + Assert.assertTrue(lockFreeSet.add(1)); + Assert.assertFalse(lockFreeSet.add(1)); + } + + @Test + public void contains() { + LockFreeSetImpl lockFreeSet = new LockFreeSetImpl(); + Assert.assertFalse(lockFreeSet.contains(1)); + Assert.assertFalse(lockFreeSet.contains(2)); + + lockFreeSet.add(1); + Assert.assertTrue(lockFreeSet.contains(1)); + Assert.assertFalse(lockFreeSet.contains(2)); + + lockFreeSet.add(2); + Assert.assertTrue(lockFreeSet.contains(1)); + Assert.assertTrue(lockFreeSet.contains(2)); + } + + @Test + public void bgigSet() { + LockFreeSetImpl lockFreeSet = new LockFreeSetImpl(); + for (int i = 0; i < 10_000; i++) { + lockFreeSet.add(i); + } + for (int i = 0; i < 10_000; i++) { + Assert.assertTrue(lockFreeSet.contains(i)); + } + } + + @Test + public void addInTheMiddle() { + LockFreeSetImpl lockFreeSet = new LockFreeSetImpl(); + Assert.assertTrue(lockFreeSet.add(1)); + Assert.assertTrue(lockFreeSet.add(10)); + Assert.assertTrue(lockFreeSet.add(5)); + Assert.assertTrue(lockFreeSet.add(15)); + Assert.assertTrue(lockFreeSet.add(7)); + } +} diff --git a/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetLinearizabilityTest.java b/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetLinearizabilityTest.java new file mode 100644 index 000000000..b7c1a78db --- /dev/null +++ b/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetLinearizabilityTest.java @@ -0,0 +1,42 @@ +import com.devexperts.dxlab.lincheck.LinChecker; +import com.devexperts.dxlab.lincheck.LoggingLevel; +import com.devexperts.dxlab.lincheck.Options; +import com.devexperts.dxlab.lincheck.annotations.Operation; +import com.devexperts.dxlab.lincheck.annotations.Param; +import com.devexperts.dxlab.lincheck.paramgen.IntGen; +import com.devexperts.dxlab.lincheck.strategy.stress.StressOptions; +import org.junit.Test; + +@Param(name = "key", gen = IntGen.class) +public class LockFreeSetLinearizabilityTest { + private LockFreeSetImpl set = new LockFreeSetImpl<>(); + + @Operation + public Boolean put(@Param(name = "key") int key) { + return set.add(key); + } + + @Operation + public Boolean delete(@Param(name = "key") int key) { + return set.remove(key); + } + + @Operation + public Boolean contains(@Param(name = "key") int key) { + return set.contains(key); + } + + @Operation + public Boolean isEmpty() { + return set.isEmpty(); + } + + @Test + public void test() { + Options opts = new StressOptions() + .iterations(5) + .threads(2) + .logLevel(LoggingLevel.DEBUG); + LinChecker.check(LockFreeSetLinearizabilityTest.class, opts); + } +} \ No newline at end of file