From fc46acb7e7b441fe23b9e84652d22a647a0cbe18 Mon Sep 17 00:00:00 2001 From: Konstantin Nisht Date: Fri, 3 Oct 2025 16:56:07 +0200 Subject: [PATCH] [threading] IJPL-206769: Prepare tests for `NonBlockingFlushQueue` GitOrigin-RevId: b756dd73cc2f24aa6a5bc7f6047827eba240006b --- .../src/com/intellij/psi/impl/DocumentCommitThread.kt | 1 - platform/locking.impl/src/NestedLocksThreadingSupport.kt | 3 +++ .../openapi/application/impl/LaterInvocatorTest.java | 8 ++++---- .../intellij/openapi/progress/util/SuvorovProgressTest.kt | 7 ++++--- .../src/com/intellij/testFramework/PlatformTestUtil.java | 8 +++++++- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/platform/ide-core-impl/src/com/intellij/psi/impl/DocumentCommitThread.kt b/platform/ide-core-impl/src/com/intellij/psi/impl/DocumentCommitThread.kt index beb7bfba3bf9..c759c38cbf49 100644 --- a/platform/ide-core-impl/src/com/intellij/psi/impl/DocumentCommitThread.kt +++ b/platform/ide-core-impl/src/com/intellij/psi/impl/DocumentCommitThread.kt @@ -268,7 +268,6 @@ class DocumentCommitThread : DocumentCommitProcessor, Disposable { while (!boundedTaskExecutor.isEmpty || commitInProgressCounter.get() > 0) { try { boundedTaskExecutor.waitAllTasksExecuted(10, TimeUnit.MILLISECONDS) - Thread.sleep(10) } catch (e: TimeoutException) { if (System.nanoTime() > deadLine) { diff --git a/platform/locking.impl/src/NestedLocksThreadingSupport.kt b/platform/locking.impl/src/NestedLocksThreadingSupport.kt index 241771ecc166..595b2c32bbe4 100644 --- a/platform/locking.impl/src/NestedLocksThreadingSupport.kt +++ b/platform/locking.impl/src/NestedLocksThreadingSupport.kt @@ -602,7 +602,10 @@ class NestedLocksThreadingSupport : ThreadingSupport { // In short, we apply the parallelization of write-intent lock here with downgrading of write permits back to write intent. val token = downgradeWriteLockToWriteIntent() val (newState, cleanup) = currentComputationState.parallelizeWriteIntent(thisLevelPermit.get() as WriteIntentPermit) + statesOfWIThread.set(statesOfWIThread.get() ?: mutableListOf()) + statesOfWIThread.get()?.add(newState) return ComputationStateContextElement(newState) to { + statesOfWIThread.get()?.removeLast() cleanup.finish() token.finish() } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/LaterInvocatorTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/LaterInvocatorTest.java index e7e7b16f3666..7624869171d9 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/LaterInvocatorTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/LaterInvocatorTest.java @@ -684,12 +684,12 @@ public class LaterInvocatorTest extends HeavyPlatformTestCase { LaterInvocator.enterModal("some object"); - UIUtil.dispatchAllInvocationEvents(); + PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue(); assertFalse(invoked.get()); LaterInvocator.enterModal(myModalDialog); - UIUtil.dispatchAllInvocationEvents(); + PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue(); assertTrue(invoked.get()); }); } @@ -711,11 +711,11 @@ public class LaterInvocatorTest extends HeavyPlatformTestCase { ApplicationManager.getApplication().invokeLater(() -> invoked.set(true), ModalityState.nonModal()); LaterInvocator.enterModal(myWindow1); - UIUtil.dispatchAllInvocationEvents(); + PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue(); assertFalse(invoked.get()); LaterInvocator.markTransparent(ModalityState.current()); - UIUtil.dispatchAllInvocationEvents(); + PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue(); assertTrue(invoked.get()); }); } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/progress/util/SuvorovProgressTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/progress/util/SuvorovProgressTest.kt index 28be089047c3..8a2452b8836d 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/progress/util/SuvorovProgressTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/openapi/progress/util/SuvorovProgressTest.kt @@ -9,7 +9,6 @@ import com.intellij.testFramework.common.timeoutRunBlocking import com.intellij.testFramework.junit5.RegistryKey import com.intellij.testFramework.junit5.TestApplication import com.intellij.testFramework.junit5.TestDisposable -import com.intellij.util.application import kotlinx.coroutines.* import kotlinx.coroutines.future.asCompletableFuture import org.assertj.core.api.Assertions.assertThat @@ -47,8 +46,10 @@ class SuvorovProgressTest { val edtActionCompleted = Job(coroutineContext.job) launch { backgroundWriteAction { - application.invokeLater { - edtActionCompleted.complete() + launch(Dispatchers.UiWithModelAccess) { + WriteIntentReadAction.run { + edtActionCompleted.complete() + } } writeActionMayFinish.asCompletableFuture().join() } diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java index 7bbcc8664b08..d34d67219ac5 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java @@ -469,6 +469,7 @@ public final class PlatformTestUtil { AtomicBoolean laterInvoked = new AtomicBoolean(); app.invokeLater(() -> laterInvoked.set(true)); dispatchAllInvocationEventsInIdeEventQueue(); + waitForAllDocumentsCommitted(10, TimeUnit.SECONDS); assertTrue(laterInvoked.get()); TimeoutUtil.sleep(sleptAlready ? 10 : delay); @@ -506,9 +507,14 @@ public final class PlatformTestUtil { IdeEventQueue eventQueue = IdeEventQueue.getInstance(); ThreadContext.resetThreadContext(() -> { TestOnlyThreading.releaseTheAcquiredWriteIntentLockThenExecuteActionAndTakeWriteIntentLockBack(() -> { + // due to non-blocking acquisition of write-intent, `NonBlockingFlushQueue` can appear in the state + // where it has stuck WI runnables. This method is called to ensure that _all_ runnables are dispatched, + // so we also want to wait for WI runnables here + AtomicBoolean canary = new AtomicBoolean(false); + ApplicationManager.getApplication().invokeLater(() -> canary.set(true), ModalityState.any()); while (true) { AWTEvent event = eventQueue.peekEvent(); - if (event == null) break; + if (event == null && canary.get()) break; event = eventQueue.getNextEvent(); if (event instanceof InvocationEvent) { eventQueue.dispatchEvent(event);