mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[threading] IJPL-195357: Make installThreadContext a real higher-order function
GitOrigin-RevId: c3a674d64040c3457fea17bc137b4a9e819eef8a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
7b5d58b273
commit
3c8cb9147e
+1
-1
@@ -81,7 +81,7 @@ internal class PlatformActivityTrackerService(private val scope: CoroutineScope)
|
||||
fun <T> trackConfigurationActivityBlocking(kind: ActivityKey, action: () -> T): T {
|
||||
val currentContext = currentThreadContext()
|
||||
return withObservationTracker(kind) { observationTracker ->
|
||||
installThreadContext(currentContext + observationTracker, true).use {
|
||||
installThreadContext(currentContext + observationTracker, true) {
|
||||
action()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ interface SideEffectGuard {
|
||||
@JvmStatic
|
||||
fun <T> computeWithAllowedSideEffectsBlocking(effects: EnumSet<EffectType>, action: () -> T): T {
|
||||
val context = currentThreadContext()
|
||||
return installThreadContext(context + AllowedSideEffectsElement(effects), replace = true).use {
|
||||
return installThreadContext(context + AllowedSideEffectsElement(effects), replace = true) {
|
||||
action()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ private fun <T> runBlockingCancellable(allowOrphan: Boolean, compensateParalleli
|
||||
|
||||
private fun getLockContext(currentThreadContext: CoroutineContext): Pair<CoroutineContext, AccessToken> {
|
||||
val parallelize = with(ApplicationManager.getApplication()) {
|
||||
installThreadContext(currentThreadContext).use {
|
||||
installThreadContext(currentThreadContext) {
|
||||
isReadAccessAllowed
|
||||
}
|
||||
}
|
||||
@@ -303,7 +303,7 @@ suspend fun <T> blockingContextScope(action: () -> T): T {
|
||||
fun <T> withCurrentThreadCoroutineScopeBlocking(action: () -> T): Pair<T, Job> {
|
||||
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 <T> blockingContextInner(currentContext: CoroutineContext, action: () -> T): T {
|
||||
val context = currentContext.prepareForInstallation()
|
||||
return installThreadContext(context).use {
|
||||
return installThreadContext(context) {
|
||||
action()
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -84,8 +84,8 @@ class CodeInsightContextManagerImpl(
|
||||
|
||||
override fun <Result> 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -476,9 +476,10 @@ public final class ProgressRunner<R> {
|
||||
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) {
|
||||
|
||||
@@ -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 <T> 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<EnvDataContextElement>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -359,7 +359,7 @@ class RunnerAndConfigurationSettingsImpl @JvmOverloads constructor(
|
||||
|
||||
var warning = ReadAction.nonBlocking<RuntimeConfigurationException?> {
|
||||
try {
|
||||
ExecutionManagerImpl.withEnvironmentDataContext(dataContext).use {
|
||||
ExecutionManagerImpl.withEnvironmentDataContext(dataContext) {
|
||||
configuration.checkConfiguration()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
})
|
||||
|
||||
+1
-1
@@ -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()
|
||||
}
|
||||
|
||||
+3
-6
@@ -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
|
||||
|
||||
+9
-7
@@ -481,9 +481,9 @@ public final class NonBlockingReadActionImpl<T> 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<T> 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<T> 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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ internal fun <X> 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<X>? = null
|
||||
val application = ApplicationManagerEx.getApplicationEx()
|
||||
val cancellation = CannotReadException.jobCancellation(readJob)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<CoreUiCoroutineScopeHolder>().coroutineScope.coroutineContext).use {
|
||||
installThreadContext(service<CoreUiCoroutineScopeHolder>().coroutineScope.coroutineContext) {
|
||||
frameHelper.windowClosing(project)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
+22
-18
@@ -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());
|
||||
}
|
||||
|
||||
+1
-1
@@ -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")
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+3
-3
@@ -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<CeProcessCanceledException> {
|
||||
installThreadContext(Job()).use {
|
||||
installThreadContext(Job()) {
|
||||
throw assertThrows<CeProcessCanceledException> {
|
||||
prepareThreadContextTest { currentJob ->
|
||||
testNoExceptions()
|
||||
@@ -67,7 +67,7 @@ class ExistingThreadContextTest : CancellationTest() {
|
||||
val job = Job()
|
||||
val t = Throwable()
|
||||
val ce = assertThrows<ProcessCanceledException> {
|
||||
installThreadContext(job).use {
|
||||
installThreadContext(job) {
|
||||
throw assertThrows<CeProcessCanceledException> {
|
||||
prepareThreadContextTest { currentJob ->
|
||||
testNoExceptions()
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ fun <T> 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)
|
||||
}
|
||||
|
||||
+2
-2
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
+5
-3
@@ -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<ClientSessionsManager<ClientAppSession>>().registerSession(testRootDisposable,
|
||||
TestClientAppSession(application as ApplicationImpl, controllerContainerClientId))
|
||||
installThreadContext(ClientIdContextElement(TEST_CLIENT_ID)).use {
|
||||
installThreadContext(ClientIdContextElement(TEST_CLIENT_ID)) {
|
||||
testRunnable.run()
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -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 {
|
||||
|
||||
+3
-3
@@ -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`()
|
||||
|
||||
+1
-1
@@ -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<Unit> = runCatching {
|
||||
assertSame(element, currentThreadContext()[TestElementKey]) // the same element must be present in another thread context
|
||||
|
||||
@@ -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<Unit> = runCatching {
|
||||
Assertions.assertSame(element, currentThreadContext()[TestElementKey]) // the same element must be present in another thread context
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -150,7 +150,7 @@ open class AsyncPromise<T> private constructor(
|
||||
override fun <SUB_RESULT : Any?> then(done: Function<in T, out SUB_RESULT>): Promise<SUB_RESULT> {
|
||||
return AsyncPromise(wrapWithCancellationPropagation { ctx ->
|
||||
f.thenApply { t ->
|
||||
installThreadContext(ctx, true).use {
|
||||
installThreadContext(ctx, true) {
|
||||
done.`fun`(t)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,8 +297,19 @@ fun <T> 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 <T> 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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -80,15 +80,16 @@ final class ContextCallable<V> implements Callable<V> {
|
||||
}
|
||||
else {
|
||||
Supplier<RunResult<V, Exception>> 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<Unit> continuation = myChildContext.getContinuation();
|
||||
if (continuation == null) {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user