FULL PRODUCT VERSION :
jara@hazelpad:~/devel/oss/maven-plugins$ java -version
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
irrelevant
EXTRA RELEVANT SYSTEM CONFIGURATION :
Paul Sandoz has confirmed it's a bug. See http://mail.openjdk.java.net/pipermail/core-libs-dev/2015-December/037432.html
A DESCRIPTION OF THE PROBLEM :
Default implementation of `compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)` in JDK8 `ConcurrentMap` assumes map implementations do not support null values.
This is the begin of the default implementation:
default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue = get(key);
for(;;) {
V newValue = remappingFunction.apply(key, oldValue);
if (newValue == null) {
// delete mapping
if (oldValue != null || containsKey(key)) {
// something to remove
if (remove(key, oldValue)) {
[...]
Let's say we have an empty map and 2 threads:
T1 is calling the `compute('foo', someFunction)`
T2 is concurrently calling calling `put('foo', 'bar');`
so the T1 will get `oldValue = null`, but `containsKey()` will return `true` - because T2 already created the mapping `foo -> bar`. Hence T1 will call `remove('foo', null)` !
Contract of `remove()` says: `throws NullPointerException if the specified key or value is null, and this map does not permit null keys or values optional.` -> the T1 will throw NPE.
REPRODUCIBILITY :
This bug can be reproduced always.
jara@hazelpad:~/devel/oss/maven-plugins$ java -version
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
irrelevant
EXTRA RELEVANT SYSTEM CONFIGURATION :
Paul Sandoz has confirmed it's a bug. See http://mail.openjdk.java.net/pipermail/core-libs-dev/2015-December/037432.html
A DESCRIPTION OF THE PROBLEM :
Default implementation of `compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)` in JDK8 `ConcurrentMap` assumes map implementations do not support null values.
This is the begin of the default implementation:
default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue = get(key);
for(;;) {
V newValue = remappingFunction.apply(key, oldValue);
if (newValue == null) {
// delete mapping
if (oldValue != null || containsKey(key)) {
// something to remove
if (remove(key, oldValue)) {
[...]
Let's say we have an empty map and 2 threads:
T1 is calling the `compute('foo', someFunction)`
T2 is concurrently calling calling `put('foo', 'bar');`
so the T1 will get `oldValue = null`, but `containsKey()` will return `true` - because T2 already created the mapping `foo -> bar`. Hence T1 will call `remove('foo', null)` !
Contract of `remove()` says: `throws NullPointerException if the specified key or value is null, and this map does not permit null keys or values optional.` -> the T1 will throw NPE.
REPRODUCIBILITY :
This bug can be reproduced always.
- duplicates
-
JDK-8174087 Implementation requirements code example for ConcurrentMap.putIfAbsent() returns incorrect value
-
- Closed
-