|
1 | 1 | package datadog.trace.instrumentation.kotlin.coroutines; |
2 | 2 |
|
| 3 | +import java.lang.invoke.MethodHandle; |
| 4 | +import java.lang.invoke.MethodHandles; |
| 5 | +import java.lang.invoke.MethodType; |
3 | 6 | import kotlin.coroutines.CoroutineContext; |
4 | 7 | import kotlinx.coroutines.AbstractCoroutine; |
| 8 | +import kotlinx.coroutines.ChildHandle; |
5 | 9 | import kotlinx.coroutines.Job; |
| 10 | +import kotlinx.coroutines.JobNode; |
| 11 | +import kotlinx.coroutines.JobSupport; |
6 | 12 | import org.jetbrains.annotations.Nullable; |
| 13 | +import org.slf4j.Logger; |
| 14 | +import org.slf4j.LoggerFactory; |
7 | 15 |
|
8 | 16 | public class CoroutineContextHelper { |
| 17 | + private static final Logger log = LoggerFactory.getLogger(CoroutineContextHelper.class); |
| 18 | + |
9 | 19 | /* |
10 | 20 | IntelliJ shows a warning here for Job being out of bounds, but that's not true, the class compiles. |
11 | 21 | */ |
12 | | - |
13 | 22 | @Nullable |
14 | 23 | @SuppressWarnings("unchecked") |
15 | 24 | public static Job getJob(final CoroutineContext context) { |
@@ -40,7 +49,57 @@ public static void closeScopeStateContext(final AbstractCoroutine<?> coroutine) |
40 | 49 | final ScopeStateCoroutineContext scopeStackContext = |
41 | 50 | getScopeStateContext(coroutine.getContext()); |
42 | 51 | if (scopeStackContext != null) { |
43 | | - scopeStackContext.maybeCloseScopeAndCancelContinuation(coroutine); |
| 52 | + scopeStackContext.maybeCloseScopeAndCancelContinuation(coroutine, getParentJob(coroutine)); |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + private static final MethodHandle PARENT_HANDLE_METHOD; |
| 57 | + private static final MethodHandle PARENT_HANDLE_FIELD; |
| 58 | + private static final MethodHandle JOB_FIELD; |
| 59 | + |
| 60 | + static { |
| 61 | + MethodHandle parentHandleMethod = null; |
| 62 | + MethodHandle parentHandleField = null; |
| 63 | + MethodHandle jobField = null; |
| 64 | + |
| 65 | + MethodHandles.Lookup lookup = MethodHandles.publicLookup(); |
| 66 | + try { |
| 67 | + // Kotlin coroutines 1.5+ |
| 68 | + parentHandleMethod = |
| 69 | + lookup.findVirtual( |
| 70 | + JobSupport.class, |
| 71 | + "getParentHandle$kotlinx_coroutines_core", |
| 72 | + MethodType.methodType(ChildHandle.class)); |
| 73 | + jobField = lookup.findGetter(JobNode.class, "job", JobSupport.class); |
| 74 | + } catch (Throwable ignore) { |
| 75 | + try { |
| 76 | + // Kotlin coroutines 1.3 |
| 77 | + parentHandleField = lookup.findGetter(JobSupport.class, "parentHandle", ChildHandle.class); |
| 78 | + jobField = lookup.findGetter(JobNode.class, "job", Job.class); |
| 79 | + } catch (Throwable e) { |
| 80 | + log.debug("Unable to access parent handle", e); |
| 81 | + } |
| 82 | + } |
| 83 | + |
| 84 | + PARENT_HANDLE_METHOD = parentHandleMethod; |
| 85 | + PARENT_HANDLE_FIELD = parentHandleField; |
| 86 | + JOB_FIELD = jobField; |
| 87 | + } |
| 88 | + |
| 89 | + private static Job getParentJob(JobSupport coroutine) { |
| 90 | + try { |
| 91 | + Object parentHandle = null; |
| 92 | + if (null != PARENT_HANDLE_METHOD) { |
| 93 | + parentHandle = PARENT_HANDLE_METHOD.invoke(coroutine); |
| 94 | + } else if (null != PARENT_HANDLE_FIELD) { |
| 95 | + parentHandle = PARENT_HANDLE_FIELD.invoke(coroutine); |
| 96 | + } |
| 97 | + if (parentHandle instanceof JobNode) { |
| 98 | + return (Job) JOB_FIELD.invoke((JobNode) parentHandle); |
| 99 | + } |
| 100 | + } catch (Throwable e) { |
| 101 | + log.debug("Unable to extract parent job", e); |
44 | 102 | } |
| 103 | + return null; |
45 | 104 | } |
46 | 105 | } |
0 commit comments