Skip to content

Commit

Permalink
Shutdown coroutines dispatchers after each module pass (#2325)
Browse files Browse the repository at this point in the history
* Update kotlinx.coroutines to 1.6.0

* Shutdown common coroutines dispatchers after each module pass

* Don't finalize coroutines in unit tests

Co-authored-by: Mikhail Zarechenskiy <mikhail.zarechenskiy@jetbrains.com>
  • Loading branch information
IgnatBeresnev and zarechenskiy committed Feb 14, 2022
1 parent 019cef4 commit a43e11e
Show file tree
Hide file tree
Showing 10 changed files with 40 additions and 31 deletions.
11 changes: 7 additions & 4 deletions core/api/core.api
Expand Up @@ -65,6 +65,7 @@ public abstract interface class org/jetbrains/dokka/DokkaConfiguration : java/io
public abstract fun getCacheRoot ()Ljava/io/File;
public abstract fun getDelayTemplateSubstitution ()Z
public abstract fun getFailOnWarning ()Z
public abstract fun getFinalizeCoroutines ()Z
public abstract fun getIncludes ()Ljava/util/Set;
public abstract fun getModuleName ()Ljava/lang/String;
public abstract fun getModuleVersion ()Ljava/lang/String;
Expand Down Expand Up @@ -168,14 +169,15 @@ public abstract interface class org/jetbrains/dokka/DokkaConfigurationBuilder {

public final class org/jetbrains/dokka/DokkaConfigurationImpl : org/jetbrains/dokka/DokkaConfiguration {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/io/File;Ljava/io/File;ZLjava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;ZZZLjava/util/Set;Z)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/io/File;Ljava/io/File;ZLjava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;ZZZLjava/util/Set;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/io/File;Ljava/io/File;ZLjava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;ZZZLjava/util/Set;ZZ)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/io/File;Ljava/io/File;ZLjava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;ZZZLjava/util/Set;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component10 ()Z
public final fun component11 ()Z
public final fun component12 ()Z
public final fun component13 ()Ljava/util/Set;
public final fun component14 ()Z
public final fun component15 ()Z
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Ljava/io/File;
public final fun component4 ()Ljava/io/File;
Expand All @@ -184,12 +186,13 @@ public final class org/jetbrains/dokka/DokkaConfigurationImpl : org/jetbrains/do
public final fun component7 ()Ljava/util/List;
public final fun component8 ()Ljava/util/List;
public final fun component9 ()Ljava/util/List;
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/io/File;Ljava/io/File;ZLjava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;ZZZLjava/util/Set;Z)Lorg/jetbrains/dokka/DokkaConfigurationImpl;
public static synthetic fun copy$default (Lorg/jetbrains/dokka/DokkaConfigurationImpl;Ljava/lang/String;Ljava/lang/String;Ljava/io/File;Ljava/io/File;ZLjava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;ZZZLjava/util/Set;ZILjava/lang/Object;)Lorg/jetbrains/dokka/DokkaConfigurationImpl;
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/io/File;Ljava/io/File;ZLjava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;ZZZLjava/util/Set;ZZ)Lorg/jetbrains/dokka/DokkaConfigurationImpl;
public static synthetic fun copy$default (Lorg/jetbrains/dokka/DokkaConfigurationImpl;Ljava/lang/String;Ljava/lang/String;Ljava/io/File;Ljava/io/File;ZLjava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;ZZZLjava/util/Set;ZZILjava/lang/Object;)Lorg/jetbrains/dokka/DokkaConfigurationImpl;
public fun equals (Ljava/lang/Object;)Z
public fun getCacheRoot ()Ljava/io/File;
public fun getDelayTemplateSubstitution ()Z
public fun getFailOnWarning ()Z
public fun getFinalizeCoroutines ()Z
public fun getIncludes ()Ljava/util/Set;
public fun getModuleName ()Ljava/lang/String;
public fun getModuleVersion ()Ljava/lang/String;
Expand Down
27 changes: 4 additions & 23 deletions core/src/main/kotlin/DokkaGenerator.kt
Expand Up @@ -2,15 +2,12 @@

package org.jetbrains.dokka

import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.scheduling.ExperimentalCoroutineDispatcher
import org.jetbrains.dokka.generation.GracefulGenerationExit
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.DokkaPlugin
import org.jetbrains.dokka.utilities.DokkaLogger
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible

/**
* DokkaGenerator is the main entry point for generating documentation
Expand Down Expand Up @@ -45,28 +42,12 @@ class DokkaGenerator(
additionalPlugins: List<DokkaPlugin> = emptyList()
) = DokkaContext.create(configuration, logger, additionalPlugins)

@OptIn(DelicateCoroutinesApi::class)
private fun finalizeCoroutines() {
runCatching {
Dispatchers.Default.closeExecutor()

Dispatchers.IO.let { dispatcher ->
dispatcher::class.memberProperties.find {
it.name == "dispatcher"
}?.also {
it.isAccessible = true
}?.call(dispatcher)
}?.closeExecutor()
if (configuration.finalizeCoroutines) {
Dispatchers.shutdown()
}
}

@OptIn(InternalCoroutinesApi::class)
private fun Any.closeExecutor() = (this as ExperimentalCoroutineDispatcher).also {
it.executor::class.members
.find { it.name == "close" }
?.also {
it.isAccessible = true
}?.call(it.executor)
}
}

class Timer internal constructor(startTime: Long, private val logger: DokkaLogger?) {
Expand Down
19 changes: 19 additions & 0 deletions core/src/main/kotlin/configuration.kt
Expand Up @@ -134,6 +134,25 @@ interface DokkaConfiguration : Serializable {
val includes: Set<File>
val suppressInheritedMembers: Boolean

/**
* Whether coroutines dispatchers should be shutdown after
* generating documentation via [DokkaGenerator.generate].
*
* It effectively stops all background threads associated with
* coroutines in order to make classes unloadable by the JVM,
* and rejects all new tasks with [RejectedExecutionException]
*
* This is primarily useful for multi-module builds where coroutines
* can be shut down after each module's partial task to avoid
* possible memory leaks.
*
* However, this can lead to problems in specific lifecycles where
* coroutines are shared and will be reused after documentation generation,
* and closing it down will leave the build in an inoperable state.
* One such example is unit tests, for which finalization should be disabled.
*/
val finalizeCoroutines: Boolean

enum class SerializationFormat : Serializable {
JSON, XML
}
Expand Down
1 change: 1 addition & 0 deletions core/src/main/kotlin/defaultConfiguration.kt
Expand Up @@ -19,6 +19,7 @@ data class DokkaConfigurationImpl(
override val suppressObviousFunctions: Boolean = DokkaDefaults.suppressObviousFunctions,
override val includes: Set<File> = emptySet(),
override val suppressInheritedMembers: Boolean = DokkaDefaults.suppressInheritedMembers,
override val finalizeCoroutines: Boolean = true,
) : DokkaConfiguration

data class PluginConfigurationImpl(
Expand Down
Expand Up @@ -54,7 +54,8 @@ class TestDokkaConfigurationBuilder {
suppressObviousFunctions = suppressObviousFunctions,
includes = includes.toSet(),
suppressInheritedMembers = suppressInheritedMembers,
delayTemplateSubstitution = delayTemplateSubstitution
delayTemplateSubstitution = delayTemplateSubstitution,
finalizeCoroutines = false
)

fun sourceSets(block: SourceSetsBuilder.() -> Unit) {
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Expand Up @@ -4,7 +4,7 @@ dokka_publication_channels=maven-central-snapshot&space-dokka-dev
dokka_integration_test_parallelism=2
# Versions
kotlin_version=1.6.10
coroutines_version=1.5.1
coroutines_version=1.6.0
kotlinx_html_version=0.7.3
kotlin_plugin_version=211-1.6.10-release-923-IJ7442.40
jsoup_version=1.13.1
Expand Down
Expand Up @@ -43,7 +43,8 @@ abstract class HtmlRenderingOnlyTestBase : RenderingOnlyTestBase<Element>() {
val files = TestOutputWriter()

open val configuration = DokkaConfigurationImpl(
sourceSets = listOf(js, jvm, native)
sourceSets = listOf(js, jvm, native),
finalizeCoroutines = false
)

override val context = MockContext(
Expand Down
Expand Up @@ -21,7 +21,7 @@ abstract class GfmRenderingOnlyTestBase : RenderingOnlyTestBase<String>() {
DokkaBase().externalLocationProviderFactory to ::DefaultExternalLocationProviderFactory,
GfmPlugin().gfmPreprocessors to { RootCreator },

testConfiguration = DokkaConfigurationImpl(moduleName = "root")
testConfiguration = DokkaConfigurationImpl(moduleName = "root", finalizeCoroutines = false)
)

override val renderedContent: String by lazy {
Expand Down
1 change: 1 addition & 0 deletions runners/cli/api/cli.api
Expand Up @@ -65,6 +65,7 @@ public final class org/jetbrains/dokka/GlobalArguments : org/jetbrains/dokka/Dok
public fun getCacheRoot ()Ljava/io/File;
public fun getDelayTemplateSubstitution ()Z
public fun getFailOnWarning ()Z
public fun getFinalizeCoroutines ()Z
public final fun getGlobalLinks ()Ljava/util/List;
public final fun getGlobalPackageOptions ()Ljava/util/List;
public final fun getGlobalSrcLink ()Ljava/util/List;
Expand Down
2 changes: 2 additions & 0 deletions runners/cli/src/main/kotlin/cli/main.kt
Expand Up @@ -88,6 +88,8 @@ class GlobalArguments(args: Array<String>) : DokkaConfiguration {
description = "Suppress members inherited from other classes"
).default(DokkaDefaults.suppressInheritedMembers)

override val finalizeCoroutines: Boolean = true

val globalPackageOptions by parser.option(
ArgType.String,
description = "List of package source sets in format \"prefix,-deprecated,-privateApi,+warnUndocumented,+suppress;...\" "
Expand Down

0 comments on commit a43e11e

Please sign in to comment.