blob: 6c60161facc9beefb07025ff0eb8a1e216d46b76 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.checks.studio
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression
/**
* Forbid ForkJoinPool usage
*/
class ForkJoinPoolDetector : Detector(), SourceCodeScanner {
companion object Issues {
private val IMPLEMENTATION = Implementation(
ForkJoinPoolDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
@JvmField
val COMMON_FJ_POOL = Issue.create(
id = "CommonForkJoinPool",
briefDescription = "Using common Fork Join Pool",
explanation = """
Using the common ForkJoinPool can lead to freezes because in many cases
the set of threads is very low.
For Android Studio, either use the IntelliJ application pool:
`com.intellij.openapi.application.Application#executeOnPooledThread`.
Or, for long running operations, prefer the
`AppExecutorUtil.getAppExecutorService()` executor.
For the Android Gradle Plugin use
`com.android.build.gradle.internal.tasks.Workers.preferWorkers` or
`com.android.build.gradle.internal.tasks.Workers.preferThreads`
For more, see `go/do-not-freeze`.
""",
category = UI_RESPONSIVENESS,
priority = 6,
severity = Severity.ERROR,
platforms = STUDIO_PLATFORMS,
implementation = IMPLEMENTATION
)
@JvmField
val NEW_FJ_POOL = Issue.create(
id = "NewForkJoinPool",
briefDescription = "Using Fork Join Pool",
explanation = """
Using new Fork Join Pools should be limited to very specific use cases.
For Android Studio, when possible, prefer using the IntelliJ application pool:
`com.intellij.openapi.application.Application#executeOnPooledThread`.
Or, for long running operations, prefer the
`AppExecutorUtil.getAppExecutorService()` executor.
For the Android Gradle Plugin use
`com.android.build.gradle.internal.tasks.Workers.preferWorkers` or
`com.android.build.gradle.internal.tasks.Workers.preferThreads`
For more, see `go/do-not-freeze`.
""",
category = UI_RESPONSIVENESS,
priority = 6,
severity = Severity.ERROR,
platforms = STUDIO_PLATFORMS,
implementation = IMPLEMENTATION
)
}
override fun getApplicableConstructorTypes() = listOf("java.util.concurrent.ForkJoinPool")
override fun visitConstructor(
context: JavaContext,
node: UCallExpression,
constructor: PsiMethod
) {
// Called constructor directly
// TODO: ForkJoinTask
context.report(
NEW_FJ_POOL, node, context.getLocation(node),
"Avoid using new ForkJoinPool instances when possible. Prefer using the IntelliJ application pool via `com.intellij.openapi.application.Application#executeOnPooledThread`, or for the Android Gradle Plugin use `com.android.build.gradle.internal.tasks.Workers`. See `go/do-not-freeze`.")
}
override fun getApplicableMethodNames() = listOf("commonPool")
override fun visitMethodCall(
context: JavaContext,
node: UCallExpression,
method: PsiMethod
) {
val evaluator = context.evaluator
// Make sure it's the right methods
when (method.name) {
"commonPool" -> {
if (!evaluator.isMemberInClass(method, "java.util.concurrent.ForkJoinPool")) {
return
}
}
else -> {
error(method.name)
}
}
context.report(
COMMON_FJ_POOL, node, context.getNameLocation(node),
"Avoid using common ForkJoinPool, directly or indirectly (for example via CompletableFuture). It has a limited set of threads on some machines which leads to hangs. See `go/do-not-freeze`."
)
}
}