Преглед на файлове

Add ComputeOnNullMutableProperty

Him188 преди 4 години
родител
ревизия
f4adc1232f

+ 49 - 0
mirai-core-utils/src/commonMain/kotlin/ComputeOnNullMutableProperty.kt

@@ -0,0 +1,49 @@
+/*
+ * 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.reflect.KProperty
+
+public fun <T : Any> computeOnNullMutableProperty(initializer: () -> T): ComputeOnNullMutableProperty<T> =
+    ComputeOnNullMutablePropertyImpl(initializer)
+
+public interface ComputeOnNullMutableProperty<V : Any> {
+    public fun get(): V
+    public fun set(value: V?)
+
+    public operator fun getValue(thisRef: Any?, property: KProperty<*>): V = get()
+    public operator fun setValue(thisRef: Any?, property: KProperty<*>, value: V?): Unit = set(value)
+}
+
+
+private class ComputeOnNullMutablePropertyImpl<T : Any>(
+    private val initializer: () -> T
+) : ComputeOnNullMutableProperty<T> {
+    private val value = atomic<T?>(null)
+
+    override tailrec fun get(): T {
+        return when (val v = this.value.value) {
+            null -> synchronized(this) {
+                if (this.value.value === null) {
+                    val value = this.initializer()
+                    // compiler inserts
+                    this.value.compareAndSet(null, value) // setValue prevails
+                    return get()
+                } else this.value.value as T
+            }
+            else -> v
+        }
+    }
+
+    override fun set(value: T?) {
+        this.value.value = value
+    }
+}

+ 51 - 0
mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ComputeOnNullMutablePropertyTest.kt

@@ -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/dev/LICENSE
+ */
+
+package net.mamoe.mirai.utils
+
+import org.junit.jupiter.api.Test
+import java.util.concurrent.atomic.AtomicBoolean
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+internal class ComputeOnNullMutablePropertyTest {
+    @Test
+    fun `can initialize`() {
+        val prop = computeOnNullMutableProperty { "ok" }
+        assertEquals("ok", prop.get())
+    }
+
+    @Test
+    fun `can override`() {
+        val called = AtomicBoolean(false)
+        val prop = computeOnNullMutableProperty { "not ok".also { called.set(true) } }
+        prop.set("ok")
+        assertEquals("ok", prop.get())
+        assertFalse { called.get() }
+    }
+
+    @Test
+    fun `can reinitialize 1`() {
+        val called = AtomicBoolean(false)
+        val prop = computeOnNullMutableProperty { "ok".also { called.set(true) } }
+        prop.set("not ok 2")
+        prop.set(null)
+        assertEquals("ok", prop.get())
+        assertTrue { called.get() }
+    }
+
+    @Test
+    fun `can reinitialize 2`() {
+        val prop = computeOnNullMutableProperty { "ok" }
+        prop.get()
+        prop.set(null)
+        assertEquals("ok", prop.get())
+    }
+}