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

Shutdown coroutines dispatchers after each module pass #2325

Merged
merged 3 commits into from Feb 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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