Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8145164

Default implementation of ConcurrentMap::compute can throw NPE

    Details

    • Type: Bug
    • Status: Closed
    • Priority: P2
    • Resolution: Fixed
    • Affects Version/s: 8u66, 9
    • Fix Version/s: 9
    • Component/s: core-libs
    • Labels:
    • Subcomponent:
    • Resolved In Build:
      b104
    • CPU:
      generic
    • OS:
      generic
    • Verification:
      Verified

      Description

      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.

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                martin Martin Buchholz
                Reporter:
                webbuggrp Webbug Group
              • Votes:
                0 Vote for this issue
                Watchers:
                8 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: