From 79476d985ad94df3e460db3bc039cea0ed18616d Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 22 Jul 2024 11:58:07 +0200 Subject: [PATCH] IJPL-158385 SingleAlarm with parent disposable must ignore request after disposal GitOrigin-RevId: c33800ea85559d26da07ba0d8db5a28a50289759 --- .../src/com/intellij/util/SingleAlarm.kt | 11 ++++++++-- .../testSrc/com/intellij/util/AlarmTest.kt | 22 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/platform/ide-core/src/com/intellij/util/SingleAlarm.kt b/platform/ide-core/src/com/intellij/util/SingleAlarm.kt index b453e3355163..5eaa46770224 100644 --- a/platform/ide-core/src/com/intellij/util/SingleAlarm.kt +++ b/platform/ide-core/src/com/intellij/util/SingleAlarm.kt @@ -292,6 +292,10 @@ class SingleAlarm @Internal constructor( } private fun request(forceRun: Boolean, delay: Int, cancelCurrent: Boolean = false) { + if (isDisposed) { + return + } + val effectiveDelay = if (forceRun) 0 else delay.toLong() synchronized(LOCK) { var prevCurrentJob = currentJob @@ -307,11 +311,14 @@ class SingleAlarm @Internal constructor( prevCurrentJob = null delay(effectiveDelay) + withContext(taskContext) { //todo fix clients and remove NonCancellable try { - Cancellation.withNonCancelableSection().use { - task.run() + if (!isDisposed) { + Cancellation.withNonCancelableSection().use { + task.run() + } } } catch (e: CancellationException) { diff --git a/platform/platform-tests/testSrc/com/intellij/util/AlarmTest.kt b/platform/platform-tests/testSrc/com/intellij/util/AlarmTest.kt index ee215033cfae..130c8edd7203 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/AlarmTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/util/AlarmTest.kt @@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException +import java.util.concurrent.atomic.AtomicInteger @TestApplication class AlarmTest { @@ -38,6 +39,27 @@ class AlarmTest { } } + @Test + fun `SingleAlarm with parent disposable must ignore request after disposal`() { + val disposable = Disposer.newDisposable() + disposable.use { + val counter = AtomicInteger() + @Suppress("DEPRECATION") + val alarm = SingleAlarm.pooledThreadSingleAlarm(delay = 1, parentDisposable = disposable) { + counter.incrementAndGet() + } + + alarm.request() + alarm.waitForAllExecuted(1, TimeUnit.SECONDS) + assertThat(counter.get()).isEqualTo(1) + Disposer.dispose(disposable) + + alarm.request() + alarm.waitForAllExecuted(1, TimeUnit.SECONDS) + assertThat(counter.get()).isEqualTo(1) + } + } + @Test fun `cancel request by task`(@TestDisposable disposable: Disposable): Unit = runBlocking(Dispatchers.EDT) { val alarm = Alarm(threadToUse = Alarm.ThreadToUse.SWING_THREAD, parentDisposable = disposable)