From edc388c26e6d2e4c0189347aa3dbacedc9ecb0ce Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Mon, 3 Feb 2020 14:19:25 +0300 Subject: [PATCH 01/15] BlockHound integration POC --- build.gradle | 2 + kotlinx-coroutines-debug/build.gradle | 2 + .../src/internal/BlockHoundIntegration.kt | 43 ++++++++++ .../src/internal/DebugProbesImpl.kt | 4 + .../test/BlockHoundTest.kt | 79 +++++++++++++++++++ 5 files changed, 130 insertions(+) create mode 100644 kotlinx-coroutines-debug/src/internal/BlockHoundIntegration.kt create mode 100644 kotlinx-coroutines-debug/test/BlockHoundTest.kt diff --git a/build.gradle b/build.gradle index 87aac2a1e0..791b2bd298 100644 --- a/build.gradle +++ b/build.gradle @@ -60,6 +60,7 @@ buildscript { repositories { jcenter() maven { url "https://kotlin.bintray.com/kotlinx" } + maven { url "https://repo.spring.io/snapshot/" } maven { url "https://kotlin.bintray.com/kotlin-dev" credentials { @@ -153,6 +154,7 @@ allprojects { jcenter() maven { url "https://kotlin.bintray.com/kotlin-dev" + url "https://repo.spring.io/snapshot/" credentials { username = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') ?: "" password = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') ?: "" diff --git a/kotlinx-coroutines-debug/build.gradle b/kotlinx-coroutines-debug/build.gradle index 7fc2e22369..65203a2028 100644 --- a/kotlinx-coroutines-debug/build.gradle +++ b/kotlinx-coroutines-debug/build.gradle @@ -22,6 +22,8 @@ dependencies { compileOnly "junit:junit:$junit_version" shadowDeps "net.bytebuddy:byte-buddy:$byte_buddy_version" shadowDeps "net.bytebuddy:byte-buddy-agent:$byte_buddy_version" + compile 'io.projectreactor.tools:blockhound:1.0.1.BUILD-SNAPSHOT' + compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" } jar { diff --git a/kotlinx-coroutines-debug/src/internal/BlockHoundIntegration.kt b/kotlinx-coroutines-debug/src/internal/BlockHoundIntegration.kt new file mode 100644 index 0000000000..fae35cd4a2 --- /dev/null +++ b/kotlinx-coroutines-debug/src/internal/BlockHoundIntegration.kt @@ -0,0 +1,43 @@ +package kotlinx.coroutines.debug.internal + +import reactor.blockhound.BlockHound +import kotlin.reflect.KClass +import kotlin.reflect.full.* + +internal object BlockHoundIntegration { + + init { + val cls = Class.forName("kotlinx.coroutines.scheduling.CoroutineScheduler\$Worker").kotlin + initializerHelper(cls) + } + + private fun initializerHelper(cls: KClass) { + val field = cls.declaredMemberProperties.find { it.name == "state" }!! + BlockHound.builder() + .addDynamicThreadPredicate(cls::isInstance) + .nonBlockingThreadPredicate { p -> + p.or { thread -> + val castThread = cls.safeCast(thread) + if (!enabled || castThread == null) { + false + } else { + val state = field(castThread) as Enum<*> + state.name == "CPU_ACQUIRED" + } + } + } + .install() + } + + @Volatile + private var enabled = false + + fun install() { + enabled = true + } + + fun uninstall() { + enabled = false + } + +} diff --git a/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt b/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt index 090d3e5d89..27bdc83a2a 100644 --- a/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt +++ b/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt @@ -66,6 +66,8 @@ internal object DebugProbesImpl { .name(cl.name) .make() .load(cl.classLoader, ClassReloadingStrategy.fromInstalledAgent()) + + BlockHoundIntegration.install() } public fun uninstall(): Unit = coroutineStateLock.write { @@ -82,6 +84,8 @@ internal object DebugProbesImpl { .name(cl.name) .make() .load(cl.classLoader, ClassReloadingStrategy.fromInstalledAgent()) + + BlockHoundIntegration.uninstall() } public fun hierarchyToString(job: Job): String = coroutineStateLock.write { diff --git a/kotlinx-coroutines-debug/test/BlockHoundTest.kt b/kotlinx-coroutines-debug/test/BlockHoundTest.kt new file mode 100644 index 0000000000..107bccea93 --- /dev/null +++ b/kotlinx-coroutines-debug/test/BlockHoundTest.kt @@ -0,0 +1,79 @@ +package kotlinx.coroutines.debug +import kotlinx.coroutines.* +import kotlinx.coroutines.debug.internal.BlockHoundIntegration +import org.junit.* +import reactor.blockhound.BlockingOperationError + +class BlockHoundTest : TestBase() { + + @Before + fun init() { + BlockHoundIntegration.install() + } + + @After + fun deinit() { + BlockHoundIntegration.uninstall() + } + + @Test(expected = BlockingOperationError::class) + fun shouldDetectBlockingInDefault() = runTest { + withContext(Dispatchers.Default) { + Thread.sleep(1) + } + } + + @Test + fun shouldNotDetectBlockingInIO() = runTest { + withContext(Dispatchers.IO) { + Thread.sleep(1) + } + } + + @Test + fun shouldNotDetectNonblocking() = runTest { + withContext(Dispatchers.Default) { + val a = 1 + val b = 2 + assert(a + b == 3) + } + } + + @Test + fun testReusingThreads() = runTest { + val n = 100 + repeat(n) { + async(Dispatchers.IO) { + Thread.sleep(1) + } + } + repeat(n) { + async(Dispatchers.Default) { + } + } + repeat(n) { + async(Dispatchers.IO) { + Thread.sleep(1) + } + } + } + + @Test(expected = BlockingOperationError::class) + fun testReusingThreadsFailure() = runTest { + val n = 100 + repeat(n) { + async(Dispatchers.IO) { + Thread.sleep(1) + } + } + async(Dispatchers.Default) { + Thread.sleep(1) + } + repeat(n) { + async(Dispatchers.IO) { + Thread.sleep(1) + } + } + } + +} From ef33b348d82854e42b3782881306a0dc89c80ebf Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Thu, 6 Feb 2020 12:36:21 +0300 Subject: [PATCH 02/15] Do not shadow ByteBuddy agent This way, it can work even with BlockHound on JDK8, which also uses ByteBuddy and thus was in conflict. Kind of solves https://github.com/Kotlin/kotlinx.coroutines/issues/1060, but since now the debugging routine depends on BlockHound, where, it seems, the same problem was not fixed, the original cause for concern probably still stands. --- gradle.properties | 2 +- kotlinx-coroutines-debug/build.gradle | 4 +++- kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index e9ebae8041..eaefcf8bcc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ knit_version=0.1.3 html_version=0.6.8 lincheck_version=2.5.3 dokka_version=0.9.16-rdev-2-mpp-hacks -byte_buddy_version=1.9.3 +byte_buddy_version=1.10.7 reactor_vesion=3.2.5.RELEASE reactive_streams_version=1.0.2 rxjava2_version=2.2.8 diff --git a/kotlinx-coroutines-debug/build.gradle b/kotlinx-coroutines-debug/build.gradle index 65203a2028..08aa834fbe 100644 --- a/kotlinx-coroutines-debug/build.gradle +++ b/kotlinx-coroutines-debug/build.gradle @@ -21,9 +21,11 @@ configurations { dependencies { compileOnly "junit:junit:$junit_version" shadowDeps "net.bytebuddy:byte-buddy:$byte_buddy_version" - shadowDeps "net.bytebuddy:byte-buddy-agent:$byte_buddy_version" + compile "net.bytebuddy:byte-buddy-agent:$byte_buddy_version" compile 'io.projectreactor.tools:blockhound:1.0.1.BUILD-SNAPSHOT' compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" + runtime "net.java.dev.jna:jna:5.5.0" + runtime "net.java.dev.jna:jna-platform:5.5.0" } jar { diff --git a/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt b/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt index 27bdc83a2a..270016f895 100644 --- a/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt +++ b/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt @@ -57,7 +57,7 @@ internal object DebugProbesImpl { public fun install(): Unit = coroutineStateLock.write { if (++installations > 1) return - ByteBuddyAgent.install() + ByteBuddyAgent.install(ByteBuddyAgent.AttachmentProvider.ForEmulatedAttachment.INSTANCE) val cl = Class.forName("kotlin.coroutines.jvm.internal.DebugProbesKt") val cl2 = Class.forName("kotlinx.coroutines.debug.DebugProbesKt") From 69f76dc332b40ca20e61636967558505b0b81d2b Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Thu, 6 Feb 2020 12:40:41 +0300 Subject: [PATCH 03/15] Fix tests not being run with the right dispatcher Integration with BlockHound revealed that several tests were performing blocking operations such as Thread.sleep in coroutine context `Dispatchers.DEFAULT`. Also, some tests hanged because the exception that notified about blocking calls were wildcard-matched. --- kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt | 13 ++++++++----- .../test/RunningThreadStackMergeTest.kt | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt b/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt index 91bd4f287d..8507721e30 100644 --- a/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt +++ b/kotlinx-coroutines-debug/test/CoroutinesDumpTest.kt @@ -39,7 +39,7 @@ class CoroutinesDumpTest : DebugTestBase() { @Test fun testRunningCoroutine() = runBlocking { - val deferred = async(Dispatchers.Default) { + val deferred = async(Dispatchers.IO) { activeMethod(shouldSuspend = false) assertTrue(true) } @@ -70,7 +70,7 @@ class CoroutinesDumpTest : DebugTestBase() { @Test fun testRunningCoroutineWithSuspensionPoint() = runBlocking { - val deferred = async(Dispatchers.Default) { + val deferred = async(Dispatchers.IO) { activeMethod(shouldSuspend = true) yield() // tail-call } @@ -100,7 +100,7 @@ class CoroutinesDumpTest : DebugTestBase() { @Test fun testCreationStackTrace() = runBlocking { - val deferred = async(Dispatchers.Default) { + val deferred = async(Dispatchers.IO) { activeMethod(shouldSuspend = true) } @@ -129,7 +129,7 @@ class CoroutinesDumpTest : DebugTestBase() { @Test fun testFinishedCoroutineRemoved() = runBlocking { - val deferred = async(Dispatchers.Default) { + val deferred = async(Dispatchers.IO) { activeMethod(shouldSuspend = true) } @@ -149,7 +149,10 @@ class CoroutinesDumpTest : DebugTestBase() { if (shouldSuspend) yield() notifyCoroutineStarted() while (coroutineContext[Job]!!.isActive) { - runCatching { Thread.sleep(60_000) } + try { + Thread.sleep(60_000) + } catch (_ : InterruptedException) { + } } } diff --git a/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt b/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt index c0b7f50134..85aa657be4 100644 --- a/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt +++ b/kotlinx-coroutines-debug/test/RunningThreadStackMergeTest.kt @@ -133,7 +133,7 @@ class RunningThreadStackMergeTest : DebugTestBase() { } private fun CoroutineScope.launchEscapingCoroutineWithoutContext() { - launch(Dispatchers.Default) { + launch(Dispatchers.IO) { suspendingFunctionWithoutContext() assertTrue(true) } From 46bbf2aa6b89df43281d32b118559352453a39d9 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Thu, 6 Feb 2020 15:59:08 +0300 Subject: [PATCH 04/15] Clean the stack traces from BlockHound artifacts --- .../test/StracktraceUtils.kt | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/kotlinx-coroutines-debug/test/StracktraceUtils.kt b/kotlinx-coroutines-debug/test/StracktraceUtils.kt index 12a39c0041..75181f1552 100644 --- a/kotlinx-coroutines-debug/test/StracktraceUtils.kt +++ b/kotlinx-coroutines-debug/test/StracktraceUtils.kt @@ -62,6 +62,31 @@ public fun verifyDump(vararg traces: String, ignoredCoroutine: String? = null, f } } +/** Clean the stacktraces from artifacts of BlockHound instrumentation + * + * BlockHound works by switching a native call by a class generated with ByteBuddy, which, if the blocking + * call is allowed in this context, in turn calls the real native call that is now available under a + * different name. + * + * The traces thus undergo the following two changes when the execution is instrumented: + * - The original native call is replaced with a non-native one with the same FQN, and + * - An additional native call is placed on top of the stack, with the original name that also has + * `$$BlockHound$$_` prepended at the last component. + */ +private fun cleanBlockHoundTraces(frames: List): List { + var result = mutableListOf() + val blockHoundSubstr = "\$\$BlockHound\$\$_" + var i = 0 + while (i < frames.size) { + result.add(frames[i].replace(blockHoundSubstr, "")) + if (frames[i].contains(blockHoundSubstr)) { + i += 1 + } + i += 1 + } + return result +} + public fun verifyDump(vararg traces: String, ignoredCoroutine: String? = null) { val baos = ByteArrayOutputStream() DebugProbes.dumpCoroutines(PrintStream(baos)) @@ -85,7 +110,7 @@ public fun verifyDump(vararg traces: String, ignoredCoroutine: String? = null) { expected.withIndex().forEach { (index, trace) -> val actualTrace = actual[index].trimStackTrace().sanitizeAddresses() val expectedTrace = trace.trimStackTrace().sanitizeAddresses() - val actualLines = actualTrace.split("\n") + val actualLines = cleanBlockHoundTraces(actualTrace.split("\n")) val expectedLines = expectedTrace.split("\n") for (i in expectedLines.indices) { assertEquals(expectedLines[i], actualLines[i]) From d1e47985ff61ad9b73d02cf2322d50fb3312de0c Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Tue, 18 Feb 2020 18:41:04 +0300 Subject: [PATCH 05/15] Intermediate value --- .../jvm/src/scheduling/CoroutineScheduler.kt | 9 +++++++ .../src/internal/BlockHoundIntegration.kt | 26 ++++--------------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt b/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt index 815fa26941..838bbba520 100644 --- a/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt +++ b/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt @@ -951,3 +951,12 @@ internal class CoroutineScheduler( TERMINATED } } + +@Suppress("UNUSED") +@JvmName("isSchedulerWorker") +internal fun isSchedulerWorker(thread: Thread) = thread is CoroutineScheduler.Worker + +@Suppress("UNUSED") +@JvmName("mayNotBlock") +internal fun mayNotBlock(thread: Thread) = thread is CoroutineScheduler.Worker && + thread.state == CoroutineScheduler.WorkerState.CPU_ACQUIRED diff --git a/kotlinx-coroutines-debug/src/internal/BlockHoundIntegration.kt b/kotlinx-coroutines-debug/src/internal/BlockHoundIntegration.kt index fae35cd4a2..4735bf4a3d 100644 --- a/kotlinx-coroutines-debug/src/internal/BlockHoundIntegration.kt +++ b/kotlinx-coroutines-debug/src/internal/BlockHoundIntegration.kt @@ -1,32 +1,16 @@ +@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") package kotlinx.coroutines.debug.internal import reactor.blockhound.BlockHound -import kotlin.reflect.KClass -import kotlin.reflect.full.* +import kotlinx.coroutines.scheduling.* internal object BlockHoundIntegration { init { - val cls = Class.forName("kotlinx.coroutines.scheduling.CoroutineScheduler\$Worker").kotlin - initializerHelper(cls) - } - - private fun initializerHelper(cls: KClass) { - val field = cls.declaredMemberProperties.find { it.name == "state" }!! BlockHound.builder() - .addDynamicThreadPredicate(cls::isInstance) - .nonBlockingThreadPredicate { p -> - p.or { thread -> - val castThread = cls.safeCast(thread) - if (!enabled || castThread == null) { - false - } else { - val state = field(castThread) as Enum<*> - state.name == "CPU_ACQUIRED" - } - } - } - .install() + .addDynamicThreadPredicate { isSchedulerWorker(it) } + .nonBlockingThreadPredicate { p -> p.or { thread -> enabled && mayNotBlock(thread) } } + .install() } @Volatile From 5058081623ee3c087fe31a5cbe76b14df462f34d Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Wed, 19 Feb 2020 14:12:45 +0300 Subject: [PATCH 06/15] Improve handling of stacktrace tests on JDK 8 Before, the tests only knew that the `park` native method was moved to `jdk.internal.misc.Unsafe` from `sun.misc.Unsafe`. However, in JDK 11, there is no `sun.misc.Unsafe` at all, it seems that it has been moved completely. This change is beneficial for this feature set because BlockHound puts its `$$BlockHound$$_park` method to `sun.misc.Unsafe` or to `jdk.internal.misc.Unsafe` depending on the JDK version. Also, judging by BlockHound's code https://github.com/reactor/BlockHound/blob/091d7b139479b1c41eea59baa23389d673fdf73b/agent/src/main/java/reactor/blockhound/BlockHound.java#L177-L187, `forkAndExec` had been moved, too, but it is not used in tests. --- kotlinx-coroutines-debug/test/StracktraceUtils.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlinx-coroutines-debug/test/StracktraceUtils.kt b/kotlinx-coroutines-debug/test/StracktraceUtils.kt index 75181f1552..8c591ebd44 100644 --- a/kotlinx-coroutines-debug/test/StracktraceUtils.kt +++ b/kotlinx-coroutines-debug/test/StracktraceUtils.kt @@ -13,7 +13,7 @@ public fun String.trimStackTrace(): String = .replace(Regex("#[0-9]+"), "") .replace(Regex("(?<=\tat )[^\n]*/"), "") .replace(Regex("\t"), "") - .replace("sun.misc.Unsafe.park", "jdk.internal.misc.Unsafe.park") // JDK8->JDK11 + .replace("sun.misc.Unsafe.", "jdk.internal.misc.Unsafe.") // JDK8->JDK11 .applyBackspace() public fun String.applyBackspace(): String { From efb4720f87e8d3eebb06935871d130fb78097ada Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Wed, 19 Feb 2020 15:04:07 +0300 Subject: [PATCH 07/15] Implement BlockHound integration properly Now it relies on the ServiceLoader mechanism to load the integration: if the user installs BlockHound and `kotlinx-coroutines-debug` is in the classpath, then BlockHound will know to detect blocking calls in scenarios that forbid it. --- .../api/kotlinx-coroutines-debug.api | 5 ++++ ...ockhound.integration.BlockHoundIntegration | 1 + .../src/CoroutinesBlockHoundIntegration.kt | 16 +++++++++++ .../src/internal/BlockHoundIntegration.kt | 27 ------------------- .../src/internal/DebugProbesImpl.kt | 4 --- .../test/BlockHoundTest.kt | 10 ++----- 6 files changed, 24 insertions(+), 39 deletions(-) create mode 100644 kotlinx-coroutines-debug/resources/META-INF/services/reactor.blockhound.integration.BlockHoundIntegration create mode 100644 kotlinx-coroutines-debug/src/CoroutinesBlockHoundIntegration.kt delete mode 100644 kotlinx-coroutines-debug/src/internal/BlockHoundIntegration.kt diff --git a/kotlinx-coroutines-debug/api/kotlinx-coroutines-debug.api b/kotlinx-coroutines-debug/api/kotlinx-coroutines-debug.api index 6061f03899..749c94619e 100644 --- a/kotlinx-coroutines-debug/api/kotlinx-coroutines-debug.api +++ b/kotlinx-coroutines-debug/api/kotlinx-coroutines-debug.api @@ -8,6 +8,11 @@ public final class kotlinx/coroutines/debug/CoroutineInfo { public fun toString ()Ljava/lang/String; } +public final class kotlinx/coroutines/debug/CoroutinesBlockHoundIntegration : reactor/blockhound/integration/BlockHoundIntegration { + public fun ()V + public fun applyTo (Lreactor/blockhound/BlockHound$Builder;)V +} + public final class kotlinx/coroutines/debug/DebugProbes { public static final field INSTANCE Lkotlinx/coroutines/debug/DebugProbes; public final fun dumpCoroutines (Ljava/io/PrintStream;)V diff --git a/kotlinx-coroutines-debug/resources/META-INF/services/reactor.blockhound.integration.BlockHoundIntegration b/kotlinx-coroutines-debug/resources/META-INF/services/reactor.blockhound.integration.BlockHoundIntegration new file mode 100644 index 0000000000..c2f1e9cf38 --- /dev/null +++ b/kotlinx-coroutines-debug/resources/META-INF/services/reactor.blockhound.integration.BlockHoundIntegration @@ -0,0 +1 @@ +kotlinx.coroutines.debug.CoroutinesBlockHoundIntegration \ No newline at end of file diff --git a/kotlinx-coroutines-debug/src/CoroutinesBlockHoundIntegration.kt b/kotlinx-coroutines-debug/src/CoroutinesBlockHoundIntegration.kt new file mode 100644 index 0000000000..f89d2be23f --- /dev/null +++ b/kotlinx-coroutines-debug/src/CoroutinesBlockHoundIntegration.kt @@ -0,0 +1,16 @@ +@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +package kotlinx.coroutines.debug + +import reactor.blockhound.BlockHound +import kotlinx.coroutines.scheduling.* +import reactor.blockhound.integration.* + +@Suppress("UNUSED") +public class CoroutinesBlockHoundIntegration: BlockHoundIntegration { + + override fun applyTo(builder: BlockHound.Builder) { + builder.addDynamicThreadPredicate { isSchedulerWorker(it) } + builder.nonBlockingThreadPredicate { p -> p.or { mayNotBlock(it) } } + } + +} diff --git a/kotlinx-coroutines-debug/src/internal/BlockHoundIntegration.kt b/kotlinx-coroutines-debug/src/internal/BlockHoundIntegration.kt deleted file mode 100644 index 4735bf4a3d..0000000000 --- a/kotlinx-coroutines-debug/src/internal/BlockHoundIntegration.kt +++ /dev/null @@ -1,27 +0,0 @@ -@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") -package kotlinx.coroutines.debug.internal - -import reactor.blockhound.BlockHound -import kotlinx.coroutines.scheduling.* - -internal object BlockHoundIntegration { - - init { - BlockHound.builder() - .addDynamicThreadPredicate { isSchedulerWorker(it) } - .nonBlockingThreadPredicate { p -> p.or { thread -> enabled && mayNotBlock(thread) } } - .install() - } - - @Volatile - private var enabled = false - - fun install() { - enabled = true - } - - fun uninstall() { - enabled = false - } - -} diff --git a/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt b/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt index 270016f895..8b7d8e7998 100644 --- a/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt +++ b/kotlinx-coroutines-debug/src/internal/DebugProbesImpl.kt @@ -66,8 +66,6 @@ internal object DebugProbesImpl { .name(cl.name) .make() .load(cl.classLoader, ClassReloadingStrategy.fromInstalledAgent()) - - BlockHoundIntegration.install() } public fun uninstall(): Unit = coroutineStateLock.write { @@ -84,8 +82,6 @@ internal object DebugProbesImpl { .name(cl.name) .make() .load(cl.classLoader, ClassReloadingStrategy.fromInstalledAgent()) - - BlockHoundIntegration.uninstall() } public fun hierarchyToString(job: Job): String = coroutineStateLock.write { diff --git a/kotlinx-coroutines-debug/test/BlockHoundTest.kt b/kotlinx-coroutines-debug/test/BlockHoundTest.kt index 107bccea93..ff5c95cdb1 100644 --- a/kotlinx-coroutines-debug/test/BlockHoundTest.kt +++ b/kotlinx-coroutines-debug/test/BlockHoundTest.kt @@ -1,19 +1,13 @@ package kotlinx.coroutines.debug import kotlinx.coroutines.* -import kotlinx.coroutines.debug.internal.BlockHoundIntegration import org.junit.* -import reactor.blockhound.BlockingOperationError +import reactor.blockhound.* class BlockHoundTest : TestBase() { @Before fun init() { - BlockHoundIntegration.install() - } - - @After - fun deinit() { - BlockHoundIntegration.uninstall() + BlockHound.install() } @Test(expected = BlockingOperationError::class) From 6e5fe9217833cb6b255dc95b8f63da36e836637e Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Wed, 19 Feb 2020 17:03:23 +0300 Subject: [PATCH 08/15] Up the BlockHound version The feature that we need was released, so no need to incclude the snapshot repositories in the build. --- build.gradle | 2 -- kotlinx-coroutines-debug/build.gradle | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 791b2bd298..87aac2a1e0 100644 --- a/build.gradle +++ b/build.gradle @@ -60,7 +60,6 @@ buildscript { repositories { jcenter() maven { url "https://kotlin.bintray.com/kotlinx" } - maven { url "https://repo.spring.io/snapshot/" } maven { url "https://kotlin.bintray.com/kotlin-dev" credentials { @@ -154,7 +153,6 @@ allprojects { jcenter() maven { url "https://kotlin.bintray.com/kotlin-dev" - url "https://repo.spring.io/snapshot/" credentials { username = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') ?: "" password = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') ?: "" diff --git a/kotlinx-coroutines-debug/build.gradle b/kotlinx-coroutines-debug/build.gradle index 08aa834fbe..017c8001d7 100644 --- a/kotlinx-coroutines-debug/build.gradle +++ b/kotlinx-coroutines-debug/build.gradle @@ -22,7 +22,7 @@ dependencies { compileOnly "junit:junit:$junit_version" shadowDeps "net.bytebuddy:byte-buddy:$byte_buddy_version" compile "net.bytebuddy:byte-buddy-agent:$byte_buddy_version" - compile 'io.projectreactor.tools:blockhound:1.0.1.BUILD-SNAPSHOT' + compile 'io.projectreactor.tools:blockhound:1.0.2.RELEASE' compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" runtime "net.java.dev.jna:jna:5.5.0" runtime "net.java.dev.jna:jna-platform:5.5.0" From 7fc4a901d72e774491a11ba244ea44b199c09570 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Fri, 13 Mar 2020 19:31:54 +0300 Subject: [PATCH 09/15] Fixes --- gradle.properties | 2 ++ kotlinx-coroutines-debug/build.gradle | 14 ++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/gradle.properties b/gradle.properties index eaefcf8bcc..eaf530679c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,6 +21,8 @@ rxjava2_version=2.2.8 javafx_version=11.0.2 javafx_plugin_version=0.0.8 binary_compatibility_validator_version=0.2.2 +blockhound_version=1.0.2.RELEASE +jna_version=5.5.0 # Android versions android_version=4.1.1.4 diff --git a/kotlinx-coroutines-debug/build.gradle b/kotlinx-coroutines-debug/build.gradle index 017c8001d7..e787f16a0f 100644 --- a/kotlinx-coroutines-debug/build.gradle +++ b/kotlinx-coroutines-debug/build.gradle @@ -21,11 +21,11 @@ configurations { dependencies { compileOnly "junit:junit:$junit_version" shadowDeps "net.bytebuddy:byte-buddy:$byte_buddy_version" - compile "net.bytebuddy:byte-buddy-agent:$byte_buddy_version" - compile 'io.projectreactor.tools:blockhound:1.0.2.RELEASE' - compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - runtime "net.java.dev.jna:jna:5.5.0" - runtime "net.java.dev.jna:jna-platform:5.5.0" + shadowDeps "net.bytebuddy:byte-buddy-agent:$byte_buddy_version" + compileOnly "io.projectreactor.tools:blockhound:$blockhound_version" + testCompile "io.projectreactor.tools:blockhound:$blockhound_version" + runtime "net.java.dev.jna:jna:$jna_version" + runtime "net.java.dev.jna:jna-platform:$jna_version" } jar { @@ -39,5 +39,7 @@ shadowJar { classifier null // Shadow only byte buddy, do not package kotlin stdlib configurations = [project.configurations.shadowDeps] - relocate 'net.bytebuddy', 'kotlinx.coroutines.repackaged.net.bytebuddy' + relocate('net.bytebuddy', 'kotlinx.coroutines.repackaged.net.bytebuddy') { + exclude 'net.bytebuddy.agent' + } } From 3fc6abc38a8d80f0e7be10c4d8fe0045128ed888 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Mon, 16 Mar 2020 13:08:31 +0300 Subject: [PATCH 10/15] Improve build configuration of integration tests * publication-validator is renamed to integration-testing; * Each test is now in a separate source set, which allows for more flexibility in their configuration; for example, failing to set `dryRun=true` doesn't prevent tests other than NPM to run, and it is possible to run the tests (and their dependencies) separately. --- build.gradle | 4 +- integration-testing/README.md | 13 ++++ integration-testing/build.gradle | 62 +++++++++++++++++++ .../kotlin}/MavenPublicationValidator.kt | 1 - .../kotlin}/NpmPublicationValidator.kt | 0 publication-validator/README.md | 13 ---- publication-validator/build.gradle | 37 ----------- settings.gradle | 2 +- 8 files changed, 78 insertions(+), 54 deletions(-) create mode 100644 integration-testing/README.md create mode 100644 integration-testing/build.gradle rename {publication-validator/src/test/kotlin/kotlinx/coroutines/tools => integration-testing/src/mavenTest/kotlin}/MavenPublicationValidator.kt (99%) rename {publication-validator/src/test/kotlin/kotlinx/coroutines/tools => integration-testing/src/npmTest/kotlin}/NpmPublicationValidator.kt (100%) delete mode 100644 publication-validator/README.md delete mode 100644 publication-validator/build.gradle diff --git a/build.gradle b/build.gradle index 87aac2a1e0..c679109ca3 100644 --- a/build.gradle +++ b/build.gradle @@ -8,8 +8,8 @@ apply from: rootProject.file("gradle/experimental.gradle") def rootModule = "kotlinx.coroutines" def coreModule = "kotlinx-coroutines-core" // Not applicable for Kotlin plugin -def sourceless = ['kotlinx.coroutines', 'site', 'kotlinx-coroutines-bom', 'publication-validator'] -def internal = ['kotlinx.coroutines', 'site', 'benchmarks', 'js-stub', 'stdlib-stubs', 'publication-validator'] +def sourceless = ['kotlinx.coroutines', 'site', 'kotlinx-coroutines-bom', 'integration-testing'] +def internal = ['kotlinx.coroutines', 'site', 'benchmarks', 'js-stub', 'stdlib-stubs', 'integration-testing'] // Not published def unpublished = internal + ['example-frontend-js', 'android-unit-tests'] diff --git a/integration-testing/README.md b/integration-testing/README.md new file mode 100644 index 0000000000..fedd9394a9 --- /dev/null +++ b/integration-testing/README.md @@ -0,0 +1,13 @@ +# Integration tests + +This is a supplementary subproject of kotlinx.coroutines that provides +integration tests. + +The tests are the following: +* `NpmPublicationValidator` tests that version of NPM artifact is correct and that it has neither source nor package dependencies on atomicfu + In order for the test to work, one needs to run gradle with `-PdryRun=true`. + `-PdryRun` affects `npmPublish` so that it only provides a packed publication + and does not in fact attempt to send the build for publication. +* `MavenPublicationValidator` depends on the published artifacts and tests artifacts binary content and absence of atomicfu in the classpath + +All the available tests can be run with `integration-testing:test`. diff --git a/integration-testing/build.gradle b/integration-testing/build.gradle new file mode 100644 index 0000000000..72fa585340 --- /dev/null +++ b/integration-testing/build.gradle @@ -0,0 +1,62 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +apply from: rootProject.file("gradle/compile-jvm.gradle") + +repositories { + mavenLocal() + mavenCentral() +} + +sourceSets { + npmTest { + kotlin + compileClasspath += sourceSets.test.runtimeClasspath + runtimeClasspath += sourceSets.test.runtimeClasspath + } + mavenTest { + kotlin + compileClasspath += sourceSets.test.runtimeClasspath + runtimeClasspath += sourceSets.test.runtimeClasspath + } +} + +task npmTest(type: Test) { + def sourceSet = sourceSets.npmTest + environment "projectRoot", project.rootDir + environment "deployVersion", version + def dryRunNpm = project.properties['dryRun'] + def doRun = dryRunNpm == "true" // so that we don't accidentally publish anything, especially before the test + onlyIf { doRun } + if (doRun) { // `onlyIf` only affects execution of the task, not the dependency subtree + dependsOn(project(':').getTasksByName("publishNpm", true)) + } + testClassesDirs = sourceSet.output.classesDirs + classpath = sourceSet.runtimeClasspath +} + +task mavenTest(type: Test) { + def sourceSet = sourceSets.mavenTest + dependsOn(project(':').getTasksByName("publishToMavenLocal", true)) + dependsOn.remove(project(':').getTasksByName("dokka", true)) + testClassesDirs = sourceSet.output.classesDirs + classpath = sourceSet.runtimeClasspath +} + +dependencies { + testCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + testCompile 'junit:junit:4.12' + npmTestCompile 'org.apache.commons:commons-compress:1.18' + npmTestCompile 'com.google.code.gson:gson:2.8.5' + mavenTestRuntimeOnly project(':kotlinx-coroutines-core') + mavenTestRuntimeOnly project(':kotlinx-coroutines-android') +} + +compileTestKotlin { + kotlinOptions.jvmTarget = "1.8" +} + +test { + dependsOn([npmTest, mavenTest]) +} diff --git a/publication-validator/src/test/kotlin/kotlinx/coroutines/tools/MavenPublicationValidator.kt b/integration-testing/src/mavenTest/kotlin/MavenPublicationValidator.kt similarity index 99% rename from publication-validator/src/test/kotlin/kotlinx/coroutines/tools/MavenPublicationValidator.kt rename to integration-testing/src/mavenTest/kotlin/MavenPublicationValidator.kt index 53fd65d31f..5089c535ae 100644 --- a/publication-validator/src/test/kotlin/kotlinx/coroutines/tools/MavenPublicationValidator.kt +++ b/integration-testing/src/mavenTest/kotlin/MavenPublicationValidator.kt @@ -6,7 +6,6 @@ package kotlinx.coroutines.validator import org.junit.* import org.junit.Assert.assertTrue -import java.io.* import java.util.jar.* class MavenPublicationValidator { diff --git a/publication-validator/src/test/kotlin/kotlinx/coroutines/tools/NpmPublicationValidator.kt b/integration-testing/src/npmTest/kotlin/NpmPublicationValidator.kt similarity index 100% rename from publication-validator/src/test/kotlin/kotlinx/coroutines/tools/NpmPublicationValidator.kt rename to integration-testing/src/npmTest/kotlin/NpmPublicationValidator.kt diff --git a/publication-validator/README.md b/publication-validator/README.md deleted file mode 100644 index a60ff00e3c..0000000000 --- a/publication-validator/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Publication validator - -This is a supplementary subproject of kotlinx.coroutines that provides a new -task, `testPublishing`, to test its publication correctness. - -The tests are the following: -* `NpmPublicationValidator` tests that version of NPM artifact is correct and that it has neither source nor package dependencies on atomicfu -* `MavenPublicationValidator` depends on the published artifacts and tests artifacts binary content and absence of atomicfu in the classpath - -To test publication, one needs to run gradle with `-PdryRun=true`, and the -task that actually does the testing is `publication-validator:test`. -`-PdryRun` affects `npmPublish` so that it only provides a packed publication -and does not in fact attempt to send the build for publication. diff --git a/publication-validator/build.gradle b/publication-validator/build.gradle deleted file mode 100644 index a22ccf46d2..0000000000 --- a/publication-validator/build.gradle +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -apply from: rootProject.file("gradle/compile-jvm.gradle") - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - testCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - testCompile 'junit:junit:4.12' - testCompile 'org.apache.commons:commons-compress:1.18' - testCompile 'com.google.code.gson:gson:2.8.5' - testCompile project(':kotlinx-coroutines-core') - testCompile project(':kotlinx-coroutines-android') -} - -compileTestKotlin { - kotlinOptions.jvmTarget = "1.8" -} - -def dryRunNpm = properties['dryRun'] - -test { - onlyIf { dryRunNpm == "true" } // so that we don't accidentally publish anything, especially before the test - doFirst { println "Verifying publishing version $version" } // all modules share the same version - environment "projectRoot", project.rootDir - environment "deployVersion", version - if (dryRunNpm == "true") { // `onlyIf` only affects execution of the task, not the dependency subtree - dependsOn(project(':').getTasksByName("publishNpm", true) + - project(':').getTasksByName("publishToMavenLocal", true)) - dependsOn.remove(project(':').getTasksByName("dokka", true)) - } -} diff --git a/settings.gradle b/settings.gradle index 64ae2ffad3..95fcd7cb2d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -45,4 +45,4 @@ if (!build_snapshot_train) { include('site') } -module('publication-validator') +module('integration-testing') From 76ab90f2d8428a6033e7a87c2ee6bd5cd7e3a359 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Mon, 16 Mar 2020 14:32:01 +0300 Subject: [PATCH 11/15] Add an integration test for coroutine debugger java agent --- integration-testing/README.md | 1 + integration-testing/build.gradle | 17 +++++++++++++++- .../debugAgentTest/kotlin/DebugAgentTest.kt | 20 +++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 integration-testing/src/debugAgentTest/kotlin/DebugAgentTest.kt diff --git a/integration-testing/README.md b/integration-testing/README.md index fedd9394a9..4754081a45 100644 --- a/integration-testing/README.md +++ b/integration-testing/README.md @@ -9,5 +9,6 @@ The tests are the following: `-PdryRun` affects `npmPublish` so that it only provides a packed publication and does not in fact attempt to send the build for publication. * `MavenPublicationValidator` depends on the published artifacts and tests artifacts binary content and absence of atomicfu in the classpath +* `DebugAgentTest` checks that the coroutine debugger can be run as a Java agent. All the available tests can be run with `integration-testing:test`. diff --git a/integration-testing/build.gradle b/integration-testing/build.gradle index 72fa585340..060eea414f 100644 --- a/integration-testing/build.gradle +++ b/integration-testing/build.gradle @@ -20,6 +20,11 @@ sourceSets { compileClasspath += sourceSets.test.runtimeClasspath runtimeClasspath += sourceSets.test.runtimeClasspath } + debugAgentTest { + kotlin + compileClasspath += sourceSets.test.runtimeClasspath + runtimeClasspath += sourceSets.test.runtimeClasspath + } } task npmTest(type: Test) { @@ -44,6 +49,14 @@ task mavenTest(type: Test) { classpath = sourceSet.runtimeClasspath } +task debugAgentTest(type: Test) { + def sourceSet = sourceSets.debugAgentTest + dependsOn(project(':kotlinx-coroutines-debug').shadowJar) + jvmArgs ('-javaagent:' + project(':kotlinx-coroutines-debug').shadowJar.outputs.files.getFiles()[0]) + testClassesDirs = sourceSet.output.classesDirs + classpath = sourceSet.runtimeClasspath +} + dependencies { testCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" testCompile 'junit:junit:4.12' @@ -51,6 +64,8 @@ dependencies { npmTestCompile 'com.google.code.gson:gson:2.8.5' mavenTestRuntimeOnly project(':kotlinx-coroutines-core') mavenTestRuntimeOnly project(':kotlinx-coroutines-android') + debugAgentTestCompile project(':kotlinx-coroutines-core') + debugAgentTestCompile project(':kotlinx-coroutines-debug') } compileTestKotlin { @@ -58,5 +73,5 @@ compileTestKotlin { } test { - dependsOn([npmTest, mavenTest]) + dependsOn([npmTest, mavenTest, debugAgentTest]) } diff --git a/integration-testing/src/debugAgentTest/kotlin/DebugAgentTest.kt b/integration-testing/src/debugAgentTest/kotlin/DebugAgentTest.kt new file mode 100644 index 0000000000..925fe077ea --- /dev/null +++ b/integration-testing/src/debugAgentTest/kotlin/DebugAgentTest.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +import org.junit.* +import kotlinx.coroutines.* +import kotlinx.coroutines.debug.* +import java.io.* + +class DebugAgentTest { + + @Test + fun agentDumpsCoroutines() = runBlocking { + val baos = ByteArrayOutputStream() + DebugProbes.dumpCoroutines(PrintStream(baos)) + // if the agent works, then dumps should contain something, + // at least the fact that this test is running. + Assert.assertTrue(baos.toString().contains("agentDumpsCoroutines")) + } + +} From e16face08c7c3c548b758ce1d3d23131c770bc35 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Mon, 16 Mar 2020 15:24:53 +0300 Subject: [PATCH 12/15] Document BlockHound integration better --- .../jvm/src/scheduling/CoroutineScheduler.kt | 12 ++++++++++++ kotlinx-coroutines-debug/README.md | 3 +++ 2 files changed, 15 insertions(+) diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt b/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt index 838bbba520..bc05d10421 100644 --- a/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt +++ b/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt @@ -952,10 +952,22 @@ internal class CoroutineScheduler( } } +/** + * Checks if the thread is part of a thread pool that supports coroutines. + * + * This function is needed for integration with BlockHound and accessed by violating the + * visibility modifier. + */ @Suppress("UNUSED") @JvmName("isSchedulerWorker") internal fun isSchedulerWorker(thread: Thread) = thread is CoroutineScheduler.Worker +/** + * Checks if the thread is running a CPU-bound task. + * + * This function is needed for integration with BlockHound and accessed by violating the + * visibility modifier. + */ @Suppress("UNUSED") @JvmName("mayNotBlock") internal fun mayNotBlock(thread: Thread) = thread is CoroutineScheduler.Worker && diff --git a/kotlinx-coroutines-debug/README.md b/kotlinx-coroutines-debug/README.md index 4128e1078b..50918abb3f 100644 --- a/kotlinx-coroutines-debug/README.md +++ b/kotlinx-coroutines-debug/README.md @@ -13,6 +13,9 @@ suspension stacktraces. Additionally, it is possible to process the list of such coroutines via [DebugProbes.dumpCoroutinesInfo] or dump isolated parts of coroutines hierarchy referenced by a [Job] or [CoroutineScope] instances using [DebugProbes.printJob] and [DebugProbes.printScope] respectively. +Additionally, this module provides a [BlockHound](https://github.com/reactor/BlockHound) service +that detects when a blocking operation was called in a coroutine context that prohibits it. + ### Using in your project Add `kotlinx-coroutines-debug` to your project test dependencies: From 15b54c9d6e7c39436e6889b4765b9738f3daa7d2 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Mon, 16 Mar 2020 15:25:15 +0300 Subject: [PATCH 13/15] Do not exclude the ByteBuddy agent from shading --- kotlinx-coroutines-debug/build.gradle | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kotlinx-coroutines-debug/build.gradle b/kotlinx-coroutines-debug/build.gradle index e787f16a0f..91c459efba 100644 --- a/kotlinx-coroutines-debug/build.gradle +++ b/kotlinx-coroutines-debug/build.gradle @@ -39,7 +39,5 @@ shadowJar { classifier null // Shadow only byte buddy, do not package kotlin stdlib configurations = [project.configurations.shadowDeps] - relocate('net.bytebuddy', 'kotlinx.coroutines.repackaged.net.bytebuddy') { - exclude 'net.bytebuddy.agent' - } + relocate('net.bytebuddy', 'kotlinx.coroutines.repackaged.net.bytebuddy') } From e68538443ad2a5328bfc3cee9d4ab575ebf434de Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 16 Mar 2020 17:02:21 +0300 Subject: [PATCH 14/15] Documentation improvement --- .../jvm/src/scheduling/CoroutineScheduler.kt | 8 ++------ kotlinx-coroutines-debug/README.md | 5 +++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt b/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt index bc05d10421..62cf80f7f8 100644 --- a/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt +++ b/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt @@ -954,9 +954,7 @@ internal class CoroutineScheduler( /** * Checks if the thread is part of a thread pool that supports coroutines. - * - * This function is needed for integration with BlockHound and accessed by violating the - * visibility modifier. + * This function is needed for integration with BlockHound. */ @Suppress("UNUSED") @JvmName("isSchedulerWorker") @@ -964,9 +962,7 @@ internal fun isSchedulerWorker(thread: Thread) = thread is CoroutineScheduler.Wo /** * Checks if the thread is running a CPU-bound task. - * - * This function is needed for integration with BlockHound and accessed by violating the - * visibility modifier. + * This function is needed for integration with BlockHound. */ @Suppress("UNUSED") @JvmName("mayNotBlock") diff --git a/kotlinx-coroutines-debug/README.md b/kotlinx-coroutines-debug/README.md index 50918abb3f..35a66b90e6 100644 --- a/kotlinx-coroutines-debug/README.md +++ b/kotlinx-coroutines-debug/README.md @@ -13,8 +13,9 @@ suspension stacktraces. Additionally, it is possible to process the list of such coroutines via [DebugProbes.dumpCoroutinesInfo] or dump isolated parts of coroutines hierarchy referenced by a [Job] or [CoroutineScope] instances using [DebugProbes.printJob] and [DebugProbes.printScope] respectively. -Additionally, this module provides a [BlockHound](https://github.com/reactor/BlockHound) service -that detects when a blocking operation was called in a coroutine context that prohibits it. +This module also provides an automatic [BlockHound](https://github.com/reactor/BlockHound) integration +that detects when a blocking operation was called in a coroutine context that prohibits it. In order to use it, +please follow the BlockHound [quick start guide](https://github.com/reactor/BlockHound/blob/master/docs/quick_start.md). ### Using in your project From 20cc2ff7db315847c1a1e8d51dc1bb5c9c8754c9 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Mon, 16 Mar 2020 17:16:16 +0300 Subject: [PATCH 15/15] Update kotlinx-coroutines-debug/README.md Co-Authored-By: Sergei Egorov --- kotlinx-coroutines-debug/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kotlinx-coroutines-debug/README.md b/kotlinx-coroutines-debug/README.md index 35a66b90e6..772433ff5f 100644 --- a/kotlinx-coroutines-debug/README.md +++ b/kotlinx-coroutines-debug/README.md @@ -15,7 +15,8 @@ of coroutines hierarchy referenced by a [Job] or [CoroutineScope] instances usin This module also provides an automatic [BlockHound](https://github.com/reactor/BlockHound) integration that detects when a blocking operation was called in a coroutine context that prohibits it. In order to use it, -please follow the BlockHound [quick start guide](https://github.com/reactor/BlockHound/blob/master/docs/quick_start.md). +please follow the BlockHound [quick start guide]( +https://github.com/reactor/BlockHound/blob/1.0.2.RELEASE/docs/quick_start.md). ### Using in your project