From e1794163eb19ee0c17e3d3874dc67f7d35ded13a Mon Sep 17 00:00:00 2001 From: Artem Demchenko Date: Mon, 6 May 2024 00:11:11 +0300 Subject: [PATCH] Implement stack with elimination --- .../ConcurrentStackWithElimination.kt | 42 ++++++++++++++++ .../stack/elimination/EliminationArray.kt | 17 +++++++ .../stack/elimination/LockFreeExchanger.kt | 48 +++++++++++++++++++ .../test/kotlin/stack/ConcurrentStackTests.kt | 5 +- 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 work1/src/main/kotlin/stack/elimination/ConcurrentStackWithElimination.kt create mode 100644 work1/src/main/kotlin/stack/elimination/EliminationArray.kt create mode 100644 work1/src/main/kotlin/stack/elimination/LockFreeExchanger.kt diff --git a/work1/src/main/kotlin/stack/elimination/ConcurrentStackWithElimination.kt b/work1/src/main/kotlin/stack/elimination/ConcurrentStackWithElimination.kt new file mode 100644 index 0000000..6b5c8f8 --- /dev/null +++ b/work1/src/main/kotlin/stack/elimination/ConcurrentStackWithElimination.kt @@ -0,0 +1,42 @@ +package stack.elimination + +import stack.common.ConcurrentStack +import stack.common.Node +import java.util.concurrent.atomic.AtomicReference + +class ConcurrentStackWithElimination : ConcurrentStack { + private val eliminationArray = EliminationArray() + private val head = AtomicReference?>() + + override fun push(x: T) { + while (true) { + val oldHead = head.get() + val newHead = Node(x, oldHead) + if (head.compareAndSet(oldHead, newHead)) { + return + } + val visitResult = eliminationArray.visit(x) + if (visitResult.isSuccess && visitResult.getOrNull() == null) { + return + } + } + } + + override fun pop(): T? { + while (true) { + val oldHead = head.get() ?: return null + val newHead = oldHead.next + if (head.compareAndSet(oldHead, newHead)) { + return oldHead.value + } + val visitResult = eliminationArray.visit(null) + if (visitResult.isSuccess && visitResult.getOrNull() != null) { + return visitResult.getOrNull() + } + } + } + + override fun top(): T? = head.get()?.value +} + + diff --git a/work1/src/main/kotlin/stack/elimination/EliminationArray.kt b/work1/src/main/kotlin/stack/elimination/EliminationArray.kt new file mode 100644 index 0000000..ba3e380 --- /dev/null +++ b/work1/src/main/kotlin/stack/elimination/EliminationArray.kt @@ -0,0 +1,17 @@ +package stack.elimination + +import kotlin.random.Random + +class EliminationArray( + private val capacity: Int = 10, + private val maxAttemptsCount: Long = 50, + randomSeed: Long = System.currentTimeMillis() +) { + private val exchanger = Array>(capacity) { LockFreeExchanger() } + private val random = Random(randomSeed) + + fun visit(value: T?): Result { + val slot = random.nextInt(capacity) + return exchanger[slot].exchange(value, maxAttemptsCount) + } +} \ No newline at end of file diff --git a/work1/src/main/kotlin/stack/elimination/LockFreeExchanger.kt b/work1/src/main/kotlin/stack/elimination/LockFreeExchanger.kt new file mode 100644 index 0000000..3bf92e5 --- /dev/null +++ b/work1/src/main/kotlin/stack/elimination/LockFreeExchanger.kt @@ -0,0 +1,48 @@ +package stack.elimination + +import java.util.concurrent.TimeoutException +import java.util.concurrent.atomic.AtomicStampedReference + +class LockFreeExchanger { + private enum class State { + EMPTY, WAITING, BUSY + } + + private val slot = AtomicStampedReference(null, State.EMPTY.ordinal) + fun exchange(myItem: T?, maxAttemptsCount: Long): Result { + val stampHolder = IntArray(1) { State.EMPTY.ordinal } + var i = 0 + while (i < maxAttemptsCount) { + var yrItem = slot.get(stampHolder) + when (stampHolder[0]) { + State.EMPTY.ordinal -> { + if (slot.compareAndSet(yrItem, myItem, State.EMPTY.ordinal, State.WAITING.ordinal)) { + while (i < maxAttemptsCount) { + yrItem = slot.get(stampHolder) + if (stampHolder[0] == State.BUSY.ordinal) { + slot.set(null, State.EMPTY.ordinal) + return Result.success(yrItem) + } + i++ + } + if (slot.compareAndSet(myItem, null, State.WAITING.ordinal, State.EMPTY.ordinal)) { + return Result.failure(TimeoutException()) + } else { + yrItem = slot.get(stampHolder) + slot.set(null, State.EMPTY.ordinal) + return Result.success(yrItem) + } + } + } + + State.WAITING.ordinal -> { + if (slot.compareAndSet(yrItem, myItem, State.WAITING.ordinal, State.BUSY.ordinal)) { + return Result.success(yrItem) + } + } + } + i++ + } + return Result.failure(TimeoutException()) + } +} \ No newline at end of file diff --git a/work1/src/test/kotlin/stack/ConcurrentStackTests.kt b/work1/src/test/kotlin/stack/ConcurrentStackTests.kt index 3287d9f..c1b6c64 100644 --- a/work1/src/test/kotlin/stack/ConcurrentStackTests.kt +++ b/work1/src/test/kotlin/stack/ConcurrentStackTests.kt @@ -5,6 +5,7 @@ import org.jetbrains.kotlinx.lincheck.check import org.jetbrains.kotlinx.lincheck.strategy.managed.modelchecking.ModelCheckingOptions import org.jetbrains.kotlinx.lincheck.strategy.stress.StressOptions import stack.common.ConcurrentStack +import stack.elimination.ConcurrentStackWithElimination import stack.simple.ConcurrentTreiberStack import kotlin.test.Test @@ -47,4 +48,6 @@ abstract class ConcurrentStackTests(private val stack: ConcurrentStack) { .check(this::class) } -class ConcurrentTreiberStackTests : ConcurrentStackTests(ConcurrentTreiberStack()) \ No newline at end of file +class ConcurrentTreiberStackTests : ConcurrentStackTests(ConcurrentTreiberStack()) + +class ConcurrentStackWithEliminationTests : ConcurrentStackTests(ConcurrentStackWithElimination()) \ No newline at end of file