summaryrefslogtreecommitdiffstats
path: root/vespajlib
diff options
context:
space:
mode:
authorJon Marius Venstad <venstad@gmail.com>2022-03-18 13:08:28 +0100
committerJon Marius Venstad <venstad@gmail.com>2022-03-18 13:08:28 +0100
commit85bd1f84ad53d96823a24cd95873c768b8877178 (patch)
treebd2d11c228cee0059664d29923b2170064f00d57 /vespajlib
parent5f94e88d349ee34f858e1674889eb1ba76c3543d (diff)
Avoid unnecessary copy when wrapped map has a single owner
Diffstat (limited to 'vespajlib')
-rw-r--r--vespajlib/src/main/java/com/yahoo/collections/CopyOnWriteHashMap.java25
-rw-r--r--vespajlib/src/test/java/com/yahoo/collections/CopyOnWriteHashMapTestCase.java3
2 files changed, 17 insertions, 11 deletions
diff --git a/vespajlib/src/main/java/com/yahoo/collections/CopyOnWriteHashMap.java b/vespajlib/src/main/java/com/yahoo/collections/CopyOnWriteHashMap.java
index 424e850426c..218d0c407ec 100644
--- a/vespajlib/src/main/java/com/yahoo/collections/CopyOnWriteHashMap.java
+++ b/vespajlib/src/main/java/com/yahoo/collections/CopyOnWriteHashMap.java
@@ -7,6 +7,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* A hashmap wrapper which defers cloning of the enclosed map until it is written to.
@@ -21,8 +22,8 @@ public class CopyOnWriteHashMap<K,V> extends AbstractMap<K,V> implements Cloneab
private Map<K,V> map;
- /** True when this class is allowed to write to the map */
- private boolean writable = true;
+ /** This class may write to the map if it is the sole owner */
+ private AtomicInteger owners = new AtomicInteger(1);
/** Lazily initialized view */
private transient Set<Map.Entry<K,V>> entrySet = null;
@@ -40,13 +41,18 @@ public class CopyOnWriteHashMap<K,V> extends AbstractMap<K,V> implements Cloneab
}
private void makeReadOnly() {
- writable = false;
+ owners.incrementAndGet();
+ }
+
+ private boolean isWritable() {
+ return owners.get() == 1;
}
private void makeWritable() {
- if (writable) return;
+ if (isWritable()) return;
map = copyMap(map);
- writable = true;
+ owners.decrementAndGet();
+ owners = new AtomicInteger(1);
entrySet = null;
}
@@ -62,8 +68,7 @@ public class CopyOnWriteHashMap<K,V> extends AbstractMap<K,V> implements Cloneab
public CopyOnWriteHashMap<K,V> clone() {
try {
CopyOnWriteHashMap<K,V> clone = (CopyOnWriteHashMap<K,V>)super.clone();
- this.makeReadOnly();
- clone.makeReadOnly();
+ makeReadOnly(); // owners shared with clone
return clone;
}
catch (CloneNotSupportedException e) {
@@ -94,7 +99,7 @@ public class CopyOnWriteHashMap<K,V> extends AbstractMap<K,V> implements Cloneab
@Override
public boolean equals(Object other) {
if ( ! (other instanceof CopyOnWriteHashMap)) return false;
- return this.map.equals(((CopyOnWriteHashMap)other).map);
+ return this.map.equals(((CopyOnWriteHashMap<?, ?>)other).map);
}
@Override
@@ -137,7 +142,7 @@ public class CopyOnWriteHashMap<K,V> extends AbstractMap<K,V> implements Cloneab
private class EntryIterator implements Iterator<Map.Entry<K,V>> {
/** Wrapped iterator */
- private Iterator<Map.Entry<K,V>> mapIterator;
+ private final Iterator<Map.Entry<K,V>> mapIterator;
public EntryIterator() {
mapIterator = map.entrySet().iterator();
@@ -152,7 +157,7 @@ public class CopyOnWriteHashMap<K,V> extends AbstractMap<K,V> implements Cloneab
}
public void remove() {
- if ( ! writable)
+ if ( ! isWritable())
throw new UnsupportedOperationException("Cannot perform the copy-on-write operation during iteration");
mapIterator.remove();
}
diff --git a/vespajlib/src/test/java/com/yahoo/collections/CopyOnWriteHashMapTestCase.java b/vespajlib/src/test/java/com/yahoo/collections/CopyOnWriteHashMapTestCase.java
index 2072247bf96..35401a0cb19 100644
--- a/vespajlib/src/test/java/com/yahoo/collections/CopyOnWriteHashMapTestCase.java
+++ b/vespajlib/src/test/java/com/yahoo/collections/CopyOnWriteHashMapTestCase.java
@@ -2,7 +2,8 @@
package com.yahoo.collections;
import org.junit.Test;
-import static org.junit.Assert.*;
+
+import static org.junit.Assert.assertEquals;
/**
* @author bratseth