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

Mutex for Kotlin Common #494

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
161 changes: 96 additions & 65 deletions atomicfu/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ kotlin {
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-test")
implementation("org.jetbrains.kotlin:kotlin-test-junit")
implementation("org.jetbrains.kotlinx:lincheck:2.34")
implementation(libs.junit.junit)
}
}
Expand All @@ -98,82 +99,113 @@ kotlin {

// Support of all non-deprecated targets from the official tier list: https://kotlinlang.org/docs/native-target-support.html
kotlin {
// Tier 1
macosX64()
macosArm64()
iosSimulatorArm64()
iosX64()

// Tier 2
linuxX64()
linuxArm64()
watchosSimulatorArm64()
watchosX64()
watchosArm32()
watchosArm64()
tvosSimulatorArm64()
tvosX64()
tvosArm64()
iosArm64()

// Tier 3
androidNativeArm32()
androidNativeArm64()
androidNativeX86()
androidNativeX64()
mingwX64()
watchosDeviceArm64()
val appleTargets = listOf(
// Tier 1
macosX64(),
macosArm64(),
iosSimulatorArm64(),
iosX64(),

// Tier 2,
watchosSimulatorArm64(),
watchosX64(),
watchosArm32(),
watchosArm64(),
tvosSimulatorArm64(),
tvosX64(),
tvosArm64(),
iosArm64(),

// Tier 3
watchosDeviceArm64(),
)

@Suppress("DEPRECATION") //https://github.com/Kotlin/kotlinx-atomicfu/issues/207
linuxArm32Hfp()

@OptIn(ExperimentalKotlinGradlePluginApi::class)
applyDefaultHierarchyTemplate {
group("native") {
group("nativeUnixLike") {
withLinux()
withApple()
val linuxTargets = listOf(
// Tier 2,
linuxX64(),
linuxArm64(),
)

val androidNativeTargets = listOf(
// Tier 3
androidNativeArm32(),
androidNativeArm64(),
androidNativeX86(),
androidNativeX64(),
)

val windowsTargets = listOf(
mingwX64(),
)

(linuxTargets + androidNativeTargets).forEach {
it.compilations.getByName("main").cinterops {
// This is a hack to fix commonization bug: KT-73136
val ulock by creating {
defFile(project.file("src/nativeInterop/cinterop/stub.def"))
packageName = "stub"
}
}
group("androidNative32Bit") {
withAndroidNativeX86()
withCompilations { compilation ->
(compilation.target as? KotlinNativeTarget)?.konanTarget?.name == "android_arm32"
val posixparking by creating {
defFile(project.file("src/nativeInterop/cinterop/posixparking.def"))
packageName = "platform.posix"
}
}
group("androidNative64Bit") {
withAndroidNativeArm64()
withAndroidNativeX64()
}

}

sourceSets {
val nativeUnixLikeMain by getting {
kotlin.srcDir("src/nativeUnixLikeMain/kotlin")
dependsOn(nativeMain.get())

appleTargets.forEach {
it.compilations.getByName("main").cinterops {
val ulock by creating {
defFile(project.file("src/nativeInterop/cinterop/ulock.def"))
packageName = "platform.darwin.ulock"
includeDirs("${project.rootDir}/atomicfu/src/nativeInterop/cinterop")
}
val posixparking by creating {
defFile(project.file("src/nativeInterop/cinterop/posixparking.def"))
packageName = "platform.posix"
}
}

val androidNative32BitMain by getting {
kotlin.srcDir("src/androidNative32BitMain/kotlin")
dependsOn(nativeMain.get())
}

windowsTargets.forEach {
it.binaries.all {
linkerOpts += "-lSynchronization"
}

val androidNative64BitMain by getting {
kotlin.srcDir("src/androidNative64BitMain/kotlin")
dependsOn(nativeMain.get())
it.compilations.getByName("main").cinterops {
// This is a hack to fix commonization bug: KT-73136
val ulock by creating {
defFile(project.file("src/nativeInterop/cinterop/stub.def"))
packageName = "stub"
}
val posixparking by creating {
defFile(project.file("src/nativeInterop/cinterop/posixparking.def"))
packageName = "platform.posix"
}
}
}

val androidNative32BitTest by getting {
kotlin.srcDir("src/androidNative32BitTest/kotlin")
dependsOn(nativeTest.get())
@Suppress("DEPRECATION") //https://github.com/Kotlin/kotlinx-atomicfu/issues/207
linuxArm32Hfp {
compilations.getByName("main").cinterops {
// This is a hack to fix commonization bug: KT-73136
val ulock by creating {
defFile(project.file("src/nativeInterop/cinterop/stub.def"))
packageName = "stub"
}
val posixparking by creating {
defFile(project.file("src/nativeInterop/cinterop/posixparking.def"))
packageName = "platform.posix"
}
}
}

val androidNative64BitTest by getting {
kotlin.srcDir("src/androidNative64BitTest/kotlin")
dependsOn(nativeTest.get())
}
applyDefaultHierarchyTemplate()
sourceSets {
val linux64Main by creating { dependsOn(nativeMain.get()) }
linuxX64Main.get().dependsOn(linux64Main)
linuxArm64Main.get().dependsOn(linux64Main)

val linux32Main by creating { dependsOn(nativeMain.get()) }
linuxArm32HfpMain.get().dependsOn(linux32Main)
}

// atomicfu-cinterop-interop.klib with an empty interop.def file will still be published for compatibility reasons (see KT-68411)
Expand Down Expand Up @@ -378,4 +410,3 @@ val jvmTest by tasks.getting(Test::class) {
)
// run them only for transformed code
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package kotlinx.atomicfu.locks

internal actual object FutexParkingDelegator: ParkingDelegator {
actual override fun createFutexPtr(): Long = PosixParkingDelegator.createFutexPtr()
actual override fun wait(futexPrt: Long): Boolean = PosixParkingDelegator.wait(futexPrt)
actual override fun wake(futexPrt: Long): Int = PosixParkingDelegator.wake(futexPrt)
actual override fun manualDeallocate(futexPrt: Long) = PosixParkingDelegator.manualDeallocate(futexPrt)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package kotlinx.atomicfu.locks

import kotlinx.cinterop.ptr

import kotlinx.cinterop.*
import platform.posix.*
import kotlin.concurrent.*
Expand All @@ -22,7 +24,7 @@ public actual class NativeMutexNode {
pthread_mutex_unlock(pMutex.ptr)
}

actual fun unlock() {
actual fun unlock() {
pthread_mutex_lock(pMutex.ptr)
isLocked = false
pthread_cond_broadcast(pCond.ptr)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package kotlinx.atomicfu.locks

import kotlinx.cinterop.*
import platform.darwin.UInt32
import platform.darwin.UInt64Var
import platform.darwin.ulock.__ulock_wait
import platform.darwin.ulock.__ulock_wake

@OptIn(ExperimentalForeignApi::class)
internal actual object FutexParkingDelegator: ParkingDelegator {
actual override fun createFutexPtr(): Long {
val signal = nativeHeap.alloc<UInt64Var>()
signal.value = 0u
return signal.ptr.toLong()
}

actual override fun wait(futexPrt: Long): Boolean {
val cPointer = futexPrt.toCPointer<UInt64Var>() ?: throw IllegalStateException("Could not create C Pointer from futex ref")
val result = __ulock_wait(UL_COMPARE_AND_WAIT, cPointer, 0u, 0u)
nativeHeap.free(cPointer)
// THere is very little information about ulock so not sure what returned int stands for an interrupt
// In any case it should be 0
return result != 0
}

actual override fun wake(futexPrt: Long): Int {
return __ulock_wake(UL_COMPARE_AND_WAIT, futexPrt.toCPointer<UInt64Var>(), 0u)
}

actual override fun manualDeallocate(futexPrt: Long) {
val cPointer = futexPrt.toCPointer<UInt64Var>() ?: throw IllegalStateException("Could not create C Pointer from futex ref")
nativeHeap.free(cPointer)
}

private const val UL_COMPARE_AND_WAIT: UInt32 = 1u
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package kotlinx.atomicfu.locks

import kotlinx.cinterop.ptr

import kotlinx.cinterop.*
import platform.posix.*
import kotlin.concurrent.Volatile
import kotlin.concurrent.*

public actual class NativeMutexNode {

Expand All @@ -22,7 +24,7 @@ public actual class NativeMutexNode {
pthread_mutex_unlock(pMutex.ptr)
}

actual fun unlock() {
actual fun unlock() {
pthread_mutex_lock(pMutex.ptr)
isLocked = false
pthread_cond_broadcast(pCond.ptr)
Expand Down
20 changes: 20 additions & 0 deletions atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/locks/Mutex.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package kotlinx.atomicfu.locks

/**
* Multiplatform mutex.
* On native based on futex(-like) system calls.
* On JVM delegates to ReentrantLock.
*/
expect class Mutex {
fun isLocked(): Boolean
fun tryLock(): Boolean
fun lock()
fun unlock()
}

fun <T> Mutex.withLock(block: () -> T): T {
lock()
val result = block()
unlock()
return result
}
bbrockbernd marked this conversation as resolved.
Show resolved Hide resolved
Loading