Skip to content

Thread parking for Kotlin/Common #498

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

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
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
8 changes: 8 additions & 0 deletions atomicfu/api/atomicfu.api
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,11 @@ public final class kotlinx/atomicfu/TraceKt {
public static final fun named (Lkotlinx/atomicfu/TraceBase;Ljava/lang/String;)Lkotlinx/atomicfu/TraceBase;
}

public final class kotlinx/atomicfu/locks/ParkingSupport {
public static final field INSTANCE Lkotlinx/atomicfu/locks/ParkingSupport;
public final fun currentThreadHandle ()Ljava/lang/Thread;
public final fun park-LRDsOJo (J)V
public final fun parkUntil (Lkotlin/time/TimeMark;)V
public final fun unpark (Ljava/lang/Thread;)V
}

158 changes: 55 additions & 103 deletions atomicfu/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,35 @@ plugins {

kotlin {
jvmToolchain(8)

// 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()

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

// JS -- always
js(IR) {
Expand All @@ -34,6 +63,31 @@ kotlin {
nodejs()
}

@OptIn(ExperimentalKotlinGradlePluginApi::class)
applyDefaultHierarchyTemplate {
common {
group("jsAndWasmShared") {
withJs()
withWasmJs()
withWasmWasi()
}
group("concurrent") {
withJvm()
group("native") {
group("nativeUnixLike") {
group("apple") {
withApple()
}
group("linux") {
withLinux()
}
}
withMingwX64()
}
}
}
}

sourceSets {
commonMain.dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib") {
Expand All @@ -46,13 +100,8 @@ kotlin {
implementation("org.jetbrains.kotlin:kotlin-test-common")
implementation("org.jetbrains.kotlin:kotlin-test-annotations-common")
}

val jsAndWasmSharedMain by creating {
dependsOn(commonMain.get())
}


jsMain {
dependsOn(jsAndWasmSharedMain)
dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-dom-api-compat")
}
Expand All @@ -64,21 +113,12 @@ kotlin {
}
}

wasmJsMain {
dependsOn(jsAndWasmSharedMain)
}

wasmJsTest {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-test-wasm-js")
}
}


wasmWasiMain {
dependsOn(jsAndWasmSharedMain)
}

wasmWasiTest {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-test-wasm-wasi")
Expand All @@ -94,93 +134,6 @@ 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()

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

@OptIn(ExperimentalKotlinGradlePluginApi::class)
applyDefaultHierarchyTemplate {
group("nativeUnixLike") {
withLinux()
}
group("androidNative32Bit") {
withAndroidNativeX86()
withCompilations { compilation ->
(compilation.target as? KotlinNativeTarget)?.konanTarget?.name == "android_arm32"
}
}
group("androidNative64Bit") {
withAndroidNativeArm64()
withAndroidNativeX64()
}
}

sourceSets {
val nativeNonAppleMain by creating {
kotlin.srcDir("src/nativeNonAppleMain/kotlin")
dependsOn(nativeMain.get())
}

val nativeUnixLikeMain by getting {
kotlin.srcDir("src/nativeUnixLikeMain/kotlin")
dependsOn(nativeNonAppleMain)
}

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

val androidNative64BitMain by getting {
kotlin.srcDir("src/androidNative64BitMain/kotlin")
dependsOn(nativeNonAppleMain)
}

val mingwMain by getting {
kotlin.srcDir("src/mingwMain/kotlin")
dependsOn(nativeNonAppleMain)
}

val androidNative32BitTest by getting {
kotlin.srcDir("src/androidNative32BitTest/kotlin")
dependsOn(nativeTest.get())
}

val androidNative64BitTest by getting {
kotlin.srcDir("src/androidNative64BitTest/kotlin")
dependsOn(nativeTest.get())
}

}

// atomicfu-cinterop-interop.klib with an empty interop.def file will still be published for compatibility reasons (see KT-68411)
// This block can be removed when this issue in K/N compiler is resolved: KT-60874
Expand Down Expand Up @@ -387,4 +340,3 @@ val jvmTest by tasks.getting(Test::class) {
)
// run them only for transformed code
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
*/
package kotlinx.atomicfu.locks

import kotlinx.atomicfu.atomic
import kotlinx.cinterop.Arena
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.UnsafeNumber
import kotlinx.cinterop.alloc
import kotlinx.cinterop.ptr
import platform.posix.*

@OptIn(ExperimentalForeignApi::class)
@OptIn(ExperimentalForeignApi::class, UnsafeNumber::class)
actual class NativeMutexNode {
actual var next: NativeMutexNode? = null

Expand Down Expand Up @@ -49,3 +51,7 @@ actual class NativeMutexNode {
arena.clear()
}
}

private val threadCounter = atomic(0L)

actual fun createThreadId(): Long = threadCounter.incrementAndGet()
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package kotlinx.atomicfu.locks

import kotlinx.cinterop.*
import platform.posix.*

@OptIn(ExperimentalForeignApi::class, UnsafeNumber::class)
internal actual object ParkingDelegator {
actual fun createRef(): ParkingData {
val mut = nativeHeap.alloc<pthread_mutex_t>().ptr
val cond = nativeHeap.alloc<pthread_cond_t>().ptr
val attr = nativeHeap.alloc<pthread_condattr_tVar>().ptr

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this is irritatingly close to pthread_condattr_t, which is used by the other Unix-like platforms, but after several attempts, I did not manage to find an incantation that would allow avoiding the code duplication for androidNativeMain.

callAndVerify { pthread_mutex_init(mut, null) }
callAndVerify { pthread_condattr_init(attr) }
callAndVerify { pthread_condattr_setclock(attr, CLOCK_MONOTONIC) }
callAndVerify { pthread_cond_init(cond, attr) }

callAndVerify { pthread_condattr_destroy(attr) }
nativeHeap.free(attr)
return ParkingData(mut, cond)
}

actual inline fun wait(ref: ParkingData, shouldWait: () -> Boolean) {
callAndVerify { pthread_mutex_lock(ref.mut) }
try {
if (shouldWait()) callAndVerify { pthread_cond_wait(ref.cond, ref.mut) }
} finally {
callAndVerify { pthread_mutex_unlock(ref.mut) }
}
}

actual inline fun timedWait(ref: ParkingData, nanos: Long, shouldWait: () -> Boolean): Unit = memScoped {
val ts = alloc<timespec>().ptr

// Add nanos to current time
callAndVerify { clock_gettime(CLOCK_MONOTONIC.convert(), ts) }
ts.pointed.tv_sec = ts.pointed.tv_sec.addNanosToSeconds(nanos)
ts.pointed.tv_nsec = (ts.pointed.tv_nsec + nanos % 1_000_000_000).convert()
//Fix overflow
if (ts.pointed.tv_nsec >= 1_000_000_000) {
ts.pointed.tv_sec = ts.pointed.tv_sec.addNanosToSeconds(1_000_000_000)
ts.pointed.tv_nsec -= 1_000_000_000
}
callAndVerify { pthread_mutex_lock(ref.mut) }
try {
if (shouldWait()) callAndVerify(0, ETIMEDOUT) { pthread_cond_timedwait(ref.cond, ref.mut, ts) }
} finally {
callAndVerify { pthread_mutex_unlock(ref.mut) }
}
}

actual fun wake(ref: ParkingData) {
callAndVerify { pthread_mutex_lock(ref.mut) }
try {
callAndVerify { pthread_cond_signal(ref.cond) }
} finally {
callAndVerify { pthread_mutex_unlock(ref.mut) }
}
}

actual fun destroyRef(ref: ParkingData) {
callAndVerify { pthread_mutex_destroy(ref.mut) }
callAndVerify { pthread_cond_destroy(ref.cond) }
nativeHeap.free(ref.mut)
nativeHeap.free(ref.cond)
}
}

internal actual class ParkingData @OptIn(UnsafeNumber::class) constructor(
val mut: CPointer<pthread_mutex_t>,
val cond: CPointer<pthread_cond_t>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package kotlinx.atomicfu.locks

import kotlinx.cinterop.CPointer
import platform.posix.CLOCK_REALTIME
import platform.posix.*

// CLOCK_REALTIME should be equal to CLOCK_SYSTEM on darwin.
// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/mach/clock_types.h#L70-L73
// Where CLOCK_CALENDAR is the time from epoch.
internal actual val posixGetTimeClockId: Int
get() = CLOCK_REALTIME

actual fun pthreadCondAttrSetClock(attr: CPointer<pthread_condattr_t>): Int = 0
Loading