Skip to content

Fix Kotlin coroutine scope propagation before suspension - #12755

Draft
amarziali wants to merge 1 commit into
masterfrom
andrea.marziali/kotlin
Draft

amarziali wants to merge 1 commit into
masterfrom
andrea.marziali/kotlin

Conversation

@amarziali

Copy link
Copy Markdown
Contributor

What Does This Do

A Kotlin coroutine can resume on another worker before the previous worker restores its thread context, losing a manually opened scope and leaving it unclosed.

Capture the scope stack before standard suspension and yield decisions, and prevent an older restoration from overwriting newer context. Enable instrumentation of SafeContinuation with a narrow allow-list exception.

Motivation

Additional Notes

Contributor Checklist

Jira ticket: [PROJ-IDENT]

@amarziali amarziali changed the title Fix Kotlin coroutine scope propagation before suspensio Fix Kotlin coroutine scope propagation before suspension Oct 6, 2026
@amarziali amarziali added the tag: override groovy enforcement Override the "Enforce Groovy Migration" check label Oct 6, 2026
@amarziali

Copy link
Copy Markdown
Contributor Author

@DataDog review

@datadog-datadog-prod-us1-2

This comment has been minimized.

@datadog-datadog-prod-us1-2 datadog-datadog-prod-us1-2 Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bits Code Review: FAIL

On kotlinx-coroutines 1.8, SelectBuilderImpl no longer implements Continuation, so Byte Buddy rejects the typed @Advice.This binding at transformation time before any runtime suppression can apply. The fix is to bind the receiver as Object and add a Continuation instanceof guard, letting modern select builders delegate to the already-instrumented CancellableContinuationImpl.

Open Bits AI session

🤖 Bits Code Review · Commit a9bcd5c · @DataDog review to ask questions

Comment on lines +41 to +42
public static void beforeSuspension(@Advice.This Continuation<?> continuation) {
DatadogThreadContextElement.beforeSuspension(continuation);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Bind advice safely for non-Continuation select builders

When legacy select code runs with supported kotlinx-coroutines 1.8, SelectBuilderImpl.getResult() matches this advice, but its receiver no longer implements Continuation. Byte Buddy rejects the transformation before suppress=Throwable can apply, producing an instrumentation error. An Object receiver with a Continuation guard preserves older-version behavior and avoids the rejection; the newer builder delegates suspension to the already-instrumented CancellableContinuationImpl.getResult().

Suggested change
public static void beforeSuspension(@Advice.This Continuation<?> continuation) {
DatadogThreadContextElement.beforeSuspension(continuation);
public static void beforeSuspension(@Advice.This Object receiver) {
if (receiver instanceof Continuation) {
DatadogThreadContextElement.beforeSuspension((Continuation<?>) receiver);
}

Was this helpful? React 👍 or 👎
🤖 Bits Code Review · @DataDog review to ask questions · Open Bits AI session

@dd-octo-sts

dd-octo-sts Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

🟢 Java Benchmark SLOs — All performance SLOs passed

Suite Status
Startup 🟢 pass

SLO thresholds are defined here based on automatically generated metrics. A warning is raised when results are within 5% of the threshold.

PR vs. master results
Scenario Candidate master Δ (95% CI of mean)
startup:insecure-bank:iast:Agent 13.98 s 13.97 s [-0.8%; +1.0%] (no difference)
startup:insecure-bank:tracing:Agent 13.01 s 13.04 s [-1.0%; +0.5%] (no difference)
startup:petclinic:appsec:Agent 17.16 s 17.03 s [-0.2%; +1.7%] (no difference)
startup:petclinic:iast:Agent 16.95 s 16.99 s [-0.9%; +0.5%] (no difference)
startup:petclinic:profiling:Agent 16.81 s 16.64 s [-0.2%; +2.2%] (no difference)
startup:petclinic:sca:Agent 17.07 s 16.96 s [-0.2%; +1.6%] (no difference)
startup:petclinic:tracing:Agent 16.30 s 16.24 s [-0.5%; +1.2%] (no difference)

Commit: a9bcd5ce · CI Pipeline · Benchmarking Platform UI


Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion.

* Snapshots context before Kotlin publishes standard suspension or yield decisions. Custom
* low-level suspension primitives can bypass these hooks.
*/
public class SuspensionInstrumentation

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could consider only enabling this if InstrumenterConfig.get().isLegacyContextManagerEnabled()

}
}

static final class State {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would Exchange read better? (avoids confusion with the other State class)

@mcculls mcculls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tag: override groovy enforcement Override the "Enforce Groovy Migration" check

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants