mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2026-10-16 00:50:34 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 721056d550 | |||
| 52a801096c | |||
| 08400381a1 | |||
| 4ec19d0da3 | |||
| 1f52bc7c99 | |||
| ec2568b988 | |||
| d082e8bdba | |||
| 71e0bf214e | |||
| 761b14dc65 | |||
| 63d66228ab | |||
| c4f15c32ea | |||
| a070f7d2f3 | |||
| 74501079c3 | |||
| 761cfffae1 | |||
| f26fb352e5 | |||
| b71781727a | |||
| 6c843c4dd4 | |||
| 9381e7d057 | |||
| 9ffc171c04 | |||
| 6104d4917c | |||
| faa3ac728f |
52
CHANGELOG.md
52
CHANGELOG.md
@@ -1,6 +1,45 @@
|
||||
# Changelog
|
||||
|
||||
## 0.30.2
|
||||
## 0.32.1
|
||||
|
||||
* `Coroutines`:
|
||||
* `SmartSemaphore`:
|
||||
* Optimize permit checks, acquisition, uncontended releases and already satisfied waits to reduce allocations while preserving cancellation handling.
|
||||
* `SmartMutex`:
|
||||
* Optimize waits for unlocked mutexes and uncontended unlocks to reduce allocations while preserving cancellation handling.
|
||||
* `SmartRWLocker`:
|
||||
* Remove redundant coroutine contexts during read releases, write unlocks and cancellation cleanup.
|
||||
* `SmartKeyRWLocker`:
|
||||
* Reduce allocations during keyed operations by inlining the locker allocation helper and using a hash map without insertion order tracking.
|
||||
|
||||
## 0.32.0
|
||||
|
||||
* `Versions`:
|
||||
* `crypto-js`: `4.1.1` -> `4.2.0`
|
||||
* `AndroidX Fragment`: `1.9.0` -> `1.9.1`
|
||||
* `AndroidX Core KTX`: `1.19.0` -> `1.19.1`
|
||||
* `NMCP`: `1.6.1` -> `1.6.2`
|
||||
* `Gradle Versions`: `0.61.0` -> `0.65.0`
|
||||
* `KotlinPoet`: `2.3.0` -> `2.4.0`
|
||||
* `Okio`: `3.18.1` -> `3.18.2`
|
||||
* `Ktor`: `3.5.2` -> `3.6.0`
|
||||
* `Exposed`: `1.5.0` -> `1.5.1`
|
||||
* `Compose Material3`: `1.11.0-alpha07` -> `1.9.0`
|
||||
* `KSLog`: `1.7.0` -> `2.1.0`
|
||||
* `Kotlin`: `2.4.10` -> `2.4.21`
|
||||
* `Compose`: `1.12.0` -> `1.12.1`
|
||||
* `Gradle wrapper`: `9.3.1` -> `9.7.0`
|
||||
* `Coroutines`:
|
||||
* `SmartSemaphore`:
|
||||
* Rewrite docs for `freePermits` property.
|
||||
* Add constructor arguments requirements to fail on invalid `permits` and `acquiredPermits` values.
|
||||
* `SmartRWLocker`:
|
||||
* Add constructor arguments requirements to fail on invalid `readPermits` values.
|
||||
* `SmartKeyRWLocker`:
|
||||
* Add constructor arguments requirements to fail on invalid `globalLockerReadPermits` and `perKeyReadPermits` values.
|
||||
|
||||
|
||||
## 0.31.1
|
||||
|
||||
* `Coroutines`:
|
||||
* `SmartRWLocker`:
|
||||
@@ -10,6 +49,17 @@
|
||||
* `SmartSemaphore`:
|
||||
* Fix same issues to avoid cancellation exceptions handling errors and several other problems
|
||||
|
||||
## 0.31.0
|
||||
|
||||
* `Versions`:
|
||||
* `Kotlin`: `2.3.21` -> `2.4.10`
|
||||
* `SQLite`: `3.53.2.1` -> `3.53.4.0`
|
||||
* `KSP`: `2.3.9` -> `2.3.11`
|
||||
* `Exposed`: `1.3.0` -> `1.5.0`
|
||||
* `NMCP`: `1.5.0` -> `1.6.1`
|
||||
* `AndroidX AppCompat`: `1.7.1` -> `1.8.0`
|
||||
* `AndroidX Fragment`: `1.8.9` -> `1.9.0`
|
||||
|
||||
## 0.30.1
|
||||
|
||||
* `Versions`:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppAndroidProject"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppAndroidProject"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
alias(libs.plugins.jb.compose)
|
||||
alias(libs.plugins.kt.jb.compose)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppAndroidProject"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
alias(libs.plugins.jb.compose)
|
||||
alias(libs.plugins.kt.jb.compose)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
@@ -13,7 +13,7 @@ kotlin {
|
||||
api libs.klock
|
||||
}
|
||||
}
|
||||
jvmMain {
|
||||
jvmAndAndroidMain {
|
||||
dependencies {
|
||||
api project(":micro_utils.coroutines")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
alias(libs.plugins.jb.compose)
|
||||
alias(libs.plugins.kt.jb.compose)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
alias(libs.plugins.jb.compose)
|
||||
alias(libs.plugins.kt.jb.compose)
|
||||
}
|
||||
|
||||
@@ -28,11 +28,16 @@ class SmartKeyRWLocker<T>(
|
||||
globalLockerWriteIsLocked: Boolean = false,
|
||||
private val perKeyReadPermits: Int = Int.MAX_VALUE
|
||||
) {
|
||||
init {
|
||||
require(globalLockerReadPermits > 0) { "The maximal number 'globalLockerReadPermits' of global permits on reading ($globalLockerReadPermits) must be positive." }
|
||||
require(perKeyReadPermits > 0) { "The maximal number 'perKeyReadPermits' of permits on reading per key ($perKeyReadPermits) must be positive." }
|
||||
}
|
||||
|
||||
private val globalRWLocker: SmartRWLocker = SmartRWLocker(
|
||||
readPermits = globalLockerReadPermits,
|
||||
writeIsLocked = globalLockerWriteIsLocked
|
||||
)
|
||||
private val lockers = mutableMapOf<T, SmartRWLocker>()
|
||||
private val lockers = hashMapOf<T, SmartRWLocker>()
|
||||
private val lockersMutex = Mutex()
|
||||
private val lockersWritingLocker = SmartSemaphore.Mutable(Int.MAX_VALUE)
|
||||
private val globalWritingLocker = SmartSemaphore.Mutable(Int.MAX_VALUE)
|
||||
@@ -40,7 +45,7 @@ class SmartKeyRWLocker<T>(
|
||||
private fun allocateLockerWithoutLock(key: T) = lockers.getOrPut(key) {
|
||||
SmartRWLocker(perKeyReadPermits)
|
||||
}
|
||||
private suspend fun allocateLocker(key: T) = lockersMutex.withLock {
|
||||
private suspend inline fun allocateLocker(key: T) = lockersMutex.withLock {
|
||||
lockers.getOrPut(key) {
|
||||
SmartRWLocker(perKeyReadPermits)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package dev.inmo.micro_utils.coroutines
|
||||
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
@@ -94,19 +95,32 @@ sealed interface SmartMutex {
|
||||
* If [isLocked] == true - will change it to false and return true. If current call will not unlock this
|
||||
* [SmartMutex] - false
|
||||
*/
|
||||
suspend fun unlock(): Boolean = withContext(NonCancellable) {
|
||||
if (_lockStateFlow.value) {
|
||||
internalChangesMutex.withLock {
|
||||
if (_lockStateFlow.value) {
|
||||
_lockStateFlow.value = false
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
suspend fun unlock(): Boolean {
|
||||
if (!_lockStateFlow.value) {
|
||||
return false
|
||||
}
|
||||
// The uncontended path cannot suspend, including during cancelled cleanup.
|
||||
if (internalChangesMutex.tryLock()) {
|
||||
try {
|
||||
return unlockState()
|
||||
} finally {
|
||||
internalChangesMutex.unlock()
|
||||
}
|
||||
}
|
||||
return withContext(NonCancellable) {
|
||||
internalChangesMutex.withLock {
|
||||
unlockState()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The caller must hold internalChangesMutex while updating the state.
|
||||
private fun unlockState(): Boolean {
|
||||
if (!_lockStateFlow.value) {
|
||||
return false
|
||||
}
|
||||
_lockStateFlow.value = false
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,4 +148,11 @@ suspend inline fun <T> SmartMutex.Mutable.withLock(action: () -> T): T {
|
||||
* Anyway, after the end of this block there are no any guaranties that [SmartMutex.isLocked] == false due to the fact
|
||||
* that some other parties may lock it again
|
||||
*/
|
||||
suspend fun SmartMutex.waitUnlock() = lockStateFlow.first { !it }
|
||||
suspend fun SmartMutex.waitUnlock(): Boolean {
|
||||
currentCoroutineContext().ensureActive()
|
||||
return if (!lockStateFlow.value) {
|
||||
false
|
||||
} else {
|
||||
lockStateFlow.first { !it }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package dev.inmo.micro_utils.coroutines
|
||||
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
@@ -17,6 +15,10 @@ import kotlin.contracts.contract
|
||||
* * [unlockWrite] will just unlock [writeMutex]
|
||||
*/
|
||||
class SmartRWLocker(private val readPermits: Int = Int.MAX_VALUE, writeIsLocked: Boolean = false) {
|
||||
init {
|
||||
require(readPermits > 0) { "The maximal number 'readPermits' of permits on reading ($readPermits) must be positive." }
|
||||
}
|
||||
|
||||
private val _readSemaphore = SmartSemaphore.Mutable(permits = readPermits, acquiredPermits = if (writeIsLocked) readPermits else 0)
|
||||
private val _writeMutex = SmartMutex.Mutable(locked = writeIsLocked)
|
||||
|
||||
@@ -35,9 +37,7 @@ class SmartRWLocker(private val readPermits: Int = Int.MAX_VALUE, writeIsLocked:
|
||||
/**
|
||||
* Release one read permit in [readSemaphore]
|
||||
*/
|
||||
suspend fun releaseRead(): Boolean = withContext(NonCancellable) {
|
||||
_readSemaphore.release()
|
||||
}
|
||||
suspend fun releaseRead(): Boolean = _readSemaphore.release()
|
||||
|
||||
/**
|
||||
* Locking [writeMutex] and wait while all [readSemaphore] permits will be freed
|
||||
@@ -47,9 +47,7 @@ class SmartRWLocker(private val readPermits: Int = Int.MAX_VALUE, writeIsLocked:
|
||||
try {
|
||||
_readSemaphore.acquire(readPermits)
|
||||
} catch (e: CancellationException) {
|
||||
withContext(NonCancellable) {
|
||||
_writeMutex.unlock()
|
||||
}
|
||||
_writeMutex.unlock()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
@@ -57,13 +55,12 @@ class SmartRWLocker(private val readPermits: Int = Int.MAX_VALUE, writeIsLocked:
|
||||
/**
|
||||
* Unlock [writeMutex]
|
||||
*/
|
||||
suspend fun unlockWrite(): Boolean = withContext(NonCancellable) {
|
||||
suspend fun unlockWrite(): Boolean =
|
||||
_writeMutex.unlock().also { unlocked ->
|
||||
if (unlocked) {
|
||||
_readSemaphore.release(readPermits)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@ package dev.inmo.micro_utils.coroutines
|
||||
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
@@ -29,8 +30,10 @@ sealed interface SmartSemaphore {
|
||||
val permitsStateFlow: StateFlow<Int>
|
||||
|
||||
/**
|
||||
* * True - locked
|
||||
* * False - unlocked
|
||||
* The number of the available permits.
|
||||
*
|
||||
* If returns `0`, it is fully acquired (locked).
|
||||
* If it returns the same value as `maxPermits`, it is fully released (unlocked).
|
||||
*/
|
||||
val freePermits: Int
|
||||
get() = permitsStateFlow.value
|
||||
@@ -47,6 +50,12 @@ sealed interface SmartSemaphore {
|
||||
* @param locked Preset state of [freePermits] and its internal [_freePermitsStateFlow]
|
||||
*/
|
||||
class Mutable(permits: Int, acquiredPermits: Int = 0) : SmartSemaphore {
|
||||
init {
|
||||
require(permits > 0) { "The maximal number 'permits' of permits ($permits) must be positive." }
|
||||
require(acquiredPermits >= 0) { "The number 'acquiredPermits' of initially acquired permits ($acquiredPermits) must be non-negative." }
|
||||
require(permits >= acquiredPermits) { "The number 'acquiredPermits' of acquired initially permits ($acquiredPermits) must not exceed the maximal number 'permits' of permits ($permits)." }
|
||||
}
|
||||
|
||||
override val maxPermits: Int = permits
|
||||
private val _freePermitsStateFlow = MutableRedeliverStateFlow<Int>(permits - acquiredPermits)
|
||||
override val permitsStateFlow: StateFlow<Int> = _freePermitsStateFlow.asStateFlow()
|
||||
@@ -55,7 +64,7 @@ sealed interface SmartSemaphore {
|
||||
|
||||
fun immutable() = Immutable(permitsStateFlow, maxPermits)
|
||||
|
||||
private fun checkedPermits(permits: Int) = permits.coerceIn(1 .. this.maxPermits)
|
||||
private fun checkedPermits(permits: Int) = permits.coerceIn(1, maxPermits)
|
||||
|
||||
/**
|
||||
* Holds call until this [SmartSemaphore] will be re-locked. That means that current method will
|
||||
@@ -67,7 +76,8 @@ sealed interface SmartSemaphore {
|
||||
do {
|
||||
val shouldContinue = internalChangesMutex.withLock {
|
||||
val requiredPermits = checkedPermits - acquiredPermits
|
||||
val acquiring = minOf(freePermits, requiredPermits).takeIf { it > 0 } ?: return@withLock true
|
||||
val acquiring = minOf(freePermits, requiredPermits)
|
||||
if (acquiring <= 0) return@withLock true
|
||||
acquiredPermits += acquiring
|
||||
_freePermitsStateFlow.value -= acquiring
|
||||
|
||||
@@ -129,20 +139,31 @@ sealed interface SmartSemaphore {
|
||||
* If [freePermits] == true - will change it to false and return true. If current call will not unlock this
|
||||
* [SmartSemaphore] - false
|
||||
*/
|
||||
suspend fun release(permits: Int = 1): Boolean = withContext(NonCancellable) {
|
||||
suspend fun release(permits: Int = 1): Boolean {
|
||||
val checkedPermits = checkedPermits(permits)
|
||||
if (_freePermitsStateFlow.value < maxPermits) {
|
||||
internalChangesMutex.withLock {
|
||||
if (_freePermitsStateFlow.value < maxPermits) {
|
||||
_freePermitsStateFlow.value = minOf(_freePermitsStateFlow.value + checkedPermits, maxPermits)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
if (_freePermitsStateFlow.value >= maxPermits) return false
|
||||
|
||||
// Updating permits without suspension remains safe in a cancelled coroutine.
|
||||
if (internalChangesMutex.tryLock()) {
|
||||
try {
|
||||
return releaseLocked(checkedPermits)
|
||||
} finally {
|
||||
internalChangesMutex.unlock()
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
return withContext(NonCancellable) {
|
||||
internalChangesMutex.withLock {
|
||||
releaseLocked(checkedPermits)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun releaseLocked(permits: Int): Boolean {
|
||||
val freePermits = _freePermitsStateFlow.value
|
||||
if (freePermits >= maxPermits) return false
|
||||
_freePermitsStateFlow.value = minOf(freePermits + permits, maxPermits)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,5 +191,14 @@ suspend inline fun <T> SmartSemaphore.Mutable.withAcquire(permits: Int = 1, acti
|
||||
* Anyway, after the end of this block there are no any guaranties that [SmartSemaphore.freePermits] >= [permits] due to
|
||||
* the fact that some other parties may lock it again
|
||||
*/
|
||||
suspend fun SmartSemaphore.waitRelease(permits: Int = 1) = permitsStateFlow.first { it >= permits }
|
||||
suspend fun SmartSemaphore.waitReleaseAll() = permitsStateFlow.first { it == maxPermits }
|
||||
suspend fun SmartSemaphore.waitRelease(permits: Int = 1): Int {
|
||||
currentCoroutineContext().ensureActive()
|
||||
val availablePermits = freePermits
|
||||
return if (availablePermits >= permits) availablePermits else permitsStateFlow.first { it >= permits }
|
||||
}
|
||||
|
||||
suspend fun SmartSemaphore.waitReleaseAll(): Int {
|
||||
currentCoroutineContext().ensureActive()
|
||||
val availablePermits = freePermits
|
||||
return if (availablePermits == maxPermits) availablePermits else permitsStateFlow.first { it == maxPermits }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import dev.inmo.micro_utils.coroutines.SmartSemaphore
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class SmartSemaphoreConcurrentReleaseTests {
|
||||
@Test
|
||||
fun queuedReleasesRestoreEveryAcquiredPermit() = runTest(timeout = 5.seconds) {
|
||||
val semaphore = SmartSemaphore.Mutable(permits = 4, acquiredPermits = 4)
|
||||
|
||||
val results = releaseWithQueuedContenders(semaphore, contenderCount = 3)
|
||||
|
||||
assertEquals(listOf(true, true, true), results)
|
||||
assertEquals(4, semaphore.freePermits)
|
||||
assertTrue(semaphore.tryAcquire(4))
|
||||
assertFalse(semaphore.tryAcquire())
|
||||
assertEquals(0, semaphore.freePermits)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun queuedReleasesRecheckCapacityAfterAcquiringTheMutex() = runTest(timeout = 5.seconds) {
|
||||
val semaphore = SmartSemaphore.Mutable(permits = 2, acquiredPermits = 2)
|
||||
|
||||
val results = releaseWithQueuedContenders(semaphore, contenderCount = 3)
|
||||
|
||||
assertEquals(1, results.count { released -> released })
|
||||
assertEquals(2, results.count { released -> !released })
|
||||
assertEquals(2, semaphore.freePermits)
|
||||
assertFalse(semaphore.release())
|
||||
}
|
||||
|
||||
private suspend fun releaseWithQueuedContenders(
|
||||
semaphore: SmartSemaphore.Mutable,
|
||||
contenderCount: Int
|
||||
): List<Boolean> = coroutineScope {
|
||||
val contenders = mutableListOf<Deferred<Boolean>>()
|
||||
val observer = launch(Dispatchers.Unconfined) {
|
||||
semaphore.permitsStateFlow.first { freePermits ->
|
||||
if (freePermits == 1) {
|
||||
// The first release still holds the changes mutex while publishing.
|
||||
// Every contender observes spare capacity before waiting for the mutex.
|
||||
repeat(contenderCount) {
|
||||
val contender = async(Dispatchers.Unconfined, start = CoroutineStart.UNDISPATCHED) {
|
||||
semaphore.release()
|
||||
}
|
||||
contenders.add(contender)
|
||||
assertFalse(contender.isCompleted, "Competing releases must wait for the publishing release")
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(semaphore.release())
|
||||
observer.join()
|
||||
assertEquals(contenderCount, contenders.size)
|
||||
contenders.awaitAll()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package dev.inmo.micro_utils.coroutines
|
||||
|
||||
import java.util.concurrent.CyclicBarrier
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.joinAll
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class SmartSemaphoreParallelReleaseTests {
|
||||
@Test
|
||||
fun parallelSinglePermitReleasesRestoreEveryPermit() = runTest(timeout = 30.seconds) {
|
||||
assertParallelReleases(permits = 8, releasePermits = List(8) { 1 }, expectedSuccessfulReleases = 8)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parallelBulkReleasesRestoreEveryPermit() = runTest(timeout = 30.seconds) {
|
||||
val releasePermits = (1 .. 8).toList()
|
||||
assertParallelReleases(
|
||||
permits = releasePermits.sum(),
|
||||
releasePermits = releasePermits,
|
||||
expectedSuccessfulReleases = releasePermits.size
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parallelExcessReleasesStopAtCapacity() = runTest(timeout = 30.seconds) {
|
||||
assertParallelReleases(permits = 3, releasePermits = List(8) { 1 }, expectedSuccessfulReleases = 3)
|
||||
assertParallelReleases(permits = 5, releasePermits = List(8) { 3 }, expectedSuccessfulReleases = 2)
|
||||
}
|
||||
|
||||
private suspend fun assertParallelReleases(
|
||||
permits: Int,
|
||||
releasePermits: List<Int>,
|
||||
expectedSuccessfulReleases: Int
|
||||
) = coroutineScope {
|
||||
val semaphore = SmartSemaphore.Mutable(permits = permits, acquiredPermits = permits)
|
||||
val barrier = CyclicBarrier(releasePermits.size)
|
||||
val results = BooleanArray(releasePermits.size)
|
||||
val rounds = 100
|
||||
|
||||
Executors.newFixedThreadPool(releasePermits.size).asCoroutineDispatcher().use { dispatcher ->
|
||||
releasePermits.mapIndexed { worker, count ->
|
||||
launch(dispatcher) {
|
||||
repeat(rounds) { round ->
|
||||
// Blocking barriers on dedicated JVM threads produce actual parallel releases.
|
||||
barrier.await(5, TimeUnit.SECONDS)
|
||||
results[worker] = semaphore.release(count)
|
||||
barrier.await(5, TimeUnit.SECONDS)
|
||||
|
||||
if (worker == 0) {
|
||||
assertEquals(permits, semaphore.freePermits, "Round $round must preserve released permits")
|
||||
assertEquals(expectedSuccessfulReleases, results.count { released -> released })
|
||||
assertFalse(semaphore.release(), "A full semaphore must reject another release")
|
||||
assertEquals(permits, semaphore.freePermits)
|
||||
if (round < rounds - 1) {
|
||||
assertTrue(semaphore.tryAcquire(permits))
|
||||
assertEquals(0, semaphore.freePermits)
|
||||
}
|
||||
}
|
||||
|
||||
// Finish validation and reset before starting the next release wave.
|
||||
barrier.await(5, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
}.joinAll()
|
||||
}
|
||||
|
||||
assertEquals(permits, semaphore.freePermits)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
@@ -21,11 +21,3 @@ kotlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDirs += [ "src/jvmMain/kotlin" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
id "org.jetbrains.dokka"
|
||||
}
|
||||
|
||||
@@ -11,13 +11,17 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
apply from: "$defaultAndroidSettings"
|
||||
|
||||
kotlin {
|
||||
jvm()
|
||||
js(IR) {
|
||||
browser()
|
||||
nodejs()
|
||||
}
|
||||
androidTarget {}
|
||||
android {
|
||||
project.ext.configureAndroidTarget(delegate)
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
@@ -137,5 +141,3 @@ dokka {
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
apply from: "$defaultAndroidSettings"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
kotlin.code.style=official
|
||||
org.gradle.parallel=true
|
||||
kotlin.js.generate.externals=true
|
||||
kotlin.incremental=true
|
||||
kotlin.incremental.js=true
|
||||
#kotlin.experimental.tryK2=true
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=2g
|
||||
|
||||
## https://github.com/google/ksp/issues/2491
|
||||
@@ -13,10 +11,10 @@ org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=2g
|
||||
|
||||
# JS NPM
|
||||
|
||||
crypto_js_version=4.1.1
|
||||
crypto_js_version=4.2.0
|
||||
|
||||
# Project data
|
||||
|
||||
group=dev.inmo
|
||||
version=0.30.2
|
||||
android_code_version=316
|
||||
version=0.32.1
|
||||
android_code_version=320
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
[versions]
|
||||
|
||||
kt = "2.3.21"
|
||||
kt = "2.4.21"
|
||||
kt-serialization = "1.11.0"
|
||||
kt-coroutines = "1.11.0"
|
||||
|
||||
kotlinx-browser = "0.5.0"
|
||||
|
||||
kslog = "1.7.0"
|
||||
kslog = "2.1.0"
|
||||
|
||||
jb-compose = "1.11.1"
|
||||
jb-compose-material3 = "1.11.0-alpha07"
|
||||
jb-compose = "1.12.1"
|
||||
jb-compose-material3 = "1.9.0"
|
||||
jb-compose-icons = "1.7.8"
|
||||
jb-exposed = "1.3.0"
|
||||
jb-exposed = "1.5.1"
|
||||
jb-dokka = "2.2.0"
|
||||
|
||||
sqlite = "3.53.2.1"
|
||||
sqlite = "3.53.4.0"
|
||||
|
||||
korlibs = "5.4.0"
|
||||
uuid = "0.8.4"
|
||||
|
||||
ktor = "3.5.2"
|
||||
ktor = "3.6.0"
|
||||
|
||||
gh-release = "2.5.2"
|
||||
|
||||
koin = "4.2.2"
|
||||
|
||||
okio = "3.18.1"
|
||||
okio = "3.18.2"
|
||||
|
||||
ksp = "2.3.9"
|
||||
kotlin-poet = "2.3.0"
|
||||
ksp = "2.3.11"
|
||||
kotlin-poet = "2.4.0"
|
||||
|
||||
versions = "0.54.0"
|
||||
nmcp = "1.5.0"
|
||||
versions = "0.65.0"
|
||||
nmcp = "1.6.2"
|
||||
|
||||
android-gradle = "8.12.+"
|
||||
android-gradle = "9.1.1"
|
||||
dexcount = "4.0.0"
|
||||
|
||||
android-coreKtx = "1.19.0"
|
||||
android-coreKtx = "1.19.1"
|
||||
android-recyclerView = "1.4.0"
|
||||
android-appCompat = "1.7.1"
|
||||
android-fragment = "1.8.9"
|
||||
android-appCompat = "1.8.0"
|
||||
android-fragment = "1.9.1"
|
||||
android-espresso = "3.7.0"
|
||||
android-test = "1.3.0"
|
||||
|
||||
android-props-minSdk = "21"
|
||||
android-props-compileSdk = "36"
|
||||
android-props-buildTools = "36.0.0"
|
||||
android-props-compileSdk = "37"
|
||||
android-props-buildTools = "37.0.0"
|
||||
|
||||
[libraries]
|
||||
|
||||
@@ -126,5 +126,5 @@ buildscript-android-dexcount = { module = "com.getkeepsafe.dexcount:dexcount-gra
|
||||
jb-compose = { id = "org.jetbrains.compose", version.ref = "jb-compose" }
|
||||
kt-jb-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kt" }
|
||||
|
||||
versions = { id = "com.github.ben-manes.versions", version.ref = "versions" }
|
||||
versions = { id = "io.github.ben-manes.versions", version.ref = "versions" }
|
||||
nmcp-aggregation = { id = "com.gradleup.nmcp.aggregation", version.ref = "nmcp" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
kotlin {
|
||||
sourceSets {
|
||||
androidUnitTest {
|
||||
androidHostTest {
|
||||
dependencies {
|
||||
implementation compose.uiTest
|
||||
}
|
||||
|
||||
@@ -1,34 +1,18 @@
|
||||
apply plugin: 'com.getkeepsafe.dexcount'
|
||||
|
||||
android {
|
||||
compileSdkVersion libs.versions.android.props.compileSdk.get().toInteger()
|
||||
buildToolsVersion libs.versions.android.props.buildTools.get()
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion libs.versions.android.props.minSdk.get().toInteger()
|
||||
compileSdkVersion libs.versions.android.props.compileSdk.get().toInteger()
|
||||
targetSdkVersion libs.versions.android.props.compileSdk.get().toInteger()
|
||||
versionCode "${android_code_version}".toInteger()
|
||||
versionName "$version"
|
||||
namespace "${project.group}.${project.name}"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
}
|
||||
debug {
|
||||
debuggable true
|
||||
ext.configureAndroidTarget = { target ->
|
||||
target.compileSdk = libs.versions.android.props.compileSdk.get().toInteger()
|
||||
target.buildToolsVersion = libs.versions.android.props.buildTools.get()
|
||||
target.minSdk = libs.versions.android.props.minSdk.get().toInteger()
|
||||
target.namespace = "${project.group}.${project.name}"
|
||||
target.packaging {
|
||||
resources {
|
||||
excludes.add 'META-INF/kotlinx-serialization-runtime.kotlin_module'
|
||||
excludes.add 'META-INF/kotlinx-serialization-cbor.kotlin_module'
|
||||
excludes.add 'META-INF/kotlinx-serialization-properties.kotlin_module'
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/kotlinx-serialization-runtime.kotlin_module'
|
||||
exclude 'META-INF/kotlinx-serialization-cbor.kotlin_module'
|
||||
exclude 'META-INF/kotlinx-serialization-properties.kotlin_module'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
target.compilerOptions.jvmTarget.set(
|
||||
target.compilerOptions.jvmTarget.get().class.fromTarget("17")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
apply from: "$defaultAndroidSettings"
|
||||
|
||||
kotlin {
|
||||
androidTarget {
|
||||
publishLibraryVariants(
|
||||
"release",
|
||||
"debug",
|
||||
)
|
||||
compilations.all {
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
android {
|
||||
project.ext.configureAndroidTarget(delegate)
|
||||
withHostTest {}
|
||||
withDeviceTest {}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
androidUnitTest {
|
||||
jvmAndAndroidMain {
|
||||
dependsOn commonMain
|
||||
kotlin.srcDir("src/jvmMain/kotlin")
|
||||
resources.srcDir("src/jvmMain/resources")
|
||||
}
|
||||
jvmMain {
|
||||
dependsOn jvmAndAndroidMain
|
||||
kotlin.setSrcDirs([])
|
||||
resources.setSrcDirs([])
|
||||
}
|
||||
androidHostTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
implementation libs.android.test.junit
|
||||
implementation libs.android.espresso
|
||||
}
|
||||
}
|
||||
androidInstrumentedTest {
|
||||
androidDeviceTest {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
implementation libs.android.test.junit
|
||||
@@ -27,8 +33,6 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
androidMain.dependsOn jvmMain
|
||||
androidMain.dependsOn jvmAndAndroidMain
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "$defaultAndroidSettings"
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.0-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
@@ -14,7 +14,7 @@ kotlin {
|
||||
api libs.uuid
|
||||
}
|
||||
}
|
||||
jvmMain {
|
||||
jvmAndAndroidMain {
|
||||
dependencies {
|
||||
api libs.kt.reflect
|
||||
api project(":micro_utils.coroutines")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
id "com.google.devtools.ksp"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
id "com.google.devtools.ksp"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
id "com.google.devtools.ksp"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
id "com.google.devtools.ksp"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -11,7 +11,7 @@ package dev.inmo.micro_utils.meta
|
||||
RequiresOptIn.Level.WARNING
|
||||
)
|
||||
@Target(
|
||||
AnnotationTarget.FIELD,
|
||||
AnnotationTarget.PROPERTY,
|
||||
)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class MetaContainerRootMapWarning
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
alias(libs.plugins.jb.compose)
|
||||
alias(libs.plugins.kt.jb.compose)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
2
repos/cache/build.gradle
vendored
2
repos/cache/build.gradle
vendored
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
@@ -17,7 +17,7 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
jvmMain {
|
||||
jvmAndAndroidMain {
|
||||
dependencies {
|
||||
api internalProject("micro_utils.common")
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
project.version = "$version"
|
||||
project.group = "$group"
|
||||
|
||||
apply from: "$defaultAndroidSettings"
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
compilations.main {
|
||||
@@ -23,12 +25,8 @@ kotlin {
|
||||
browser()
|
||||
nodejs()
|
||||
}
|
||||
androidTarget {
|
||||
compilations.all {
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
android {
|
||||
project.ext.configureAndroidTarget(delegate)
|
||||
}
|
||||
linuxX64()
|
||||
mingwX64()
|
||||
@@ -51,6 +49,11 @@ kotlin {
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
androidMain {
|
||||
dependencies {
|
||||
implementation kotlin('test-junit')
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation kotlin('test-js')
|
||||
@@ -61,13 +64,10 @@ kotlin {
|
||||
mingwX64Main.dependsOn nativeMain
|
||||
linuxArm64Main.dependsOn nativeMain
|
||||
|
||||
androidMain.dependsOn jvmMain
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "$defaultAndroidSettings"
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
id "com.google.devtools.ksp"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
@@ -20,4 +20,4 @@ kotlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
alias(libs.plugins.compose)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.kotlin.plugin.serialization"
|
||||
id "com.android.library"
|
||||
id "com.android.kotlin.multiplatform.library"
|
||||
}
|
||||
|
||||
apply from: "$mppJvmJsWasmJsAndroidLinuxMingwLinuxArm64Project"
|
||||
|
||||
Reference in New Issue
Block a user