Skip to content

Commit 2788193

Browse files
committed
feat(virtual-thread): Use context swapping instead of scope activation
Replaces the `ConcurrentState` approach (which activated/closed individual scopes) with `VirtualThreadState` that swaps the entire scope stack via `Context#swap()`. This correctly handles child spans created during virtual thread execution and avoids out-of-order scope closing. The new approach mirrors the ZIO FiberContext and Kotlin coroutines instrumentation patterns.
1 parent d0e39d9 commit 2788193

File tree

2 files changed

+14
-8
lines changed

2 files changed

+14
-8
lines changed

dd-java-agent/instrumentation-testing/src/main/java/datadog/trace/agent/test/AbstractInstrumentationTest.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.function.Predicate;
3131
import net.bytebuddy.agent.ByteBuddyAgent;
3232
import org.junit.jupiter.api.AfterEach;
33+
import org.junit.jupiter.api.BeforeAll;
3334
import org.junit.jupiter.api.BeforeEach;
3435
import org.junit.jupiter.api.extension.ExtendWith;
3536
import org.opentest4j.AssertionFailedError;
@@ -42,13 +43,6 @@
4243
*/
4344
@ExtendWith(TestClassShadowingExtension.class)
4445
public abstract class AbstractInstrumentationTest {
45-
static {
46-
// Allow re-registration of ContextManagers so each test can use a fresh tracer.
47-
// This mirrors DDSpecification.allowContextTesting() for the JUnit 5 test framework.
48-
datadog.context.ContextManager.allowTesting();
49-
datadog.context.ContextBinder.allowTesting();
50-
}
51-
5246
static final Instrumentation INSTRUMENTATION = ByteBuddyAgent.getInstrumentation();
5347

5448
static final long TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(20);
@@ -60,6 +54,18 @@ public abstract class AbstractInstrumentationTest {
6054
protected ClassFileTransformer activeTransformer;
6155
protected ClassFileTransformerListener transformerLister;
6256

57+
@BeforeAll
58+
static void allowContextTesting() {
59+
// Allow re-registration of context managers so each test can use a fresh tracer.
60+
// This mirrors DDSpecification.allowContextTesting() for the Spock test framework.
61+
try {
62+
Class.forName("datadog.context.ContextManager").getMethod("allowTesting").invoke(null);
63+
Class.forName("datadog.context.ContextBinder").getMethod("allowTesting").invoke(null);
64+
} catch (Throwable ignore) {
65+
// don't block testing if context types aren't available
66+
}
67+
}
68+
6369
@BeforeEach
6470
public void init() {
6571
// If this fails, it's likely the result of another test loading Config before it can be

dd-java-agent/instrumentation/java/java-lang/java-lang-21.0/src/main/java/datadog/trace/instrumentation/java/lang/jdk21/VirtualThreadInstrumentation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public static final class Construct {
106106
public static void afterInit(@Advice.This Object virtualThread) {
107107
Context context = current();
108108
if (context == root()) {
109-
return; // no active context to propagate
109+
return; // No active context to propagate, avoid creating state
110110
}
111111
VirtualThreadState state = new VirtualThreadState(context, captureActiveSpan());
112112
ContextStore<Object, Object> store =

0 commit comments

Comments
 (0)