diff --git a/rhino/src/main/java/org/mozilla/javascript/EmbeddedSlotMap.java b/rhino/src/main/java/org/mozilla/javascript/EmbeddedSlotMap.java index 837a8ca5fb..373739b147 100644 --- a/rhino/src/main/java/org/mozilla/javascript/EmbeddedSlotMap.java +++ b/rhino/src/main/java/org/mozilla/javascript/EmbeddedSlotMap.java @@ -130,7 +130,7 @@ private void createNewSlot(SlotMapOwner owner, Slot newSlot) { // Check if the table is not too full before inserting. if (4 * (count + 1) > 3 * slots.length) { // table size must be a power of 2 -- always grow by x2! - if (count > SlotMapContainer.LARGE_HASH_SIZE) { + if (count > SlotMapOwner.LARGE_HASH_SIZE) { promoteMap(owner, newSlot); return; } diff --git a/rhino/src/main/java/org/mozilla/javascript/LockAwareSlotMap.java b/rhino/src/main/java/org/mozilla/javascript/LockAwareSlotMap.java index 0f1084b382..195f46ee1c 100644 --- a/rhino/src/main/java/org/mozilla/javascript/LockAwareSlotMap.java +++ b/rhino/src/main/java/org/mozilla/javascript/LockAwareSlotMap.java @@ -1,10 +1,9 @@ package org.mozilla.javascript; /** - * Extends {@link SlotMap} with a set of "WithLock" methods. which - * will not acquire the lock. These should only be used internally by - * implementation, or by other {@link SlotMap}s which share the same - * lock. + * Extends {@link SlotMap} with a set of "WithLock" methods. which will not acquire the lock. These + * should only be used internally by implementation, or by other {@link SlotMap}s which share the + * same lock. */ interface LockAwareSlotMap extends SlotMap { /** The equivalent of {@link SlotMap#size()}. */ @@ -19,7 +18,10 @@ interface LockAwareSlotMap extends SlotMap { /** The equivalent of {@link SlotMap#query(Object, int)}. */ Slot queryWithLock(Object key, int index); - /** The equivalent of {@link SlotMap#compute(SlotMapOwner, Object, int, org.mozilla.javascript.SlotMap.SlotComputer)}. */ + /** + * The equivalent of {@link SlotMap#compute(SlotMapOwner, Object, int, + * org.mozilla.javascript.SlotMap.SlotComputer)}. + */ S computeWithLock( SlotMapOwner owner, Object key, int index, SlotComputer compute); diff --git a/rhino/src/main/java/org/mozilla/javascript/SlotMapContainer.java b/rhino/src/main/java/org/mozilla/javascript/SlotMapContainer.java deleted file mode 100644 index 377ff67cd8..0000000000 --- a/rhino/src/main/java/org/mozilla/javascript/SlotMapContainer.java +++ /dev/null @@ -1,255 +0,0 @@ -/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.javascript; - -import java.util.Collections; -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.Objects; - -/** - * This class holds the various SlotMaps of various types, and knows how to atomically switch - * between them when we need to so that we use the right data structure at the right time. - */ -class SlotMapContainer extends SlotMapOwner implements SlotMap { - - /** - * Once the object has this many properties in it, we will replace the EmbeddedSlotMap with - * HashSlotMap. We can adjust this parameter to balance performance for typical objects versus - * performance for huge objects with many collisions. - */ - static final int LARGE_HASH_SIZE = 2000; - - static final int DEFAULT_SIZE = 10; - - private static final class EmptySlotMap implements SlotMap { - - @Override - public Iterator iterator() { - return Collections.emptyIterator(); - } - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) { - var newSlot = new Slot(key, index, attributes); - var map = new SingleEntrySlotMap(newSlot); - owner.setMap(map); - return newSlot; - } - - @Override - public Slot query(Object key, int index) { - return null; - } - - @Override - public void add(SlotMapOwner owner, Slot newSlot) { - if (newSlot != null) { - var map = new SingleEntrySlotMap(newSlot); - owner.setMap(map); - } - } - - @Override - public S compute( - SlotMapOwner owner, Object key, int index, SlotComputer c) { - var newSlot = c.compute(key, index, null); - if (newSlot != null) { - var map = new SingleEntrySlotMap(newSlot); - owner.setMap(map); - } - return newSlot; - } - } - - private static final class Iter implements Iterator { - private Slot next; - - Iter(Slot slot) { - next = slot; - } - - @Override - public boolean hasNext() { - return next != null; - } - - @Override - public Slot next() { - Slot ret = next; - if (ret == null) { - throw new NoSuchElementException(); - } - next = next.orderedNext; - return ret; - } - } - - static final class SingleEntrySlotMap implements SlotMap { - - SingleEntrySlotMap(Slot slot) { - assert (slot != null); - this.slot = slot; - } - - private final Slot slot; - - @Override - public Iterator iterator() { - return new Iter(slot); - } - - @Override - public int size() { - return 1; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) { - final int indexOrHash = (key != null ? key.hashCode() : index); - - if (indexOrHash == slot.indexOrHash && Objects.equals(slot.name, key)) { - return slot; - } - Slot newSlot = new Slot(key, index, attributes); - add(owner, newSlot); - return newSlot; - } - - @Override - public Slot query(Object key, int index) { - final int indexOrHash = (key != null ? key.hashCode() : index); - - if (indexOrHash == slot.indexOrHash && Objects.equals(slot.name, key)) { - return slot; - } - return null; - } - - @Override - public void add(SlotMapOwner owner, Slot newSlot) { - if (owner == null) { - throw new IllegalStateException(); - } else { - var newMap = new EmbeddedSlotMap(); - owner.setMap(newMap); - newMap.add(owner, slot); - newMap.add(owner, newSlot); - } - } - - @Override - public S compute( - SlotMapOwner owner, Object key, int index, SlotComputer c) { - var newMap = new EmbeddedSlotMap(); - owner.setMap(newMap); - newMap.add(owner, slot); - return newMap.compute(owner, key, index, c); - } - } - - static SlotMap EMPTY_SLOT_MAP = new EmptySlotMap(); - - SlotMapContainer() { - this(DEFAULT_SIZE); - } - - SlotMapContainer(int initialSize) { - super(initialMap(initialSize)); - } - - private static SlotMap initialMap(int initialSize) { - if (initialSize == 0) { - return EMPTY_SLOT_MAP; - } else if (initialSize > LARGE_HASH_SIZE) { - return new HashSlotMap(); - } else { - return new EmbeddedSlotMap(); - } - } - - @Override - public int size() { - return getMap().size(); - } - - @Override - public int dirtySize() { - return getMap().size(); - } - - @Override - public boolean isEmpty() { - return getMap().isEmpty(); - } - - @Override - public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) { - return getMap().modify(this, key, index, attributes); - } - - @Override - public S compute( - SlotMapOwner owner, Object key, int index, SlotComputer c) { - return getMap().compute(this, key, index, c); - } - - @Override - public Slot query(Object key, int index) { - return getMap().query(key, index); - } - - @Override - public void add(SlotMapOwner owner, Slot newSlot) { - getMap().add(this, newSlot); - } - - @Override - public Iterator iterator() { - return getMap().iterator(); - } - - @Override - public long readLock() { - // No locking in the default implementation - return 0L; - } - - @Override - public void unlockRead(long stamp) { - // No locking in the default implementation - } - - /** - * Before inserting a new item in the map, check and see if we need to expand from the embedded - * map to a HashMap that is more robust against large numbers of hash collisions. - */ - protected void checkMapSize() { - var map = getMap(); - if (map == EMPTY_SLOT_MAP) { - setMap(new EmbeddedSlotMap()); - } else if ((map instanceof EmbeddedSlotMap) && map.size() >= LARGE_HASH_SIZE) { - SlotMap newMap = new HashSlotMap(map); - setMap(newMap); - } - } -} diff --git a/rhino/src/main/java/org/mozilla/javascript/SlotMapOwner.java b/rhino/src/main/java/org/mozilla/javascript/SlotMapOwner.java index 0a7f3ca417..19372b28ec 100644 --- a/rhino/src/main/java/org/mozilla/javascript/SlotMapOwner.java +++ b/rhino/src/main/java/org/mozilla/javascript/SlotMapOwner.java @@ -2,10 +2,239 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; +import java.util.Collections; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; public abstract class SlotMapOwner { private static final long serialVersionUID = 1L; + static final int LARGE_HASH_SIZE = 2000; + + static final SlotMap EMPTY_SLOT_MAP = new EmptySlotMap(); + + static final SlotMap THREAD_SAFE_EMPTY_SLOT_MAP = new ThreadSafeEmptySlotMap(); + + private static class EmptySlotMap implements SlotMap { + + @Override + public Iterator iterator() { + return Collections.emptyIterator(); + } + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) { + var newSlot = new Slot(key, index, attributes); + var map = new SingleEntrySlotMap(newSlot); + owner.setMap(map); + return newSlot; + } + + @Override + public Slot query(Object key, int index) { + return null; + } + + @Override + public void add(SlotMapOwner owner, Slot newSlot) { + if (newSlot != null) { + var map = new SingleEntrySlotMap(newSlot); + owner.setMap(map); + } + } + + @Override + public S compute( + SlotMapOwner owner, Object key, int index, SlotComputer c) { + var newSlot = c.compute(key, index, null); + if (newSlot != null) { + var map = new SingleEntrySlotMap(newSlot); + owner.setMap(map); + } + return newSlot; + } + } + + private static final class ThreadSafeEmptySlotMap extends EmptySlotMap { + + @Override + public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) { + var newSlot = new Slot(key, index, attributes); + var currentMap = replaceMapAndAddSlot(owner, newSlot); + if (currentMap != this) { + return currentMap.modify(owner, key, index, attributes); + } + return newSlot; + } + + @Override + public void add(SlotMapOwner owner, Slot newSlot) { + if (newSlot != null) { + var currentMap = replaceMapAndAddSlot(owner, newSlot); + if (currentMap != this) { + currentMap.add(owner, newSlot); + } + return; + } + } + + @Override + public S compute( + SlotMapOwner owner, Object key, int index, SlotComputer c) { + var newSlot = c.compute(key, index, null); + if (newSlot != null) { + var currentMap = replaceMapAndAddSlot(owner, newSlot); + if (currentMap != this) { + return currentMap.compute(owner, key, index, c); + } + } + return newSlot; + } + + private SlotMap replaceMapAndAddSlot(SlotMapOwner owner, Slot newSlot) { + var map = new ThreadSafeSingleEntrySlotMap(newSlot); + return owner.checkAndReplaceMap(this, map); + } + } + + private static final class Iter implements Iterator { + private Slot next; + + Iter(Slot slot) { + next = slot; + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public Slot next() { + Slot ret = next; + if (ret == null) { + throw new NoSuchElementException(); + } + next = next.orderedNext; + return ret; + } + } + + static class SingleEntrySlotMap implements SlotMap { + + SingleEntrySlotMap(Slot slot) { + assert (slot != null); + this.slot = slot; + } + + protected final Slot slot; + + @Override + public Iterator iterator() { + return new Iter(slot); + } + + @Override + public int size() { + return 1; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) { + final int indexOrHash = (key != null ? key.hashCode() : index); + + if (indexOrHash == slot.indexOrHash && Objects.equals(slot.name, key)) { + return slot; + } + Slot newSlot = new Slot(key, index, attributes); + add(owner, newSlot); + return newSlot; + } + + @Override + public Slot query(Object key, int index) { + final int indexOrHash = (key != null ? key.hashCode() : index); + + if (indexOrHash == slot.indexOrHash && Objects.equals(slot.name, key)) { + return slot; + } + return null; + } + + @Override + public void add(SlotMapOwner owner, Slot newSlot) { + if (owner == null) { + throw new IllegalStateException(); + } else { + var newMap = new EmbeddedSlotMap(); + owner.setMap(newMap); + newMap.add(owner, slot); + newMap.add(owner, newSlot); + } + } + + @Override + public S compute( + SlotMapOwner owner, Object key, int index, SlotComputer c) { + var newMap = new EmbeddedSlotMap(); + owner.setMap(newMap); + newMap.add(owner, slot); + return newMap.compute(owner, key, index, c); + } + } + + static final class ThreadSafeSingleEntrySlotMap extends SingleEntrySlotMap { + + ThreadSafeSingleEntrySlotMap(Slot slot) { + super(slot); + } + + @Override + public void add(SlotMapOwner owner, Slot newSlot) { + if (owner == null) { + throw new IllegalStateException(); + } else { + var newMap = new ThreadSafeEmbeddedSlotMap(2); + newMap.add(null, slot); + var currentMap = owner.checkAndReplaceMap(this, newMap); + if (currentMap == this) { + newMap.add(owner, newSlot); + } else { + currentMap.add(owner, newSlot); + } + } + } + + @Override + public S compute( + SlotMapOwner owner, Object key, int index, SlotComputer c) { + var newMap = new ThreadSafeEmbeddedSlotMap(2); + newMap.add(null, slot); + var currentMap = owner.checkAndReplaceMap(this, newMap); + if (currentMap == this) { + return newMap.compute(owner, key, index, c); + } else { + return currentMap.compute(owner, key, index, c); + } + } + } + /** * This holds all the slots. It may or may not be thread-safe, and may expand itself to a * different data structure depending on the size of the object. @@ -28,15 +257,15 @@ protected static SlotMap createSlotMap(int initialSize) { Context cx = Context.getCurrentContext(); if ((cx != null) && cx.hasFeature(Context.FEATURE_THREAD_SAFE_OBJECTS)) { if (initialSize == 0) { - return ThreadSafeSlotMapContainer.EMPTY_SLOT_MAP; - } else if (initialSize > SlotMapContainer.LARGE_HASH_SIZE) { + return THREAD_SAFE_EMPTY_SLOT_MAP; + } else if (initialSize > LARGE_HASH_SIZE) { return new ThreadSafeHashSlotMap(initialSize); } else { return new ThreadSafeEmbeddedSlotMap(); } } else if (initialSize == 0) { - return SlotMapContainer.EMPTY_SLOT_MAP; - } else if (initialSize > SlotMapContainer.LARGE_HASH_SIZE) { + return EMPTY_SLOT_MAP; + } else if (initialSize > LARGE_HASH_SIZE) { return new HashSlotMap(); } else { return new EmbeddedSlotMap(); diff --git a/rhino/src/main/java/org/mozilla/javascript/ThreadSafeSlotMapContainer.java b/rhino/src/main/java/org/mozilla/javascript/ThreadSafeSlotMapContainer.java deleted file mode 100644 index a90c006359..0000000000 --- a/rhino/src/main/java/org/mozilla/javascript/ThreadSafeSlotMapContainer.java +++ /dev/null @@ -1,312 +0,0 @@ -/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.javascript; - -import java.util.Collections; -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.concurrent.locks.StampedLock; - -/** - * This class extends the SlotMapContainer so that we have thread-safe access to all the properties - * of an object. - */ -@SuppressWarnings("AndroidJdkLibsChecker") -class ThreadSafeSlotMapContainer extends SlotMapContainer { - - private final StampedLock lock = new StampedLock(); - - ThreadSafeSlotMapContainer() {} - - ThreadSafeSlotMapContainer(int initialSize) { - super(initialSize); - } - - private static final class EmptySlotMap implements SlotMap { - - @Override - public Iterator iterator() { - return Collections.emptyIterator(); - } - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) { - var newSlot = new Slot(key, index, attributes); - var currentMap = replaceMapAndAddSlot(owner, newSlot); - if (currentMap != this) { - return currentMap.modify(owner, key, index, attributes); - } - return newSlot; - } - - @Override - public Slot query(Object key, int index) { - return null; - } - - @Override - public void add(SlotMapOwner owner, Slot newSlot) { - if (newSlot != null) { - var currentMap = replaceMapAndAddSlot(owner, newSlot); - if (currentMap != this) { - currentMap.add(owner, newSlot); - } - return; - } - } - - @Override - public S compute( - SlotMapOwner owner, Object key, int index, SlotComputer c) { - var newSlot = c.compute(key, index, null); - if (newSlot != null) { - var currentMap = replaceMapAndAddSlot(owner, newSlot); - if (currentMap != this) { - return currentMap.compute(owner, key, index, c); - } - } - return newSlot; - } - - private SlotMap replaceMapAndAddSlot(SlotMapOwner owner, Slot newSlot) { - var map = new SingleEntrySlotMap(newSlot); - return owner.checkAndReplaceMap(this, map); - } - } - - private static final class Iter implements Iterator { - private Slot next; - - Iter(Slot slot) { - next = slot; - } - - @Override - public boolean hasNext() { - return next != null; - } - - @Override - public Slot next() { - Slot ret = next; - if (ret == null) { - throw new NoSuchElementException(); - } - next = next.orderedNext; - return ret; - } - } - - static final class SingleEntrySlotMap implements SlotMap { - - SingleEntrySlotMap(Slot slot) { - assert (slot != null); - this.slot = slot; - } - - private final Slot slot; - - @Override - public Iterator iterator() { - return new Iter(slot); - } - - @Override - public int size() { - return 1; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) { - final int indexOrHash = (key != null ? key.hashCode() : index); - - if (indexOrHash == slot.indexOrHash && Objects.equals(slot.name, key)) { - return slot; - } - Slot newSlot = new Slot(key, index, attributes); - add(owner, newSlot); - return newSlot; - } - - @Override - public Slot query(Object key, int index) { - final int indexOrHash = (key != null ? key.hashCode() : index); - - if (indexOrHash == slot.indexOrHash && Objects.equals(slot.name, key)) { - return slot; - } - return null; - } - - @Override - public void add(SlotMapOwner owner, Slot newSlot) { - if (owner == null) { - throw new IllegalStateException(); - } else { - var newMap = new ThreadSafeEmbeddedSlotMap(2); - newMap.add(null, slot); - var currentMap = owner.checkAndReplaceMap(this, newMap); - if (currentMap == this) { - newMap.add(owner, newSlot); - } else { - currentMap.add(owner, newSlot); - } - } - } - - @Override - public S compute( - SlotMapOwner owner, Object key, int index, SlotComputer c) { - var newMap = new ThreadSafeEmbeddedSlotMap(2); - newMap.add(null, slot); - var currentMap = owner.checkAndReplaceMap(this, newMap); - if (currentMap == this) { - return newMap.compute(owner, key, index, c); - } else { - return currentMap.compute(owner, key, index, c); - } - } - } - - static SlotMap EMPTY_SLOT_MAP = new EmptySlotMap(); - - @Override - public int size() { - long stamp = lock.tryOptimisticRead(); - int s = getMap().size(); - if (lock.validate(stamp)) { - return s; - } - - stamp = lock.readLock(); - try { - return getMap().size(); - } finally { - lock.unlockRead(stamp); - } - } - - @Override - public int dirtySize() { - assert lock.isReadLocked(); - return getMap().size(); - } - - @Override - public boolean isEmpty() { - long stamp = lock.tryOptimisticRead(); - boolean e = getMap().isEmpty(); - if (lock.validate(stamp)) { - return e; - } - - stamp = lock.readLock(); - try { - return getMap().isEmpty(); - } finally { - lock.unlockRead(stamp); - } - } - - @Override - public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) { - final long stamp = lock.writeLock(); - try { - return getMap().modify(this, key, index, attributes); - } finally { - lock.unlockWrite(stamp); - } - } - - @Override - public S compute( - SlotMapOwner owner, Object key, int index, SlotComputer c) { - final long stamp = lock.writeLock(); - try { - return getMap().compute(this, key, index, c); - } finally { - lock.unlockWrite(stamp); - } - } - - @Override - public Slot query(Object key, int index) { - long stamp = lock.tryOptimisticRead(); - Slot s = getMap().query(key, index); - if (lock.validate(stamp)) { - return s; - } - - stamp = lock.readLock(); - try { - return getMap().query(key, index); - } finally { - lock.unlockRead(stamp); - } - } - - @Override - public void add(SlotMapOwner owner, Slot newSlot) { - final long stamp = lock.writeLock(); - try { - getMap().add(this, newSlot); - } finally { - lock.unlockWrite(stamp); - } - } - - /** - * Take out a read lock on the slot map, if locking is implemented. The caller MUST call this - * method before using the iterator, and MUST NOT call this method otherwise. - */ - @Override - public long readLock() { - return lock.readLock(); - } - - /** - * Unlock the lock taken out by readLock. - * - * @param stamp the value returned by readLock. - */ - @Override - public void unlockRead(long stamp) { - lock.unlockRead(stamp); - } - - @Override - public Iterator iterator() { - assert lock.isReadLocked(); - return getMap().iterator(); - } - - /** - * Before inserting a new item in the map, check and see if we need to expand from the embedded - * map to a HashMap that is more robust against large numbers of hash collisions. - */ - @Override - protected void checkMapSize() { - assert lock.isWriteLocked(); - super.checkMapSize(); - } -} diff --git a/rhino/src/test/java/org/mozilla/javascript/SlotMapTest.java b/rhino/src/test/java/org/mozilla/javascript/SlotMapTest.java index 370ecef1a9..0df41ae1e3 100644 --- a/rhino/src/test/java/org/mozilla/javascript/SlotMapTest.java +++ b/rhino/src/test/java/org/mozilla/javascript/SlotMapTest.java @@ -46,18 +46,16 @@ public SlotMapTest(Supplier mapSupplier) { public static Collection mapTypes() { List> suppliers = List.of( - () -> SlotMapContainer.EMPTY_SLOT_MAP, - () -> new SlotMapContainer.SingleEntrySlotMap(new Slot(new Object(), 0, 0)), + () -> SlotMapOwner.EMPTY_SLOT_MAP, + () -> new SlotMapOwner.SingleEntrySlotMap(new Slot(new Object(), 0, 0)), () -> new EmbeddedSlotMap(), () -> new HashSlotMap(), - () -> new SlotMapContainer(), - () -> ThreadSafeSlotMapContainer.EMPTY_SLOT_MAP, + () -> SlotMapOwner.THREAD_SAFE_EMPTY_SLOT_MAP, () -> - new ThreadSafeSlotMapContainer.SingleEntrySlotMap( + new SlotMapOwner.ThreadSafeSingleEntrySlotMap( new Slot(new Object(), 0, 0)), () -> new ThreadSafeEmbeddedSlotMap(), - () -> new ThreadSafeHashSlotMap(), - () -> new ThreadSafeSlotMapContainer()); + () -> new ThreadSafeHashSlotMap()); return suppliers.stream().map(i -> new Object[] {i}).collect(Collectors.toList()); } @@ -260,9 +258,7 @@ public void manyKeysAndIndices() { private void verifyIndicesAndKeys() { long lockStamp = 0; - if (obj.getMap() instanceof SlotMapContainer) { - lockStamp = ((SlotMapContainer) obj.getMap()).readLock(); - } + lockStamp = obj.getMap().readLock(); try { Iterator it = obj.getMap().iterator(); for (int i = 0; i < startingSize; i++) { @@ -285,9 +281,7 @@ private void verifyIndicesAndKeys() { } assertFalse(it.hasNext()); } finally { - if (obj.getMap() instanceof SlotMapContainer) { - ((SlotMapContainer) obj.getMap()).unlockRead(lockStamp); - } + obj.getMap().unlockRead(lockStamp); } }