diff --git a/platform/backend/observation/src/com/intellij/platform/backend/observation/PlatformActivityTrackerService.kt b/platform/backend/observation/src/com/intellij/platform/backend/observation/PlatformActivityTrackerService.kt index 656f0de29b75..1c5554f930bb 100644 --- a/platform/backend/observation/src/com/intellij/platform/backend/observation/PlatformActivityTrackerService.kt +++ b/platform/backend/observation/src/com/intellij/platform/backend/observation/PlatformActivityTrackerService.kt @@ -81,7 +81,7 @@ internal class PlatformActivityTrackerService(private val scope: CoroutineScope) fun trackConfigurationActivityBlocking(kind: ActivityKey, action: () -> T): T { val currentContext = currentThreadContext() return withObservationTracker(kind) { observationTracker -> - installThreadContext(currentContext + observationTracker, true).use { + installThreadContext(currentContext + observationTracker, true) { action() } } diff --git a/platform/core-api/src/com/intellij/model/SideEffectGuard.kt b/platform/core-api/src/com/intellij/model/SideEffectGuard.kt index 3ffb3dfd471a..78df451019d4 100644 --- a/platform/core-api/src/com/intellij/model/SideEffectGuard.kt +++ b/platform/core-api/src/com/intellij/model/SideEffectGuard.kt @@ -38,7 +38,7 @@ interface SideEffectGuard { @JvmStatic fun computeWithAllowedSideEffectsBlocking(effects: EnumSet, action: () -> T): T { val context = currentThreadContext() - return installThreadContext(context + AllowedSideEffectsElement(effects), replace = true).use { + return installThreadContext(context + AllowedSideEffectsElement(effects), replace = true) { action() } } diff --git a/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt b/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt index 4c1a51ed2f60..5a6151756a70 100644 --- a/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt +++ b/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt @@ -160,7 +160,7 @@ private fun runBlockingCancellable(allowOrphan: Boolean, compensateParalleli private fun getLockContext(currentThreadContext: CoroutineContext): Pair { val parallelize = with(ApplicationManager.getApplication()) { - installThreadContext(currentThreadContext).use { + installThreadContext(currentThreadContext) { isReadAccessAllowed } } @@ -303,7 +303,7 @@ suspend fun blockingContextScope(action: () -> T): T { fun withCurrentThreadCoroutineScopeBlocking(action: () -> T): Pair { val currentContext = currentThreadContext() val checkpoint = getFixThreadScopeElements(currentContext) - return installThreadContext(currentContext + checkpoint, true).use { + return installThreadContext(currentContext + checkpoint, true) { val actionResult = try { action() } @@ -428,7 +428,7 @@ fun CoroutineContext.prepareForInstallation(): CoroutineContext = this.minusKey( @Throws(ProcessCanceledException::class) internal fun blockingContextInner(currentContext: CoroutineContext, action: () -> T): T { val context = currentContext.prepareForInstallation() - return installThreadContext(context).use { + return installThreadContext(context) { action() } } diff --git a/platform/core-impl/src/com/intellij/codeInsight/multiverse/CodeInsightContextManagerImpl.kt b/platform/core-impl/src/com/intellij/codeInsight/multiverse/CodeInsightContextManagerImpl.kt index c2461a534700..f061ed772a98 100644 --- a/platform/core-impl/src/com/intellij/codeInsight/multiverse/CodeInsightContextManagerImpl.kt +++ b/platform/core-impl/src/com/intellij/codeInsight/multiverse/CodeInsightContextManagerImpl.kt @@ -84,8 +84,8 @@ class CodeInsightContextManagerImpl( override fun performCodeInsightSession(context: CodeInsightContext, block: CodeInsightSession.() -> Result): Result { val session = CodeInsightSessionImpl(context) - installThreadContext(currentThreadContext() + CodeInsightSessionElement(session)).use { - return block(session) + return installThreadContext(currentThreadContext() + CodeInsightSessionElement(session)) { + block(session) } } diff --git a/platform/core-impl/src/com/intellij/openapi/progress/impl/ProgressRunner.java b/platform/core-impl/src/com/intellij/openapi/progress/impl/ProgressRunner.java index 31142458c4a2..f0b4170260a6 100644 --- a/platform/core-impl/src/com/intellij/openapi/progress/impl/ProgressRunner.java +++ b/platform/core-impl/src/com/intellij/openapi/progress/impl/ProgressRunner.java @@ -476,9 +476,10 @@ public final class ProgressRunner { childContext.runInChildContext(() -> { CoroutineContext effectiveContext = ThreadContext.currentThreadContext().plus(asContextElement(progressIndicator.getModalityState()).plus(sharedPermit)); - try (AccessToken ignored = ThreadContext.installThreadContext(effectiveContext, true)) { + ThreadContext.installThreadContext(effectiveContext, true, () -> { runnable.run(); - } + return Unit.INSTANCE; + }); }); }; switch (myThreadToUse) { diff --git a/platform/execution-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.kt b/platform/execution-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.kt index d9c60e0d610f..c1956051baaf 100644 --- a/platform/execution-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.kt +++ b/platform/execution-impl/src/com/intellij/execution/impl/ExecutionManagerImpl.kt @@ -94,12 +94,21 @@ open class ExecutionManagerImpl(private val project: Project, coroutineScope: Co return currentThreadContext()[EnvDataContextElement]?.dataContext } + @Suppress("unused") // used in 3rd party plugin @Internal fun withEnvironmentDataContext(dataContext: DataContext?): AccessToken { val context = currentThreadContext() return installThreadContext(context + EnvDataContextElement(dataContext), true) } + @Internal + fun withEnvironmentDataContext(dataContext: DataContext?, action: () -> T): T { + val context = currentThreadContext() + return installThreadContext(context + EnvDataContextElement(dataContext), true).use { + action() + } + } + private class EnvDataContextElement(val dataContext: DataContext?) : CoroutineContext.Element, IntelliJContextElement { companion object : CoroutineContext.Key @@ -731,7 +740,7 @@ open class ExecutionManagerImpl(private val project: Project, coroutineScope: Co @ApiStatus.Internal fun executeConfiguration(environment: ExecutionEnvironment, showSettings: Boolean, assignNewId: Boolean = true) { - withEnvironmentDataContext(environment.dataContext).use { + withEnvironmentDataContext(environment.dataContext) { val runnerAndConfigurationSettings = environment.runnerAndConfigurationSettings val project = environment.project val runner = environment.runner @@ -741,14 +750,14 @@ open class ExecutionManagerImpl(private val project: Project, coroutineScope: Co handleExecutionError(environment, ExecutionException( ProgramRunnerUtil.getCannotRunOnErrorMessage(environment.runProfile, environment.executionTarget))) processNotStarted(environment, null) - return + return@withEnvironmentDataContext } if (!DumbService.isDumb(project)) { if (showSettings && runnerAndConfigurationSettings.isEditBeforeRun) { if (!RunDialog.editConfiguration(environment, ExecutionBundle.message("dialog.title.edit.configuration", 0))) { processNotStarted(environment, null) - return + return@withEnvironmentDataContext } editConfigurationUntilSuccess(environment, assignNewId) } @@ -777,7 +786,7 @@ open class ExecutionManagerImpl(private val project: Project, coroutineScope: Co .expireWith(this) .submit(AppExecutorUtil.getAppExecutorService()) } - return + return@withEnvironmentDataContext } } diff --git a/platform/execution-impl/src/com/intellij/execution/impl/RunnerAndConfigurationSettingsImpl.kt b/platform/execution-impl/src/com/intellij/execution/impl/RunnerAndConfigurationSettingsImpl.kt index 9895bcb937f4..3146953e576c 100644 --- a/platform/execution-impl/src/com/intellij/execution/impl/RunnerAndConfigurationSettingsImpl.kt +++ b/platform/execution-impl/src/com/intellij/execution/impl/RunnerAndConfigurationSettingsImpl.kt @@ -359,7 +359,7 @@ class RunnerAndConfigurationSettingsImpl @JvmOverloads constructor( var warning = ReadAction.nonBlocking { try { - ExecutionManagerImpl.withEnvironmentDataContext(dataContext).use { + ExecutionManagerImpl.withEnvironmentDataContext(dataContext) { configuration.checkConfiguration() } } diff --git a/platform/locking.impl/src/NestedLocksThreadingSupport.kt b/platform/locking.impl/src/NestedLocksThreadingSupport.kt index af5880dae8e1..c33db65df4c1 100644 --- a/platform/locking.impl/src/NestedLocksThreadingSupport.kt +++ b/platform/locking.impl/src/NestedLocksThreadingSupport.kt @@ -1397,7 +1397,7 @@ class NestedLocksThreadingSupport : ThreadingSupport { finally { // non-cancellable section here because we need to prohibit prompt cancellation of lock acquisition in this `finally` // otherwise the outer release in `runWriteIntentReadAction` would fail with NPE - installThreadContext(currentThreadContext().minusKey(Job), true).use { + installThreadContext(currentThreadContext().minusKey(Job), true) { state.acquireWriteIntentPermit() } } diff --git a/platform/platform-impl/concurrency/src/concurrency/JobLauncherImpl.java b/platform/platform-impl/concurrency/src/concurrency/JobLauncherImpl.java index 48efc997fdfd..c733af155484 100644 --- a/platform/platform-impl/concurrency/src/concurrency/JobLauncherImpl.java +++ b/platform/platform-impl/concurrency/src/concurrency/JobLauncherImpl.java @@ -1,7 +1,6 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.concurrency; -import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ex.ApplicationUtil; import com.intellij.openapi.diagnostic.Logger; @@ -397,9 +396,16 @@ public final class JobLauncherImpl extends JobLauncher { result[0] = true; break; } - try (AccessToken ignored = ThreadContext.installThreadContext(myContext, true)) { - ProgressManager.checkCanceled(); - if (!thingProcessor.process(element)) { + try { + T finalElement = element; + boolean shouldBreak = ThreadContext.installThreadContext(myContext, true, () -> { + ProgressManager.checkCanceled(); + if (!thingProcessor.process(finalElement)) { + return true; + } + return false; + }); + if (shouldBreak) { break; } } diff --git a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.kt b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.kt index 0fe51dcbb05d..4faf8816f604 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.kt +++ b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.kt @@ -794,7 +794,7 @@ class IdeEventQueue private constructor() : EventQueue() { // the manual call of the former event's dispatch() is required here because EventQueue.invokeAndWait() expects // that the invocation event's notifier is signaled and isDispatched() == true. // If not dispatch the original event, it hangs forever - installThreadContext(captured).use { + installThreadContext(captured) { event.dispatch() } }) diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.kt b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.kt index 31fe0510cae5..d95c4c6ea68a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.kt +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.kt @@ -1159,7 +1159,7 @@ open class ActionManagerImpl protected constructor(private val coroutineScope: C ClientId.coroutineContext() + ActionContextElement.create(actionId, event.place, event.inputEvent, component) val coroutineContext2 = coroutineContext + ThreadScopeCheckpoint(coroutineContext) // permit `currentThreadCoroutineScope` inside - installThreadContext(coroutineContext2.minusKey(ContinuationInterceptor), replace = true).use { _ -> + installThreadContext(coroutineContext2.minusKey(ContinuationInterceptor), replace = true) { SlowOperations.startSection(SlowOperations.ACTION_PERFORM).use { _ -> runnable.run() } diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java index 001543b0be4d..7d5d0658312a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java @@ -53,8 +53,6 @@ import com.intellij.ui.ComponentUtil; import com.intellij.ui.scale.JBUIScale; import com.intellij.util.*; import com.intellij.util.concurrency.*; -import com.intellij.util.concurrency.annotations.RequiresBackgroundThread; -import com.intellij.util.concurrency.annotations.RequiresWriteLock; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.messages.Topic; import com.intellij.util.ui.EDT; @@ -69,7 +67,6 @@ import org.jetbrains.annotations.*; import javax.swing.*; import java.awt.*; -import java.lang.reflect.InvocationTargetException; import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; @@ -567,10 +564,10 @@ public final class ApplicationImpl extends ClientAwareComponentManager implement new Runnable() { @Override public void run() { - try (AccessToken ignored = ThreadContext.installThreadContext( - ThreadContext.currentThreadContext().plus(asContextElement(modalityState)), true)) { + ThreadContext.installThreadContext(ThreadContext.currentThreadContext().plus(asContextElement(modalityState)), true, () -> { runIntendedWriteActionOnCurrentThread(runnable); - } + return Unit.INSTANCE; + }); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/NonBlockingReadActionImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/NonBlockingReadActionImpl.java index 273b32edf96e..d216f564328e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/NonBlockingReadActionImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/NonBlockingReadActionImpl.java @@ -481,9 +481,9 @@ public final class NonBlockingReadActionImpl implements NonBlockingReadAction try { boolean computationSuccessful; if (AppExecutorUtil.propagateContext()) { - try (AccessToken ignored = ThreadContext.installThreadContext(myChildContext.getContext(), true)) { - computationSuccessful = attemptComputation(); - } + computationSuccessful = ThreadContext.installThreadContext(myChildContext.getContext(), true, () -> { + return attemptComputation(); + }); } else { computationSuccessful = attemptComputation(); } @@ -519,9 +519,10 @@ public final class NonBlockingReadActionImpl implements NonBlockingReadAction else { context = ThreadContext.currentThreadContext(); } - try (AccessToken ignored = ThreadContext.installThreadContext(context, true)) { + ThreadContext.installThreadContext(context, true, () -> { attemptComputation(); - } + return Unit.INSTANCE; + }); if (isDone()) { if (isCancelled()) { @@ -748,9 +749,10 @@ public final class NonBlockingReadActionImpl implements NonBlockingReadAction if (isSucceeded()) { // in case when another thread managed to cancel it just before `setResult` try { if (AppExecutorUtil.propagateContext()) { - try (AccessToken ignored = ThreadContext.installThreadContext(myChildContext.getContext(), false)) { + ThreadContext.installThreadContext(myChildContext.getContext(), false, () -> { builder.myUiThreadAction.accept(result); - } + return Unit.INSTANCE; + }); } else { builder.myUiThreadAction.accept(result); } diff --git a/platform/platform-impl/src/com/intellij/openapi/application/rw/cancellableReadAction.kt b/platform/platform-impl/src/com/intellij/openapi/application/rw/cancellableReadAction.kt index 13165aebcc61..2b3a998822c3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/rw/cancellableReadAction.kt +++ b/platform/platform-impl/src/com/intellij/openapi/application/rw/cancellableReadAction.kt @@ -21,7 +21,7 @@ internal fun cancellableReadActionInternal(ctx: CoroutineContext, action: () // A child Job is started to be externally cancellable by a write action without cancelling the current Job. val readJob = Job(parent = ctx[Job]) return try { - installThreadContext(ctx.prepareForInstallation() + readJob).use { + installThreadContext(ctx.prepareForInstallation() + readJob) { var resultRef: Value? = null val application = ApplicationManagerEx.getApplicationEx() val cancellation = CannotReadException.jobCancellation(readJob) diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java index cae0ef6de591..1639c748222a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java @@ -443,9 +443,10 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer { ? new Pair<>(EmptyCoroutineContext.INSTANCE, emptyFunction) : IntelliJLockingUtil.getGlobalThreadingSupport().getPermitAsContextElement(ThreadContext.currentThreadContext(), true); lockContextWrapper = (r) -> { - try (AccessToken ignored = ThreadContext.installThreadContext(pair.getFirst(), true)) { + ThreadContext.installThreadContext(pair.getFirst(), true, () -> { r.run(); - } + return Unit.INSTANCE; + }); }; lockCleanup = pair.getSecond(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ProjectFrameHelper.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ProjectFrameHelper.kt index 3bac5b0402a5..6e1a19d6e15a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ProjectFrameHelper.kt +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ProjectFrameHelper.kt @@ -631,7 +631,7 @@ private object WindowCloseListener : WindowAdapter() { if (app != null && !app.isDisposed) { // The project closing process is also subject to cancellation checks. // Here we run the closing process in the scope of the application, so that the user gets the chance to abort a project closing process. - installThreadContext(service().coroutineScope.coroutineContext).use { + installThreadContext(service().coroutineScope.coroutineContext) { frameHelper.windowClosing(project) } } diff --git a/platform/platform-tests/testSrc/com/intellij/concurrency/ThreadContextTest.kt b/platform/platform-tests/testSrc/com/intellij/concurrency/ThreadContextTest.kt index d4b9011ff5b1..df850b0ba653 100644 --- a/platform/platform-tests/testSrc/com/intellij/concurrency/ThreadContextTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/concurrency/ThreadContextTest.kt @@ -12,7 +12,7 @@ class ThreadContextTest { @Test fun `reset context`() { assertNull(currentThreadContextOrNull()) - installThreadContext(EmptyCoroutineContext).use { + installThreadContext(EmptyCoroutineContext) { assertNotNull(currentThreadContextOrNull()) resetThreadContext() { assertNull(currentThreadContextOrNull()) @@ -25,17 +25,17 @@ class ThreadContextTest { @Test fun `replace context`() { val outerElement1 = TestElement("outer1") - installThreadContext(outerElement1).use { + installThreadContext(outerElement1) { assertSame(currentThreadContext(), outerElement1) val innerElement1 = TestElement("inner1") - installThreadContext(innerElement1, replace = true).use { + installThreadContext(innerElement1, replace = true) { assertSame(currentThreadContext(), innerElement1) } assertSame(currentThreadContext(), outerElement1) val innerElement2 = TestElement2("inner2") - installThreadContext(innerElement2, replace = true).use { + installThreadContext(innerElement2, replace = true) { assertSame(currentThreadContext(), innerElement2) } assertSame(currentThreadContext(), outerElement1) diff --git a/platform/platform-tests/testSrc/com/intellij/execution/wsl/WslDistributionSafeNullableLazyValueTest.java b/platform/platform-tests/testSrc/com/intellij/execution/wsl/WslDistributionSafeNullableLazyValueTest.java index a449a9b8f57d..045424857592 100644 --- a/platform/platform-tests/testSrc/com/intellij/execution/wsl/WslDistributionSafeNullableLazyValueTest.java +++ b/platform/platform-tests/testSrc/com/intellij/execution/wsl/WslDistributionSafeNullableLazyValueTest.java @@ -1,6 +1,7 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.execution.wsl; +import com.intellij.concurrency.ThreadContext; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; @@ -87,9 +88,7 @@ public final class WslDistributionSafeNullableLazyValueTest { return CompletableFuture .supplyAsync( () -> { - try (var ignored = installThreadContext(coroutineContext, true)) { - return lazyValue.getValue(); - } + return ThreadContext.installThreadContext(coroutineContext, true, () -> lazyValue.getValue()); }, AppExecutorUtil.getAppExecutorService()) .get(WAIT_TIMEOUT_IN_SECONDS, SECONDS); @@ -232,11 +231,11 @@ public final class WslDistributionSafeNullableLazyValueTest { })); EdtTestUtil.runInEdtAndGet(() -> { - try (var ignored = installThreadContext(coroutineContext, false)) { + return ThreadContext.installThreadContext(coroutineContext, false, () -> { final String result = lazyValue.getValueOrElse("not yet"); Assertions.assertThat(result).isEqualTo("not yet"); return null; - } + }); }); error.join(); @@ -276,19 +275,20 @@ public final class WslDistributionSafeNullableLazyValueTest { return CompletableFuture .allOf( CompletableFuture.runAsync(() -> { - try (var ignored = installThreadContext(coroutineContext, false)) { + ThreadContext.installThreadContext(coroutineContext, false, () -> { try { lazyValue.getValue(); ProgressManager.checkCanceled(); } catch (ProcessCanceledException e) { - return; + return null; } Assertions.fail("This line must not execute, because the progress must have been cancelled"); - } + return null; + }); }), CompletableFuture.runAsync(() -> { - try (var ignored = installThreadContext(coroutineContext, false)) { + installThreadContext(coroutineContext, false, () -> { ProgressManager.checkCanceled(); // Should not be canceled by the moment. try { Thread.sleep(100); @@ -297,7 +297,8 @@ public final class WslDistributionSafeNullableLazyValueTest { // Nothing. } getJob(currentThreadContext()).cancel(new CancellationException()); - } + return null; + }); }) ).get(); })); @@ -318,11 +319,12 @@ public final class WslDistributionSafeNullableLazyValueTest { return prepareThreadContext(coroutineContext -> safeRethrow(() -> { CompletableFuture .runAsync(() -> safeRethrow(() -> { - try (var ignored = installThreadContext(coroutineContext, false)) { + installThreadContext(coroutineContext, false, () -> { Assertions.assertThat(lazyValue.getValue()).isEqualTo("finished"); ProgressManager.checkCanceled(); // Should not throw. return null; - } + }); + return null; })) .get(); return null; @@ -408,12 +410,14 @@ public final class WslDistributionSafeNullableLazyValueTest { try { return ProgressManager.getInstance().runProcess( () -> prepareThreadContext(coroutineContext -> { - try (var ignored = installThreadContext(coroutineContext, true)) { - return body.compute(); - } - catch (Exception err) { - throw new Holder(err); - } + return installThreadContext(coroutineContext, true, () -> { + try { + return body.compute(); + } + catch (Exception err) { + throw new Holder(err); + } + }); }), new ProgressIndicatorBase()); } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/actionSystem/ActionUpdaterTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/actionSystem/ActionUpdaterTest.kt index 9094d9bf46fe..e1a60a6adad1 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/actionSystem/ActionUpdaterTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/openapi/actionSystem/ActionUpdaterTest.kt @@ -279,7 +279,7 @@ class ActionUpdaterTest { e!! val actualElement = currentThreadContext()[MyContextElement] assertEquals(1, actualElement?.value, "MyContextElement must be propagated to getChildren") - installThreadContext(currentThreadContext() + MyContextElement(2), true).use { + installThreadContext(currentThreadContext() + MyContextElement(2), true) { e.updateSession.sharedData(key) { val actualElement = currentThreadContext()[MyContextElement] assertEquals(2, actualElement?.value, "MyContextElement must be propagated to sharedData") diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/progress/DefaultModalityTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/progress/DefaultModalityTest.kt index 65150b08ec70..f2394d41d7e0 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/progress/DefaultModalityTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/openapi/progress/DefaultModalityTest.kt @@ -65,7 +65,7 @@ class DefaultModalityTest : CancellationTest() { val outerModality = createFakeModality() val nestedModality = createFakeModality() val nestedModality2 = createFakeModality() - installThreadContext(outerModality.asContextElement()).use { + installThreadContext(outerModality.asContextElement()) { assertModality(outerModality) withIndicator(EmptyProgressIndicator(nestedModality)) { assertModality(nestedModality) // IJPL-155640 diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/progress/ExistingThreadContextTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/progress/ExistingThreadContextTest.kt index 11641c7096d3..aded28ecebf8 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/progress/ExistingThreadContextTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/openapi/progress/ExistingThreadContextTest.kt @@ -28,7 +28,7 @@ class ExistingThreadContextTest : CancellationTest() { @Test fun context() { val tc = Dispatchers.Default + TestElement("e") - installThreadContext(tc).use { + installThreadContext(tc) { assertSame(tc, currentThreadContextOrNull()) prepareThreadContext { prepared -> assertNull(currentThreadContextOrNull()) @@ -42,7 +42,7 @@ class ExistingThreadContextTest : CancellationTest() { fun cancellation() { val t = object : Throwable() {} val ce = assertThrows { - installThreadContext(Job()).use { + installThreadContext(Job()) { throw assertThrows { prepareThreadContextTest { currentJob -> testNoExceptions() @@ -67,7 +67,7 @@ class ExistingThreadContextTest : CancellationTest() { val job = Job() val t = Throwable() val ce = assertThrows { - installThreadContext(job).use { + installThreadContext(job) { throw assertThrows { prepareThreadContextTest { currentJob -> testNoExceptions() diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/progress/prepareThreadContextTests.kt b/platform/platform-tests/testSrc/com/intellij/openapi/progress/prepareThreadContextTests.kt index e42d4ce61024..47c437d6a3f7 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/progress/prepareThreadContextTests.kt +++ b/platform/platform-tests/testSrc/com/intellij/openapi/progress/prepareThreadContextTests.kt @@ -71,7 +71,7 @@ fun prepareThreadContextTest(action: (Job) -> T): T { assertNull(currentThreadContextOrNull()) assertNull(ProgressManager.getGlobalProgressIndicator()) val job = assertNotNull(ctx[Job]) - installThreadContext(ctx).use { + installThreadContext(ctx) { assertSame(job, Cancellation.currentJob()) action(job) } diff --git a/platform/platform-tests/testSrc/com/intellij/util/concurrency/CancellationPropagationTest.kt b/platform/platform-tests/testSrc/com/intellij/util/concurrency/CancellationPropagationTest.kt index 646c989f2b49..d653ac7b22d9 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/concurrency/CancellationPropagationTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/util/concurrency/CancellationPropagationTest.kt @@ -141,7 +141,7 @@ class CancellationPropagationTest { @Test fun `expired invokeLater does not prevent completion of parent job`(): Unit = timeoutRunBlocking(60.seconds) { - installThreadContext(coroutineContext).use { + installThreadContext(coroutineContext) { val expired = AtomicBoolean(false) ApplicationManager.getApplication().withModality { val runnable = Runnable { @@ -934,7 +934,7 @@ class CancellationPropagationTest { assertTrue(Cancellation.isInNonCancelableSection()) } Cancellation.executeInNonCancelableSection { - installThreadContext(Job(currentThreadContext().job), true).use { + installThreadContext(Job(currentThreadContext().job), true) { assertFalse(Cancellation.isInNonCancelableSection()) } } diff --git a/platform/platform-tests/testSrc/com/intellij/util/concurrency/ClientIdPropagationTest.kt b/platform/platform-tests/testSrc/com/intellij/util/concurrency/ClientIdPropagationTest.kt index 44be476a3414..cc937223aa7e 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/concurrency/ClientIdPropagationTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/util/concurrency/ClientIdPropagationTest.kt @@ -17,8 +17,10 @@ import com.intellij.testFramework.LightPlatformTestCase import com.intellij.util.Alarm import com.intellij.util.application import io.kotest.assertions.failure -import kotlinx.coroutines.* -import java.lang.Runnable +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit import javax.swing.SwingUtilities @@ -148,7 +150,7 @@ class ClientIdPropagationTest : LightPlatformTestCase() { private fun doTest(expectedClientId: ClientId = TEST_CLIENT_ID, controllerContainerClientId: ClientId = TEST_CLIENT_ID, testRunnable: Runnable) { service>().registerSession(testRootDisposable, TestClientAppSession(application as ApplicationImpl, controllerContainerClientId)) - installThreadContext(ClientIdContextElement(TEST_CLIENT_ID)).use { + installThreadContext(ClientIdContextElement(TEST_CLIENT_ID)) { testRunnable.run() } diff --git a/platform/platform-tests/testSrc/com/intellij/util/concurrency/CurrentThreadCoroutineScopeTest.kt b/platform/platform-tests/testSrc/com/intellij/util/concurrency/CurrentThreadCoroutineScopeTest.kt index ab4e860693aa..e5d2580b1532 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/concurrency/CurrentThreadCoroutineScopeTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/util/concurrency/CurrentThreadCoroutineScopeTest.kt @@ -149,7 +149,7 @@ class CurrentThreadCoroutineScopeTest { fun `blockingContextScope retains only those elements that were present at the moment of invocation`(): Unit = timeoutRunBlocking { withContext(E2() + E3()) { blockingContextScope { - installThreadContext(currentThreadContext() + E1(), true).use { + installThreadContext(currentThreadContext() + E1(), true) { application.executeOnPooledThread { val context = currentThreadContext() assertNull(context[E1]) @@ -165,7 +165,7 @@ class CurrentThreadCoroutineScopeTest { fun `fixCurrentThreadScope captures the context of its definition`(): Unit = timeoutRunBlocking { withContext(E1()) { val (_, job) = withCurrentThreadCoroutineScopeBlocking { - installThreadContext(currentThreadContext() + E2(), true).use { + installThreadContext(currentThreadContext() + E2(), true) { currentThreadCoroutineScope().launch { val context = currentThreadContext() assertNotNull(context[E1]) @@ -205,7 +205,7 @@ class CurrentThreadCoroutineScopeTest { val latch = Job(coroutineContext.job) val wasCancelled = AtomicBoolean(false) - installThreadContext(currentThreadContext() + parentJob).use { + installThreadContext(currentThreadContext() + parentJob) { val (_, job) = withCurrentThreadCoroutineScopeBlocking { currentThreadCoroutineScope().launch { try { diff --git a/platform/platform-tests/testSrc/com/intellij/util/concurrency/ImplicitBlockingContextTest.kt b/platform/platform-tests/testSrc/com/intellij/util/concurrency/ImplicitBlockingContextTest.kt index 346a508edd80..8827acc192f1 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/concurrency/ImplicitBlockingContextTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/util/concurrency/ImplicitBlockingContextTest.kt @@ -91,7 +91,7 @@ class ImplicitBlockingContextTest { withContext(E()) { val context = coroutineContext val newContext = currentThreadContext() + F() - installThreadContext(newContext).use { + installThreadContext(newContext) { assertEquals(IntellijCoroutines.currentThreadCoroutineContext(), context) assertNotEquals(context, currentThreadContext()) assertEquals(newContext, currentThreadContext()) @@ -142,12 +142,12 @@ class ImplicitBlockingContextTest { assertEquals(e, currentThreadContext()[E]) } - installThreadContext(currentThreadContext() + e).use { + installThreadContext(currentThreadContext() + e) { `has e, not f`() @Suppress("SSBasedInspection") CoroutineScope(f + handler).launch(start = CoroutineStart.UNDISPATCHED) { `has f, not e`() - installThreadContext(e + handler).use { + installThreadContext(e + handler) { `has e, not f`() } `has f, not e`() diff --git a/platform/platform-tests/testSrc/com/intellij/util/concurrency/ThreadContextPropagationTest.kt b/platform/platform-tests/testSrc/com/intellij/util/concurrency/ThreadContextPropagationTest.kt index 1307e5a21b8e..2034239610d0 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/concurrency/ThreadContextPropagationTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/util/concurrency/ThreadContextPropagationTest.kt @@ -139,7 +139,7 @@ class ThreadContextPropagationTest { val element = TestElement("element") withContext(element) { suspendCancellableCoroutine { continuation -> - installThreadContext(continuation.context).use { // install context in calling thread + installThreadContext(continuation.context) { // install context in calling thread submit { // switch to another thread val result: Result = runCatching { assertSame(element, currentThreadContext()[TestElementKey]) // the same element must be present in another thread context diff --git a/platform/platform-tests/testSrc/com/intellij/util/concurrency/testPropagation.kt b/platform/platform-tests/testSrc/com/intellij/util/concurrency/testPropagation.kt index 18172f34d36b..048e5e889535 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/concurrency/testPropagation.kt +++ b/platform/platform-tests/testSrc/com/intellij/util/concurrency/testPropagation.kt @@ -16,7 +16,7 @@ import org.junit.jupiter.api.Assertions internal suspend fun doPropagationTest(submit: (() -> Unit) -> Unit) { return suspendCancellableCoroutine { continuation -> val element = TestElement("element") - installThreadContext(element).use { // install context in calling thread + installThreadContext(element) { // install context in calling thread submit { // switch to another thread val result: Result = runCatching { Assertions.assertSame(element, currentThreadContext()[TestElementKey]) // the same element must be present in another thread context diff --git a/platform/util/api-dump-experimental.txt b/platform/util/api-dump-experimental.txt index 1a5921cbac3c..4a1ac98a211d 100644 --- a/platform/util/api-dump-experimental.txt +++ b/platform/util/api-dump-experimental.txt @@ -12,7 +12,9 @@ *f:com.intellij.concurrency.ThreadContext - sf:currentThreadContext():kotlin.coroutines.CoroutineContext - sf:installThreadContext(kotlin.coroutines.CoroutineContext,Z):com.intellij.openapi.application.AccessToken +- sf:installThreadContext(kotlin.coroutines.CoroutineContext,Z,kotlin.jvm.functions.Function0):java.lang.Object - bs:installThreadContext$default(kotlin.coroutines.CoroutineContext,Z,I,java.lang.Object):com.intellij.openapi.application.AccessToken +- bs:installThreadContext$default(kotlin.coroutines.CoroutineContext,Z,kotlin.jvm.functions.Function0,I,java.lang.Object):java.lang.Object - sf:resetThreadContext():com.intellij.openapi.application.AccessToken - sf:resetThreadContext(kotlin.jvm.functions.Function0):java.lang.Object *:com.intellij.openapi.diagnostic.ReportingClassSubstitutor diff --git a/platform/util/concurrency/src/org/jetbrains/concurrency/AsyncPromise.kt b/platform/util/concurrency/src/org/jetbrains/concurrency/AsyncPromise.kt index 7c5536479b08..56a8f907e7bf 100644 --- a/platform/util/concurrency/src/org/jetbrains/concurrency/AsyncPromise.kt +++ b/platform/util/concurrency/src/org/jetbrains/concurrency/AsyncPromise.kt @@ -150,7 +150,7 @@ open class AsyncPromise private constructor( override fun then(done: Function): Promise { return AsyncPromise(wrapWithCancellationPropagation { ctx -> f.thenApply { t -> - installThreadContext(ctx, true).use { + installThreadContext(ctx, true) { done.`fun`(t) } } diff --git a/platform/util/src/com/intellij/concurrency/threadContext.kt b/platform/util/src/com/intellij/concurrency/threadContext.kt index ad917c9924be..ebbbc2969721 100644 --- a/platform/util/src/com/intellij/concurrency/threadContext.kt +++ b/platform/util/src/com/intellij/concurrency/threadContext.kt @@ -297,8 +297,19 @@ fun resetThreadContext(action: () -> T): T { * Installs [coroutineContext] as the current thread context. * If [replace] is `false` (default) and the current thread already has context, then this function logs an error. * + */ +fun installThreadContext(coroutineContext: CoroutineContext, replace: Boolean = false, action: () -> T): T { + installThreadContext(coroutineContext, replace = replace).use { + return action() + } +} + +/** + * This function is not visible in stacktraces. Consider using a sibling higher-order function + * * @return handle to restore the previous thread context */ +@Deprecated("Use higher-order function for installation of thread context") fun installThreadContext(coroutineContext: CoroutineContext, replace: Boolean = false): AccessToken { return withThreadLocal(tlCoroutineContext) { previousContext -> @OptIn(InternalCoroutinesApi::class) diff --git a/platform/util/src/com/intellij/openapi/progress/Cancellation.java b/platform/util/src/com/intellij/openapi/progress/Cancellation.java index 95337af0a513..f049cb26a2dc 100644 --- a/platform/util/src/com/intellij/openapi/progress/Cancellation.java +++ b/platform/util/src/com/intellij/openapi/progress/Cancellation.java @@ -134,6 +134,8 @@ public final class Cancellation { if (isInNonCancelableSectionInternal()) { return computable.compute(); } + // we use a deprecated method here to handle the exception correctly + //noinspection deprecation try (@NotNull AccessToken ignored = ThreadContext.installThreadContext( ThreadContext.currentThreadContext().plus(NonCancellable.INSTANCE), true)) { return computable.compute(); diff --git a/platform/util/src/com/intellij/util/concurrency/ContextCallable.java b/platform/util/src/com/intellij/util/concurrency/ContextCallable.java index b54dc61fc633..69666bc14c01 100644 --- a/platform/util/src/com/intellij/util/concurrency/ContextCallable.java +++ b/platform/util/src/com/intellij/util/concurrency/ContextCallable.java @@ -80,15 +80,16 @@ final class ContextCallable implements Callable { } else { Supplier> temp = () -> { - try (AccessToken ignored = ThreadContext.installThreadContext(myChildContext.getContext(), true); - AccessToken ignored2 = myChildContext.applyContextActions(false)) { - try { - return new RunResult<>(myCallable.call()); + return ThreadContext.installThreadContext(myChildContext.getContext(), true, () -> { + try (AccessToken ignored2 = myChildContext.applyContextActions(false)) { + try { + return new RunResult<>(myCallable.call()); + } + catch (Exception e) { + return new RunResult<>(e); + } } - catch (Exception e) { - return new RunResult<>(e); - } - } + }); }; Continuation continuation = myChildContext.getContinuation(); if (continuation == null) { diff --git a/platform/util/src/com/intellij/util/concurrency/propagation.kt b/platform/util/src/com/intellij/util/concurrency/propagation.kt index 5e901abac4cd..22532adea0dd 100644 --- a/platform/util/src/com/intellij/util/concurrency/propagation.kt +++ b/platform/util/src/com/intellij/util/concurrency/propagation.kt @@ -229,8 +229,8 @@ fun createChildContextWithContextJob(debugName: @NonNls String) : ChildContext = @Internal fun createChildContextIgnoreStructuredConcurrency(debugName: @NonNls String) : ChildContext { // probably we need to exclude some elements like PlatformActivityTrackerService.ObservationTracker - installThreadContext(currentThreadContext().minusKey(BlockingJob), true).use { - return createChildContext(debugName) + return installThreadContext(currentThreadContext().minusKey(BlockingJob), true) { + createChildContext(debugName) } } @@ -544,7 +544,7 @@ internal fun capturePropagationContext( val capturedRunnable1 = captureClientIdInRunnable(runnable) val capturedRunnable2 = Runnable { // no cancellation tracker here: this is a periodic runnable that is restarted - installThreadContext(childContext.context, false).use { + installThreadContext(childContext.context, false) { childContext.applyContextActions(false).use { capturedRunnable1.run() }