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

Loom support. #1176

Merged
merged 17 commits into from Jan 5, 2023
6 changes: 5 additions & 1 deletion okio/src/commonMain/kotlin/okio/-CommonPlatform.kt
Expand Up @@ -23,7 +23,11 @@ internal expect fun String.asUtf8ToByteArray(): ByteArray
// TODO make internal https://youtrack.jetbrains.com/issue/KT-37316
expect class ArrayIndexOutOfBoundsException(message: String?) : IndexOutOfBoundsException

internal expect inline fun <R> synchronized(lock: Any, block: () -> R): R
expect class ALock
yschimke marked this conversation as resolved.
Show resolved Hide resolved

internal expect fun newLock(): ALock

internal expect inline fun <R> synchronized(lock: ALock, block: () -> R): R
yschimke marked this conversation as resolved.
Show resolved Hide resolved

expect open class IOException(message: String?, cause: Throwable?) : Exception {
constructor(message: String? = null)
Expand Down
26 changes: 14 additions & 12 deletions okio/src/commonMain/kotlin/okio/FileHandle.kt
Expand Up @@ -52,6 +52,8 @@ abstract class FileHandle(
*/
private var openStreamCount = 0

val aLock: ALock = newLock()

/**
* Reads at least 1, and up to [byteCount] bytes from this starting at [fileOffset] and copies
* them to [array] at [arrayOffset]. Returns the number of bytes read, or -1 if [fileOffset]
Expand All @@ -64,7 +66,7 @@ abstract class FileHandle(
arrayOffset: Int,
byteCount: Int
): Int {
synchronized(this) {
synchronized(aLock) {
yschimke marked this conversation as resolved.
Show resolved Hide resolved
check(!closed) { "closed" }
}
return protectedRead(fileOffset, array, arrayOffset, byteCount)
Expand All @@ -76,7 +78,7 @@ abstract class FileHandle(
*/
@Throws(IOException::class)
fun read(fileOffset: Long, sink: Buffer, byteCount: Long): Long {
synchronized(this) {
synchronized(aLock) {
check(!closed) { "closed" }
}
return readNoCloseCheck(fileOffset, sink, byteCount)
Expand All @@ -87,7 +89,7 @@ abstract class FileHandle(
*/
@Throws(IOException::class)
fun size(): Long {
synchronized(this) {
synchronized(aLock) {
check(!closed) { "closed" }
}
return protectedSize()
Expand All @@ -100,7 +102,7 @@ abstract class FileHandle(
@Throws(IOException::class)
fun resize(size: Long) {
check(readWrite) { "file handle is read-only" }
synchronized(this) {
synchronized(aLock) {
check(!closed) { "closed" }
}
return protectedResize(size)
Expand All @@ -114,7 +116,7 @@ abstract class FileHandle(
byteCount: Int
) {
check(readWrite) { "file handle is read-only" }
synchronized(this) {
synchronized(aLock) {
check(!closed) { "closed" }
}
return protectedWrite(fileOffset, array, arrayOffset, byteCount)
Expand All @@ -124,7 +126,7 @@ abstract class FileHandle(
@Throws(IOException::class)
fun write(fileOffset: Long, source: Buffer, byteCount: Long) {
check(readWrite) { "file handle is read-only" }
synchronized(this) {
synchronized(aLock) {
check(!closed) { "closed" }
}
writeNoCloseCheck(fileOffset, source, byteCount)
Expand All @@ -134,7 +136,7 @@ abstract class FileHandle(
@Throws(IOException::class)
fun flush() {
check(readWrite) { "file handle is read-only" }
synchronized(this) {
synchronized(aLock) {
check(!closed) { "closed" }
}
return protectedFlush()
Expand All @@ -146,7 +148,7 @@ abstract class FileHandle(
*/
@Throws(IOException::class)
fun source(fileOffset: Long = 0L): Source {
synchronized(this) {
synchronized(aLock) {
check(!closed) { "closed" }
openStreamCount++
}
Expand Down Expand Up @@ -216,7 +218,7 @@ abstract class FileHandle(
@Throws(IOException::class)
fun sink(fileOffset: Long = 0L): Sink {
check(readWrite) { "file handle is read-only" }
synchronized(this) {
synchronized(aLock) {
check(!closed) { "closed" }
openStreamCount++
}
Expand Down Expand Up @@ -282,7 +284,7 @@ abstract class FileHandle(

@Throws(IOException::class)
final override fun close() {
synchronized(this) {
synchronized(aLock) {
if (closed) return@close
closed = true
if (openStreamCount != 0) return@close
Expand Down Expand Up @@ -405,7 +407,7 @@ abstract class FileHandle(
override fun close() {
if (closed) return
closed = true
synchronized(fileHandle) {
synchronized(fileHandle.aLock) {
fileHandle.openStreamCount--
if (fileHandle.openStreamCount != 0 || !fileHandle.closed) return@close
}
Expand All @@ -431,7 +433,7 @@ abstract class FileHandle(
override fun close() {
if (closed) return
closed = true
synchronized(fileHandle) {
synchronized(fileHandle.aLock) {
fileHandle.openStreamCount--
if (fileHandle.openStreamCount != 0 || !fileHandle.closed) return@close
}
Expand Down
11 changes: 8 additions & 3 deletions okio/src/jvmMain/kotlin/okio/-JvmPlatform.kt
Expand Up @@ -16,16 +16,21 @@

package okio

import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock

internal actual fun ByteArray.toUtf8String(): String = String(this, Charsets.UTF_8)

internal actual fun String.asUtf8ToByteArray(): ByteArray = toByteArray(Charsets.UTF_8)

// TODO remove if https://youtrack.jetbrains.com/issue/KT-20641 provides a better solution
actual typealias ArrayIndexOutOfBoundsException = java.lang.ArrayIndexOutOfBoundsException

internal actual inline fun <R> synchronized(lock: Any, block: () -> R): R {
return kotlin.synchronized(lock, block)
}
actual typealias ALock = ReentrantLock

internal actual fun newLock(): ALock = ReentrantLock()

internal actual inline fun <R> synchronized(lock: ALock, block: () -> R): R = lock.withLock(block)

actual typealias IOException = java.io.IOException

Expand Down
22 changes: 12 additions & 10 deletions okio/src/jvmMain/kotlin/okio/AsyncTimeout.kt
Expand Up @@ -18,6 +18,9 @@ package okio
import java.io.IOException
import java.io.InterruptedIOException
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.Condition
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock

/**
* This timeout uses a background thread to take action exactly when the timeout occurs. Use this to
Expand Down Expand Up @@ -179,7 +182,7 @@ open class AsyncTimeout : Timeout() {
while (true) {
try {
var timedOut: AsyncTimeout? = null
synchronized(AsyncTimeout::class.java) {
AsyncTimeout.lock.withLock {
timedOut = awaitTimeout()

// The queue is completely empty. Let this thread exit and let another watchdog thread
Expand All @@ -199,6 +202,9 @@ open class AsyncTimeout : Timeout() {
}

companion object {
val lock: ReentrantLock = ReentrantLock()
val condition: Condition = lock.newCondition()

/**
* Don't write more than 64 KiB of data at a time, give or take a segment. Otherwise slow
* connections may suffer timeouts even when they're making (slow) progress. Without this,
Expand All @@ -221,7 +227,7 @@ open class AsyncTimeout : Timeout() {
private var head: AsyncTimeout? = null

private fun scheduleTimeout(node: AsyncTimeout, timeoutNanos: Long, hasDeadline: Boolean) {
synchronized(AsyncTimeout::class.java) {
AsyncTimeout.lock.withLock {
check(!node.inQueue) { "Unbalanced enter/exit" }
node.inQueue = true

Expand Down Expand Up @@ -253,7 +259,7 @@ open class AsyncTimeout : Timeout() {
prev.next = node
if (prev === head) {
// Wake up the watchdog when inserting at the front.
(AsyncTimeout::class.java as Object).notify()
condition.signal()
Copy link
Member

Choose a reason for hiding this comment

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

Nice

}
break
}
Expand All @@ -264,7 +270,7 @@ open class AsyncTimeout : Timeout() {

/** Returns true if the timeout occurred. */
private fun cancelScheduledTimeout(node: AsyncTimeout): Boolean {
synchronized(AsyncTimeout::class.java) {
AsyncTimeout.lock.withLock {
if (!node.inQueue) return false
node.inQueue = false

Expand Down Expand Up @@ -299,7 +305,7 @@ open class AsyncTimeout : Timeout() {
// The queue is empty. Wait until either something is enqueued or the idle timeout elapses.
if (node == null) {
val startNanos = System.nanoTime()
(AsyncTimeout::class.java as Object).wait(IDLE_TIMEOUT_MILLIS)
condition.await(IDLE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
return if (head!!.next == null && System.nanoTime() - startNanos >= IDLE_TIMEOUT_NANOS) {
head // The idle timeout elapsed.
} else {
Expand All @@ -311,11 +317,7 @@ open class AsyncTimeout : Timeout() {

// The head of the queue hasn't timed out yet. Await that.
if (waitNanos > 0) {
// Waiting is made complicated by the fact that we work in nanoseconds,
// but the API wants (millis, nanos) in two arguments.
val waitMillis = waitNanos / 1000000L
waitNanos -= waitMillis * 1000000L
(AsyncTimeout::class.java as Object).wait(waitMillis, waitNanos.toInt())
condition.await(waitNanos, TimeUnit.NANOSECONDS)
return null
}

Expand Down
1 change: 1 addition & 0 deletions okio/src/jvmMain/kotlin/okio/JvmFileHandle.kt
Expand Up @@ -21,6 +21,7 @@ internal class JvmFileHandle(
readWrite: Boolean,
private val randomAccessFile: RandomAccessFile
) : FileHandle(readWrite) {

@Synchronized
override fun protectedResize(size: Long) {
val currentSize = size()
Expand Down