From 802d6c1224435454a38497e8d25642d63d7c17ed Mon Sep 17 00:00:00 2001 From: Aleksandr Eslikov Date: Fri, 17 May 2019 15:51:06 +0300 Subject: [PATCH 1/2] hw2 --- .../EslikovAV/LockFreeSetImpl.java | 122 ++++++++++++++++++ .../LockFreeSetLinearizabilityTest.java | 42 ++++++ 2 files changed, 164 insertions(+) create mode 100644 csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImpl.java create mode 100644 csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetLinearizabilityTest.java diff --git a/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImpl.java b/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImpl.java new file mode 100644 index 000000000..8b81c6e8f --- /dev/null +++ b/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImpl.java @@ -0,0 +1,122 @@ +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() { + return head.next.getReference() == 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/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 From 3564f0739f4c54422eae987bd2c05f8e0bc3e587 Mon Sep 17 00:00:00 2001 From: Aleksandr Eslikov Date: Sat, 18 May 2019 19:48:53 +0300 Subject: [PATCH 2/2] Fix after exam --- .../EslikovAV/LockFreeSetImpl.java | 8 +- .../EslikovAV/LockFreeSetImplTest.java | 93 +++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImplTest.java diff --git a/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImpl.java b/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImpl.java index 8b81c6e8f..f096281f1 100644 --- a/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImpl.java +++ b/csc/2019/2.LockFreeSet/EslikovAV/LockFreeSetImpl.java @@ -58,7 +58,11 @@ public boolean contains(T value) { @Override public boolean isEmpty() { - return head.next.getReference() == null; + Node current = head.next.getReference(); + while (current != null && current.next.isMarked()) { + current = current.next.getReference(); + } + return current == null; } @Override @@ -84,7 +88,7 @@ private Window find(T value) { current = successor; } else { - if (current.value.compareTo(value) > 0) { + if (current.value.compareTo(value) >= 0) { return new Window(previous, current); } previous = current; 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)); + } +}