mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-03 15:10:14 +08:00
Add AtomicLazy
This commit is contained in:
parent
2014c34102
commit
48564056df
@ -15,7 +15,7 @@ plugins {
|
|||||||
kotlin("multiplatform")
|
kotlin("multiplatform")
|
||||||
kotlin("plugin.serialization")
|
kotlin("plugin.serialization")
|
||||||
|
|
||||||
//id("kotlinx-atomicfu")
|
id("kotlinx-atomicfu")
|
||||||
id("net.mamoe.kotlin-jvm-blocking-bridge")
|
id("net.mamoe.kotlin-jvm-blocking-bridge")
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
id("com.jfrog.bintray")
|
id("com.jfrog.bintray")
|
||||||
|
51
mirai-core-utils/src/commonMain/kotlin/AtomicLazy.kt
Normal file
51
mirai-core-utils/src/commonMain/kotlin/AtomicLazy.kt
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
||||||
|
*
|
||||||
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||||
|
*
|
||||||
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.utils
|
||||||
|
|
||||||
|
import kotlinx.atomicfu.atomic
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
private val UNINITIALIZED: Any? = Symbol("UNINITIALIZED")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* - [initializer] is supported to be called at most once, however multiple invocations may happen if executed by multiple coroutines in single thread.
|
||||||
|
* - [ReadWriteProperty.setValue] prevails on competition with [initializer].
|
||||||
|
*/
|
||||||
|
public fun <T> lateinitMutableProperty(initializer: () -> T): ReadWriteProperty<Any?, T> =
|
||||||
|
LateinitMutableProperty(initializer)
|
||||||
|
|
||||||
|
private class LateinitMutableProperty<T>(
|
||||||
|
initializer: () -> T
|
||||||
|
) : ReadWriteProperty<Any?, T> {
|
||||||
|
private val value = atomic(UNINITIALIZED)
|
||||||
|
|
||||||
|
private var initializer: (() -> T)? = initializer
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||||
|
return when (val v = this.value.value) {
|
||||||
|
UNINITIALIZED -> synchronized(this) {
|
||||||
|
val initializer = initializer
|
||||||
|
if (initializer != null && this.value.value === UNINITIALIZED) {
|
||||||
|
val value = initializer()
|
||||||
|
this.value.compareAndSet(UNINITIALIZED, value) // setValue prevails
|
||||||
|
this.initializer = null
|
||||||
|
value
|
||||||
|
} else v as T
|
||||||
|
}
|
||||||
|
else -> v as T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||||
|
this.value.value = value
|
||||||
|
}
|
||||||
|
}
|
@ -41,4 +41,24 @@ public inline fun CoroutineScope.launchWithPermit(
|
|||||||
return launch(coroutineContext) {
|
return launch(coroutineContext) {
|
||||||
semaphore.withPermit { block() }
|
semaphore.withPermit { block() }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a child scope of the receiver scope.
|
||||||
|
*/
|
||||||
|
public fun CoroutineScope.childScope(
|
||||||
|
coroutineContext: CoroutineContext = EmptyCoroutineContext,
|
||||||
|
): CoroutineScope {
|
||||||
|
val ctx = this.coroutineContext + coroutineContext
|
||||||
|
return CoroutineScope(ctx + SupervisorJob(ctx.job))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a child scope of the receiver context scope.
|
||||||
|
*/
|
||||||
|
public fun CoroutineContext.childScope(
|
||||||
|
coroutineContext: CoroutineContext = EmptyCoroutineContext,
|
||||||
|
): CoroutineScope {
|
||||||
|
val ctx = this + coroutineContext
|
||||||
|
return CoroutineScope(ctx + SupervisorJob(ctx.job))
|
||||||
}
|
}
|
21
mirai-core-utils/src/commonMain/kotlin/Symbol.kt
Normal file
21
mirai-core-utils/src/commonMain/kotlin/Symbol.kt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
||||||
|
*
|
||||||
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||||
|
*
|
||||||
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.utils
|
||||||
|
|
||||||
|
public class Symbol private constructor(name: String) {
|
||||||
|
private val str = "Symbol($name)"
|
||||||
|
override fun toString(): String = str
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
@Suppress("RedundantNullableReturnType")
|
||||||
|
@JvmName("create")
|
||||||
|
public operator fun invoke(name: String): Any? = Symbol(name) // calls constructor
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
||||||
|
*
|
||||||
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||||
|
*
|
||||||
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.utils
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertSame
|
||||||
|
|
||||||
|
internal class LateinitMutablePropertyTest {
|
||||||
|
@Test
|
||||||
|
fun canInitialize() {
|
||||||
|
val value = Symbol("expected")
|
||||||
|
val prop by lateinitMutableProperty { value }
|
||||||
|
assertSame(value, prop)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun canOverride() {
|
||||||
|
val value = Symbol("expected")
|
||||||
|
val overrode = Symbol("override")
|
||||||
|
|
||||||
|
var prop by lateinitMutableProperty { value }
|
||||||
|
prop = overrode
|
||||||
|
assertSame(overrode, prop)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun initializerCalledOnce() {
|
||||||
|
val value = Symbol("expected")
|
||||||
|
val counter = AtomicInteger(0)
|
||||||
|
|
||||||
|
val prop by lateinitMutableProperty {
|
||||||
|
counter.incrementAndGet()
|
||||||
|
value
|
||||||
|
}
|
||||||
|
assertSame(value, prop)
|
||||||
|
assertSame(value, prop)
|
||||||
|
assertEquals(1, counter.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun initializerCalledOnceConcurrent() = runBlocking {
|
||||||
|
val value = Symbol("expected")
|
||||||
|
val counter = AtomicInteger(0)
|
||||||
|
|
||||||
|
val verySlowInitializer = CompletableFuture<Unit>()
|
||||||
|
|
||||||
|
|
||||||
|
val prop by lateinitMutableProperty {
|
||||||
|
counter.incrementAndGet()
|
||||||
|
verySlowInitializer.join() // do not use coroutine: coroutines run in same thread so `synchronized` doesnt work.
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val lock = CompletableDeferred<Unit>()
|
||||||
|
repeat(10) {
|
||||||
|
launch {
|
||||||
|
lock.join()
|
||||||
|
@Suppress("UNUSED_EXPRESSION")
|
||||||
|
prop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lock.complete(Unit) // resume callers
|
||||||
|
|
||||||
|
|
||||||
|
verySlowInitializer.complete(Unit)
|
||||||
|
|
||||||
|
assertSame(value, prop)
|
||||||
|
assertEquals(1, counter.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setValuePrevailsOnCompetitionWithInitializer() {
|
||||||
|
val verySlowInitializer = CompletableFuture<Unit>()
|
||||||
|
val override = Symbol("override")
|
||||||
|
val initializer = Symbol("initializer")
|
||||||
|
|
||||||
|
var prop by lateinitMutableProperty {
|
||||||
|
verySlowInitializer.join()
|
||||||
|
initializer
|
||||||
|
}
|
||||||
|
|
||||||
|
thread { println(prop) }
|
||||||
|
prop = override
|
||||||
|
verySlowInitializer.complete(Unit)
|
||||||
|
|
||||||
|
assertSame(override, prop)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user