add tests for smartrwlocker

This commit is contained in:
InsanusMokrassar 2023-08-12 22:37:35 +06:00
parent ce7d4fe9a2
commit 6ce1eb3f2d
8 changed files with 72 additions and 7 deletions

View File

@ -12,9 +12,9 @@ import kotlin.contracts.contract
* * [lockWrite] will lock [writeMutex] and then await while all [readSemaphore] will be freed * * [lockWrite] will lock [writeMutex] and then await while all [readSemaphore] will be freed
* * [unlockWrite] will just unlock [writeMutex] * * [unlockWrite] will just unlock [writeMutex]
*/ */
class SmartRWLocker(private val readPermits: Int = Int.MAX_VALUE) { class SmartRWLocker(private val readPermits: Int = Int.MAX_VALUE, writeIsLocked: Boolean = false) {
private val _readSemaphore = SmartSemaphore.Mutable(permits = readPermits, acquiredPermits = 0) private val _readSemaphore = SmartSemaphore.Mutable(permits = readPermits, acquiredPermits = 0)
private val _writeMutex = SmartMutex.Mutable(locked = false) private val _writeMutex = SmartMutex.Mutable(locked = writeIsLocked)
val readSemaphore: SmartSemaphore.Immutable = _readSemaphore.immutable() val readSemaphore: SmartSemaphore.Immutable = _readSemaphore.immutable()
val writeMutex: SmartMutex.Immutable = _writeMutex.immutable() val writeMutex: SmartMutex.Immutable = _writeMutex.immutable()

View File

@ -80,9 +80,9 @@ sealed interface SmartSemaphore {
*/ */
suspend fun tryAcquire(permits: Int = 1): Boolean { suspend fun tryAcquire(permits: Int = 1): Boolean {
val checkedPermits = checkedPermits(permits) val checkedPermits = checkedPermits(permits)
return if (_permitsStateFlow.value >= checkedPermits) { return if (_permitsStateFlow.value < checkedPermits) {
internalChangesMutex.withLock { internalChangesMutex.withLock {
if (_permitsStateFlow.value >= checkedPermits) { if (_permitsStateFlow.value < checkedPermits) {
_permitsStateFlow.value -= checkedPermits _permitsStateFlow.value -= checkedPermits
true true
} else { } else {
@ -100,10 +100,10 @@ sealed interface SmartSemaphore {
*/ */
suspend fun release(permits: Int = 1): Boolean { suspend fun release(permits: Int = 1): Boolean {
val checkedPermits = checkedPermits(permits) val checkedPermits = checkedPermits(permits)
return if (this.permits - _permitsStateFlow.value > checkedPermits) { return if (_permitsStateFlow.value < this.permits) {
internalChangesMutex.withLock { internalChangesMutex.withLock {
if (this.permits - _permitsStateFlow.value > checkedPermits) { if (_permitsStateFlow.value < this.permits) {
_permitsStateFlow.value += checkedPermits _permitsStateFlow.value = minOf(_permitsStateFlow.value + checkedPermits, this.permits)
true true
} else { } else {
false false

View File

@ -0,0 +1,60 @@
import dev.inmo.micro_utils.coroutines.SmartRWLocker
import dev.inmo.micro_utils.coroutines.withReadAcquire
import dev.inmo.micro_utils.coroutines.withWriteLock
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.delay
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class SmartRWLockerTests {
@Test
fun compositeTest() {
val locker = SmartRWLocker()
val readAndWriteWorkers = 10
runTest {
var started = 0
var done = 0
val doneMutex = Mutex()
val readWorkers = (0 until readAndWriteWorkers).map {
launch(start = CoroutineStart.LAZY) {
locker.withReadAcquire {
doneMutex.withLock {
started++
}
delay(100L)
doneMutex.withLock {
done++
}
}
}
}
var doneWrites = 0
val writeWorkers = (0 until readAndWriteWorkers).map {
launch(start = CoroutineStart.LAZY) {
locker.withWriteLock {
assertTrue(done == readAndWriteWorkers || started == 0)
delay(10L)
doneWrites++
}
}
}
readWorkers.forEach { it.start() }
writeWorkers.forEach { it.start() }
readWorkers.joinAll()
writeWorkers.joinAll()
assertEquals(expected = readAndWriteWorkers, actual = done)
assertEquals(expected = readAndWriteWorkers, actual = doneWrites)
}
}
}

View File

@ -18,6 +18,7 @@ kotlin {
dependencies { dependencies {
implementation kotlin('test-common') implementation kotlin('test-common')
implementation kotlin('test-annotations-common') implementation kotlin('test-annotations-common')
implementation libs.kt.coroutines.test
} }
} }
} }

View File

@ -22,6 +22,7 @@ kotlin {
dependencies { dependencies {
implementation kotlin('test-common') implementation kotlin('test-common')
implementation kotlin('test-annotations-common') implementation kotlin('test-annotations-common')
implementation libs.kt.coroutines.test
} }
} }

View File

@ -28,6 +28,7 @@ kotlin {
dependencies { dependencies {
implementation kotlin('test-common') implementation kotlin('test-common')
implementation kotlin('test-annotations-common') implementation kotlin('test-annotations-common')
implementation libs.kt.coroutines.test
} }
} }

View File

@ -32,6 +32,7 @@ kotlin {
dependencies { dependencies {
implementation kotlin('test-common') implementation kotlin('test-common')
implementation kotlin('test-annotations-common') implementation kotlin('test-annotations-common')
implementation libs.kt.coroutines.test
} }
} }
jvmTest { jvmTest {

View File

@ -31,6 +31,7 @@ kotlin {
dependencies { dependencies {
implementation kotlin('test-common') implementation kotlin('test-common')
implementation kotlin('test-annotations-common') implementation kotlin('test-annotations-common')
implementation libs.kt.coroutines.test
} }
} }
jvmMain { jvmMain {