From a391f202f5cccfeaa55ed30cd43f1b2a697aeb73 Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 10 Sep 2025 14:40:06 +0100 Subject: Fix threading issues in ClassHandleProvider that are likely to come up if a plugin frequently invalidates class handles --- .../enigma/classhandle/ClassHandleProvider.java | 58 ++++++++++++++++++---- 1 file changed, 49 insertions(+), 9 deletions(-) (limited to 'enigma') diff --git a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java index f441bf4..cb80dda 100644 --- a/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java +++ b/enigma/src/main/java/cuchaz/enigma/classhandle/ClassHandleProvider.java @@ -18,6 +18,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import javax.swing.SwingUtilities; + import org.jetbrains.annotations.Nullable; import cuchaz.enigma.EnigmaProject; @@ -230,6 +232,8 @@ public final class ClassHandleProvider { private final AtomicInteger mappedVersion = new AtomicInteger(); private final ReadWriteLock lock = new ReentrantReadWriteLock(); + private final Object decompileCompleteMutex = new Object(); + private final Object mappedCompleteMutex = new Object(); private Entry(ClassHandleProvider p, ClassEntry entry) { this.p = p; @@ -287,10 +291,26 @@ public final class ClassHandleProvider { } Result uncommentedSource = Result.ok(p.decompiler.getSource(entry.getFullName())); - Entry.this.uncommentedSource = uncommentedSource; - Entry.this.waitingUncommentedSources.forEach(f -> f.complete(uncommentedSource)); - Entry.this.waitingUncommentedSources.clear(); - withLock(lock.readLock(), () -> new ArrayList<>(handles)).forEach(h -> h.onUncommentedSourceChanged(uncommentedSource)); + + synchronized (decompileCompleteMutex) { + if (decompileVersion.get() != v) { + return null; + } + + Entry.this.uncommentedSource = uncommentedSource; + + synchronized (Entry.this.waitingUncommentedSources) { + Entry.this.waitingUncommentedSources.forEach(f -> f.complete(uncommentedSource)); + Entry.this.waitingUncommentedSources.clear(); + } + + SwingUtilities.invokeLater(() -> { + if (decompileVersion.get() == v) { + withLock(lock.readLock(), () -> new ArrayList<>(handles)).forEach(h -> h.onUncommentedSourceChanged(uncommentedSource)); + } + }); + } + return uncommentedSource; }, p.pool); } @@ -303,7 +323,13 @@ public final class ClassHandleProvider { } Result jdSource = res.map(s -> s.withJavadocs(p.project.getMapper())); - withLock(lock.readLock(), () -> new ArrayList<>(handles)).forEach(h -> h.onDocsChanged(jdSource)); + + SwingUtilities.invokeLater(() -> { + if (javadocVersion.get() == v) { + withLock(lock.readLock(), () -> new ArrayList<>(handles)).forEach(h -> h.onDocsChanged(jdSource)); + } + }); + return jdSource; }, p.pool); } @@ -341,10 +367,24 @@ public final class ClassHandleProvider { return; } - Entry.this.source = res; - Entry.this.waitingSources.forEach(s -> s.complete(source)); - Entry.this.waitingSources.clear(); - withLock(lock.readLock(), () -> new ArrayList<>(handles)).forEach(h -> h.onMappedSourceChanged(source)); + synchronized (mappedCompleteMutex) { + if (mappedVersion.get() != v) { + return; + } + + Entry.this.source = res; + + synchronized (Entry.this.waitingSources) { + Entry.this.waitingSources.forEach(s -> s.complete(source)); + Entry.this.waitingSources.clear(); + } + + SwingUtilities.invokeLater(() -> { + if (mappedVersion.get() == v) { + withLock(lock.readLock(), () -> new ArrayList<>(handles)).forEach(h -> h.onMappedSourceChanged(source)); + } + }); + } }); } -- cgit v1.2.3