From e2199bd6e697b5e30c0b07a9cb7a2f9ca92def9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petrus=20Nguy=E1=BB=85n=20Th=C3=A1i=20H=E1=BB=8Dc?= Date: Wed, 20 Jul 2022 17:27:14 +0700 Subject: [PATCH 1/4] fix: `freeze` initial value of native `AtomicRef` --- .../kotlin/com/hoc081098/flowext/internal/AtomicRef.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nativeMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt b/src/nativeMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt index bb4f59bb..88528ea5 100644 --- a/src/nativeMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt +++ b/src/nativeMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt @@ -25,9 +25,10 @@ package com.hoc081098.flowext.internal import kotlin.native.concurrent.AtomicReference as NativeAtomicReference +import kotlin.native.concurrent.freeze internal actual class AtomicRef actual constructor(value: T) { - private val atomic = NativeAtomicReference(value) + private val atomic = NativeAtomicReference(value.freeze()) actual var value: T by atomic::value From 061d5bfd8f73aa8b0b181dc1c40e7ed2c8860396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petrus=20Nguy=E1=BB=85n=20Th=C3=A1i=20H=E1=BB=8Dc?= Date: Thu, 21 Jul 2022 10:22:20 +0700 Subject: [PATCH 2/4] fix(atomic) --- .../hoc081098/flowext/internal/AtomicRef.kt | 2 +- .../hoc081098/flowext/internal/AtomicRef.kt | 4 +++- .../hoc081098/flowext/internal/AtomicRef.kt | 4 ++-- .../hoc081098/flowext/internal/AtomicRef.kt | 22 ++++++++++++++----- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/commonMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt b/src/commonMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt index 973087cd..f069a032 100644 --- a/src/commonMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt +++ b/src/commonMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt @@ -24,7 +24,7 @@ package com.hoc081098.flowext.internal -internal expect class AtomicRef(value: T) { +internal expect class AtomicRef(initialValue: T) { var value: T fun compareAndSet(expect: T, update: T): Boolean diff --git a/src/jsMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt b/src/jsMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt index c83946ad..89c91c76 100644 --- a/src/jsMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt +++ b/src/jsMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt @@ -24,7 +24,9 @@ package com.hoc081098.flowext.internal -internal actual class AtomicRef actual constructor(actual var value: T) { +internal actual class AtomicRef actual constructor(initialValue: T) { + actual var value: T = initialValue + actual fun compareAndSet(expect: T, update: T): Boolean = if (expect == value) { value = update true diff --git a/src/jvmMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt b/src/jvmMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt index 0cc53e39..f6ba587a 100644 --- a/src/jvmMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt +++ b/src/jvmMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt @@ -26,8 +26,8 @@ package com.hoc081098.flowext.internal import java.util.concurrent.atomic.AtomicReference as JavaAtomicReference -internal actual class AtomicRef actual constructor(value: T) { - private val atomic = JavaAtomicReference(value) +internal actual class AtomicRef actual constructor(initialValue: T) { + private val atomic = JavaAtomicReference(initialValue) actual var value: T get() = atomic.get() diff --git a/src/nativeMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt b/src/nativeMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt index 88528ea5..3fdb504b 100644 --- a/src/nativeMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt +++ b/src/nativeMain/kotlin/com/hoc081098/flowext/internal/AtomicRef.kt @@ -24,13 +24,25 @@ package com.hoc081098.flowext.internal -import kotlin.native.concurrent.AtomicReference as NativeAtomicReference +import kotlin.native.concurrent.FreezableAtomicReference import kotlin.native.concurrent.freeze +import kotlin.native.concurrent.isFrozen -internal actual class AtomicRef actual constructor(value: T) { - private val atomic = NativeAtomicReference(value.freeze()) +internal actual class AtomicRef actual constructor(initialValue: T) { + private val atomic = FreezableAtomicReference(initialValue) - actual var value: T by atomic::value + actual var value: T + get() = atomic.value + set(value) { + atomic.value = value.freezeIfNeeded() + } - actual fun compareAndSet(expect: T, update: T): Boolean = atomic.compareAndSet(expect, update) + actual fun compareAndSet(expect: T, update: T): Boolean = + atomic.compareAndSet(expect, update.freezeIfNeeded()) + + private inline fun T.freezeIfNeeded(): T = if (atomic.isFrozen) { + freeze() + } else { + this + } } From 2f38115b40917a3a55ddf4623294037e3cb27b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petrus=20Nguy=E1=BB=85n=20Th=C3=A1i=20H=E1=BB=8Dc?= Date: Thu, 21 Jul 2022 12:09:32 +0700 Subject: [PATCH 3/4] test(atomic) --- .../{ => internal}/AtomicBooleanTest.kt | 3 +- .../flowext/internal/AtomicRefTest.kt | 71 ++++++++++++++++ .../flowext/internal/AtomicRefNativeTest.kt | 80 +++++++++++++++++++ 3 files changed, 152 insertions(+), 2 deletions(-) rename src/commonTest/kotlin/com/hoc081098/flowext/{ => internal}/AtomicBooleanTest.kt (96%) create mode 100644 src/commonTest/kotlin/com/hoc081098/flowext/internal/AtomicRefTest.kt create mode 100644 src/nativeTest/kotlin/com/hoc081098/flowext/internal/AtomicRefNativeTest.kt diff --git a/src/commonTest/kotlin/com/hoc081098/flowext/AtomicBooleanTest.kt b/src/commonTest/kotlin/com/hoc081098/flowext/internal/AtomicBooleanTest.kt similarity index 96% rename from src/commonTest/kotlin/com/hoc081098/flowext/AtomicBooleanTest.kt rename to src/commonTest/kotlin/com/hoc081098/flowext/internal/AtomicBooleanTest.kt index 2f61d29d..7df0e1dd 100644 --- a/src/commonTest/kotlin/com/hoc081098/flowext/AtomicBooleanTest.kt +++ b/src/commonTest/kotlin/com/hoc081098/flowext/internal/AtomicBooleanTest.kt @@ -22,9 +22,8 @@ * SOFTWARE. */ -package com.hoc081098.flowext +package com.hoc081098.flowext.internal -import com.hoc081098.flowext.internal.AtomicBoolean import kotlin.test.Test import kotlin.test.assertFalse import kotlin.test.assertTrue diff --git a/src/commonTest/kotlin/com/hoc081098/flowext/internal/AtomicRefTest.kt b/src/commonTest/kotlin/com/hoc081098/flowext/internal/AtomicRefTest.kt new file mode 100644 index 00000000..927d22f3 --- /dev/null +++ b/src/commonTest/kotlin/com/hoc081098/flowext/internal/AtomicRefTest.kt @@ -0,0 +1,71 @@ +/* + * MIT License + * + * Copyright (c) 2021-2022 Petrus Nguyễn Thái Học + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.hoc081098.flowext.internal + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertSame +import kotlin.test.assertTrue + +class AtomicRefTest { + @Test + fun returns_initial_value_WHEN_created() { + val ref = AtomicRef(VALUE_1) + + assertEquals(VALUE_1, ref.value) + } + + @Test + fun returns_updated_value() { + val ref = AtomicRef(VALUE_1) + + ref.value = VALUE_2 + + assertEquals(VALUE_2, ref.value) + } + + @Test + fun compareAndSet_success() { + val ref = AtomicRef(VALUE_1) + val result = ref.compareAndSet(VALUE_1, VALUE_2) + assertTrue(result) + assertSame(VALUE_2, ref.value) + } + + @Test + fun compareAndSet_fail() { + val ref = AtomicRef(VALUE_1) + val result = ref.compareAndSet(VALUE_2, VALUE_3) + assertFalse(result) + assertSame(VALUE_1, ref.value) + } + + private companion object { + private const val VALUE_1 = "a" + private const val VALUE_2 = "b" + private const val VALUE_3 = "c" + } +} diff --git a/src/nativeTest/kotlin/com/hoc081098/flowext/internal/AtomicRefNativeTest.kt b/src/nativeTest/kotlin/com/hoc081098/flowext/internal/AtomicRefNativeTest.kt new file mode 100644 index 00000000..d84127f1 --- /dev/null +++ b/src/nativeTest/kotlin/com/hoc081098/flowext/internal/AtomicRefNativeTest.kt @@ -0,0 +1,80 @@ +/* + * MIT License + * + * Copyright (c) 2021-2022 Petrus Nguyễn Thái Học + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.hoc081098.flowext.internal + +import kotlin.native.concurrent.freeze +import kotlin.native.concurrent.isFrozen +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class AtomicRefNativeTest { + @Test + fun accepts_not_frozen_initial_value() { + AtomicRef(Any()) + } + + @Test + fun accepts_frozen_initial_value() { + val ref = AtomicRef(Any().freeze()) + + assertTrue(ref.value.isFrozen) + } + + @Test + fun does_not_freeze_initial_value() { + val ref = AtomicRef(Any()) + + assertFalse(ref.value.isFrozen) + } + + @Test + fun freezes_current_value_WHEN_frozen() { + val ref = AtomicRef(Any()) + + ref.freeze() + + assertTrue(ref.value.isFrozen) + } + + @Test + fun does_not_freeze_new_value_IF_not_frozen() { + val ref = AtomicRef(Any()) + + ref.value = Any() + + assertFalse(ref.value.isFrozen) + } + + @Test + fun freezes_new_value_IF_frozen() { + val ref = AtomicRef(Any()) + + ref.freeze() + ref.value = Any() + + assertTrue(ref.value.isFrozen) + } +} From bdcabedcdcf41143ffba81e763d2a348d994425b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petrus=20Nguy=E1=BB=85n=20Th=C3=A1i=20H=E1=BB=8Dc?= Date: Thu, 21 Jul 2022 12:10:44 +0700 Subject: [PATCH 4/4] CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 365cf51c..a05c7d08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ - `neverFlow()` now returns `NeverFlow`. - `takeUntil`: change `notifier` type to `Flow` +- Internal fix for `AtomicRef`: freeze `value` if `AtomicRef` is frozen. + - Support for Apple Silicon targets - `iosSimulatorArm64`. - `macosArm64`.