Scheduled Executor With Zero Core Threads
ID |
kotlin.executor_zero_core_threads |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Kotlin |
Tags |
concurrency, reliability |
Description
Reports a java.util.concurrent.ScheduledThreadPoolExecutor whose corePoolSize is set to zero,
either as the constructor’s first argument or through a later write to the pool size — both the
setCorePoolSize(0) call and Kotlin’s property form corePoolSize = 0.
A size that is not statically resolvable — a function parameter, a computed value — is not
flagged. A plain ThreadPoolExecutor is out of scope: a zero core size there is a legitimate
"grow on demand" configuration.
Rationale
A ScheduledThreadPoolExecutor’s pool is sized by `corePoolSize alone, unlike a general
ThreadPoolExecutor, which can also grow up to a separate maximum. With corePoolSize at zero,
the executor has no threads at all: every task submitted to it is accepted without error and
simply never runs.
import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.TimeUnit
fun startFlusher() {
val scheduler = ScheduledThreadPoolExecutor(0) // FLAW
scheduler.scheduleAtFixedRate(::flushBuffer, 0, 5, TimeUnit.SECONDS) // never executes
}
fun shrinkFlusher() {
val resized = ScheduledThreadPoolExecutor(4)
resized.corePoolSize = 0 // FLAW — same defect, written as a property
}
Nothing fails loudly. The scheduler keeps accepting work, its queue keeps growing, and the effect shows up only as the silent absence of whatever the scheduled task was meant to do.
Remediation
Size the pool with a value greater than zero, chosen for the number of scheduled tasks the application actually runs concurrently:
import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.TimeUnit
fun startFlusher() {
val scheduler = ScheduledThreadPoolExecutor(4)
scheduler.scheduleAtFixedRate(::flushBuffer, 0, 5, TimeUnit.SECONDS)
}