Skip to content

Commit 73a6920

Browse files
hinermCopilot
andcommitted
Prevent Script Editor title deadlocks
Queue title updates and title calculation on the EDT. Avoid holding the TextEditor monitor while Frame.setTitle fires Swing property changes, preventing lock inversion with the AWT tree lock during window closure. Add a regression test confirming setTitle(String) is not synchronized. Co-authored-by: Copilot <copilot@github.com>
1 parent 088456a commit 73a6920

2 files changed

Lines changed: 18 additions & 8 deletions

File tree

‎src/main/java/org/scijava/ui/swing/script/TextEditor.java‎

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2590,14 +2590,13 @@ public void setEditorPaneFileName(final File file) {
25902590
}
25912591

25922592
void setTitle() {
2593-
final EditorPane editorPane = getEditorPane();
2594-
2595-
final boolean fileChanged = editorPane.fileChanged();
2596-
final String fileName = editorPane.getFileName();
2597-
final String title =
2598-
(fileChanged ? "*" : "") + fileName +
2599-
(executingTasks.isEmpty() ? "" : " (Running)");
26002593
SwingUtilities.invokeLater(() -> {
2594+
final EditorPane editorPane = getEditorPane();
2595+
final boolean fileChanged = editorPane.fileChanged();
2596+
final String fileName = editorPane.getFileName();
2597+
final String title =
2598+
(fileChanged ? "*" : "") + fileName +
2599+
(executingTasks.isEmpty() ? "" : " (Running)");
26012600
setTitle(title); // to the main window
26022601
// Update all tabs: could have changed
26032602
for (int i = 0; i < tabbed.getTabCount(); i++)
@@ -2607,7 +2606,11 @@ void setTitle() {
26072606
}
26082607

26092608
@Override
2610-
public synchronized void setTitle(final String title) {
2609+
public void setTitle(final String title) {
2610+
if (!SwingUtilities.isEventDispatchThread()) {
2611+
SwingUtilities.invokeLater(() -> setTitle(title));
2612+
return;
2613+
}
26112614
super.setTitle(title);
26122615
final int index = tabsMenuTabsStart + tabbed.getSelectedIndex();
26132616
if (index < tabsMenu.getItemCount()) {

‎src/test/java/org/scijava/ui/swing/script/TextEditorThreadOwnershipTest.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import static org.junit.Assert.assertSame;
3535
import static org.junit.Assert.assertTrue;
3636

37+
import java.lang.reflect.Modifier;
3738
import java.util.concurrent.CountDownLatch;
3839
import java.util.concurrent.Future;
3940
import java.util.concurrent.TimeUnit;
@@ -61,6 +62,12 @@ public void tearDown() {
6162
context.dispose();
6263
}
6364

65+
@Test
66+
public void testTitleSetterDoesNotSynchronizeOnEditor() throws Exception {
67+
assertFalse(Modifier.isSynchronized(TextEditor.class.getMethod("setTitle",
68+
String.class).getModifiers()));
69+
}
70+
6471
@Test
6572
public void testOrdinaryChildThreadBelongsToRun() {
6673
final ThreadGroup runGroup = new ThreadGroup("script-run");

0 commit comments

Comments
 (0)