Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-closed threadpool treated as inifinitely running #542

Open
dmitrii-artuhov opened this issue Feb 20, 2025 · 0 comments
Open

Non-closed threadpool treated as inifinitely running #542

dmitrii-artuhov opened this issue Feb 20, 2025 · 0 comments

Comments

@dmitrii-artuhov
Copy link
Collaborator

dmitrii-artuhov commented Feb 20, 2025

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)
class DaemonThreadPoolTest {
    @Test
    fun testUselessThreadPool() {
        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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants