[threading] IJPL-148438: Move handling of listeners to ThreadingSupport

GitOrigin-RevId: ca68876b52ec298efbfa01b43fd730b8d2502d0d
This commit is contained in:
Konstantin Nisht
2025-06-12 18:24:11 +02:00
committed by intellij-monorepo-bot
parent 099e00a013
commit 6180423383
4 changed files with 245 additions and 348 deletions

View File

@@ -50,26 +50,6 @@ interface ThreadingSupport {
@ApiStatus.Internal
fun <T> runUnlockingIntendedWrite(action: () -> T): T
/**
* Set a [ReadActionListener].
*
* Only one listener can be set. It is error to set second listener.
*
* @param listener the listener to set
*/
@ApiStatus.Internal
fun setReadActionListener(listener: ReadActionListener)
/**
* Removes a [ReadActionListener].
*
* It is error to remove listener which was not set early.
*
* @param listener the listener to remove
*/
@ApiStatus.Internal
fun removeReadActionListener(listener: ReadActionListener)
@RequiresBlockingContext
fun <T> runReadAction(clazz: Class<*>, action: () -> T): T
@@ -97,39 +77,50 @@ interface ThreadingSupport {
/**
* Adds a [WriteActionListener].
*
* Only one listener can be set. It is error to set second listener.
*
* @param listener the listener to set
*/
fun setWriteActionListener(listener: WriteActionListener)
fun addWriteActionListener(listener: WriteActionListener)
/**
* Removes a [WriteActionListener].
*
* It is error to remove listener which was not added early.
*
* @param listener the listener to remove
*/
@ApiStatus.Internal
fun removeWriteActionListener(listener: WriteActionListener)
/**
* Adds a [WriteIntentReadActionListener].
*
* Only one listener can be set. It is an error to set the second listener.
*
* @param listener the listener to set
*/
fun setWriteIntentReadActionListener(listener: WriteIntentReadActionListener)
fun addWriteIntentReadActionListener(listener: WriteIntentReadActionListener)
/**
* Removes a [WriteIntentReadActionListener].
*
* It is an error to remove the listener which was not set early.
* It is an error to remove the listener which was not added early.
*
* @param listener the listener to remove
*/
fun removeWriteIntentReadActionListener(listener: WriteIntentReadActionListener)
/**
* Removes a [WriteActionListener].
* Set a [ReadActionListener].
*
* It is error to remove listener which was not set early.
* @param listener the listener to set
*/
fun addReadActionListener(listener: ReadActionListener)
/**
* Removes a [ReadActionListener].
*
* @param listener the listener to remove
*/
@ApiStatus.Internal
fun removeWriteActionListener(listener: WriteActionListener)
fun removeReadActionListener(listener: ReadActionListener)
@RequiresBlockingContext
fun <T> runWriteAction(clazz: Class<*>, action: () -> T): T

View File

@@ -9,6 +9,7 @@ import com.intellij.openapi.application.*
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.Cancellation
import com.intellij.openapi.util.text.StringUtil
import com.intellij.platform.locking.impl.listeners.ErrorHandler
import com.intellij.platform.locking.impl.listeners.LegacyProgressIndicatorProvider
import com.intellij.platform.locking.impl.listeners.LockAcquisitionListener
import com.intellij.util.ReflectionUtil
@@ -16,6 +17,7 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.internal.intellij.IntellijCoroutines
import org.jetbrains.annotations.ApiStatus
import java.util.*
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicReference
import kotlin.Result
@@ -194,13 +196,16 @@ class NestedLocksThreadingSupport : ThreadingSupport {
*/
private val statesOfWIThread: ThreadLocal<MutableList<ComputationState>?> = ThreadLocal.withInitial { null }
private var myReadActionListener: ReadActionListener? = null
private var myWriteActionListener: WriteActionListener? = null
private var myWriteIntentActionListener: WriteIntentReadActionListener? = null
private val readActionListeners: CopyOnWriteArrayList<ReadActionListener> = CopyOnWriteArrayList()
private val myWriteActionListeners: CopyOnWriteArrayList<WriteActionListener> = CopyOnWriteArrayList()
private val myWriteIntentActionListeners: CopyOnWriteArrayList<WriteIntentReadActionListener> = CopyOnWriteArrayList()
private var myLockAcquisitionListener: LockAcquisitionListener? = null
private var myWriteLockReacquisitionListener: WriteLockReacquisitionListener? = null
private var myLegacyProgressIndicatorProvider: LegacyProgressIndicatorProvider? = null
@Volatile
private var errorHandler: ErrorHandler? = null
private val myWriteActionsStack = Collections.synchronizedList(ArrayList<Class<*>>())
private var myWriteStackBase = 0
@@ -250,6 +255,62 @@ class NestedLocksThreadingSupport : ThreadingSupport {
*/
private val pendingWriteActionFollowup: MutableList<Runnable> = ArrayList()
private inline fun <T> List<T>.traverse(action: (T) -> Unit) {
var index = 0
while (index < size) {
try {
action(this[index++])
}
catch (_: CancellationException) {
// ignored
}
catch (e: Throwable) {
try {
errorHandler?.handleError(e)
}
catch (e: Throwable) {
// swallowing error :(
}
}
}
}
private inline fun <T> List<T>.traverseBackwards(action: (T) -> Unit) {
var index = lastIndex
while (index >= 0) {
try {
action(this[index--])
}
catch (_: CancellationException) {
// ignored
}
catch (e: Throwable) {
try {
errorHandler?.handleError(e)
}
catch (e: Throwable) {
// swallowing error :(
}
}
}
}
/**
* Shallow clone of [CopyOnWriteArrayList] that wraps the underlying array.
* I swear that I will not modify the array further
*/
private fun <T> CopyOnWriteArrayList<T>.doClone(): List<T> {
@Suppress("UNCHECKED_CAST")
return clone() as List<T>
}
fun setErrorHandler(handler: ErrorHandler) {
errorHandler = handler
}
fun removeErrorHandler() {
errorHandler = null
}
/**
* The locking state that is shared by all computations belonging to the same level.
@@ -581,8 +642,8 @@ class NestedLocksThreadingSupport : ThreadingSupport {
}
fun <T> doRunWriteIntentReadAction(computation: () -> T): T {
val listener = myWriteIntentActionListener
fireBeforeWriteIntentReadActionStart(listener, computation.javaClass)
val frozenListeners = myWriteIntentActionListeners.doClone()
fireBeforeWriteIntentReadActionStart(frozenListeners, computation.javaClass)
val currentReadState = myTopmostReadAction.get()
myTopmostReadAction.set(false)
val currentWriteIntentState = myWriteIntentAcquired.get()
@@ -601,11 +662,11 @@ class NestedLocksThreadingSupport : ThreadingSupport {
is ParallelizablePermit.WriteIntent, is ParallelizablePermit.Write -> {}
}
try {
fireWriteIntentActionStarted(listener, computation.javaClass)
fireWriteIntentActionStarted(frozenListeners, computation.javaClass)
return computation()
}
finally {
fireWriteIntentActionFinished(listener, computation.javaClass)
fireWriteIntentActionFinished(frozenListeners, computation.javaClass)
if (permitToRelease != null) {
/**
* The permit to release can be changed because of [releaseTheAcquiredWriteIntentLockThenExecuteActionAndTakeWriteIntentLockBack] inside
@@ -618,7 +679,7 @@ class NestedLocksThreadingSupport : ThreadingSupport {
finally {
myWriteIntentAcquired.set(currentWriteIntentState)
myTopmostReadAction.set(currentReadState)
afterWriteIntentReadActionFinished(listener, computation.javaClass)
afterWriteIntentReadActionFinished(frozenListeners, computation.javaClass)
}
}
@@ -638,17 +699,15 @@ class NestedLocksThreadingSupport : ThreadingSupport {
}
@ApiStatus.Internal
override fun setReadActionListener(listener: ReadActionListener) {
if (myReadActionListener != null)
error("ReadActionListener already registered")
myReadActionListener = listener
override fun addReadActionListener(listener: ReadActionListener) {
readActionListeners.add(listener)
}
@ApiStatus.Internal
override fun removeReadActionListener(listener: ReadActionListener) {
if (myReadActionListener != listener)
error("ReadActionListener is not registered")
myReadActionListener = null
check(readActionListeners.remove(listener)) {
"ReadActionListener $listener is not registered"
}
}
private fun smartAcquireReadPermit(state: ComputationState): ReadPermit {
@@ -657,7 +716,9 @@ class NestedLocksThreadingSupport : ThreadingSupport {
return permit
}
myReadActionListener?.fastPathAcquisitionFailed()
readActionListeners.forEach {
it.fastPathAcquisitionFailed()
}
// Check for cancellation
val indicator = myLegacyProgressIndicatorProvider?.obtainProgressIndicator()
@@ -698,8 +759,8 @@ class NestedLocksThreadingSupport : ThreadingSupport {
override fun <T> runReadAction(clazz: Class<*>, action: () -> T): T {
handleLockAccess("read lock")
val listener = myReadActionListener
fireBeforeReadActionStart(listener, clazz)
val frozenListeners = readActionListeners.doClone()
fireBeforeReadActionStart(frozenListeners, clazz)
val currentReadState = myTopmostReadAction.get()
myTopmostReadAction.set(true)
@@ -719,27 +780,27 @@ class NestedLocksThreadingSupport : ThreadingSupport {
myReadActionsInThread.set(myReadActionsInThread.get() + 1)
try {
fireReadActionStarted(listener, clazz)
fireReadActionStarted(frozenListeners, clazz)
val rv = action()
return rv
}
finally {
fireReadActionFinished(listener, clazz)
fireReadActionFinished(frozenListeners, clazz)
myReadActionsInThread.set(myReadActionsInThread.get() - 1)
if (readPermitToRelease != null) {
computationState.releaseReadPermit(readPermitToRelease)
}
myTopmostReadAction.set(currentReadState)
fireAfterReadActionFinished(listener, clazz)
fireAfterReadActionFinished(frozenListeners, clazz)
}
}
override fun tryRunReadAction(action: Runnable): Boolean {
handleLockAccess("fail-fast read lock")
val listener = myReadActionListener
fireBeforeReadActionStart(listener, action.javaClass)
val frozenListeners = readActionListeners.doClone()
fireBeforeReadActionStart(frozenListeners, action.javaClass)
val currentReadState = myTopmostReadAction.get()
myTopmostReadAction.set(true)
@@ -761,12 +822,12 @@ class NestedLocksThreadingSupport : ThreadingSupport {
myReadActionsInThread.set(myReadActionsInThread.get() + 1)
try {
fireReadActionStarted(listener, action.javaClass)
fireReadActionStarted(frozenListeners, action.javaClass)
action.run()
return true
}
finally {
fireReadActionFinished(listener, action.javaClass)
fireReadActionFinished(frozenListeners, action.javaClass)
myReadActionsInThread.set(myReadActionsInThread.get() - 1)
if (readPermitToRelease != null) {
@@ -776,7 +837,7 @@ class NestedLocksThreadingSupport : ThreadingSupport {
}
finally {
myTopmostReadAction.set(currentReadState)
fireAfterReadActionFinished(listener, action.javaClass)
fireAfterReadActionFinished(frozenListeners, action.javaClass)
}
}
@@ -786,30 +847,26 @@ class NestedLocksThreadingSupport : ThreadingSupport {
}
@ApiStatus.Internal
override fun setWriteActionListener(listener: WriteActionListener) {
if (myWriteActionListener != null)
error("WriteActionListener already registered")
myWriteActionListener = listener
override fun addWriteActionListener(listener: WriteActionListener) {
myWriteActionListeners.add(listener)
}
@ApiStatus.Internal
override fun setWriteIntentReadActionListener(listener: WriteIntentReadActionListener) {
if (myWriteIntentActionListener != null)
error("WriteIntentReadActionListener already registered")
myWriteIntentActionListener = listener
override fun addWriteIntentReadActionListener(listener: WriteIntentReadActionListener) {
myWriteIntentActionListeners.add(listener)
}
override fun removeWriteIntentReadActionListener(listener: WriteIntentReadActionListener) {
if (myWriteIntentActionListener != listener)
error("WriteIntentReadActionListener is not registered")
myWriteIntentActionListener = null
check(myWriteIntentActionListeners.remove(listener)) {
"WriteIntentReadActionListener $listener is not registered"
}
}
@ApiStatus.Internal
override fun removeWriteActionListener(listener: WriteActionListener) {
if (myWriteActionListener != listener)
error("WriteActionListener is not registered")
myWriteActionListener = null
check(myWriteActionListeners.remove(listener)) {
"WriteActionListener $listener is not registered"
}
}
fun setLockAcquisitionListener(listener: LockAcquisitionListener) {
@@ -884,7 +941,7 @@ class NestedLocksThreadingSupport : ThreadingSupport {
* This is needed for code that temporarily releases the write lock.
* Since we still want to preserve the atomicity of write action, we pre-acquire the write-intent lock before write.
*/
private data class PreparatoryWriteIntent(val permit: Permit, val needRelease: Boolean, val state: ComputationState, val listener: WriteActionListener?) {
private data class PreparatoryWriteIntent(val permit: Permit, val needRelease: Boolean, val state: ComputationState, val listeners: List<WriteActionListener>) {
fun release() {
if (!(needRelease && permit is WriteIntentPermit)) {
return
@@ -894,7 +951,7 @@ class NestedLocksThreadingSupport : ThreadingSupport {
}
private fun prepareWriteIntentAcquiredBeforeWrite(computationState: ComputationState, clazz: Class<*>): PreparatoryWriteIntent {
val listener = myWriteActionListener
val frozenListeners = myWriteActionListeners.doClone()
// Read permit is incompatible
check(computationState.getThisThreadPermit() !is ParallelizablePermit.Read) { "WriteAction can not be called from ReadAction" }
@@ -910,7 +967,7 @@ class NestedLocksThreadingSupport : ThreadingSupport {
startPendingWriteAction(computationState)
if (myWriteActionsStack.isEmpty()) {
fireBeforeWriteActionStart(listener, clazz)
fireBeforeWriteActionStart(frozenListeners, clazz)
}
val permit = computationState.getThisThreadPermit()
@@ -919,18 +976,18 @@ class NestedLocksThreadingSupport : ThreadingSupport {
when (permit) {
null -> {
val writeIntent = computationState.acquireWriteIntentPermit()
return PreparatoryWriteIntent(writeIntent, true, computationState, listener)
return PreparatoryWriteIntent(writeIntent, true, computationState, frozenListeners)
}
is ParallelizablePermit.Read -> {
error("WriteAction can not be called from ReadAction")
}
is ParallelizablePermit.WriteIntent -> {
checkWriteFromRead("Write", "Write Intent")
return PreparatoryWriteIntent(permit.writeIntentPermit, false, computationState, listener)
return PreparatoryWriteIntent(permit.writeIntentPermit, false, computationState, frozenListeners)
}
is ParallelizablePermit.Write -> {
checkWriteFromRead("Write", "Write")
return PreparatoryWriteIntent(permit.writePermit, false, computationState, listener)
return PreparatoryWriteIntent(permit.writePermit, false, computationState, frozenListeners)
}
}
} catch (e : Throwable) {
@@ -965,9 +1022,9 @@ class NestedLocksThreadingSupport : ThreadingSupport {
myWriteActionsStack.add(clazz)
fireWriteActionStarted(preparatoryWriteIntent.listener, clazz)
fireWriteActionStarted(preparatoryWriteIntent.listeners, clazz)
return WriteLockInitResult(shouldRelease, currentReadState, preparatoryWriteIntent.listener, state, clazz, this)
return WriteLockInitResult(shouldRelease, currentReadState, preparatoryWriteIntent.listeners, state, clazz, this)
}
private fun startPendingWriteAction(state: ComputationState) {
@@ -983,13 +1040,13 @@ class NestedLocksThreadingSupport : ThreadingSupport {
private data class WriteLockInitResult(
val shouldRelease: Boolean,
val currentReadState: Boolean,
val listener: WriteActionListener?,
val listeners: List<WriteActionListener>,
val state: ComputationState,
val clazz: Class<*>,
val support: NestedLocksThreadingSupport,
) {
fun release() {
support.fireWriteActionFinished(listener, clazz)
support.fireWriteActionFinished(listeners, clazz)
support.myWriteActionsStack.removeLast()
if (shouldRelease) {
support.myWriteAcquired = null
@@ -997,7 +1054,7 @@ class NestedLocksThreadingSupport : ThreadingSupport {
}
support.myTopmostReadAction.set(currentReadState)
if (shouldRelease) {
support.fireAfterWriteActionFinished(listener, clazz)
support.fireAfterWriteActionFinished(listeners, clazz)
support.drainWriteActionFollowups()
}
}
@@ -1098,7 +1155,7 @@ class NestedLocksThreadingSupport : ThreadingSupport {
if (currentPermit is ReadPermit || currentPermit is WriteIntentPermit) {
return { }
}
val capturedListener = myReadActionListener
val capturedListener = readActionListeners
val capturedPermit = run {
fireBeforeReadActionStart(capturedListener, javaClass)
val p = computationState.acquireReadPermit()
@@ -1167,99 +1224,76 @@ class NestedLocksThreadingSupport : ThreadingSupport {
}
}
private fun fireBeforeReadActionStart(listener: ReadActionListener?, clazz: Class<*>) {
try {
listener?.beforeReadActionStart(clazz)
}
catch (_: Throwable) {
private fun fireBeforeReadActionStart(list: List<ReadActionListener>, clazz: Class<*>) {
list.traverse {
it.beforeReadActionStart(clazz)
}
}
private fun fireReadActionStarted(listener: ReadActionListener?, clazz: Class<*>) {
try {
listener?.readActionStarted(clazz)
}
catch (_: Throwable) {
private fun fireReadActionStarted(list: List<ReadActionListener>, clazz: Class<*>) {
list.traverse {
it.readActionStarted(clazz)
}
}
private fun fireReadActionFinished(listener: ReadActionListener?, clazz: Class<*>) {
try {
listener?.readActionFinished(clazz)
}
catch (_: Throwable) {
private fun fireReadActionFinished(list: List<ReadActionListener>, clazz: Class<*>) {
list.traverseBackwards {
it.readActionFinished(clazz)
}
}
private fun fireAfterReadActionFinished(listener: ReadActionListener?, clazz: Class<*>) {
try {
listener?.afterReadActionFinished(clazz)
}
catch (_: Throwable) {
private fun fireAfterReadActionFinished(list: List<ReadActionListener>, clazz: Class<*>) {
list.traverseBackwards {
it.afterReadActionFinished(clazz)
}
}
private fun fireBeforeWriteActionStart(listener: WriteActionListener?, clazz: Class<*>) {
try {
listener?.beforeWriteActionStart(clazz)
}
catch (_: Throwable) {
private fun fireBeforeWriteActionStart(listeners: List<WriteActionListener>, clazz: Class<*>) {
listeners.traverse {
it.beforeWriteActionStart(clazz)
}
}
private fun fireWriteActionStarted(listener: WriteActionListener?, clazz: Class<*>) {
try {
listener?.writeActionStarted(clazz)
}
catch (_: Throwable) {
private fun fireWriteActionStarted(listeners: List<WriteActionListener>, clazz: Class<*>) {
listeners.traverse {
it.writeActionStarted(clazz)
}
}
private fun fireWriteActionFinished(listener: WriteActionListener?, clazz: Class<*>) {
try {
listener?.writeActionFinished(clazz)
}
catch (_: Throwable) {
private fun fireWriteActionFinished(listeners: List<WriteActionListener>, clazz: Class<*>) {
listeners.traverseBackwards {
it.writeActionFinished(clazz)
}
}
private fun fireAfterWriteActionFinished(listener: WriteActionListener?, clazz: Class<*>) {
try {
listener?.afterWriteActionFinished(clazz)
}
catch (_: Throwable) {
private fun fireAfterWriteActionFinished(listeners: List<WriteActionListener>, clazz: Class<*>) {
listeners.traverseBackwards {
it.afterWriteActionFinished(clazz)
}
}
private fun fireBeforeWriteIntentReadActionStart(listener: WriteIntentReadActionListener?, clazz: Class<*>) {
try {
listener?.beforeWriteIntentReadActionStart(clazz)
}
catch (_: Throwable) {
private fun fireBeforeWriteIntentReadActionStart(listeners: List<WriteIntentReadActionListener>, clazz: Class<*>) {
listeners.traverse {
it.beforeWriteIntentReadActionStart(clazz)
}
}
private fun fireWriteIntentActionStarted(listener: WriteIntentReadActionListener?, clazz: Class<*>) {
try {
listener?.writeIntentReadActionStarted(clazz)
private fun fireWriteIntentActionStarted(listeners: List<WriteIntentReadActionListener>, clazz: Class<*>) {
listeners.traverse {
it.writeIntentReadActionStarted(clazz)
}
catch (_: Throwable) {
}
private fun fireWriteIntentActionFinished(listeners: List<WriteIntentReadActionListener>, clazz: Class<*>) {
listeners.traverseBackwards {
it.writeIntentReadActionFinished(clazz)
}
}
private fun fireWriteIntentActionFinished(listener: WriteIntentReadActionListener?, clazz: Class<*>) {
try {
listener?.writeIntentReadActionFinished(clazz)
}
catch (_: Throwable) {
}
}
private fun afterWriteIntentReadActionFinished(listener: WriteIntentReadActionListener?, clazz: Class<*>) {
try {
listener?.afterWriteIntentReadActionFinished(clazz)
}
catch (_: Throwable) {
private fun afterWriteIntentReadActionFinished(listeners: List<WriteIntentReadActionListener>, clazz: Class<*>) {
listeners.traverseBackwards {
it.afterWriteIntentReadActionFinished(clazz)
}
}

View File

@@ -0,0 +1,9 @@
// 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.platform.locking.impl.listeners
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
fun interface ErrorHandler {
fun handleError(error: Throwable)
}

View File

@@ -23,7 +23,6 @@ import com.intellij.openapi.application.ex.ApplicationUtil;
import com.intellij.openapi.client.ClientAwareComponentManager;
import com.intellij.openapi.components.impl.stores.IComponentStore;
import com.intellij.openapi.diagnostic.Attachment;
import com.intellij.openapi.diagnostic.ControlFlowException;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.progress.*;
@@ -46,6 +45,7 @@ import com.intellij.platform.diagnostic.telemetry.TelemetryManager;
import com.intellij.platform.diagnostic.telemetry.helpers.TraceKt;
import com.intellij.platform.locking.impl.IntelliJLockingUtil;
import com.intellij.platform.locking.impl.NestedLocksThreadingSupport;
import com.intellij.platform.locking.impl.listeners.ErrorHandler;
import com.intellij.platform.locking.impl.listeners.LegacyProgressIndicatorProvider;
import com.intellij.platform.locking.impl.listeners.LockAcquisitionListener;
import com.intellij.psi.util.ReadActionCache;
@@ -55,7 +55,6 @@ 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.containers.DisposableWrapperList;
import com.intellij.util.messages.Topic;
import com.intellij.util.ui.EDT;
import io.opentelemetry.api.trace.Span;
@@ -70,13 +69,13 @@ import org.jetbrains.annotations.*;
import javax.swing.*;
import java.awt.*;
import java.lang.reflect.InvocationTargetException;
import java.util.EventListener;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -103,8 +102,47 @@ public final class ApplicationImpl extends ClientAwareComponentManager implement
private final ModalityInvokator myInvokator = new ModalityInvokatorImpl();
private final EventDispatcher<ApplicationListener> myDispatcher = EventDispatcher.create(ApplicationListener.class);
private final WriteActionListener appListenerDispatcherWrapper = new WriteActionListener() {
@Override
public void beforeWriteActionStart(@NotNull Class<?> action) {
ActivityTracker.getInstance().inc();
myDispatcher.getMulticaster().beforeWriteActionStart(action);
}
private final LockDispatchListener myLockDispatcherListener = new LockDispatchListener();
@Override
public void writeActionStarted(@NotNull Class<?> action) {
myDispatcher.getMulticaster().writeActionStarted(action);
}
@Override
public void writeActionFinished(@NotNull Class<?> action) {
myDispatcher.getMulticaster().writeActionFinished(action);
}
@Override
public void afterWriteActionFinished(@NotNull Class<?> action) {
otelMonitor.get().writeActionExecuted();
myDispatcher.getMulticaster().afterWriteActionFinished(action);
}
};
private final ReadActionListener customReadActionListener = new ReadActionListener() {
@Override
public void readActionFinished(@NotNull Class<?> action) {
myReadActionCacheImpl.clear();
otelMonitor.get().readActionExecuted();
}
@Override
public void fastPathAcquisitionFailed() {
// Impatient reader not in non-cancellable session will not wait
if (myImpatientReader.get() && !Cancellation.isInNonCancelableSection()) {
throw ApplicationUtil.CannotRunReadActionException.create();
}
}
};
private static final ErrorHandler lockingErrorHandler = (error) -> getLogger().error(error);
private final boolean myTestModeFlag;
private final boolean myHeadlessMode;
@@ -390,12 +428,10 @@ public final class ApplicationImpl extends ClientAwareComponentManager implement
@Override
public void dispose() {
getThreadingSupport().removeReadActionListener(myLockDispatcherListener);
getThreadingSupport().removeWriteActionListener(myLockDispatcherListener);
getThreadingSupport().removeWriteIntentReadActionListener(myLockDispatcherListener);
lock.removeLockAcquisitionListener(myLockDispatcherListener);
getThreadingSupport().removeWriteLockReacquisitionListener(myLockDispatcherListener);
lock.removeErrorHandler();
lock.removeLegacyIndicatorProvider(myLegacyIndicatorProvider);
lock.removeWriteActionListener(appListenerDispatcherWrapper);
lock.removeReadActionListener(customReadActionListener);
//noinspection deprecation
myDispatcher.getMulticaster().applicationExiting();
@@ -940,7 +976,7 @@ public final class ApplicationImpl extends ClientAwareComponentManager implement
private boolean canExit(boolean restart) {
for (ApplicationListener applicationListener : myDispatcher.getListeners()) {
if (restart && !applicationListener.canRestartApplication()
if (restart && !applicationListener.canRestartApplication()
|| !restart && !applicationListener.canExitApplication()) {
return false;
}
@@ -1268,23 +1304,6 @@ public final class ApplicationImpl extends ClientAwareComponentManager implement
myDispatcher.removeListener(l);
}
private void fireBeforeWriteActionStart(@NotNull Class<?> action) {
myDispatcher.getMulticaster().beforeWriteActionStart(action);
}
private void fireWriteActionStarted(@NotNull Class<?> action) {
myDispatcher.getMulticaster().writeActionStarted(action);
}
private void fireWriteActionFinished(@NotNull Class<?> action) {
myDispatcher.getMulticaster().writeActionFinished(action);
}
private void fireAfterWriteActionFinished(@NotNull Class<?> action) {
myDispatcher.getMulticaster().afterWriteActionFinished(action);
otelMonitor.get().writeActionExecuted();
}
@Override
public void saveSettings() {
if (mySaveAllowed) {
@@ -1324,10 +1343,10 @@ public final class ApplicationImpl extends ClientAwareComponentManager implement
+ (isHeadlessEnvironment() ? " (headless)" : "")
+ (isCommandLine() ? " (command line)" : "")
+ (writeActionPending || writeActionInProgress || writeAccessAllowed ? " (WA" +
(writeActionPending ? " pending" : "") +
(writeActionInProgress ? " inProgress" : "") +
(writeAccessAllowed ? " allowed" : "") +
")" : "")
(writeActionPending ? " pending" : "") +
(writeActionInProgress ? " inProgress" : "") +
(writeAccessAllowed ? " allowed" : "") +
")" : "")
+ (isReadAccessAllowed() ? " (RA allowed)" : "")
+ (isInImpatientReader() ? " (impatient reader)" : "")
+ (isExitInProgress() ? " (exit in progress)" : "")
@@ -1382,12 +1401,10 @@ public final class ApplicationImpl extends ClientAwareComponentManager implement
return true;
}, app.getCoroutineScope());
app.lock.setReadActionListener(app.myLockDispatcherListener);
app.lock.setWriteActionListener(app.myLockDispatcherListener);
app.lock.setWriteIntentReadActionListener(app.myLockDispatcherListener);
app.lock.setLockAcquisitionListener(app.myLockDispatcherListener);
app.lock.setWriteLockReacquisitionListener(app.myLockDispatcherListener);
app.lock.addReadActionListener(app.customReadActionListener);
app.lock.addWriteActionListener(app.appListenerDispatcherWrapper);
app.lock.setLegacyIndicatorProvider(myLegacyIndicatorProvider);
app.lock.setErrorHandler(lockingErrorHandler);
if (ThreadingRuntimeFlagsKt.getInstallSuvorovProgress()) {
SwingUtilities.invokeLater(() -> {
SuvorovProgress.INSTANCE.init(app);
@@ -1417,21 +1434,25 @@ public final class ApplicationImpl extends ClientAwareComponentManager implement
@Override
public void addWriteActionListener(@NotNull WriteActionListener listener, @NotNull Disposable parentDisposable) {
myLockDispatcherListener.addWriteActionListener(listener, parentDisposable);
lock.addWriteActionListener(listener);
Disposer.register(parentDisposable, () -> lock.removeWriteActionListener(listener));
}
@Override
public void addReadActionListener(@NotNull ReadActionListener listener, @NotNull Disposable parentDisposable) {
myLockDispatcherListener.addReadActionListener(listener, parentDisposable);
lock.addReadActionListener(listener);
Disposer.register(parentDisposable, () -> lock.removeReadActionListener(listener));
}
@Override
public void addWriteIntentReadActionListener(@NotNull WriteIntentReadActionListener listener, @NotNull Disposable parentDisposable) {
myLockDispatcherListener.addWriteIntentReadActionListener(listener, parentDisposable);
lock.addWriteIntentReadActionListener(listener);
Disposer.register(parentDisposable, () -> lock.removeWriteIntentReadActionListener(listener));
}
public void addLockAcquisitionListener(@NotNull LockAcquisitionListener listener, @NotNull Disposable parentDisposable) {
myLockDispatcherListener.addLockAcquisitionListener(listener, parentDisposable);
lock.setLockAcquisitionListener(listener);
Disposer.register(parentDisposable, () -> lock.removeLockAcquisitionListener(listener));
}
@Override
@@ -1456,168 +1477,10 @@ public final class ApplicationImpl extends ClientAwareComponentManager implement
@Override
public void addSuspendingWriteActionListener(@NotNull WriteLockReacquisitionListener listener, @NotNull Disposable parentDisposable) {
myLockDispatcherListener.addSuspendingWriteActionListener(listener, parentDisposable);
lock.setWriteLockReacquisitionListener(listener);
Disposer.register(parentDisposable, () -> lock.removeWriteLockReacquisitionListener(listener));
}
/**
* inner class by intention
*/
private class LockDispatchListener
implements ReadActionListener, WriteActionListener, WriteIntentReadActionListener, LockAcquisitionListener,
WriteLockReacquisitionListener {
private final DisposableWrapperList<WriteActionListener> myWriteActionListeners = new DisposableWrapperList<>();
private final DisposableWrapperList<ReadActionListener> myReadActionListeners = new DisposableWrapperList<>();
private final DisposableWrapperList<WriteIntentReadActionListener> myWriteIntentReadActionListeners =
new DisposableWrapperList<>();
private final DisposableWrapperList<LockAcquisitionListener> myLockAcquisitionListeners =
new DisposableWrapperList<>();
private final DisposableWrapperList<WriteLockReacquisitionListener> myWriteLockReacquisitionListeners =
new DisposableWrapperList<>();
public void addWriteActionListener(WriteActionListener listener, Disposable disposable) {
addListener(myWriteActionListeners, listener, disposable);
}
public void addReadActionListener(ReadActionListener listener, Disposable disposable) {
addListener(myReadActionListeners, listener, disposable);
}
public void addWriteIntentReadActionListener(WriteIntentReadActionListener listener, Disposable disposable) {
addListener(myWriteIntentReadActionListeners, listener, disposable);
}
public void addSuspendingWriteActionListener(WriteLockReacquisitionListener listener, Disposable disposable) {
addListener(myWriteLockReacquisitionListeners, listener, disposable);
}
public void addLockAcquisitionListener(LockAcquisitionListener listener, Disposable disposable) {
addListener(myLockAcquisitionListeners, listener, disposable);
}
private static <T extends EventListener> void addListener(DisposableWrapperList<T> list, T listener, Disposable disposable) {
list.add(listener, disposable);
}
@Override
public void writeActionFinished(@NotNull Class<?> action) {
ApplicationImpl.this.fireWriteActionFinished(action);
invokeListeners(myWriteActionListeners, WriteActionListener::writeActionFinished, action);
}
@Override
public void afterWriteActionFinished(@NotNull Class<?> action) {
ApplicationImpl.this.fireAfterWriteActionFinished(action);
invokeListeners(myWriteActionListeners, WriteActionListener::afterWriteActionFinished, action);
}
@Override
public void beforeWriteLockAcquired() {
invokeListeners(myLockAcquisitionListeners, LockAcquisitionListener::beforeWriteLockAcquired);
}
@Override
public void afterWriteLockAcquired() {
invokeListeners(myLockAcquisitionListeners, LockAcquisitionListener::afterWriteLockAcquired);
}
@Override
public void writeIntentReadActionStarted(@NotNull Class<?> action) {
invokeListeners(myWriteIntentReadActionListeners, WriteIntentReadActionListener::writeIntentReadActionStarted, action);
}
@Override
public void writeIntentReadActionFinished(@NotNull Class<?> action) {
invokeListeners(myWriteIntentReadActionListeners, WriteIntentReadActionListener::writeIntentReadActionFinished, action);
}
@Override
public void beforeWriteIntentReadActionStart(@NotNull Class<?> action) {
invokeListeners(myWriteIntentReadActionListeners, WriteIntentReadActionListener::beforeWriteIntentReadActionStart, action);
}
@Override
public void afterWriteIntentReadActionFinished(@NotNull Class<?> action) {
invokeListeners(myWriteIntentReadActionListeners, WriteIntentReadActionListener::afterWriteIntentReadActionFinished, action);
}
@Override
public void beforeWriteLockReacquired() {
invokeListeners(myWriteLockReacquisitionListeners, WriteLockReacquisitionListener::beforeWriteLockReacquired);
}
@Override
public void readActionStarted(@NotNull Class<?> action) {
invokeListeners(myReadActionListeners, ReadActionListener::readActionStarted, action);
}
@Override
public void fastPathAcquisitionFailed() {
// Impatient reader not in non-cancellable session will not wait
if (myImpatientReader.get() && !Cancellation.isInNonCancelableSection()) {
throw ApplicationUtil.CannotRunReadActionException.create();
}
}
@Override
public void beforeReadActionStart(@NotNull Class<?> action) {
invokeListeners(myReadActionListeners, ReadActionListener::beforeReadActionStart, action);
}
@Override
public void afterReadActionFinished(@NotNull Class<?> action) {
invokeListeners(myReadActionListeners, ReadActionListener::afterReadActionFinished, action);
}
@Override
public void readActionFinished(@NotNull Class<?> action) {
myReadActionCacheImpl.clear();
invokeListeners(myReadActionListeners, ReadActionListener::readActionFinished, action);
otelMonitor.get().readActionExecuted();
}
@Override
public void beforeWriteActionStart(@NotNull Class<?> action) {
ActivityTracker.getInstance().inc();
ApplicationImpl.this.fireBeforeWriteActionStart(action);
invokeListeners(myWriteActionListeners, WriteActionListener::beforeWriteActionStart, action);
}
@Override
public void writeActionStarted(@NotNull Class<?> action) {
ApplicationImpl.this.fireWriteActionStarted(action);
invokeListeners(myWriteActionListeners, WriteActionListener::writeActionStarted, action);
}
private static <T extends EventListener> void invokeListeners(List<T> listeners, BiConsumer<T, Class<?>> applier, Class<?> arg) {
invokeListeners(listeners, (listener) -> applier.accept(listener, arg));
}
private static <T extends EventListener> void invokeListeners(List<T> listeners, Consumer<T> applier) {
List<Throwable> exceptions = new SmartList<>();
for (T listener : listeners) {
try {
applier.accept(listener);
}
catch (Throwable t) {
exceptions.add(t);
}
}
for (Throwable exception : exceptions) {
if (exception instanceof CancellationException || exception instanceof ControlFlowException) {
continue;
}
getLogger().error(exception);
}
}
}
@Override
public kotlin.Pair<CoroutineContext, AccessToken> getLockStateAsCoroutineContext(CoroutineContext baseContext, boolean shared) {
var pair = getThreadingSupport().getPermitAsContextElement(baseContext, shared);
@@ -1635,7 +1498,7 @@ public final class ApplicationImpl extends ClientAwareComponentManager implement
}
public @NotNull ThreadingSupport getThreadingSupport() {
return IntelliJLockingUtil.getGlobalThreadingSupport();
return lock;
}
@RequiresBackgroundThread(generateAssertion = false)