diff --git a/okio/src/commonMain/kotlin/okio/CommonPlatform.kt b/okio/src/commonMain/kotlin/okio/CommonPlatform.kt index e073d0b342..308fd1396b 100644 --- a/okio/src/commonMain/kotlin/okio/CommonPlatform.kt +++ b/okio/src/commonMain/kotlin/okio/CommonPlatform.kt @@ -31,6 +31,7 @@ expect class Lock expect inline fun Lock.withLock(action: () -> T): T internal expect fun newLock(): Lock +internal expect inline fun Lock.destroy() expect open class IOException(message: String?, cause: Throwable?) : Exception { constructor(message: String?) diff --git a/okio/src/commonMain/kotlin/okio/FileHandle.kt b/okio/src/commonMain/kotlin/okio/FileHandle.kt index d05c3c9939..385487f263 100644 --- a/okio/src/commonMain/kotlin/okio/FileHandle.kt +++ b/okio/src/commonMain/kotlin/okio/FileHandle.kt @@ -289,6 +289,7 @@ abstract class FileHandle( closed = true if (openStreamCount != 0) return } + lock.destroy() protectedClose() } diff --git a/okio/src/jsMain/kotlin/okio/JsPlatform.kt b/okio/src/jsMain/kotlin/okio/JsPlatform.kt index 04350d157a..6205c8009f 100644 --- a/okio/src/jsMain/kotlin/okio/JsPlatform.kt +++ b/okio/src/jsMain/kotlin/okio/JsPlatform.kt @@ -15,6 +15,9 @@ */ package okio +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + /* * This file exposes Node.js `os` and `path` APIs to Kotlin/JS, using reasonable default behaviors * if those symbols aren't available. @@ -50,3 +53,16 @@ private val path: dynamic internal val tmpdir: String get() = os?.tmpdir() as? String ?: "/tmp" + +actual typealias Lock = Unit + +internal actual fun newLock(): Lock = Unit +internal actual inline fun Lock.destroy() = Unit + +actual inline fun Lock.withLock(action: () -> T): T { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + } + + return action() +} diff --git a/okio/src/jvmMain/kotlin/okio/-JvmPlatform.kt b/okio/src/jvmMain/kotlin/okio/-JvmPlatform.kt index a95b242538..431f81cbaa 100644 --- a/okio/src/jvmMain/kotlin/okio/-JvmPlatform.kt +++ b/okio/src/jvmMain/kotlin/okio/-JvmPlatform.kt @@ -31,6 +31,7 @@ actual typealias ArrayIndexOutOfBoundsException = java.lang.ArrayIndexOutOfBound actual typealias Lock = ReentrantLock internal actual fun newLock(): Lock = ReentrantLock() +internal actual inline fun Lock.destroy() = Unit actual inline fun Lock.withLock(action: () -> T): T { contract { diff --git a/okio/src/mingwX64Main/kotlin/okio/WindowsPlatform.kt b/okio/src/mingwX64Main/kotlin/okio/WindowsPlatform.kt new file mode 100644 index 0000000000..945461cf94 --- /dev/null +++ b/okio/src/mingwX64Main/kotlin/okio/WindowsPlatform.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2026 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package okio + +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import platform.windows.CloseHandle +import platform.windows.CreateMutexA +import platform.windows.INFINITE +import platform.windows.ReleaseMutex +import platform.windows.WaitForSingleObject + +actual class Lock : Closeable { + val mutex = CreateMutexA( + null, + 0, + null + ) ?: throw lastErrorToIOException() + + override fun close() { + CloseHandle(mutex) + } +} + +internal actual fun newLock(): Lock = Lock() +internal actual inline fun Lock.destroy() = close() + +actual inline fun Lock.withLock(action: () -> T): T { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + } + + try { + WaitForSingleObject(mutex, INFINITE) + return action() + } finally { + ReleaseMutex(mutex) + } +} diff --git a/okio/src/nonJvmMain/kotlin/okio/NonJvmPlatform.kt b/okio/src/nonJvmMain/kotlin/okio/NonJvmPlatform.kt index ffe9253f4c..50b229b71b 100644 --- a/okio/src/nonJvmMain/kotlin/okio/NonJvmPlatform.kt +++ b/okio/src/nonJvmMain/kotlin/okio/NonJvmPlatform.kt @@ -16,8 +16,6 @@ package okio -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract import okio.internal.commonAsUtf8ToByteArray import okio.internal.commonToUtf8String @@ -31,22 +29,6 @@ actual open class ArrayIndexOutOfBoundsException actual constructor( message: String?, ) : IndexOutOfBoundsException(message) -actual class Lock { - companion object { - val instance = Lock() - } -} - -internal actual fun newLock(): Lock = Lock.instance - -actual inline fun Lock.withLock(action: () -> T): T { - contract { - callsInPlace(action, InvocationKind.EXACTLY_ONCE) - } - - return action() -} - actual open class IOException actual constructor( message: String?, cause: Throwable?, diff --git a/okio/src/unixMain/kotlin/okio/UnixPlatform.kt b/okio/src/unixMain/kotlin/okio/UnixPlatform.kt new file mode 100644 index 0000000000..4bc3ddfca4 --- /dev/null +++ b/okio/src/unixMain/kotlin/okio/UnixPlatform.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2026 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package okio + +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlinx.cinterop.alloc +import kotlinx.cinterop.free +import kotlinx.cinterop.nativeHeap +import kotlinx.cinterop.ptr +import platform.posix.errno +import platform.posix.pthread_mutex_destroy +import platform.posix.pthread_mutex_init +import platform.posix.pthread_mutex_lock +import platform.posix.pthread_mutex_t +import platform.posix.pthread_mutex_unlock + +actual class Lock : Closeable { + val mutex = nativeHeap.alloc().apply { + if (pthread_mutex_init(ptr, null) != 0) { + throw errnoToIOException(errno) + } + } + + override fun close() { + pthread_mutex_destroy(mutex.ptr) + nativeHeap.free(mutex) + } +} + +internal actual fun newLock(): Lock = Lock() +internal actual inline fun Lock.destroy() = close() + +actual inline fun Lock.withLock(action: () -> T): T { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + } + + try { + pthread_mutex_lock(mutex.ptr) + return action() + } finally { + pthread_mutex_unlock(mutex.ptr) + } +} diff --git a/okio/src/wasmMain/kotlin/okio/WasmPlatform.kt b/okio/src/wasmMain/kotlin/okio/WasmPlatform.kt index 4a874ec826..fa54b701b9 100644 --- a/okio/src/wasmMain/kotlin/okio/WasmPlatform.kt +++ b/okio/src/wasmMain/kotlin/okio/WasmPlatform.kt @@ -15,5 +15,23 @@ */ package okio +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + internal actual val PLATFORM_DIRECTORY_SEPARATOR: String get() = "/" + +actual typealias Lock = Unit + +internal actual fun newLock(): Lock = Unit +internal actual inline fun Lock.destroy() = Unit + +@OptIn(ExperimentalContracts::class) +actual inline fun Lock.withLock(action: () -> T): T { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + } + + return action() +}