Skip to content

Commit

Permalink
#1412 fix concurrency issue in CopyOnWriteMap
Browse files Browse the repository at this point in the history
the default method `Map.getOrDefault` is not thread-safe: the condition `if (v == null && containsKey(key))` leaves `v = null` if the `key` was added by another thread exactly at the same moment. Then both conditions are true: `v==null` and `containsKey(key)`.

Now `CopyOnWriteMap.getOrDefault` does NOT check `containsKey(key)`. As a consequence, `CopyOnWriteMap` doesn't allow putting `null` values to the map.
  • Loading branch information
asolntsev committed Nov 1, 2024
1 parent 1dc01bb commit 583e81d
Showing 1 changed file with 15 additions and 0 deletions.
15 changes: 15 additions & 0 deletions src/main/java/net/datafaker/internal/helper/CopyOnWriteMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import java.util.Set;
import java.util.function.Supplier;

import static java.util.Objects.requireNonNull;
import static java.util.Objects.requireNonNullElse;

/**
* This is a Copy On Write map. The main idea behind this is that
* there is lots of static info per provider to make the providers operate.
Expand Down Expand Up @@ -59,6 +62,7 @@ public V putIfAbsent(K key, V value) {

@Override
public V put(K key, V value) {
requireNonNull(value, () -> "value is null for key " + key);
Map<K, V> newMap = mapSupplier.get();
newMap.putAll(map);
final V result = newMap.put(key, value);
Expand Down Expand Up @@ -102,4 +106,15 @@ public Collection<V> values() {
public Set<Entry<K, V>> entrySet() {
return map.entrySet();
}

@Override
public String toString() {
return map.toString();
}

@Override
public V getOrDefault(Object key, V defaultValue) {
V v = get(key);
return requireNonNullElse(v, defaultValue);
}
}

0 comments on commit 583e81d

Please sign in to comment.