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("plugin.serialization")
|
||||
|
||||
//id("kotlinx-atomicfu")
|
||||
id("kotlinx-atomicfu")
|
||||
id("net.mamoe.kotlin-jvm-blocking-bridge")
|
||||
`maven-publish`
|
||||
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
|
||||
}
|
||||
}
|
@ -42,3 +42,23 @@ public inline fun CoroutineScope.launchWithPermit(
|
||||
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