Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import io.micrometer.core.instrument.internal.TimedExecutorService;
import io.micrometer.core.instrument.internal.TimedScheduledExecutorService;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
Expand Down Expand Up @@ -60,7 +62,7 @@ public class ExecutorServiceMetrics implements MeterBinder {
private static final String CLASS_NAME_THREAD_PER_TASK_EXECUTOR = "java.util.concurrent.ThreadPerTaskExecutor";

@Nullable
private static final Method METHOD_THREAD_COUNT_FROM_THREAD_PER_TASK_EXECUTOR = getMethodForThreadCountFromThreadPerTaskExecutor();
private static final MethodHandle METHOD_HANDLE_THREAD_COUNT_FROM_THREAD_PER_TASK_EXECUTOR = getMethodHandleForThreadCountFromThreadPerTaskExecutor();

private static boolean allowIllegalReflectiveAccess = true;

Expand Down Expand Up @@ -461,20 +463,20 @@ private void monitorThreadPerTaskExecutor(MeterRegistry registry, ExecutorServic

private static long getThreadCountFromThreadPerTaskExecutor(ExecutorService executorService) {
try {
return (long) METHOD_THREAD_COUNT_FROM_THREAD_PER_TASK_EXECUTOR.invoke(executorService);
return (long) METHOD_HANDLE_THREAD_COUNT_FROM_THREAD_PER_TASK_EXECUTOR.invoke(executorService);
}
catch (Throwable e) {
throw new RuntimeException(e);
}
}

@Nullable
private static Method getMethodForThreadCountFromThreadPerTaskExecutor() {
private static MethodHandle getMethodHandleForThreadCountFromThreadPerTaskExecutor() {
try {
Class<?> clazz = Class.forName(CLASS_NAME_THREAD_PER_TASK_EXECUTOR);

Choose a reason for hiding this comment

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

I think this could work

        Class<?> clazz = Class.forName(CLASS_NAME_THREAD_PER_TASK_EXECUTOR);
        // Get private access to the class
        MethodHandles.Lookup privateLookup = MethodHandles.privateLookupIn(clazz, MethodHandles.lookup());
        // Find the private method
        return privateLookup.findVirtual(clazz, "threadCount", MethodType.methodType(long.class));

Choose a reason for hiding this comment

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

with methodhandles in theory the accesscheck occurs only once (during the lookup) but with plain reflection the access check occurs in every invocation. At least this is my understanding

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@juliojgd MethodHandles.privateLookupIn() requires JDK 9+, but Micrometer needs to work on JDK 8, so we can't call it directly.

Method method = clazz.getMethod("threadCount");
method.setAccessible(true);
return method;
return MethodHandles.lookup().unreflect(method);
}
catch (Throwable e) {
return null;
Expand Down