You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
GPMC counts idle threads in threadpools as actively running and reports deadlock on them.
Problem
Minimal reproducing test (note that writing explicit pool closing such as pool.close() or pool.use {...} solves the issue, but the gpmc still explores schedulings of the idle threads, which is probably not what we want)
@OptIn(ExperimentalModelCheckingAPI::class)
classDaemonThreadPoolTest {
@Test
funtestUselessThreadPool() {
runConcurrentTest(1000) {
val pool =Executors.newFixedThreadPool(1).asCoroutineDispatcher()
runBlocking(pool) { /* do nothing */ }
// pool.close() -- this call tells gpmc that threads are not in infinite spinning,// but interleavings exploration still happens
}
}
}
Output:
= Concurrent test has hung =
The following interleaving leads to the error:
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- || Thread 1 | Thread 2 || ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- || testUselessThreadPool$1.invoke() at DaemonThreadPoolTest$testUselessThreadPool$1.invoke(DaemonThreadPoolTest.kt:24) | |
| BuildersKt.runBlocking(ExecutorCoroutineDispatcherImpl#1,<cont>): Unit#1 at DaemonThreadPoolTest$testUselessThreadPool$1.invoke(DaemonThreadPoolTest.kt:26) | |
| BuildersKt__BuildersKt.runBlocking(ExecutorCoroutineDispatcherImpl#1,<cont>): Unit#1 at BuildersKt.runBlocking(:1) | || CoroutineContextKt.newCoroutineContext(GlobalScope#1,ExecutorCoroutineDispatcherImpl#1): CombinedContext#1 at BuildersKt__BuildersKt.runBlocking(Builders.kt:55) | || <cont>.getParentHandle$kotlinx_coroutines_core(): null at JobSupport.initParentJob(JobSupport.kt:145) | |
| <cont>.setParentHandle$kotlinx_coroutines_core(NonDisposableHandle#1) at JobSupport.initParentJob(JobSupport.kt:147) | |
| <cont>.start(DEFAULT,<cont>,<cont>) at BuildersKt__BuildersKt.runBlocking(Builders.kt:58) | || <cont>.joinBlocking(): Unit#1 at BuildersKt__BuildersKt.runBlocking(Builders.kt:59) | || AbstractTimeSourceKt.getTimeSource(): null at BlockingCoroutine.joinBlocking(Builders.kt:78) | || /* The following events repeat infinitely: */ | || ┌╶> isCompleted(): false at BlockingCoroutine.joinBlocking(Builders.kt:87) | || | AbstractTimeSourceKt.getTimeSource(): null at BlockingCoroutine.joinBlocking(Builders.kt:88) | || | LockSupport.parkNanos(<cont>,9223372036854775807) at BlockingCoroutine.joinBlocking(Builders.kt:88) | || └╶╶ switch (reason: active lock detected) | || | run() || | ThreadPoolExecutor#1.runWorker(Worker#1) at ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) |
| | Worker#1.firstTask.READ: <cont> at ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1117) || | Worker#1.firstTask.WRITE(null) at ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1118) || | Worker#1.unlock() at ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1119) || | Worker#1.lock() at ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1123) || | AtomicInteger#1.get(): -536870911 at ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) || | <cont>.run() at ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) || | Worker#1.completedTasks.READ: 0 at ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) || | Worker#1.completedTasks.WRITE(1) at ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) || | Worker#1.unlock() at ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) || | getTask() at ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1122) || | AtomicInteger#1.get(): -536870911 at ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1038) || | allowCoreThreadTimeOut.READ: false at ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1050) || | corePoolSize.READ: 1 at ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1050) || | maximumPoolSize.READ: 1 at ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1052) || | LinkedBlockingQueue#1.take() at ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1062) || | ReentrantLock#2.lockInterruptibly() at LinkedBlockingQueue.take(LinkedBlockingQueue.java:432) || | AtomicInteger#3.get(): 0 at LinkedBlockingQueue.take(LinkedBlockingQueue.java:434) || | ConditionObject#1.await() at LinkedBlockingQueue.take(LinkedBlockingQueue.java:435) || | enableWait(ConditionNode#1): 1 at AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1618) |
| | LockSupport.setCurrentBlocker(ConditionObject#1) at AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1619) |
| | canReacquire(ConditionNode#1): false at AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1621) |
| | ConditionNode#1.status.READ: 3 at AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1625) |
| | ForkJoinPool.managedBlock(ConditionNode#1) at AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1630) |
| | ForkJoinPool.unmanagedBlock(ConditionNode#1) at ForkJoinPool.managedBlock(ForkJoinPool.java:3436) || | ConditionNode#1.isReleasable(): false at ForkJoinPool.unmanagedBlock(ForkJoinPool.java:3465) || | ConditionNode#1.block() at ForkJoinPool.unmanagedBlock(ForkJoinPool.java:3465) || | /* The following events repeat infinitely: */ || | ┌╶> isReleasable(): false at AbstractQueuedSynchronizer$ConditionNode.block(AbstractQueuedSynchronizer.java:506) |
| | | LockSupport.park() at AbstractQueuedSynchronizer$ConditionNode.block(AbstractQueuedSynchronizer.java:506) |
| | └╶╶ switch (reason: active lock detected) || isCompleted(): true at BlockingCoroutine.joinBlocking(Builders.kt:87) | || AbstractTimeSourceKt.getTimeSource(): null at BlockingCoroutine.joinBlocking(Builders.kt:94) | || getState$kotlinx_coroutines_core(): Unit#1 at BlockingCoroutine.joinBlocking(Builders.kt:97) | |
| result: kotlin.Unit | || | /* The following events repeat infinitely: */ || | ┌╶> isReleasable(): false at AbstractQueuedSynchronizer$ConditionNode.block(AbstractQueuedSynchronizer.java:506) |
| | | LockSupport.park() at AbstractQueuedSynchronizer$ConditionNode.block(AbstractQueuedSynchronizer.java:506) |
| | └╶╶ switch (reason: active lock detected) || ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
All unfinished threads are in deadlock
Expected behaviour
GPMC should not report deadlock on daemon threads
GPMC should not explaore interleavings of idle threads
The text was updated successfully, but these errors were encountered:
GPMC counts idle threads in threadpools as actively running and reports deadlock on them.
Problem
Minimal reproducing test (note that writing explicit pool closing such as
pool.close()
orpool.use {...}
solves the issue, but the gpmc still explores schedulings of the idle threads, which is probably not what we want)Output:
Expected behaviour
The text was updated successfully, but these errors were encountered: