Skip to content

Commit

Permalink
Fix issue #280 thread renaming for ScheduledThreadPoolExecutor
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsonlee authored and neighbWang committed Mar 5, 2022
1 parent 5c73ac7 commit eeb29a6
Show file tree
Hide file tree
Showing 2 changed files with 228 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package com.didiglobal.booster.instrument;

import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;

/**
* @author johnsonlee
*/
public class ShadowScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {

/**
* Initialize {@code ScheduledThreadPoolExecutor} with new thread name, this constructor is used by {@code ThreadTransformer} for thread renaming
*
* @param corePoolSize the number of threads to keep in the pool, even if they are idle,
* unless {@code allowCoreThreadTimeOut} is set
* @param prefix the prefix of new thread
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ShadowScheduledThreadPoolExecutor(
final int corePoolSize,
final String prefix
) {
this(corePoolSize, prefix, false);
}

/**
* Initialize {@code ScheduledThreadPoolExecutor} with new thread name, this constructor is used by {@code ThreadTransformer} for thread renaming
*
* @param corePoolSize the number of threads to keep in the pool, even if they are idle,
* unless {@code allowCoreThreadTimeOut} is set
* @param prefix the prefix of new thread
* @param optimize the value indicates that the thread pool optimization should be applied
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ShadowScheduledThreadPoolExecutor(
final int corePoolSize,
final String prefix,
final boolean optimize
) {
super(corePoolSize, new NamedThreadFactory(prefix));
if (optimize) {
allowCoreThreadTimeOut(true);
}
}

/**
* Initialize {@code ScheduledThreadPoolExecutor} with new thread name, this constructor is used by {@code ThreadTransformer} for thread renaming
*
* @param corePoolSize the number of threads to keep in the pool, even if they are idle,
* unless {@code allowCoreThreadTimeOut} is set
* @param threadFactory the factory to use when the executor creates a new thread
* @param prefix the prefix of new thread
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ShadowScheduledThreadPoolExecutor(
final int corePoolSize,
final ThreadFactory threadFactory,
final String prefix
) {
this(corePoolSize, threadFactory, prefix, false);
}

/**
* Initialize {@code ScheduledThreadPoolExecutor} with new thread name, this constructor is used by {@code ThreadTransformer} for thread renaming
*
* @param corePoolSize the number of threads to keep in the pool, even if they are idle,
* unless {@code allowCoreThreadTimeOut} is set
* @param threadFactory the factory to use when the executor creates a new thread
* @param prefix the prefix of new thread
* @param optimize the value indicates that the thread pool optimization should be applied
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ShadowScheduledThreadPoolExecutor(
final int corePoolSize,
final ThreadFactory threadFactory,
final String prefix,
final boolean optimize
) {
super(corePoolSize, new NamedThreadFactory(threadFactory, prefix));
if (optimize) {
allowCoreThreadTimeOut(true);
}
}

/**
* Initialize {@code ScheduledThreadPoolExecutor} with new thread name, this constructor is used by {@code ThreadTransformer} for thread renaming
*
* @param corePoolSize the number of threads to keep in the pool, even if they are idle,
* unless {@code allowCoreThreadTimeOut} is set
* @param handler the handler to use when execution is blocked because the thread bounds and queue capacities are reached
* @param prefix the prefix of new thread
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ShadowScheduledThreadPoolExecutor(
final int corePoolSize,
final RejectedExecutionHandler handler,
final String prefix
) {
this(corePoolSize, handler, prefix, false);
}

/**
* Initialize {@code ScheduledThreadPoolExecutor} with new thread name, this constructor is used by {@code ThreadTransformer} for thread renaming
*
* @param corePoolSize the number of threads to keep in the pool, even if they are idle,
* unless {@code allowCoreThreadTimeOut} is set
* @param handler the handler to use when execution is blocked because the thread bounds and queue capacities are reached
* @param prefix the prefix of new thread
* @param optimize the value indicates that the thread pool optimization should be applied
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ShadowScheduledThreadPoolExecutor(
final int corePoolSize,
final RejectedExecutionHandler handler,
final String prefix,
final boolean optimize
) {
super(corePoolSize, new NamedThreadFactory(prefix), handler);
if (optimize) {
allowCoreThreadTimeOut(true);
}
}

/**
* Initialize {@code ScheduledThreadPoolExecutor} with new thread name, this constructor is used by {@code ThreadTransformer} for thread renaming
*
* @param corePoolSize the number of threads to keep in the pool, even if they are idle,
* unless {@code allowCoreThreadTimeOut} is set
* @param threadFactory the factory to use when the executor creates a new thread
* @param handler the handler to use when execution is blocked because the thread bounds and queue capacities are reached
* @param prefix the prefix of new thread
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ShadowScheduledThreadPoolExecutor(
final int corePoolSize,
final ThreadFactory threadFactory,
final RejectedExecutionHandler handler,
final String prefix
) {
this(corePoolSize, threadFactory, handler, prefix, false);
}

/**
* Initialize {@code ScheduledThreadPoolExecutor} with new thread name, this constructor is used by {@code ThreadTransformer} for thread renaming
*
* @param corePoolSize the number of threads to keep in the pool, even if they are idle,
* unless {@code allowCoreThreadTimeOut} is set
* @param threadFactory the factory to use when the executor creates a new thread
* @param handler the handler to use when execution is blocked because the thread bounds and queue capacities are reached
* @param prefix the prefix of new thread
* @param optimize the value indicates that the thread pool optimization should be applied
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ShadowScheduledThreadPoolExecutor(
final int corePoolSize,
final ThreadFactory threadFactory,
final RejectedExecutionHandler handler,
final String prefix,
final boolean optimize
) {
super(corePoolSize, new NamedThreadFactory(threadFactory, prefix), handler);
if (optimize) {
allowCoreThreadTimeOut(true);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class ThreadTransformer : ClassTransformer {
* - `java.lang.Thread`
* - `java.util.Timer`
* - `java.util.concurrent.ThreadPoolExecutor`
* - `java.util.concurrent.ScheduledThreadPoolExecutor`
* - `android.os.HandlerThread`
*/
private fun MethodInsnNode.transformInvokeSpecial(@Suppress("UNUSED_PARAMETER") context: TransformContext, klass: ClassNode, method: MethodNode) {
Expand All @@ -115,12 +116,13 @@ class ThreadTransformer : ClassTransformer {
HANDLER_THREAD -> transformHandlerThreadInvokeSpecial(klass, method)
TIMER -> transformTimerInvokeSpecial(klass, method)
THREAD_POOL_EXECUTOR -> transformThreadPoolExecutorInvokeSpecial(klass, method)
SCHEDULED_THREAD_POOL_EXECUTOR -> transformScheduledThreadPoolExecutorInvokeSpecial(klass, method)
}
}

private fun MethodInsnNode.transformThreadPoolExecutorInvokeSpecial(klass: ClassNode, method: MethodNode, init: MethodInsnNode = this) {
when (this.desc) {
// ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue)
// ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue) => ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue, ThreadFactory)
"(IIJLjava/util/concurrent/TimeUnit;Ljava/util/concurrent/BlockingQueue;)V" -> {
method.instructions.apply {
// ..., queue => ..., queue, prefix
Expand All @@ -139,7 +141,7 @@ class ThreadTransformer : ClassTransformer {
insertBefore(init, MethodInsnNode(Opcodes.INVOKESTATIC, NAMED_THREAD_FACTORY, "newInstance", "(Ljava/util/concurrent/ThreadFactory;Ljava/lang/String;)Ljava/util/concurrent/ThreadFactory;"))
}
}
// ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue, RejectedExecutionHandler)
// ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue, RejectedExecutionHandler) => ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue, ThreadFactory, RejectedExecutionHandler)
"(IIJLjava/util/concurrent/TimeUnit;Ljava/util/concurrent/BlockingQueue;Ljava/util/concurrent/RejectedExecutionHandler;)V" -> {
method.instructions.apply {
// ..., handler => ..., handler, prefix
Expand Down Expand Up @@ -167,6 +169,55 @@ class ThreadTransformer : ClassTransformer {
}
}

private fun MethodInsnNode.transformScheduledThreadPoolExecutorInvokeSpecial(klass: ClassNode, method: MethodNode, init: MethodInsnNode = this) {
when (this.desc) {
// ScheduledThreadPoolExecutor(int) => ScheduledThreadPoolExecutor(int, ThreadFactory)
"(I)V" -> {
method.instructions.apply {
// ..., coreSize => ..., coreSize, prefix
insertBefore(init, LdcInsnNode(makeThreadName(klass.className)))
// ..., queue, prefix => ..., queue, factory
insertBefore(init, MethodInsnNode(Opcodes.INVOKESTATIC, NAMED_THREAD_FACTORY, "newInstance", "(Ljava/lang/String;)Ljava/util/concurrent/ThreadFactory;", false))
}
this.desc = "(IIJLjava/util/concurrent/TimeUnit;Ljava/util/concurrent/BlockingQueue;Ljava/util/concurrent/ThreadFactory;)V"
}
// ScheduledThreadPoolExecutor(int, ThreadFactory)
"(ILjava/util/concurrent/ThreadFactory;)V" -> {
method.instructions.apply {
// ..., factory => ..., factory, prefix
insertBefore(init, LdcInsnNode(makeThreadName(klass.className)))
// ..., factory, prefix => ..., factory
insertBefore(init, MethodInsnNode(Opcodes.INVOKESTATIC, NAMED_THREAD_FACTORY, "newInstance", "(Ljava/util/concurrent/ThreadFactory;Ljava/lang/String;)Ljava/util/concurrent/ThreadFactory;"))
}
}
// ScheduledThreadPoolExecutor(int, RejectedExecutionHandler) => ScheduledThreadPoolExecutor(int, ThreadFactory, RejectedExecutionHandler)
"(ILjava/util/concurrent/RejectedExecutionHandler;)V" -> {
method.instructions.apply {
// ..., handler => ..., handler, prefix
insertBefore(init, LdcInsnNode(makeThreadName(klass.className)))
// ..., handler, prefix => ..., handler, factory
insertBefore(init, MethodInsnNode(Opcodes.INVOKESTATIC, NAMED_THREAD_FACTORY, "newInstance", "(Ljava/lang/String;)Ljava/util/concurrent/ThreadFactory;", false))
// ..., handler, factory => ..., factory, handler
insertBefore(init, InsnNode(Opcodes.SWAP))
}
this.desc = "(IIJLjava/util/concurrent/TimeUnit;Ljava/util/concurrent/BlockingQueue;Ljava/util/concurrent/ThreadFactory;Ljava/util/concurrent/RejectedExecutionHandler;)V"
}
// ScheduledThreadPoolExecutor(int, ThreadFactory, RejectedExecutionHandler)
"(ILjava/util/concurrent/ThreadFactory;Ljava/util/concurrent/RejectedExecutionHandler;)V" -> {
method.instructions.apply {
// ..., factory, handler => ..., handler, factory
insertBefore(init, InsnNode(Opcodes.SWAP))
// ..., handler, factory => ..., handler, factory, prefix
insertBefore(init, LdcInsnNode(makeThreadName(klass.className)))
// ..., handler, factory, prefix => ..., handler, factory
insertBefore(init, MethodInsnNode(Opcodes.INVOKESTATIC, NAMED_THREAD_FACTORY, "newInstance", "(Ljava/util/concurrent/ThreadFactory;Ljava/lang/String;)Ljava/util/concurrent/ThreadFactory;"))
// ..., handler, factory => ..., factory, handler
insertBefore(init, InsnNode(Opcodes.SWAP))
}
}
}
}

private fun MethodInsnNode.transformTimerInvokeSpecial(klass: ClassNode, method: MethodNode, init: MethodInsnNode = this) {
when (this.desc) {
// Timer()
Expand Down Expand Up @@ -338,10 +389,11 @@ class ThreadTransformer : ClassTransformer {

private fun TypeInsnNode.transform(context: TransformContext, klass: ClassNode, method: MethodNode) {
when (this.desc) {
/*-*/ HANDLER_THREAD -> this.transformNew(context, klass, method, SHADOW_HANDLER_THREAD)
/*---------*/ THREAD -> this.transformNew(context, klass, method, SHADOW_THREAD)
THREAD_POOL_EXECUTOR -> this.transformNew(context, klass, method, SHADOW_THREAD_POOL_EXECUTOR, true)
/*----------*/ TIMER -> this.transformNew(context, klass, method, SHADOW_TIMER)
/*-----------*/ HANDLER_THREAD -> this.transformNew(context, klass, method, SHADOW_HANDLER_THREAD)
/*-------------------*/ THREAD -> this.transformNew(context, klass, method, SHADOW_THREAD)
/*-----*/ THREAD_POOL_EXECUTOR -> this.transformNew(context, klass, method, SHADOW_THREAD_POOL_EXECUTOR, true)
SCHEDULED_THREAD_POOL_EXECUTOR -> this.transformNew(context, klass, method, SHADOW_SCHEDULED_THREAD_POOL_EXECUTOR, true)
/*--------------------*/ TIMER -> this.transformNew(context, klass, method, SHADOW_TIMER)
}
}

Expand Down Expand Up @@ -405,6 +457,7 @@ internal const val SHADOW_THREAD = "${SHADOW}Thread"
internal const val SHADOW_TIMER = "${SHADOW}Timer"
internal const val SHADOW_EXECUTORS = "${SHADOW}Executors"
internal const val SHADOW_THREAD_POOL_EXECUTOR = "${SHADOW}ThreadPoolExecutor"
internal const val SHADOW_SCHEDULED_THREAD_POOL_EXECUTOR = "${SHADOW}ScheduledThreadPoolExecutor"
internal const val SHADOW_ASYNC_TASK = "${SHADOW}AsyncTask"
internal const val NAMED_THREAD_FACTORY = "${BOOSTER_INSTRUMENT}NamedThreadFactory"

Expand All @@ -416,3 +469,4 @@ internal const val THREAD = "java/lang/Thread"
internal const val TIMER = "${JAVA_UTIL}Timer"
internal const val EXECUTORS = "${JAVA_UTIL_CONCURRENT}Executors"
internal const val THREAD_POOL_EXECUTOR = "${JAVA_UTIL_CONCURRENT}ThreadPoolExecutor"
internal const val SCHEDULED_THREAD_POOL_EXECUTOR = "${JAVA_UTIL_CONCURRENT}ScheduledThreadPoolExecutor"

0 comments on commit eeb29a6

Please sign in to comment.