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

ConcurrentValidator.validateBeanNames and thread-safety

    Details

    • Steps to Reproduce:
      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

      Description

      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

        Gliffy Diagrams

          Attachments

            Activity

              People

              • Assignee:
                manovotn Matej Novotny
                Reporter:
                maldivia Michael Rasmussen
              • Votes:
                0 Vote for this issue
                Watchers:
                3 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: