Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Back-port arrow-atomic form 2.x.x #2903

Merged
merged 2 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions arrow-libs/core/arrow-atomic/api/arrow-atomic.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
public final class arrow/atomic/AtomicBoolean {
public fun <init> (Z)V
public final fun compareAndSet (ZZ)Z
public final fun get ()Z
public final fun getAndSet (Z)Z
public final fun getValue ()Z
public final fun set (Z)V
public final fun setValue (Z)V
}

public final class arrow/atomic/AtomicBooleanKt {
public static final fun getAndUpdate (Larrow/atomic/AtomicBoolean;Lkotlin/jvm/functions/Function1;)Z
public static final fun loop (Larrow/atomic/AtomicBoolean;Lkotlin/jvm/functions/Function1;)Ljava/lang/Void;
public static final fun tryUpdate (Larrow/atomic/AtomicBoolean;Lkotlin/jvm/functions/Function1;)Z
public static final fun update (Larrow/atomic/AtomicBoolean;Lkotlin/jvm/functions/Function1;)V
public static final fun updateAndGet (Larrow/atomic/AtomicBoolean;Lkotlin/jvm/functions/Function1;)Z
}

public final class arrow/atomic/AtomicIntKt {
public static final fun getAndUpdate (Ljava/util/concurrent/atomic/AtomicInteger;Lkotlin/jvm/functions/Function1;)I
public static final fun getValue (Ljava/util/concurrent/atomic/AtomicInteger;)I
public static final fun loop (Ljava/util/concurrent/atomic/AtomicInteger;Lkotlin/jvm/functions/Function1;)Ljava/lang/Void;
public static final fun setValue (Ljava/util/concurrent/atomic/AtomicInteger;I)V
public static final fun tryUpdate (Ljava/util/concurrent/atomic/AtomicInteger;Lkotlin/jvm/functions/Function1;)Z
public static final fun update (Ljava/util/concurrent/atomic/AtomicInteger;Lkotlin/jvm/functions/Function1;)V
public static final fun updateAndGet (Ljava/util/concurrent/atomic/AtomicInteger;Lkotlin/jvm/functions/Function1;)I
}

public final class arrow/atomic/AtomicKt {
public static final fun getAndUpdate (Ljava/util/concurrent/atomic/AtomicReference;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun getValue (Ljava/util/concurrent/atomic/AtomicReference;)Ljava/lang/Object;
public static final fun loop (Ljava/util/concurrent/atomic/AtomicReference;Lkotlin/jvm/functions/Function1;)Ljava/lang/Void;
public static final fun setValue (Ljava/util/concurrent/atomic/AtomicReference;Ljava/lang/Object;)V
public static final fun tryUpdate (Ljava/util/concurrent/atomic/AtomicReference;Lkotlin/jvm/functions/Function1;)Z
public static final fun update (Ljava/util/concurrent/atomic/AtomicReference;Lkotlin/jvm/functions/Function1;)V
public static final fun updateAndGet (Ljava/util/concurrent/atomic/AtomicReference;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
}

public final class arrow/atomic/AtomicLongKt {
public static final fun getAndUpdate (Ljava/util/concurrent/atomic/AtomicLong;Lkotlin/jvm/functions/Function1;)J
public static final fun getValue (Ljava/util/concurrent/atomic/AtomicLong;)J
public static final fun loop (Ljava/util/concurrent/atomic/AtomicLong;Lkotlin/jvm/functions/Function1;)Ljava/lang/Void;
public static final fun setValue (Ljava/util/concurrent/atomic/AtomicLong;J)V
public static final fun tryUpdate (Ljava/util/concurrent/atomic/AtomicLong;Lkotlin/jvm/functions/Function1;)Z
public static final fun update (Ljava/util/concurrent/atomic/AtomicLong;Lkotlin/jvm/functions/Function1;)V
public static final fun updateAndGet (Ljava/util/concurrent/atomic/AtomicLong;Lkotlin/jvm/functions/Function1;)J
}

74 changes: 74 additions & 0 deletions arrow-libs/core/arrow-atomic/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
plugins {
id(libs.plugins.kotlin.multiplatform.get().pluginId)
alias(libs.plugins.arrowGradleConfig.kotlin)
alias(libs.plugins.arrowGradleConfig.publish)
alias(libs.plugins.kotest.multiplatform)
}

apply(from = property("TEST_COVERAGE"))
apply(from = property("ANIMALSNIFFER_MPP"))

val enableCompatibilityMetadataVariant =
providers.gradleProperty("kotlin.mpp.enableCompatibilityMetadataVariant")
.forUseAtConfigurationTime().orNull?.toBoolean() == true

if (enableCompatibilityMetadataVariant) {
tasks.withType<Test>().configureEach {
exclude("**/*")
}
}

kotlin {
sourceSets {
commonMain {
dependencies {
api(libs.kotlin.stdlibCommon)
}
}

if (!enableCompatibilityMetadataVariant) {
commonTest {
dependencies {
implementation(projects.arrowFxCoroutines)
implementation(libs.kotest.frameworkEngine)
implementation(libs.kotest.assertionsCore)
implementation(libs.kotest.property)
}
}

jvmTest {
dependencies {
runtimeOnly(libs.kotest.runnerJUnit5)
}
}
}

jvmMain {
dependencies {
implementation(libs.kotlin.stdlibJDK8)
}
}

jsMain {
dependencies {
implementation(libs.kotlin.stdlibJS)
}
}

if (!enableCompatibilityMetadataVariant) {
commonTest {
dependencies {
implementation(projects.arrowFxCoroutines)
implementation(libs.kotest.frameworkEngine)
implementation(libs.kotest.assertionsCore)
implementation(libs.kotest.property)
}
}
jvmTest {
dependencies {
runtimeOnly(libs.kotest.runnerJUnit5)
}
}
}
}
}
4 changes: 4 additions & 0 deletions arrow-libs/core/arrow-atomic/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Maven publishing configuration
pom.name=Arrow Atomic
# Build configuration
kapt.incremental.apt=false
5 changes: 5 additions & 0 deletions arrow-libs/core/arrow-atomic/knit.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
knit.package=arrow.atomic.examples
knit.dir=src/jvmTest/kotlin/examples/

test.package=arrow.atomic.examples.test
test.dir=src/jvmTest/kotlin/examples/autogenerated/
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package arrow.atomic

/**
* [Atomic] value of [A].
*
* ```kotlin
* import arrow.atomic.AtomicInt
* import arrow.atomic.update
* import arrow.atomic.value
* import arrow.fx.coroutines.parMap
*
* suspend fun main() {
* val count = AtomicInt(0)
* (0 until 20_000).parMap {
* count.update(Int::inc)
* }
* println(count.value)
* }
* ```
* <!--- KNIT example-atomic-01.kt -->
*
* [Atomic] also offers some other interesting operators such as [loop], [update], [tryUpdate], etc.
*
* **WARNING**: Use [AtomicInt] and [AtomicLong] for [Int] and [Long] on Kotlin Native!
*/
public expect class Atomic<V>(initialValue: V) {
public fun get(): V
public fun set(value: V)
public fun getAndSet(value: V): V

/**
* Compare current value with expected and set to new if they're the same. Note, 'compare' is checking
* the actual object id, not 'equals'.
*/
public fun compareAndSet(expected: V, new: V): Boolean
}

public var <T> Atomic<T>.value: T
get() = get()
set(value) {
set(value)
}

/**
* Infinite loop that reads this atomic variable and performs the specified [action] on its value.
*/
public inline fun <V> Atomic<V>.loop(action: (V) -> Unit): Nothing {
while (true) {
action(value)
}
}

public fun <V> Atomic<V>.tryUpdate(function: (V) -> V): Boolean {
val cur = value
val upd = function(cur)
return compareAndSet(cur, upd)
}

public inline fun <V> Atomic<V>.update(function: (V) -> V) {
while (true) {
val cur = value
val upd = function(cur)
if (compareAndSet(cur, upd)) return
}
}

/**
* Updates variable atomically using the specified [function] of its value and returns its old value.
*/
public inline fun <V> Atomic<V>.getAndUpdate(function: (V) -> V): V {
while (true) {
val cur = value
val upd = function(cur)
if (compareAndSet(cur, upd)) return cur
}
}

/**
* Updates variable atomically using the specified [function] of its value and returns its new value.
*/
public inline fun <V> Atomic<V>.updateAndGet(function: (V) -> V): V {
while (true) {
val cur = value
val upd = function(cur)
if (compareAndSet(cur, upd)) return upd
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package arrow.atomic

public class AtomicBoolean(value: Boolean) {
private val inner = AtomicInt(value.toInt())

public var value: Boolean
get() = inner.value != 0
set(value) {
inner.value = value.toInt()
}

public fun compareAndSet(expected: Boolean, new: Boolean): Boolean =
inner.compareAndSet(expected.toInt(), new.toInt())

public fun get(): Boolean = value
public fun set(value: Boolean) {
inner.value = value.toInt()
}

public fun getAndSet(value: Boolean): Boolean =
inner.getAndSet(value.toInt()) == 1

private fun Boolean.toInt(): Int =
if (this) 1 else 0
}


/**
* Infinite loop that reads this atomic variable and performs the specified [action] on its value.
*/
public inline fun AtomicBoolean.loop(action: (Boolean) -> Unit): Nothing {
while (true) {
action(value)
}
}

public fun AtomicBoolean.tryUpdate(function: (Boolean) -> Boolean): Boolean {
val cur = value
val upd = function(cur)
return compareAndSet(cur, upd)
}

public inline fun AtomicBoolean.update(function: (Boolean) -> Boolean) {
while (true) {
val cur = value
val upd = function(cur)
if (compareAndSet(cur, upd)) return
}
}

/**
* Updates variable atomically using the specified [function] of its value and returns its old value.
*/
public inline fun AtomicBoolean.getAndUpdate(function: (Boolean) -> Boolean): Boolean {
while (true) {
val cur = value
val upd = function(cur)
if (compareAndSet(cur, upd)) return cur
}
}

/**
* Updates variable atomically using the specified [function] of its value and returns its new value.
*/
public inline fun AtomicBoolean.updateAndGet(function: (Boolean) -> Boolean): Boolean {
while (true) {
val cur = value
val upd = function(cur)
if (compareAndSet(cur, upd)) return upd
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package arrow.atomic

public expect class AtomicInt(initialValue: Int) {

public fun get(): Int
public fun set(newValue: Int)
public fun getAndSet(value: Int): Int

public fun incrementAndGet(): Int
public fun decrementAndGet(): Int

public fun addAndGet(delta: Int): Int

public fun compareAndSet(expected: Int, new: Int): Boolean
}

public var AtomicInt.value: Int
get() = get()
set(value) {
set(value)
}

/**
* Infinite loop that reads this atomic variable and performs the specified [action] on its value.
*/
public inline fun AtomicInt.loop(action: (Int) -> Unit): Nothing {
while (true) {
action(value)
}
}

public fun AtomicInt.tryUpdate(function: (Int) -> Int): Boolean {
val cur = value
val upd = function(cur)
return compareAndSet(cur, upd)
}

public inline fun AtomicInt.update(function: (Int) -> Int) {
while (true) {
val cur = value
val upd = function(cur)
if (compareAndSet(cur, upd)) return
}
}

/**
* Updates variable atomically using the specified [function] of its value and returns its old value.
*/
public inline fun AtomicInt.getAndUpdate(function: (Int) -> Int): Int {
while (true) {
val cur = value
val upd = function(cur)
if (compareAndSet(cur, upd)) return cur
}
}

/**
* Updates variable atomically using the specified [function] of its value and returns its new value.
*/
public inline fun AtomicInt.updateAndGet(function: (Int) -> Int): Int {
while (true) {
val cur = value
val upd = function(cur)
if (compareAndSet(cur, upd)) return upd
}
}

Loading