ursus
07/01/2025, 10:42 PMsolonovamax
07/02/2025, 4:57 PMlaunch { }
instead of threads. and if spawning them on <http://Dispatchers.IO|Dispatchers.IO>
, then each consumer & producer will very likely have its own dedicated OS thread. this is because <http://Dispatchers.IO|Dispatchers.IO>
will either use 64 threads or the number of available processors, whichever is greater. (unless you override it with -<http://Dkotlinx.coroutines.io|Dkotlinx.coroutines.io>.parallelism=...
)
you probably want some kind of pool of bytebufs or smth like what netty uses, and then return the bytebuf to the pool at the end (see: PooledByteBufAllocator
, you can also use the "default bytebuf allocator" by using ByteBufAllocator.DEFAULT
). but once the bytebuf gets passed to the channel, and until it gets freed again, treat it as fully immutable.
when you're done with a bytebuf, you just call .release()
on it to return it back to the pool. you can also have netty warn you about any unreleased bytebufs. look at ResourceLeakDetector
for how to enable that.
also, I've personally found the shutdown to be quite finicky in fact, and cause issues where sometimes it shuts down when it shouldn't or doesn't start up things properly when it should when in developer mode & using
application.monitor.subscribe(ApplicationStopping) {
// ...
}
to shutdown an exposed database.
my workaround has just been to not even subscribe to that hook when in developer mode and not use a shutdown hook at all lol
there are two different ways you can listen for shutdowns in ktor:
• subscribing to the monitor I posted above
• add a shutdown hook (jvm only).
if you want to add a jvm shutdown hook, I have this small snippet of code that I generally copy around my different projects bc shutdown hooks are quite a common thing (consider it as dual-licensed under CC0 or the Unlicense. take your pick of whichever you prefer)
private val shutdownHooks: MutableList<() -> Unit> = Collections.synchronizedList(mutableListOf())
private lateinit var shutdownHook: Thread
fun onJvmShutdown(block: () -> Unit) {
shutdownHooks += block
if (!::shutdownHook.isInitialized) {
shutdownHook = ShutdownHook("JVM-Shutdown-Thread", block)
Runtime.addShutdownHook(shutdownHook)
}
}
private class ShutdownHook(name: String, private val stopFunction: () -> Unit) : Thread(name) {
private val shouldStop = AtomicBoolean(true)
override fun run() {
if (shouldStop.compareAndSet(true, false)) {
stopFunction()
}
Runtime.removeShutdownHook(this)
}
}
val Runtime: Runtime
get() = JvmRuntime.getRuntime()
I just usually throw it in a file named JVMUtil
or something.ursus
07/02/2025, 5:11 PMursus
07/02/2025, 5:12 PMursus
07/02/2025, 5:20 PMsolonovamax
07/02/2025, 5:22 PM-- but in general, won't I take unnecesary perf hit using coroutines for this? -- if I know I'll always use just 2 threads (1 producer, 1 consumer)I cannot say for sure, however my guess would be that no you wouldn't but, characterising the performance of things is extremely difficult and the best thing I can recommend is https://tryitands.ee the only real way to know if something like that will perform badly is to try both ways and benchmark them.
ursus
07/02/2025, 5:23 PMursus
07/02/2025, 5:24 PMsolonovamax
07/02/2025, 5:31 PMval channel = Channel(128 /* replace with appropriate buffer size */, BufferOverflow.DROP_OLDEST)
the advantage is that you get to use apis that feel a bit nicer to use in kotlin. sure, the native jvm apis might have a slight advantage in performance (but that would need to be tested of course, I can't say that for sure and kotlin's apis could easily be faster), but the kotlin apis are easier to use and imo easier to iterate with. so I'm willing to have the chance that I'm missing out on a bit of performance if it means I can use smth that's a bit easierursus
07/02/2025, 5:32 PMursus
07/02/2025, 5:33 PMsolonovamax
07/02/2025, 5:36 PMursus
07/02/2025, 5:40 PMursus
07/02/2025, 5:40 PMsolonovamax
07/02/2025, 5:40 PMursus
07/02/2025, 5:41 PMursus
07/02/2025, 5:42 PMsolonovamax
07/02/2025, 5:47 PMursus
07/02/2025, 5:48 PMapplication.monitor.subscribe(ApplicationStopping) {
breakLoop = true
}
so simply just this?solonovamax
07/02/2025, 5:50 PMursus
07/02/2025, 5:51 PMsolonovamax
07/02/2025, 5:54 PMThread.currentThread().interrupt();
and then make sure to catch InterruptedException
& check for Thread.currentThread().isInterrupted()
solonovamax
07/02/2025, 5:57 PMursus
07/02/2025, 6:44 PMursus
07/02/2025, 6:44 PM