[platform] IJ-CR-152034 QD-10403: Cleanup canceled invokeLater

(cherry picked from commit 970a71bb513fcd2029bff2f237a342c4737fb779)

GitOrigin-RevId: 50865014a604a94de21938b9f2d8b6fc635fc0f7
This commit is contained in:
Konstantin Nisht
2024-11-28 17:07:49 +01:00
committed by intellij-monorepo-bot
parent 363e9973c0
commit d173a80534
2 changed files with 20 additions and 7 deletions

View File

@@ -11,6 +11,7 @@ import com.intellij.platform.ide.progress.TaskCancellation
import com.intellij.testFramework.common.timeoutRunBlocking
import com.intellij.testFramework.junit5.SystemProperty
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.util.application
import com.intellij.util.getValue
import com.intellij.util.setValue
import kotlinx.coroutines.*
@@ -350,4 +351,17 @@ class ThreadContextPropagationTest {
}
Assertions.assertTrue(tracker.get())
}
@Test
fun `cancellation of invokeLater triggers cleanup events`() = timeoutRunBlocking {
val tracker = AtomicBoolean(false)
val expiration = AtomicBoolean(false)
withContext(Dispatchers.EDT + MyIjElement(tracker)) {
@Suppress("ForbiddenInSuspectContextMethod")
application.invokeLater({ Assertions.fail() }, { expiration.get() })
expiration.set(true)
}
delay(100)
Assertions.assertTrue(tracker.get())
}
}

View File

@@ -338,10 +338,8 @@ fun capturePropagationContext(r: Runnable, expired: Condition<*>, signalRunnable
var expired = expired
command = ContextRunnable(childContext, command)
val cont = childContext.continuation
if (cont != null) {
val childJob = cont.context.job
expired = cancelIfExpired(expired, childJob)
}
val childJob = cont?.context?.job
expired = cleanupIfExpired(expired, childContext, childJob)
return JBPair.create(command, expired)
}
@@ -353,17 +351,18 @@ fun <T, U> captureBiConsumerThreadContext(f: BiConsumer<T, U>): BiConsumer<T, U>
return f
}
private fun <T> cancelIfExpired(expiredCondition: Condition<in T>, childJob: Job): Condition<T> {
private fun <T> cleanupIfExpired(expiredCondition: Condition<in T>, childContext: ChildContext, childJob: Job?): Condition<T> {
return Condition { t: T ->
val expired = expiredCondition.value(t)
if (expired) {
// Cancel to avoid a hanging child job which will prevent completion of the parent one.
childJob.cancel(null)
childJob?.cancel(null)
childContext.applyContextActions(false).finish()
true
}
else {
// Treat runnable as expired if its job was already cancelled.
childJob.isCancelled
childJob?.isCancelled == true
}
}
}