diff --git a/python-multiplatform/src/nativeMain/kotlin/python/multiplatform/reflection/PlatformMutex.native.kt b/python-multiplatform/src/nativeMain/kotlin/python/multiplatform/reflection/PlatformMutex.native.kt index 96b8fa7c..293e8ccb 100644 --- a/python-multiplatform/src/nativeMain/kotlin/python/multiplatform/reflection/PlatformMutex.native.kt +++ b/python-multiplatform/src/nativeMain/kotlin/python/multiplatform/reflection/PlatformMutex.native.kt @@ -1,34 +1,29 @@ package python.multiplatform.reflection -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.alloc -import kotlinx.cinterop.nativeHeap -import kotlinx.cinterop.ptr -import platform.posix.pthread_mutex_init -import platform.posix.pthread_mutex_lock -import platform.posix.pthread_mutex_t -import platform.posix.pthread_mutex_unlock +import kotlin.concurrent.atomics.AtomicInt +import kotlin.concurrent.atomics.ExperimentalAtomicApi /** - * A default (non-recursive) POSIX mutex, the one primitive iOS and androidNative both provide. + * A non-recursive spin lock on one atomic word, for iOS and androidNative alike (#329). * - * The `pthread_mutex_t` lives in [nativeHeap] and is never freed: the only user is the - * [HandleTable] singleton, which lives for the life of the process, so there is nothing to - * release it for. + * Not a `pthread_mutex_t`: `pthread_mutex_init` is not part of the POSIX surface Kotlin/Native + * commonizes across Apple and Linux, so `nativeMain`'s shared metadata compile (the one publishing + * runs) could not resolve it even though every per-target compile did. A spin lock needs only the + * common atomics. That is enough here because [PlatformMutex]'s contract keeps every critical section + * to a few array and counter operations, never a call into Python, user code or a cleaner, so a + * waiter spins for a bounded, short time and a free-threaded stop-the-world pause cannot wait on it. */ -@OptIn(ExperimentalForeignApi::class) +@OptIn(ExperimentalAtomicApi::class) internal actual class PlatformMutex actual constructor() { - private val mutex = nativeHeap.alloc() - - init { - pthread_mutex_init(mutex.ptr, null) - } + private val held = AtomicInt(0) actual fun lock() { - pthread_mutex_lock(mutex.ptr) + while (!held.compareAndSet(0, 1)) { + // Spin: the holder is inside a section of a few array operations. + } } actual fun unlock() { - pthread_mutex_unlock(mutex.ptr) + held.store(0) } }