Log when executors are full.
This commit is contained in:
parent
16ab27084c
commit
fcf62512a7
2 changed files with 44 additions and 12 deletions
|
@ -192,6 +192,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
|
|||
|
||||
ApplicationDependencies.getFrameRateTracker().start();
|
||||
ApplicationDependencies.getMegaphoneRepository().onAppForegrounded();
|
||||
ApplicationDependencies.getDeadlockDetector().start();
|
||||
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
FeatureFlags.refreshIfNecessary();
|
||||
|
@ -202,7 +203,6 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
|
|||
KeyCachingService.onAppForegrounded(this);
|
||||
ApplicationDependencies.getShakeToReport().enable();
|
||||
checkBuildExpiration();
|
||||
ApplicationDependencies.getDeadlockDetector().start();
|
||||
});
|
||||
|
||||
Log.d(TAG, "onStart() took " + (System.currentTimeMillis() - startTime) + " ms");
|
||||
|
|
|
@ -2,6 +2,8 @@ package org.signal.core.util.concurrent
|
|||
|
||||
import android.os.Handler
|
||||
import org.signal.core.util.logging.Log
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.ThreadPoolExecutor
|
||||
|
||||
/**
|
||||
* A class that polls active threads at a set interval and logs when multiple threads are BLOCKED.
|
||||
|
@ -32,22 +34,32 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval
|
|||
thread.state == Thread.State.BLOCKED || (thread.state == Thread.State.WAITING && stack.any { it.methodName.startsWith("lock") })
|
||||
}
|
||||
val blockedIds: Set<Long> = blocked.keys.map(Thread::getId).toSet()
|
||||
val stillBlocked: Set<Long> = blockedIds.intersect(previouslyBlocked)
|
||||
|
||||
if (blocked.size > 1) {
|
||||
Log.w(TAG, buildLogString("Found multiple blocked threads! Possible deadlock.", blocked))
|
||||
} else {
|
||||
val stillBlocked: Set<Long> = blockedIds.intersect(previouslyBlocked)
|
||||
} else if (stillBlocked.isNotEmpty()) {
|
||||
val stillBlockedMap: Map<Thread, Array<StackTraceElement>> = stillBlocked
|
||||
.map { blockedId ->
|
||||
val key: Thread = blocked.keys.first { it.id == blockedId }
|
||||
val value: Array<StackTraceElement> = blocked[key]!!
|
||||
Pair(key, value)
|
||||
}
|
||||
.toMap()
|
||||
|
||||
if (stillBlocked.isNotEmpty()) {
|
||||
val stillBlockedMap: Map<Thread, Array<StackTraceElement>> = stillBlocked
|
||||
.map { blockedId ->
|
||||
val key: Thread = blocked.keys.first { it.id == blockedId }
|
||||
val value: Array<StackTraceElement> = blocked[key]!!
|
||||
Pair(key, value)
|
||||
}
|
||||
Log.w(TAG, buildLogString("Found a long block! Blocked for at least $pollingInterval ms.", stillBlockedMap))
|
||||
}
|
||||
|
||||
val fullExecutors: List<ExecutorInfo> = EXECUTORS.filter { isExecutorFull(it.executor) }
|
||||
|
||||
if (fullExecutors.isNotEmpty()) {
|
||||
fullExecutors.forEach { executorInfo ->
|
||||
val fullMap: Map<Thread, Array<StackTraceElement>> = threads
|
||||
.filter { it.key.name.startsWith(executorInfo.namePrefix) }
|
||||
.toMap()
|
||||
|
||||
Log.w(TAG, buildLogString("Found a long block! Blocked for at least $pollingInterval ms.", stillBlockedMap))
|
||||
val executor: ThreadPoolExecutor = executorInfo.executor as ThreadPoolExecutor
|
||||
Log.w(TAG, buildLogString("Found a full executor! ${executor.activeCount}/${executor.corePoolSize} threads active with ${executor.queue.size} tasks queued.", fullMap))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,9 +71,21 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval
|
|||
}
|
||||
}
|
||||
|
||||
private data class ExecutorInfo(
|
||||
val executor: ExecutorService,
|
||||
val namePrefix: String
|
||||
)
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(DeadlockDetector::class.java)
|
||||
|
||||
private val EXECUTORS: Set<ExecutorInfo> = setOf(
|
||||
ExecutorInfo(SignalExecutors.BOUNDED, "signal-bounded-"),
|
||||
ExecutorInfo(SignalExecutors.BOUNDED_IO, "signal-bounded-io-")
|
||||
)
|
||||
|
||||
private const val CONCERNING_QUEUE_THRESHOLD = 4
|
||||
|
||||
private fun buildLogString(description: String, blocked: Map<Thread, Array<StackTraceElement>>): String {
|
||||
val stringBuilder = StringBuilder()
|
||||
stringBuilder.append(description).append("\n")
|
||||
|
@ -78,5 +102,13 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval
|
|||
|
||||
return stringBuilder.toString()
|
||||
}
|
||||
|
||||
private fun isExecutorFull(executor: ExecutorService): Boolean {
|
||||
return if (executor is ThreadPoolExecutor) {
|
||||
executor.queue.size > CONCERNING_QUEUE_THRESHOLD
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue