1919package co .elastic .apm .agent .scalaconcurrent ;
2020
2121import co .elastic .apm .agent .bci .TracerAwareInstrumentation ;
22+ import co .elastic .apm .agent .collections .WeakConcurrentProviderImpl ;
2223import co .elastic .apm .agent .impl .transaction .AbstractSpan ;
23- import co .elastic .apm .agent .sdk .weakconcurrent .WeakConcurrent ;
2424import co .elastic .apm .agent .sdk .weakconcurrent .WeakMap ;
2525import net .bytebuddy .asm .Advice ;
2626import net .bytebuddy .description .method .MethodDescription ;
3939public abstract class FutureInstrumentation extends TracerAwareInstrumentation {
4040
4141 @ SuppressWarnings ("WeakerAccess" )
42- public static final WeakMap <Object , AbstractSpan <?>> promisesToContext =
43- WeakConcurrent .buildMap ();
42+ public static final WeakMap <Object , AbstractSpan <?>> promisesToContext = WeakConcurrentProviderImpl .createWeakSpanMap ();
4443
4544 @ Nonnull
4645 @ Override
@@ -66,9 +65,6 @@ public static void onExit(@Advice.This Object thiz) {
6665 final AbstractSpan <?> context = tracer .getActive ();
6766 if (context != null ) {
6867 promisesToContext .put (thiz , context );
69- // this span might be ended before the Promise$Transformation#run method starts
70- // we have to avoid that this span gets recycled, even in the above mentioned case
71- context .incrementReferences ();
7268 }
7369 }
7470 }
@@ -91,12 +87,13 @@ public static class AdviceClass {
9187 @ Nullable
9288 @ Advice .OnMethodEnter (suppress = Throwable .class , inline = false )
9389 public static Object onEnter (@ Advice .This Object thiz ) {
94- AbstractSpan <?> context = promisesToContext .remove (thiz );
90+ // We cannot remove yet, as this may decrement the ref count of the span to 0 if it has already ended,
91+ // thus causing it to be recycled just before we activate it on the current thread. So we first get().
92+ AbstractSpan <?> context = promisesToContext .get (thiz );
9593 if (context != null ) {
9694 context .activate ();
97- // decrements the reference we incremented to avoid that the parent context gets recycled before the promise is run
98- // because we have activated it, we can be sure it doesn't get recycled until we deactivate in the OnMethodExit advice
99- context .decrementReferences ();
95+ // Now it's safe to remove, as ref count is at least 2
96+ promisesToContext .remove (thiz );
10097 }
10198 return context ;
10299 }
0 commit comments