Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial commit without Iterator #257

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions csc/2019/2.LockFreeSet/ChausovAG/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>dxahtepb</groupId>
<artifactId>lock-free-set</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>

<repositories>
<repository>
<id>bintray--maven</id>
<name>bintray</name>
<url>https://dl.bintray.com/devexperts/Maven</url>
</repository>
</repositories>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.devexperts.lincheck</groupId>
<artifactId>lincheck</artifactId>
<version>2.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package ru.cscenter.dxahtepb.lockfreeset;

/**
* Lock-Free множество.
* @param <T> Тип ключей
*/
public interface LockFreeSet<T extends Comparable<T>> {
/**
* Добавить ключ к множеству
*
* Алгоритм должен быть как минимум lock-free
*
* @param value значение ключа
* @return false если value уже существует в множестве, true если элемент был добавлен
*/
boolean add(T value);

/**
* Удалить ключ из множества
*
* Алгоритм должен быть как минимум lock-free
*
* @param value значение ключа
* @return false если ключ не был найден, true если ключ успешно удален
*/
boolean remove(T value);

/**
* Проверка наличия ключа в множестве
*
* Алгоритм должен быть как минимум wait-free
*
* @param value значение ключа
* @return true если элемент содержится в множестве, иначе - false
*/
boolean contains(T value);

/**
* Проверка множества на пустоту
*
* Алгоритм должен быть как минимум wait-free
*
* @return true если множество пусто, иначе - false
*/
boolean isEmpty();

/**
* Возвращает lock-free итератор для множества
*
* Итератор должен базироваться на концепции снапшота, см.
* @see <a href="http://www.cs.technion.ac.il/~erez/Papers/iterators-disc13.pdf">Lock-Free Data-Structure Iterators</a>
*
* @return новый экземпляр итератор для множества
*/
java.util.Iterator<T> iterator();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package ru.cscenter.dxahtepb.lockfreeset;

import java.util.concurrent.atomic.AtomicMarkableReference;

public class LockFreeSetImpl<T extends Comparable<T>> implements LockFreeSet<T> {
private final Node head = new Node(null, null);

@Override
public boolean add(final T value) {
while (true) {
final Pair pair = find(value);
final Node prev = pair.prevNode;
final Node curr = pair.nextNode;

if (curr == null || curr.value.compareTo(value) != 0) {
final Node node = new Node(value, curr);
if (prev.next.compareAndSet(curr, node, false, false)) {
return true;
}
} else {
return false;
}
}
}

@Override
public boolean remove(final T value) {
while (true) {
final Pair pair = find(value);
final Node prev = pair.prevNode;
final Node curr = pair.nextNode;

if (curr == null || curr.value.compareTo(value) != 0) {
return false;
} else {
final Node succ = curr.next.getReference();
if (!curr.next.compareAndSet(succ, succ, false, true)) {
continue;
}
prev.next.compareAndSet(curr, succ, false, false);
return true;
}
}

}

@Override
public boolean contains(final T value) {
final boolean[] holder = new boolean[1];
Node curr = head.next.get(holder);
while (curr != null && curr.value.compareTo(value) < 0) {
curr = curr.next.get(holder);
}
final boolean isCurrMarked = holder[0];
return curr != null && curr.value.compareTo(value) == 0 && !isCurrMarked;
}

private Pair find(final T value) {
retry:
while (true) {
Node prev = head;
Node curr = prev.next.getReference();

while (true) {
if (curr == null) {
return new Pair(prev, null);
}

final boolean[] holder = new boolean[1];
final Node succ = curr.next.get(holder);
final boolean isCurrMarked = holder[0];

if (isCurrMarked && !prev.next.compareAndSet(curr, succ, false, false)) {
continue retry;
} else {
if (!isCurrMarked && value.compareTo(curr.value) <= 0) {
return new Pair(prev, curr);
}
prev = curr;
curr = succ;
}
}
}
}

@Override
public boolean isEmpty() {
return head.next.getReference() == null;
}

@Override
public java.util.Iterator<T> iterator() {
return null;
}

private class Node {
final T value;
final AtomicMarkableReference<Node> next;

Node(final T value, final Node next) {
this.value = value;
this.next = new AtomicMarkableReference<>(next, false);
}

@Override
public String toString() {
return "Node(" + (value != null ? value.toString() : "null") + ")";
}
}

private class Pair {
final Node prevNode;
final Node nextNode;

Pair(final Node prevNode, final Node nextNode) {
this.prevNode = prevNode;
this.nextNode = nextNode;
}

@Override
public String toString() {
return "Pair("
+ (prevNode != null ? prevNode.toString() : "null")
+ ", "
+ (nextNode != null ? nextNode.toString() : "null")
+ ")";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ru.cscenter.dxahtepb.lockfreeset;

import com.devexperts.dxlab.lincheck.LinChecker;
import com.devexperts.dxlab.lincheck.annotations.Operation;
import com.devexperts.dxlab.lincheck.annotations.Param;
import com.devexperts.dxlab.lincheck.strategy.stress.StressCTest;
import com.devexperts.dxlab.lincheck.strategy.stress.StressOptions;
import com.devexperts.dxlab.lincheck.verifier.linearizability.LinearizabilityVerifier;
import org.junit.Test;

@StressCTest(threads = 12)
public class LockFreeSetImplLincheck {

private LockFreeSetImpl<Integer> lockFreeSet = new LockFreeSetImpl<>();

@Operation
public boolean add(@Param(name = "x") Integer x) {
return lockFreeSet.add(x);
}

@Operation
public boolean remove(@Param(name = "x") Integer x) {
return lockFreeSet.remove(x);
}

@Operation
public boolean contains(@Param(name = "x") Integer x) {
return lockFreeSet.contains(x);
}

@Operation
public boolean isEmpty(@Param(name = "x") Integer x) {
return lockFreeSet.contains(x);
}

@Test
public void runTest() {
LinChecker.check(LockFreeSetImpl.class, new StressOptions()
.iterations(20000)
.threads(12)
.actorsPerThread(100)
.invocationsPerIteration(200)
.verifier(LinearizabilityVerifier.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package ru.cscenter.dxahtepb.lockfreeset;

import org.junit.Test;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class LockFreeSetImplTest {

@Test
public void testEmptyByDefault() {
LockFreeSetImpl<Integer> set = new LockFreeSetImpl<>();
assertTrue(set.isEmpty());
}

@Test
public void testBasicOperations() {
LockFreeSetImpl<Integer> set = new LockFreeSetImpl<>();

assertFalse(set.contains(1));

assertTrue(set.add(1));
assertTrue(set.add(2));
assertTrue(set.add(4));
assertTrue(set.add(3));
assertFalse(set.add(1));
assertFalse(set.add(3));

assertTrue(set.contains(1));
assertTrue(set.contains(2));
assertTrue(set.contains(3));
assertTrue(set.contains(4));

assertTrue(set.remove(1));
assertFalse(set.contains(1));

assertFalse(set.remove(20));

assertFalse(set.isEmpty());

assertTrue(set.remove(2));
assertTrue(set.remove(3));
assertTrue(set.remove(4));
assertTrue(set.isEmpty());
}
}