Repository: ktorio/ktor
File: ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationEngine.kt (Kotlin)

Code before:
        } else {
            EventLoopGroupProxy.create(configuration.callGroupSize)
        }
    }

    private val workerDispatcher by lazy {
        workerEventGroup.asCoroutineDispatcher()
    }

    private var cancellationJob: CompletableJob? = null

    private var channels: List<Channel>? = null
    internal val bootstraps: List<ServerBootstrap> by lazy {
        configuration.connectors.map(::createBootstrap)
    }

    private fun createBootstrap(connector: EngineConnectorConfig): ServerBootstrap {
        return customBootstrap.clone().apply {
            if (config().group() == null && config().childGroup() == null) {
                group(connectionEventGroup, workerEventGroup)
            }

            if (config().channelFactory() == null) {
                channel(getChannelClass().java)
            }

            val userContext = applicationProvider().coroutineContext +
                NettyApplicationCallHandler.CallHandlerCoroutineName +
                DefaultUncaughtExceptionHandler(environment.log)

            childHandler(
                NettyChannelInitializer(
                    applicationProvider,
                    pipeline,
                    environment,
                    callEventGroup,
                    workerDispatcher,
                    userContext,
                    connector,
                    configuration.runningLimit,
                    configuration.responseWriteTimeoutSeconds,
                    configuration.requestReadTimeoutSeconds,
                    configuration.httpServerCodec,
                    configuration.channelPipelineConfig,
                    configuration.enableHttp2
                )
            )
            if (configuration.tcpKeepAlive) {
                childOption(ChannelOption.SO_KEEPALIVE, true)
            }
        }

Review comment (suggestion):
This is a significant change. I know a lot of `embeddedServer` users provide a dispatcher explicitly, so I think we should check `parentCoroutineContext != EmptyCoroutineContext` before setting a different dispatcher.

Code after:
            EventLoopGroupProxy.create(configuration.callGroupSize)
        }
    }

    private val workerDispatcher by lazy {
        workerEventGroup.asCoroutineDispatcher()
    }

    private var cancellationJob: CompletableJob? = null

    private var channels: List<Channel>? = null
    internal val bootstraps: List<ServerBootstrap> by lazy {
        configuration.connectors.map(::createBootstrap)
    }

    private fun createBootstrap(connector: EngineConnectorConfig): ServerBootstrap {
        return customBootstrap.clone().apply {
            if (config().group() == null && config().childGroup() == null) {
                group(connectionEventGroup, workerEventGroup)
            }

            if (config().channelFactory() == null) {
                channel(getChannelClass().java)
            }

            val userContext = applicationProvider().coroutineContext +
                NettyApplicationCallHandler.CallHandlerCoroutineName +
                DefaultUncaughtExceptionHandler(environment.log)

            childHandler(
                NettyChannelInitializer(
                    applicationProvider,
                    pipeline,
                    environment,
                    callEventGroup,
                    workerDispatcher,
                    userContext,
                    connector,
                    configuration.runningLimit,
                    configuration.responseWriteTimeoutSeconds,
                    configuration.requestReadTimeoutSeconds,
                    configuration.httpServerCodec,
                    configuration.channelPipelineConfig,
                    configuration.enableHttp2
                )
            )
            if (configuration.tcpKeepAlive) {
                childOption(ChannelOption.SO_KEEPALIVE, true)
            }
        }
    }