IJPL-162040 Store lock permits in context to allow lock inheritance between coroutines.

- Fix check for inheritance in case of Fleet or other coroutine framework.
 - Make tests stricter.
 - Mark new Application API as internal and experimental, as ot is needed only for this code and should not be used in other places.


(cherry picked from commit 63798b6160ca5963499ae066db149d3b2af6017d)

IJ-CR-147289

GitOrigin-RevId: 700746a8c9590945893ef389f2c86724eb8a83d5
This commit is contained in:
Lev Serebryakov
2024-10-16 19:09:18 +02:00
committed by intellij-monorepo-bot
parent ecb3e54b63
commit c91c8b6702
4 changed files with 10 additions and 4 deletions

View File

@@ -1100,10 +1100,8 @@ com.intellij.openapi.application.Application
- exit(Z,Z,Z,I):V
- a:getDefaultModalityState():com.intellij.openapi.application.ModalityState
- a:getIdleTime():J
- getLockStateAsCoroutineContext():kotlin.coroutines.CoroutineContext
- a:getModalityStateForComponent(java.awt.Component):com.intellij.openapi.application.ModalityState
- a:getStartTime():J
- hasLockStateInContext(kotlin.coroutines.CoroutineContext):Z
- a:hasWriteAction(java.lang.Class):Z
- a:invokeAndWait(java.lang.Runnable):V
- a:invokeAndWait(java.lang.Runnable,com.intellij.openapi.application.ModalityState):V

View File

@@ -675,10 +675,14 @@ public interface Application extends ComponentManager {
}
//</editor-fold>
@ApiStatus.Experimental
@ApiStatus.Internal
default CoroutineContext getLockStateAsCoroutineContext() {
return EmptyCoroutineContext.INSTANCE;
}
@ApiStatus.Experimental
@ApiStatus.Internal
default boolean hasLockStateInContext(CoroutineContext context) {
return false;
}

View File

@@ -37,7 +37,7 @@ internal class InternalReadAction<T>(
}
}
else {
if (isLockStoredInContext && application.isReadAccessAllowed && !application.isWriteIntentLockAcquired) {
if (isLockStoredInContext && application.hasLockStateInContext(currentCoroutineContext())) {
val unsatisfiedConstraint = findUnsatisfiedConstraint()
check(unsatisfiedConstraint == null) {
"Cannot suspend until constraints are satisfied while holding the read lock: $unsatisfiedConstraint"
@@ -47,7 +47,7 @@ internal class InternalReadAction<T>(
}
}
withContext(Dispatchers.Default) {
check(isLockStoredInContext || !application.isReadAccessAllowed) {
check(!application.isReadAccessAllowed) {
"This thread unexpectedly holds the read lock"
}
readLoop()

View File

@@ -8,6 +8,7 @@ import com.intellij.openapi.util.Disposer
import com.intellij.testFramework.common.timeoutRunBlocking
import com.intellij.util.concurrency.ImplicitBlockingContextTest
import com.intellij.util.concurrency.Semaphore
import com.intellij.util.concurrency.ThreadingAssertions
import com.intellij.util.concurrency.runWithImplicitBlockingContextEnabled
import kotlinx.coroutines.*
import org.junit.jupiter.api.Assertions.*
@@ -156,8 +157,10 @@ abstract class SuspendingReadActionTest : CancellableReadActionTests() {
@RepeatedTest(REPETITIONS)
fun `read action works if already obtained`(): Unit = timeoutRunBlocking {
cra {
assertTrue(ApplicationManager.getApplication().isReadAccessAllowed)
runBlockingCancellable {
assertEquals(42, cra {
assertTrue(ApplicationManager.getApplication().isReadAccessAllowed)
42
})
}
@@ -387,6 +390,7 @@ class NonBlockingUndispatchedSuspendingReadActionTest : SuspendingReadActionTest
override suspend fun awaitConstraint(): Unit = fail("must not be called")
}
cra {
assertTrue(ApplicationManager.getApplication().isReadAccessAllowed)
runBlockingCancellable {
assertThrows<IllegalStateException> {
cra(unsatisfiableConstraint) {