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

KTOR-7729 Add logging for tracking concurrent access in the dev mode #4477

Merged
merged 2 commits into from
Nov 14, 2024
Merged
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
34 changes: 32 additions & 2 deletions ktor-io/common/src/io/ktor/utils/io/ByteChannel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import kotlin.concurrent.Volatile
import kotlin.coroutines.*
import kotlin.jvm.*

internal expect val DEVELOPMENT_MODE: Boolean
internal const val CHANNEL_MAX_SIZE: Int = 1024 * 1024

/**
Expand Down Expand Up @@ -189,13 +190,16 @@ public class ByteChannel(public val autoFlush: Boolean = false) : ByteReadChanne
// Resume the previous task
when (previous) {
is TaskType ->
previous.resume(ConcurrentIOException(slot.taskName()))
previous.resume(ConcurrentIOException(slot.taskName(), previous.created))

is Slot.Task ->
previous.resume()

is Slot.Closed -> {
slot.resume(previous.cause)
return
}

Slot.Empty -> {}
}

Expand All @@ -219,6 +223,8 @@ public class ByteChannel(public val autoFlush: Boolean = false) : ByteReadChanne
data class Closed(val cause: Throwable?) : Slot

sealed interface Task : Slot {
val created: Throwable?

val continuation: Continuation<Unit>

fun taskName(): String
Expand All @@ -231,10 +237,30 @@ public class ByteChannel(public val autoFlush: Boolean = false) : ByteReadChanne
}

class Read(override val continuation: Continuation<Unit>) : Task {
override var created: Throwable? = null

init {
if (DEVELOPMENT_MODE) {
created = Throwable("ReadTask 0x${continuation.hashCode().toString(16)}").also {
Copy link
Contributor

Choose a reason for hiding this comment

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

It'd be nice if we appended some diagnostic info from the coroutine context here too, since sometimes the stacktraces can be hard to follow as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

we don't have it in the init block, let me check what we can do

it.stackTraceToString()
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this also { it.stackTraceToString() } doing anything here?

Copy link
Member Author

Choose a reason for hiding this comment

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

yep, by default the stacktrace is not filled

}
}
}

override fun taskName(): String = "read"
}

class Write(override val continuation: Continuation<Unit>) : Task {
override var created: Throwable? = null

init {
if (DEVELOPMENT_MODE) {
created = Throwable("WriteTask 0x${continuation.hashCode().toString(16)}").also {
it.stackTraceToString()
}
}
}

override fun taskName(): String = "write"
}
}
Expand All @@ -243,4 +269,8 @@ public class ByteChannel(public val autoFlush: Boolean = false) : ByteReadChanne
/**
* Thrown when a coroutine awaiting I/O is replaced by another.
*/
public class ConcurrentIOException(taskName: String) : IllegalStateException("Concurrent $taskName attempts")
public class ConcurrentIOException(
taskName: String,
cause: Throwable? = null
) : IllegalStateException("Concurrent $taskName attempts", cause) {
Comment on lines +272 to +275
Copy link
Member

Choose a reason for hiding this comment

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

Should we provide a @Deprecated(HIDDEN) constructor with an old signature for binary compatibility purpose?

Copy link
Member Author

Choose a reason for hiding this comment

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

yep, let me add

}
10 changes: 10 additions & 0 deletions ktor-io/jvm/src/io/ktor/utils/io/ByteChannel.jvm.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package io.ktor.utils.io

private const val DEVELOPMENT_MODE_KEY: String = "io.ktor.development"

internal actual val DEVELOPMENT_MODE: Boolean
get() = System.getProperty(DEVELOPMENT_MODE_KEY)?.toBoolean() == true
8 changes: 8 additions & 0 deletions ktor-io/posix/src/io/ktor/utils/io/ByteChannel.posix.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package io.ktor.utils.io

internal actual val DEVELOPMENT_MODE: Boolean
get() = false