GP-4534 Remove memory map address set cache lock contention

This commit is contained in:
emteere 2024-04-18 13:25:19 -04:00
parent a36671d9d3
commit 4494e32596
4 changed files with 154 additions and 137 deletions

View file

@ -135,14 +135,21 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
} }
} }
void buildAddressSets() { MemoryAddressSetView buildAddressSets() {
if (addrSetView != null) {
return addrSetView;
}
lock.acquire(); lock.acquire();
try { try {
// null addrSet signals address sets must be built otherwise return // have to try and get it again, another thread may have already filled it out
if (addrSetView != null) { if (addrSetView != null) {
return; return addrSetView;
} }
addrSetView = new MemoryAddressSetView();
// set cached addressSet view to null, so other threads will end up here waiting
// on the above lock if they try to access the cached address sets
addrSetView = null;
MemoryAddressSetView newAddrSetView = new MemoryAddressSetView();
// The allAddrSet instance is generally maintained with all memory // The allAddrSet instance is generally maintained with all memory
// block addresses and need only be updated if currently empty. // block addresses and need only be updated if currently empty.
@ -155,15 +162,18 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
for (MemoryBlockDB block : blocks) { for (MemoryBlockDB block : blocks) {
block.clearMappedBlockList(); block.clearMappedBlockList();
if (!block.isMapped()) { if (!block.isMapped()) {
addBlockAddresses(block, addToAll); addBlockAddresses(newAddrSetView, block, addToAll);
} }
} }
// process all mapped blocks after non-mapped-blocks above // process all mapped blocks after non-mapped-blocks above
for (MemoryBlockDB block : blocks) { for (MemoryBlockDB block : blocks) {
if (block.isMapped()) { if (block.isMapped()) {
addBlockAddresses(block, addToAll); addBlockAddresses(newAddrSetView, block, addToAll);
} }
} }
addrSetView = newAddrSetView;
return addrSetView;
} }
finally { finally {
lock.release(); lock.release();
@ -177,17 +187,17 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
* @param block memory block * @param block memory block
* @param addToAll if true the allAddrSet should be updated with the specified block's address range * @param addToAll if true the allAddrSet should be updated with the specified block's address range
*/ */
private void addBlockAddresses(MemoryBlockDB block, boolean addToAll) { private void addBlockAddresses(MemoryAddressSetView newSet, MemoryBlockDB block, boolean addToAll) {
Address start = block.getStart(); Address start = block.getStart();
Address end = block.getEnd(); Address end = block.getEnd();
if (addToAll) { if (addToAll) {
allAddrSet.add(start, end); allAddrSet.add(start, end);
} }
if (block.isExternalBlock()) { if (block.isExternalBlock()) {
addrSetView.externalBlock.add(start, end); newSet.externalBlock.add(start, end);
} }
else if (block.isExecute()) { else if (block.isExecute()) {
addrSetView.execute.add(start, end); newSet.execute.add(start, end);
} }
if (block.isMapped()) { if (block.isMapped()) {
// Identify source-blocks which block maps onto and add as a mapped-block to each of these // Identify source-blocks which block maps onto and add as a mapped-block to each of these
@ -198,15 +208,15 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
b.addMappedBlock(block); b.addMappedBlock(block);
} }
} }
AddressSet mappedSet = getMappedIntersection(block, addrSetView.initialized); AddressSet mappedSet = getMappedIntersection(block, newSet.initialized);
addrSetView.initialized.add(mappedSet); newSet.initialized.add(mappedSet);
addrSetView.initializedAndLoaded newSet.initializedAndLoaded
.add(getMappedIntersection(block, addrSetView.initializedAndLoaded)); .add(getMappedIntersection(block, newSet.initializedAndLoaded));
} }
else if (block.isInitialized()) { else if (block.isInitialized()) {
addrSetView.initialized.add(block.getStart(), block.getEnd()); newSet.initialized.add(block.getStart(), block.getEnd());
if (block.isLoaded()) { if (block.isLoaded()) {
addrSetView.initializedAndLoaded.add(block.getStart(), block.getEnd()); newSet.initializedAndLoaded.add(block.getStart(), block.getEnd());
} }
} }
} }
@ -312,16 +322,8 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
@Override @Override
public AddressSetView getAllInitializedAddressSet() { public AddressSetView getAllInitializedAddressSet() {
lock.acquire(); MemoryAddressSetView localAddrSetView = buildAddressSets();
try { return new AddressSetViewAdapter(localAddrSetView.initialized);
if (addrSetView == null) {
buildAddressSets();
}
return new AddressSetViewAdapter(addrSetView.initialized);
}
finally {
lock.release();
}
} }
@Override @Override
@ -329,16 +331,21 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
if (liveMemory != null) { if (liveMemory != null) {
return this; // all memory is initialized! return this; // all memory is initialized!
} }
lock.acquire();
try { MemoryAddressSetView localAddrSetView = buildAddressSets();
if (addrSetView == null) { return new AddressSetViewAdapter(localAddrSetView.initializedAndLoaded);
buildAddressSets(); }
}
return new AddressSetViewAdapter(addrSetView.initializedAndLoaded); @Override
} public boolean isExternalBlockAddress(Address addr) {
finally { MemoryAddressSetView localAddrSetView = buildAddressSets();
lock.release(); return localAddrSetView.externalBlock.contains(addr);
} }
@Override
public AddressSetView getExecuteSet() {
MemoryAddressSetView localAddrSetView = buildAddressSets();
return new AddressSetViewAdapter(localAddrSetView.execute);
} }
void checkMemoryWrite(MemoryBlockDB block, Address start, long length) void checkMemoryWrite(MemoryBlockDB block, Address start, long length)
@ -920,13 +927,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
@Override @Override
public long getSize() { public long getSize() {
lock.acquire(); return allAddrSet.getNumAddresses();
try {
return allAddrSet.getNumAddresses();
}
finally {
lock.release();
}
} }
@Override @Override
@ -1404,22 +1405,15 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
@Override @Override
public byte getByte(Address addr) throws MemoryAccessException { public byte getByte(Address addr) throws MemoryAccessException {
lock.acquire(); if (liveMemory != null) {
try { return liveMemory.getByte(addr);
if (liveMemory != null) {
return liveMemory.getByte(addr);
}
MemoryBlock block = getBlockDB(addr);
if (block == null) {
throw new MemoryAccessException(
"Address " + addr.toString(true) + " does not exist in memory");
}
return block.getByte(addr);
} }
finally { MemoryBlock block = getBlockDB(addr);
lock.release(); if (block == null) {
throw new MemoryAccessException(
"Address " + addr.toString(true) + " does not exist in memory");
} }
return block.getByte(addr);
} }
@Override @Override
@ -1804,13 +1798,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
@Override @Override
public boolean contains(Address addr) { public boolean contains(Address addr) {
lock.acquire(); return allAddrSet.contains(addr);
try {
return allAddrSet.contains(addr);
}
finally {
lock.release();
}
} }
@Override @Override
@ -1828,20 +1816,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
return allAddrSet.isEmpty(); return allAddrSet.isEmpty();
} }
@Override
public boolean isExternalBlockAddress(Address addr) {
lock.acquire();
try {
if (addrSetView == null) {
buildAddressSets();
}
return addrSetView.externalBlock.contains(addr);
}
finally {
lock.release();
}
}
@Override @Override
public Address getMinAddress() { public Address getMinAddress() {
return allAddrSet.getMinAddress(); return allAddrSet.getMinAddress();
@ -2147,20 +2121,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
return super.hashCode(); return super.hashCode();
} }
@Override
public AddressSetView getExecuteSet() {
lock.acquire();
try {
if (addrSetView == null) {
buildAddressSets();
}
return new AddressSetViewAdapter(addrSetView.execute);
}
finally {
lock.release();
}
}
@Override @Override
public void memoryChanged(Address addr, int size) { public void memoryChanged(Address addr, int size) {
fireBytesChanged(addr, size); fireBytesChanged(addr, size);

View file

@ -40,10 +40,12 @@ import ghidra.program.model.address.*;
*/ */
class RecoverableAddressIterator implements AddressIterator { class RecoverableAddressIterator implements AddressIterator {
private AddressSetView set; private SynchronizedAddressSet synchSet;
private AddressSet internalSet;
private boolean forward; private boolean forward;
private AddressIterator iterator; private AddressIterator iterator;
private Address next; private Address next;
private int changeID;
/** /**
* Construct iterator * Construct iterator
@ -51,8 +53,10 @@ class RecoverableAddressIterator implements AddressIterator {
* @param start address to start iterating at in the address set or null for all addresses * @param start address to start iterating at in the address set or null for all addresses
* @param forward if true address are return from lowest to highest, else from highest to lowest * @param forward if true address are return from lowest to highest, else from highest to lowest
*/ */
RecoverableAddressIterator(AddressSetView set, Address start, boolean forward) { RecoverableAddressIterator(SynchronizedAddressSet set, Address start, boolean forward) {
this.set = set; this.synchSet = set;
this.internalSet = set.getInternalSet();
changeID = set.getModificationID();
this.forward = forward; this.forward = forward;
initIterator(start); initIterator(start);
this.next = iterator.next(); this.next = iterator.next();
@ -60,10 +64,10 @@ class RecoverableAddressIterator implements AddressIterator {
private void initIterator(Address start) { private void initIterator(Address start) {
if (start == null) { if (start == null) {
iterator = set.getAddresses(forward); iterator = internalSet.getAddresses(forward);
} }
else { else {
iterator = set.getAddresses(start, forward); iterator = internalSet.getAddresses(start, forward);
} }
} }
@ -77,9 +81,14 @@ class RecoverableAddressIterator implements AddressIterator {
Address addr = next; Address addr = next;
if (addr != null) { if (addr != null) {
try { try {
next = iterator.next(); if (synchSet.hasChanged(changeID)) {
next = recoverNext(addr);
} else {
next = iterator.next();
}
} }
catch (ConcurrentModificationException e) { catch (ConcurrentModificationException e) {
// will never happen, set in hand is never changed
next = recoverNext(addr); next = recoverNext(addr);
} }
} }
@ -87,6 +96,8 @@ class RecoverableAddressIterator implements AddressIterator {
} }
private Address recoverNext(Address lastAddr) { private Address recoverNext(Address lastAddr) {
changeID = synchSet.getModificationID();
internalSet = synchSet.getInternalSet();
while (true) { while (true) {
try { try {
initIterator(lastAddr); initIterator(lastAddr);
@ -97,6 +108,7 @@ class RecoverableAddressIterator implements AddressIterator {
return a; return a;
} }
catch (ConcurrentModificationException e) { catch (ConcurrentModificationException e) {
// will never happen, set in hand is never changed
// set must have changed - try re-initializing again // set must have changed - try re-initializing again
} }
} }

View file

@ -39,10 +39,12 @@ import ghidra.program.model.address.*;
*/ */
class RecoverableAddressRangeIterator implements AddressRangeIterator { class RecoverableAddressRangeIterator implements AddressRangeIterator {
private AddressSetView set; private SynchronizedAddressSet synchSet;
private AddressSet internalSet;
private boolean forward; private boolean forward;
private AddressRangeIterator iterator; private AddressRangeIterator iterator;
private AddressRange next; private AddressRange next;
private int changeID;
/** /**
* Construct iterator * Construct iterator
@ -50,8 +52,10 @@ class RecoverableAddressRangeIterator implements AddressRangeIterator {
* @param start the address the the first range should contain. * @param start the address the the first range should contain.
* @param forward true iterators forward, false backwards * @param forward true iterators forward, false backwards
*/ */
RecoverableAddressRangeIterator(AddressSetView set, Address start, boolean forward) { RecoverableAddressRangeIterator(SynchronizedAddressSet set, Address start, boolean forward) {
this.set = set; this.synchSet = set;
this.internalSet = set.getInternalSet();
changeID = set.getModificationID();
this.forward = forward; this.forward = forward;
initIterator(start); initIterator(start);
try { try {
@ -64,10 +68,10 @@ class RecoverableAddressRangeIterator implements AddressRangeIterator {
private void initIterator(Address start) { private void initIterator(Address start) {
if (start == null) { if (start == null) {
iterator = set.getAddressRanges(forward); iterator = internalSet.getAddressRanges(forward);
} }
else { else {
iterator = set.getAddressRanges(start, forward); iterator = internalSet.getAddressRanges(start, forward);
} }
} }
@ -83,9 +87,14 @@ class RecoverableAddressRangeIterator implements AddressRangeIterator {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
try { try {
next = iterator.next(); if (synchSet.hasChanged(changeID)) {
next = recoverNext(range);
} else {
next = iterator.next();
}
} }
catch (ConcurrentModificationException e) { catch (ConcurrentModificationException e) {
// will never happen, set in hand is never changed
next = recoverNext(range); next = recoverNext(range);
} }
catch (NoSuchElementException e) { catch (NoSuchElementException e) {
@ -95,6 +104,8 @@ class RecoverableAddressRangeIterator implements AddressRangeIterator {
} }
private AddressRange recoverNext(AddressRange lastRange) { private AddressRange recoverNext(AddressRange lastRange) {
changeID = synchSet.getModificationID();
internalSet = synchSet.getInternalSet();
while (true) { while (true) {
try { try {
Address lastAddr = forward ? lastRange.getMaxAddress() : lastRange.getMinAddress(); Address lastAddr = forward ? lastRange.getMaxAddress() : lastRange.getMinAddress();
@ -114,6 +125,7 @@ class RecoverableAddressRangeIterator implements AddressRangeIterator {
return iterator.next(); // skip range and return next return iterator.next(); // skip range and return next
} }
catch (ConcurrentModificationException e) { catch (ConcurrentModificationException e) {
// will never happen, set in hand is never changed
// set must have changed - try re-initializing again // set must have changed - try re-initializing again
} }
catch (NoSuchElementException e) { catch (NoSuchElementException e) {

View file

@ -28,10 +28,20 @@ import ghidra.program.model.address.*;
class SynchronizedAddressSet implements AddressSetView { class SynchronizedAddressSet implements AddressSetView {
private AddressSet set; private AddressSet set;
private int modificationID = 1; // updated if set above ever replaced
SynchronizedAddressSet() { SynchronizedAddressSet() {
set = new AddressSet(); set = new AddressSet();
} }
/**
* get the modification number of the internal address set
* If the underlying set if ever replaced, modification id is changed
* @return current modification id
*/
int getModificationID() {
return modificationID;
}
/** /**
* Add all addresses of the given AddressSet to this set. * Add all addresses of the given AddressSet to this set.
@ -39,7 +49,10 @@ class SynchronizedAddressSet implements AddressSetView {
* @see AddressSet#add(AddressSetView) * @see AddressSet#add(AddressSetView)
*/ */
synchronized void add(AddressSet addrSet) { synchronized void add(AddressSet addrSet) {
set.add(addrSet); AddressSet newSet = new AddressSet(set);
newSet.add(addrSet);
set = newSet;
modificationID++;
} }
/** /**
@ -49,7 +62,10 @@ class SynchronizedAddressSet implements AddressSetView {
* @see AddressSet#add(Address, Address) * @see AddressSet#add(Address, Address)
*/ */
synchronized void add(Address start, Address end) { synchronized void add(Address start, Address end) {
set.add(start, end); AddressSet newSet = new AddressSet(set);
newSet.add(start, end);
set = newSet;
modificationID++;
} }
/** /**
@ -59,57 +75,75 @@ class SynchronizedAddressSet implements AddressSetView {
* @see AddressSet#delete(Address, Address) * @see AddressSet#delete(Address, Address)
*/ */
synchronized void delete(Address start, Address end) { synchronized void delete(Address start, Address end) {
set.delete(start, end); AddressSet newSet = new AddressSet(set);
newSet.delete(start, end);
set = newSet;
modificationID++;
}
synchronized AddressSet getInternalSet() {
return set;
}
/**
* Check if the internal set has been modified
* If the mod id is different, then the set has changed.
*
* @param modID modification id to check
* @return true if internal mod id is different
*/
public boolean hasChanged(int modID) {
return modID != modificationID;
} }
@Override @Override
public synchronized boolean contains(Address addr) { public boolean contains(Address addr) {
return set.contains(addr); return set.contains(addr);
} }
@Override @Override
public synchronized boolean contains(Address start, Address end) { public boolean contains(Address start, Address end) {
return set.contains(start, end); return set.contains(start, end);
} }
@Override @Override
public synchronized boolean contains(AddressSetView addrSet) { public boolean contains(AddressSetView addrSet) {
return set.contains(addrSet); return set.contains(addrSet);
} }
@Override @Override
public synchronized boolean isEmpty() { public boolean isEmpty() {
return set.isEmpty(); return set.isEmpty();
} }
@Override @Override
public synchronized Address getMinAddress() { public Address getMinAddress() {
return set.getMinAddress(); return set.getMinAddress();
} }
@Override @Override
public synchronized Address getMaxAddress() { public Address getMaxAddress() {
return set.getMaxAddress(); return set.getMaxAddress();
} }
@Override @Override
public synchronized int getNumAddressRanges() { public int getNumAddressRanges() {
return set.getNumAddressRanges(); return set.getNumAddressRanges();
} }
@Override @Override
public synchronized AddressRangeIterator getAddressRanges() { public AddressRangeIterator getAddressRanges() {
return set.getAddressRanges(); return set.getAddressRanges();
} }
@Override @Override
public synchronized AddressRangeIterator getAddressRanges(boolean forward) { public AddressRangeIterator getAddressRanges(boolean forward) {
return set.getAddressRanges(forward); return set.getAddressRanges(forward);
} }
@Override @Override
public synchronized AddressRangeIterator getAddressRanges(Address start, boolean forward) { public synchronized AddressRangeIterator getAddressRanges(Address start, boolean forward) {
return new RecoverableAddressRangeIterator(set, start, forward); return new RecoverableAddressRangeIterator(this, start, forward);
} }
@Override @Override
@ -118,103 +152,102 @@ class SynchronizedAddressSet implements AddressSetView {
} }
@Override @Override
public synchronized Iterator<AddressRange> iterator(boolean forward) { public Iterator<AddressRange> iterator(boolean forward) {
return set.getAddressRanges(forward); return set.getAddressRanges(forward);
} }
@Override @Override
public synchronized Iterator<AddressRange> iterator(Address start, boolean forward) { public Iterator<AddressRange> iterator(Address start, boolean forward) {
return set.getAddressRanges(start, forward); return set.getAddressRanges(start, forward);
} }
@Override @Override
public synchronized long getNumAddresses() { public long getNumAddresses() {
return set.getNumAddresses(); return set.getNumAddresses();
} }
@Override @Override
public synchronized AddressIterator getAddresses(boolean forward) { public synchronized AddressIterator getAddresses(boolean forward) {
return new RecoverableAddressIterator(set, null, forward); return new RecoverableAddressIterator(this, null, forward);
} }
@Override @Override
public synchronized AddressIterator getAddresses(Address start, boolean forward) { public synchronized AddressIterator getAddresses(Address start, boolean forward) {
return new RecoverableAddressIterator(set, start, forward); return new RecoverableAddressIterator(this, start, forward);
} }
@Override @Override
public synchronized boolean intersects(AddressSetView addrSet) { public boolean intersects(AddressSetView addrSet) {
return set.intersects(addrSet); return set.intersects(addrSet);
} }
@Override @Override
public synchronized boolean intersects(Address start, Address end) { public boolean intersects(Address start, Address end) {
return set.intersects(start, end); return set.intersects(start, end);
} }
@Override @Override
public synchronized AddressSet intersect(AddressSetView addrSet) { public AddressSet intersect(AddressSetView addrSet) {
return set.intersect(addrSet); return set.intersect(addrSet);
} }
@Override @Override
public synchronized AddressSet intersectRange(Address start, Address end) { public AddressSet intersectRange(Address start, Address end) {
return set.intersectRange(start, end); return set.intersectRange(start, end);
} }
@Override @Override
public synchronized AddressSet union(AddressSetView addrSet) { public AddressSet union(AddressSetView addrSet) {
return set.union(addrSet); return set.union(addrSet);
} }
@Override @Override
public synchronized AddressSet subtract(AddressSetView addrSet) { public AddressSet subtract(AddressSetView addrSet) {
return set.subtract(addrSet); return set.subtract(addrSet);
} }
@Override @Override
public synchronized AddressSet xor(AddressSetView addrSet) { public AddressSet xor(AddressSetView addrSet) {
return set.xor(addrSet); return set.xor(addrSet);
} }
@Override @Override
public synchronized boolean hasSameAddresses(AddressSetView addrSet) { public boolean hasSameAddresses(AddressSetView addrSet) {
return set.hasSameAddresses(addrSet); return set.hasSameAddresses(addrSet);
} }
@Override @Override
public synchronized AddressRange getFirstRange() { public AddressRange getFirstRange() {
return set.getFirstRange(); return set.getFirstRange();
} }
@Override @Override
public synchronized AddressRange getLastRange() { public AddressRange getLastRange() {
return set.getLastRange(); return set.getLastRange();
} }
@Override @Override
public synchronized AddressRange getRangeContaining(Address address) { public AddressRange getRangeContaining(Address address) {
return set.getRangeContaining(address); return set.getRangeContaining(address);
} }
@Override @Override
public synchronized Address findFirstAddressInCommon(AddressSetView addrSet) { public Address findFirstAddressInCommon(AddressSetView addrSet) {
return set.findFirstAddressInCommon(addrSet); return set.findFirstAddressInCommon(addrSet);
} }
@Override @Override
public synchronized int hashCode() { public int hashCode() {
return set.hashCode(); return set.hashCode();
} }
@Override @Override
public synchronized boolean equals(Object obj) { public boolean equals(Object obj) {
return set.equals(obj); return set.equals(obj);
} }
@Override @Override
public synchronized String toString() { public String toString() {
return set.toString(); return set.toString();
} }
} }