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-6030 Migrate Ktor to kotlinx-io #4032

Merged
merged 37 commits into from
Jul 15, 2024
Merged

KTOR-6030 Migrate Ktor to kotlinx-io #4032

merged 37 commits into from
Jul 15, 2024

Conversation

e5l
Copy link
Member

@e5l e5l commented May 15, 2024

Fix KTOR-6030 Migrate to new kotlinx.io library

@e5l e5l requested review from bjhham, marychatte and Stexxe May 15, 2024 08:59
@e5l e5l self-assigned this May 15, 2024
@e5l e5l changed the title Migrate Ktor to kotlinx-io KTOR-6030 Migrate Ktor to kotlinx-io May 15, 2024
@e5l e5l marked this pull request as ready for review May 17, 2024 08:16
Copy link
Contributor

@bjhham bjhham left a comment

Choose a reason for hiding this comment

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

A glorious amount of deleted code!

Seems that compilation is broken for common code in the CI. I also can't seem to build on my machine, I might need some instruction if there is any config changes or missing gradle configs.

@@ -138,6 +138,7 @@ internal actual class ConnectionPipeline actual constructor(
if (shouldClose) break
}
} finally {
@Suppress("DEPRECATION")
Copy link
Contributor

Choose a reason for hiding this comment

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

Are these close deprecation warnings something we should follow up on? Maybe create a task in YT? The recommendation seems to be using either flushAndClose or cancel.

@@ -214,7 +216,7 @@ class HttpTimeoutTest : ClientLoader() {
parameter("delay", 500)
}.body<ByteReadChannel>()

assertFailsWith<HttpRequestTimeoutException> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should consider wrapping this once again with the HttpRequestTimeoutException... Since we don't have checked exceptions, this could cause some tough-to-diagnose issues for consumers that rely on the current timeout exceptions.

Comment on lines 9 to 22
@Suppress("UnusedReceiverParameter", "UNUSED_PARAMETER")
public fun ByteChannel.attachJob(job: Job) {
job.invokeOnCompletion {
if (it != null) {
cancel(it)
}
}
}

public fun ByteChannel.attachJob(job: ChannelJob) {
attachJob(job.job)
}

public fun ByteChannel(@Suppress("UNUSED_PARAMETER") block: (Throwable?) -> Throwable?): ByteChannel = ByteChannel()
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like these functions need some documentation.

Copy link
Member Author

Choose a reason for hiding this comment

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

thanks, will add


internal class SourceByteReadChannel(private val source: Source) : ByteReadChannel {

val created = Exception()
Copy link
Contributor

Choose a reason for hiding this comment

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

Another created exception property?

Copy link
Member Author

Choose a reason for hiding this comment

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

thanks, will fix

this[index + 1] = (value shr 16).toByte()
this[index + 2] = (value shr 8).toByte()
this[index + 3] = value.toByte()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be cool if kotlinx.io provided these kinds of random-access extensions for ByteArray too since they're all implemented for Buffer.

Copy link
Contributor

Choose a reason for hiding this comment

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

as far as I see, it's used only in WebSocket implementation and can be rather easily replaced with operations on kotlinx-io primitives.
IMO, there is no need to have such functions exposed nor in ktor, nor in kotlinx-io, as more high level primitives should be used.
Additionally, there will be something like this for unsafe access described in Kotlin/kotlinx-io#135 (comment)

}

/**
* Resume waiter.
*/
fun resume() {
suspension.getAndSet(null)?.complete()
val continuation = suspension.getAndUpdate {
if (it == CLOSED) CLOSED else null
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be it as? ClosedSlot so that we include the cause / new instance from line 44 val closeContinuation = if (cause != null) ClosedSlot(cause) else CLOSED.

ktor-utils/jvm/src/io/ktor/util/cio/FileChannels.kt Outdated Show resolved Hide resolved
@e5l
Copy link
Member Author

e5l commented May 27, 2024

Hey @bjhham, thank you for the review. Indeed looks like compilation is broken since rebase. I'll check it and address the comments!

Copy link
Contributor

@whyoleg whyoleg left a comment

Choose a reason for hiding this comment

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

This is really a big step forward for the ecosystem!

Mostly skimmed though public declarations

/*
* 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.core

public expect interface Closeable {
Copy link
Contributor

Choose a reason for hiding this comment

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

should it be replaced with AutoCloseable (with a deprecated typealias, same for use function) as it's stable in Kotlin 2.0?

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, will replace this with 2.0.0 bump

}
@Deprecated(
"Use transferTo instead",
ReplaceWith("output.transferTo(this)", "kotlinx.io.transferTo"),
Copy link
Contributor

Choose a reason for hiding this comment

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

replace with is not the same as implementation transferFrom vs transferTo

this[index + 1] = (value shr 16).toByte()
this[index + 2] = (value shr 8).toByte()
this[index + 3] = value.toByte()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

as far as I see, it's used only in WebSocket implementation and can be rather easily replaced with operations on kotlinx-io primitives.
IMO, there is no need to have such functions exposed nor in ktor, nor in kotlinx-io, as more high level primitives should be used.
Additionally, there will be something like this for unsafe access described in Kotlin/kotlinx-io#135 (comment)


package io.ktor.utils.io.errors

public typealias IOException = kotlinx.io.IOException
Copy link
Contributor

Choose a reason for hiding this comment

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

may be those should be deprecated, so users will migrate to kotlinx.io.* types?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you, good catch! Will add

private const val DEFAULT_POOL_ARRAY_SIZE = 4096
private const val DEFAULT_POOL_CAPACITY = 128

public val ByteArrayPool: ObjectPool<ByteArray> = object : DefaultPool<ByteArray>(DEFAULT_POOL_CAPACITY) {
Copy link
Contributor

Choose a reason for hiding this comment

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

can it be deprecated and replaced with kotlinx-io APIs (I mean, creating Buffer and using it)

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure, it can be. Let me check

Copy link
Member Author

Choose a reason for hiding this comment

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

It needs a huge rewrite in the other ktor modules. I prefer to keep it in the separate PR for stability

Copy link
Contributor

Choose a reason for hiding this comment

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

Better to create YT issue and link it 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.

Sure, will add

ktor-io/js/src/io/ktor/utils/io/charsets/TextEncoder.js.kt Outdated Show resolved Hide resolved
@bjhham
Copy link
Contributor

bjhham commented May 28, 2024

Looks like ktor-benchmarks is missing the required repository for the snapshot of kotlinx-io?

14:56:11     Execution failed for task ':compileKotlin'.
14:56:11     > Could not resolve all files for configuration ':compileClasspath'.
14:56:11        > Could not find org.jetbrains.kotlinx:kotlinx-io-core:0.3.2-SNAPSHOT.
14:56:11          Searched in the following locations:
14:56:11            - file:/home/teamcity/.m2/repository/org/jetbrains/kotlinx/kotlinx-io-core/0.3.2-SNAPSHOT/maven-metadata.xml
14:56:11            - file:/home/teamcity/.m2/repository/org/jetbrains/kotlinx/kotlinx-io-core/0.3.2-SNAPSHOT/kotlinx-io-core-0.3.2-SNAPSHOT.pom
14:56:11            - https://repo.maven.apache.org/maven2/org/jetbrains/kotlinx/kotlinx-io-core/0.3.2-SNAPSHOT/maven-metadata.xml
14:56:11            - https://repo.maven.apache.org/maven2/org/jetbrains/kotlinx/kotlinx-io-core/0.3.2-SNAPSHOT/kotlinx-io-core-0.3.2-SNAPSHOT.pom
14:56:11            - https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev/org/jetbrains/kotlinx/kotlinx-io-core/0.3.2-SNAPSHOT/maven-metadata.xml
14:56:11            - https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev/org/jetbrains/kotlinx/kotlinx-io-core/0.3.2-SNAPSHOT/kotlinx-io-core-0.3.2-SNAPSHOT.pom
14:56:11            - https://maven.pkg.jetbrains.space/public/p/ktor/eap/org/jetbrains/kotlinx/kotlinx-io-core/0.3.2-SNAPSHOT/maven-metadata.xml
14:56:11            - https://maven.pkg.jetbrains.space/public/p/ktor/eap/org/jetbrains/kotlinx/kotlinx-io-core/0.3.2-SNAPSHOT/kotlinx-io-core-0.3.2-SNAPSHOT.pom
14:56:11          Required by:
14:56:11              project : > io.ktor:ktor-server-core:1.0.0-BENCHMARKS > io.ktor:ktor-server-core-jvm:1.0.0-BENCHMARKS > io.ktor:ktor-utils:1.0.0-BENCHMARKS > io.ktor:ktor-utils-jvm:1.0.0-BENCHMARKS > io.ktor:ktor-io:1.0.0-BENCHMARKS > io.ktor:ktor-io-jvm:1.0.0-BENCHMARKS

Copy link
Contributor

@bjhham bjhham left a comment

Choose a reason for hiding this comment

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

Looks like she's good to go, pending the kotlinx-io release.

/**
* Sequential (non-concurrent) byte channel implementation
*/
public class ByteChannel(public val autoFlush: Boolean = false) : ByteReadChannel, BufferedByteWriteChannel {
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like we can drop this argument here.

public class ByteChannel(public val autoFlush: Boolean = false) : ByteReadChannel, BufferedByteWriteChannel {
private val _closedCause = atomic<CloseToken?>(null)
private val slot = AwaitingSlot()
private val flushBuffer: Buffer = Buffer()
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the intent with this class to eventually drop the three buffers and flush orchestration in favour of just using a single buffer with some synchronization? Seems like we could just have a single bounded buffer that blocks writes until there's space.

Copy link
Member Author

@e5l e5l Jun 13, 2024

Choose a reason for hiding this comment

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

The problem is that not all operations writing this buffer are suspend, so you need actually block the thread

Copy link
Member Author

Choose a reason for hiding this comment

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

Anyway let's discuss this idea

@mgroth0
Copy link

mgroth0 commented Jun 28, 2024

Great work. Looking forward to this integration!

@e5l e5l force-pushed the e5l/kotlinx-io-2 branch 2 times, most recently from bb406bf to 93b6a55 Compare July 11, 2024 09:27
Comment on lines +65 to +85
val buffer = Buffer()
try {
DefaultDatagramChunkBufferPool.useInstance { buffer ->
element.packet.copy().readAvailable(buffer)

val bytes = element.packet.copy().readBytes()
val bytesWritten = sento(element, bytes)

result = when (bytesWritten) {
0 -> throw IOException("Failed writing to closed socket")
-1 -> {
if (errno == EAGAIN) {
false
} else {
throw PosixException.forErrno()
}
element.packet.copy().readAvailable(buffer)

val bytes = element.packet.copy().readBytes()
val bytesWritten = sento(element, bytes)

result = when (bytesWritten) {
0 -> throw kotlinx.io.IOException("Failed writing to closed socket")
-1 -> {
if (errno == EAGAIN) {
false
} else {
throw PosixException.forErrno()
}
else -> true
}

else -> true
}
} finally {
buffer.close()
Copy link
Contributor

@Thomas-Vos Thomas-Vos Jul 11, 2024

Choose a reason for hiding this comment

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

While trying to fix another issue I noticed that buffer may not actually be used. The packet is copied into buffer, but then never read. I guess the buffer can just be removed, right? Or use the buffer properly instead of readBytes.

@e5l e5l merged commit 40042dc into main Jul 15, 2024
10 of 17 checks passed
@e5l e5l deleted the e5l/kotlinx-io-2 branch July 15, 2024 08:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants