SpinLockThreadMutex

This commit is contained in:
Andrew Golovashevich 2025-09-11 19:59:56 +03:00
parent e37e39a496
commit f8cb01799d
8 changed files with 210 additions and 6 deletions

View File

@ -13,7 +13,7 @@ buildscript {
} }
plugins { plugins {
kotlin("multiplatform") version "2.2.20-Beta1" kotlin("multiplatform") version "2.2.20"
`maven-publish` `maven-publish`
} }
@ -63,6 +63,13 @@ kotlin {
} }
val commonTest by getting val commonTest by getting
this@kotlin.jvm().compilations.getByName("main").defaultSourceSet {
dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-annotations-jvm:${this@kotlin.coreLibrariesVersion}")
}
}
val notJvmMain by creating { dependsOn(commonMain) } val notJvmMain by creating { dependsOn(commonMain) }
val notJvmTest by creating { dependsOn(commonTest) } val notJvmTest by creating { dependsOn(commonTest) }

View File

@ -2,9 +2,9 @@ package ru.landgrafhomyak.multitasking_0
import kotlin.RuntimeException import kotlin.RuntimeException
public class WrongCallerThreadException : RuntimeException { public expect class WrongCallerThreadException : RuntimeException {
public constructor() : super() public constructor()
public constructor(message: String?) : super(message) public constructor(message: String?)
public constructor(message: String?, cause: Throwable?) : super(message, cause) public constructor(message: String?, cause: Throwable?)
public constructor(cause: Throwable?) : super(cause) public constructor(cause: Throwable?)
} }

View File

@ -0,0 +1,158 @@
package ru.landgrafhomyak.multitasking_0.threads.sync;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.jetbrains.annotations.NotNull;
import kotlin.annotations.jvm.KotlinActual;
@KotlinActual
public class SpinLockThreadMutex implements ThreadMutex {
private static final long CLOSED_USAGES_COUNT = -0x8000_0000_0000_0000L;
private final AtomicLong _usagesCount;
private final AtomicReference<Thread> _currentOwner;
@KotlinActual
public SpinLockThreadMutex() {
this._usagesCount = new AtomicLong();
this._currentOwner = new AtomicReference<>(null);
}
@KotlinActual
@Override
public void lock() {
if (this._usagesCount.getAndIncrement() < 0) {
this._usagesCount.getAndDecrement();
throw new IllegalStateException("Mutex was destroyed");
}
final Thread thisOwner = Thread.currentThread();
while (!this._currentOwner.compareAndSet(null, thisOwner))
Thread.onSpinWait();
}
@KotlinActual
@Override
public void unlock() {
if (!this._currentOwner.compareAndSet(Thread.currentThread(), null)) {
if (this._usagesCount.get() < 0)
throw new IllegalStateException("Mutex was destroyed");
throw new WrongThreadException("Thread doesn't hold this mutex");
}
this._usagesCount.getAndDecrement();
}
@KotlinActual
@Override
public void destroy() {
final long cachedUsagesCount = this._usagesCount.compareAndExchange(0, CLOSED_USAGES_COUNT);
if (cachedUsagesCount < 0)
throw new IllegalStateException("Mutex was destroyed");
if (cachedUsagesCount > 0)
throw new IllegalStateException("There are thread owning or waiting on this mutex");
}
@KotlinActual
@Override
@NotNull
public ThreadCondition newAssociatedCondition() {
return this.new ConditionImpl();
}
private class ConditionImpl implements ThreadCondition {
private static class QueueNode {
public QueueNode next;
public final AtomicBoolean isWaiting;
public QueueNode() {
this.next = null;
this.isWaiting = new AtomicBoolean(true);
}
}
private final AtomicReference<QueueNode> _next;
private final AtomicLong _usagesCount;
public ConditionImpl() {
if (SpinLockThreadMutex.this._usagesCount.getAndIncrement() < 0) {
SpinLockThreadMutex.this._usagesCount.getAndDecrement();
throw new IllegalStateException("Mutex was destroyed");
}
this._next = new AtomicReference<>(null);
this._usagesCount = new AtomicLong(0);
}
private void _throwDestroyed() {
if (SpinLockThreadMutex.this._usagesCount.get() < 0)
throw new IllegalStateException("Condition and associated mutex were destroyed");
throw new IllegalStateException("Condition was destroyed");
}
@Override
public void await() {
if (this._usagesCount.getAndIncrement() < 0) {
SpinLockThreadMutex.this._usagesCount.getAndDecrement();
this._throwDestroyed();
}
final Thread currentThread = Thread.currentThread();
if (SpinLockThreadMutex.this._currentOwner.get() != currentThread)
throw new WrongThreadException("Thread doesn't hold this mutex");
final QueueNode node = new QueueNode();
do {
node.next = this._next.get();
} while (!this._next.compareAndSet(node.next, node));
SpinLockThreadMutex.this._currentOwner.set(null);
while (node.isWaiting.get())
Thread.onSpinWait();
while (!SpinLockThreadMutex.this._currentOwner.compareAndSet(null, currentThread))
Thread.onSpinWait();
this._usagesCount.getAndDecrement();
}
private boolean _wakeOne() {
while (true) {
final QueueNode node = this._next.get();
if (node == null) return false;
if (!this._next.compareAndSet(node, node.next))
continue;
node.isWaiting.set(false);
return true;
}
}
@Override
public void wakeOne() {
if (this._usagesCount.get() < 0)
this._throwDestroyed();
this._wakeOne();
}
@SuppressWarnings("StatementWithEmptyBody")
@Override
public void wakeAll() {
if (this._usagesCount.get() < 0)
this._throwDestroyed();
while (this._wakeOne()) {
}
}
@Override
public void destroy() {
final long cachedUsagesCount = this._usagesCount.compareAndExchange(0, SpinLockThreadMutex.CLOSED_USAGES_COUNT);
if (cachedUsagesCount < 0)
throw new IllegalStateException("Condition already was destroyed");
if (cachedUsagesCount > 0)
throw new IllegalStateException("There are thread waiting on this condition");
}
}
}

View File

@ -0,0 +1,3 @@
package ru.landgrafhomyak.multitasking_0
public actual typealias WrongCallerThreadException = java.lang.WrongThreadException

View File

@ -0,0 +1,10 @@
package ru.landgrafhomyak.multitasking_0.threads.sync
public expect class SpinLockThreadMutex : ThreadMutex {
public constructor()
override fun lock()
override fun unlock()
override fun newAssociatedCondition(): ThreadCondition
override fun destroy()
}

View File

@ -0,0 +1,8 @@
package ru.landgrafhomyak.multitasking_0.threads.sync
public interface ThreadCondition {
public fun await()
public fun wakeOne()
public fun wakeAll()
public fun destroy()
}

View File

@ -0,0 +1,8 @@
package ru.landgrafhomyak.multitasking_0.threads.sync
public interface ThreadMutex {
public fun lock()
public fun unlock()
public fun newAssociatedCondition(): ThreadCondition
public fun destroy()
}

View File

@ -0,0 +1,10 @@
package ru.landgrafhomyak.multitasking_0
import kotlin.RuntimeException
public actual class WrongCallerThreadException : RuntimeException {
public actual constructor() : super()
public actual constructor(message: String?) : super(message)
public actual constructor(message: String?, cause: Throwable?) : super(message, cause)
public actual constructor(cause: Throwable?) : super(cause)
}