Uploaded image for project: 'Weld'
  1. Weld
  2. WELD-2545

ConcurrentValidator.validateBeanNames and thread-safety

XMLWordPrintable

    • Hide
      package com.test;
      
      import org.jboss.weld.util.collections.SetMultimap;
      
      import java.lang.reflect.Field;
      import java.util.HashMap;
      
      public class Test2 {
        public static void main(String[] args) throws Exception {
          SetMultimap<Integer, Integer> smm = SetMultimap.newSetMultimap();
          for (int i = 0; i < 13; i++) {
            smm.get(i).add(i);
          }
      
          Class<?> klass = Class.forName("org.jboss.weld.util.collections.AbstractMultimap");
          Field field = klass.getDeclaredField("map");
          field.setAccessible(true);
          Object map = field.get(smm);
      
          Field table = HashMap.class.getDeclaredField("table");
          table.setAccessible(true);
          System.out.println(smm.containsKey(1));
          System.out.println(table.get(map));
          smm.get(1);
          System.out.println(table.get(map));
        }
      }
      

      Running the above snippet with 3.0.5.Final shows how calling SetMultimap.get with a known key causes a resize of the underlying map (the table array changes), and seen in the output below:

      true
      [Ljava.util.HashMap$Node;@2e817b38
      [Ljava.util.HashMap$Node;@c4437c4
      
      Show
      package com.test; import org.jboss.weld.util.collections.SetMultimap; import java.lang.reflect.Field; import java.util.HashMap; public class Test2 { public static void main( String [] args) throws Exception { SetMultimap< Integer , Integer > smm = SetMultimap.newSetMultimap(); for ( int i = 0; i < 13; i++) { smm.get(i).add(i); } Class <?> klass = Class .forName( "org.jboss.weld.util.collections.AbstractMultimap" ); Field field = klass.getDeclaredField( "map" ); field.setAccessible( true ); Object map = field.get(smm); Field table = HashMap. class. getDeclaredField( "table" ); table.setAccessible( true ); System .out.println(smm.containsKey(1)); System .out.println(table.get(map)); smm.get(1); System .out.println(table.get(map)); } } Running the above snippet with 3.0.5.Final shows how calling SetMultimap.get with a known key causes a resize of the underlying map (the table array changes), and seen in the output below: true [Ljava.util.HashMap$Node;@2e817b38 [Ljava.util.HashMap$Node;@c4437c4

      In the method validateBeanNames in org.jboss.weld.bootstrap.ConcurrentValidator, the variable namedAccessibleBeans is a SetMultimap, but not a concurrent one, thus basically a wrapper for HashMap<K, HashSet<V>>.

      If the number of keys in the map is higher than the threshold for when the underlying HashMap resizes itself (for instance size=13 for current OpenJDK defaults), calling SetMultimap.get() will cause a resize, as the underlying implementation uses HashMap.computeIfAbsent, which resizes the map if size is above threshold regardless of if the key is absent or not.

      This is an issue since in the method this map is used concurrently, iterating over existing keys, thus under the assumption that such a resize will not happen, as it can have weird consequences.

      Also see http://mail.openjdk.java.net/pipermail/core-libs-dev/2018-November/056736.html

              manovotn Matěj Novotný
              jmichaelras Michael Rasmussen (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Created:
                Updated:
                Resolved: