From 1f1a5dc33c20508a7e7a5b17c7eddaef823d5437 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Thu, 9 Jul 2026 12:33:20 -0400 Subject: [PATCH] GP-7051 - Fixed deadlock seen when saving a program --- .../util/PersistentOpenCloseManager.java | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/PersistentOpenCloseManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/PersistentOpenCloseManager.java index 21c0af5e54..1be8c37d50 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/PersistentOpenCloseManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/util/PersistentOpenCloseManager.java @@ -26,7 +26,6 @@ import ghidra.program.model.util.VoidPropertyMap; */ public class PersistentOpenCloseManager implements OpenCloseManager { private boolean openByDefault = true; - private VoidPropertyMap booleanProperty; private ProgramUserData programUserData; // Often, isOpen will be called on the same function address many times in a row so cache @@ -35,32 +34,53 @@ public class PersistentOpenCloseManager implements OpenCloseManager { private boolean cachedResult; private String defaultOpenClosePropertyname; + private String owner; + private String propertyName; + private VoidPropertyMap cachedPropertyMap; + public PersistentOpenCloseManager(ProgramUserData data, String owner, String propertyName) { + this.owner = owner; + this.propertyName = propertyName; this.defaultOpenClosePropertyname = propertyName + "Default"; programUserData = data; - int tx = programUserData.startTransaction(); - try { - booleanProperty = - programUserData.getBooleanProperty(owner, propertyName, true); - } - finally { - programUserData.endTransaction(tx); - } - // Get the default open state. Only addresses different from default have properties stored. String functionState = programUserData.getStringProperty(defaultOpenClosePropertyname, "Open"); openByDefault = functionState.equals("Open"); } + private VoidPropertyMap getExistingProperty() { + if (cachedPropertyMap != null) { + return cachedPropertyMap; + } + + cachedPropertyMap = programUserData.getBooleanProperty(owner, propertyName, false); + return cachedPropertyMap; + } + + private VoidPropertyMap getOrCreatePropertyMap() { + if (cachedPropertyMap != null) { + return cachedPropertyMap; + } + + cachedPropertyMap = programUserData.getBooleanProperty(owner, propertyName, true); + return cachedPropertyMap; + } + @Override public boolean isOpen(Address address) { + VoidPropertyMap propertyMap = getExistingProperty(); + if (propertyMap == null) { + return openByDefault; + } + if (address.equals(cachedAddress)) { return cachedResult; } + cachedAddress = address; - boolean contains = booleanProperty.hasProperty(address); + boolean contains = propertyMap.hasProperty(address); cachedResult = openByDefault ? !contains : contains; return cachedResult; } @@ -111,7 +131,8 @@ public class PersistentOpenCloseManager implements OpenCloseManager { private void addAddressProperty(Address address) { int tx = programUserData.startTransaction(); try { - booleanProperty.add(address); + VoidPropertyMap propertyMap = getOrCreatePropertyMap(); + propertyMap.add(address); } finally { programUserData.endTransaction(tx); @@ -121,7 +142,8 @@ public class PersistentOpenCloseManager implements OpenCloseManager { private void removeAddressProperty(Address address) { int tx = programUserData.startTransaction(); try { - booleanProperty.remove(address); + VoidPropertyMap propertyMap = getOrCreatePropertyMap(); + propertyMap.remove(address); } finally { programUserData.endTransaction(tx); @@ -131,7 +153,8 @@ public class PersistentOpenCloseManager implements OpenCloseManager { private void clearProperties() { int tx = programUserData.startTransaction(); try { - booleanProperty.clear(); + VoidPropertyMap propertyMap = getOrCreatePropertyMap(); + propertyMap.clear(); } finally { programUserData.endTransaction(tx);