ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/jtreg/util/Map/Defaults.java
(Generate patch)

Comparing jsr166/src/test/jtreg/util/Map/Defaults.java (file contents):
Revision 1.2 by jsr166, Mon Sep 14 16:47:42 2015 UTC vs.
Revision 1.3 by jsr166, Mon Dec 14 21:15:46 2015 UTC

# Line 48 | Line 48 | import java.util.WeakHashMap;
48   import java.util.concurrent.ConcurrentMap;
49   import java.util.concurrent.ConcurrentHashMap;
50   import java.util.concurrent.ConcurrentSkipListMap;
51 + import java.util.concurrent.atomic.AtomicBoolean;
52   import java.util.function.BiFunction;
53 + import java.util.function.Function;
54   import java.util.function.Supplier;
55  
56   import org.testng.annotations.Test;
57   import org.testng.annotations.DataProvider;
58 + import static java.util.Objects.requireNonNull;
59   import static org.testng.Assert.fail;
60   import static org.testng.Assert.assertEquals;
61   import static org.testng.Assert.assertTrue;
# Line 533 | Line 536 | public class Defaults {
536                  "Should throw NPE");
537      }
538  
539 +    /** A function that flipflops between running two other functions. */
540 +    static <T,U,V> BiFunction<T,U,V> twoStep(AtomicBoolean b,
541 +                                             BiFunction<T,U,V> first,
542 +                                             BiFunction<T,U,V> second) {
543 +        return (t, u) -> {
544 +            boolean bb = b.get();
545 +            try {
546 +                return (b.get() ? first : second).apply(t, u);
547 +            } finally {
548 +                b.set(!bb);
549 +            }};
550 +    }
551 +
552 +    /**
553 +     * Simulates races by modifying the map within the mapping function.
554 +     */
555 +    @Test
556 +    public void testConcurrentMap_computeIfAbsent_racy() {
557 +        final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
558 +        final Long two = 2L;
559 +        Function<Long,Long> f, g;
560 +
561 +        // race not detected if function returns null
562 +        f = (k) -> { map.put(two, 42L); return null; };
563 +        assertNull(map.computeIfAbsent(two, f));
564 +        assertEquals(42L, (long)map.get(two));
565 +
566 +        map.clear();
567 +        f = (k) -> { map.put(two, 42L); return 86L; };
568 +        assertEquals(42L, (long)map.computeIfAbsent(two, f));
569 +        assertEquals(42L, (long)map.get(two));
570 +
571 +        // mapping function ignored if value already exists
572 +        map.put(two, 99L);
573 +        assertEquals(99L, (long)map.computeIfAbsent(two, f));
574 +        assertEquals(99L, (long)map.get(two));
575 +    }
576 +
577 +    /**
578 +     * Simulates races by modifying the map within the remapping function.
579 +     */
580 +    @Test
581 +    public void testConcurrentMap_computeIfPresent_racy() {
582 +        final AtomicBoolean b = new AtomicBoolean(true);
583 +        final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
584 +        final Long two = 2L;
585 +        BiFunction<Long,Long,Long> f, g;
586 +
587 +        for (Long val : new Long[] { null, 86L }) {
588 +            map.clear();
589 +
590 +            // Function not invoked if no mapping exists
591 +            f = (k, v) -> { map.put(two, 42L); return val; };
592 +            assertNull(map.computeIfPresent(two, f));
593 +            assertNull(map.get(two));
594 +
595 +            map.put(two, 42L);
596 +            f = (k, v) -> { map.put(two, 86L); return val; };
597 +            g = (k, v) -> {
598 +                assertSame(two, k);
599 +                assertEquals(86L, (long)v);
600 +                return null;
601 +            };
602 +            assertNull(map.computeIfPresent(two, twoStep(b, f, g)));
603 +            assertFalse(map.containsKey(two));
604 +            assertTrue(b.get());
605 +
606 +            map.put(two, 42L);
607 +            f = (k, v) -> { map.put(two, 86L); return val; };
608 +            g = (k, v) -> {
609 +                assertSame(two, k);
610 +                assertEquals(86L, (long)v);
611 +                return 99L;
612 +            };
613 +            assertEquals(99L, (long)map.computeIfPresent(two, twoStep(b, f, g)));
614 +            assertTrue(map.containsKey(two));
615 +            assertTrue(b.get());
616 +        }
617 +    }
618 +
619 +    @Test
620 +    public void testConcurrentMap_compute_simple() {
621 +        final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
622 +        BiFunction<Long,Long,Long> fun = (k, v) -> ((v == null) ? 0L : k + v);
623 +        assertEquals(Long.valueOf(0L), map.compute(3L, fun));
624 +        assertEquals(Long.valueOf(3L), map.compute(3L, fun));
625 +        assertEquals(Long.valueOf(6L), map.compute(3L, fun));
626 +        assertNull(map.compute(3L, (k, v) -> null));
627 +        assertTrue(map.isEmpty());
628 +
629 +        assertEquals(Long.valueOf(0L), map.compute(new Long(3L), fun));
630 +        assertEquals(Long.valueOf(3L), map.compute(new Long(3L), fun));
631 +        assertEquals(Long.valueOf(6L), map.compute(new Long(3L), fun));
632 +        assertNull(map.compute(3L, (k, v) -> null));
633 +        assertTrue(map.isEmpty());
634 +    }
635 +
636 +    /**
637 +     * Simulates races by modifying the map within the remapping function.
638 +     */
639 +    @Test
640 +    public void testConcurrentMap_compute_racy() {
641 +        final AtomicBoolean b = new AtomicBoolean(true);
642 +        final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
643 +        final Long two = 2L;
644 +        BiFunction<Long,Long,Long> f, g;
645 +
646 +        // null -> null is a no-op; race not detected
647 +        f = (k, v) -> { map.put(two, 42L); return null; };
648 +        assertNull(map.compute(two, f));
649 +        assertEquals(42L, (long)map.get(two));
650 +
651 +        for (Long val : new Long[] { null, 86L }) {
652 +            map.clear();
653 +
654 +            f = (k, v) -> { map.put(two, 42L); return 86L; };
655 +            g = (k, v) -> {
656 +                assertSame(two, k);
657 +                assertEquals(42L, (long)v);
658 +                return k + v;
659 +            };
660 +            assertEquals(44L, (long)map.compute(two, twoStep(b, f, g)));
661 +            assertEquals(44L, (long)map.get(two));
662 +            assertTrue(b.get());
663 +
664 +            f = (k, v) -> { map.remove(two); return val; };
665 +            g = (k, v) -> {
666 +                assertSame(two, k);
667 +                assertNull(v);
668 +                return 44L;
669 +            };
670 +            assertEquals(44L, (long)map.compute(two, twoStep(b, f, g)));
671 +            assertEquals(44L, (long)map.get(two));
672 +            assertTrue(map.containsKey(two));
673 +            assertTrue(b.get());
674 +
675 +            f = (k, v) -> { map.remove(two); return val; };
676 +            g = (k, v) -> {
677 +                assertSame(two, k);
678 +                assertNull(v);
679 +                return null;
680 +            };
681 +            assertNull(map.compute(two, twoStep(b, f, g)));
682 +            assertNull(map.get(two));
683 +            assertFalse(map.containsKey(two));
684 +            assertTrue(b.get());
685 +        }
686 +    }
687 +
688 +    /**
689 +     * Simulates races by modifying the map within the remapping function.
690 +     */
691 +    @Test
692 +    public void testConcurrentMap_merge_racy() {
693 +        final AtomicBoolean b = new AtomicBoolean(true);
694 +        final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
695 +        final Long two = 2L;
696 +        BiFunction<Long,Long,Long> f, g;
697 +
698 +        for (Long val : new Long[] { null, 86L }) {
699 +            map.clear();
700 +
701 +            f = (v, w) -> { throw new AssertionError(); };
702 +            assertEquals(99L, (long)map.merge(two, 99L, f));
703 +            assertEquals(99L, (long)map.get(two));
704 +
705 +            f = (v, w) -> { map.put(two, 42L); return val; };
706 +            g = (v, w) -> {
707 +                assertEquals(42L, (long)v);
708 +                assertEquals(3L, (long)w);
709 +                return v + w;
710 +            };
711 +            assertEquals(45L, (long)map.merge(two, 3L, twoStep(b, f, g)));
712 +            assertEquals(45L, (long)map.get(two));
713 +            assertTrue(b.get());
714 +
715 +            f = (v, w) -> { map.remove(two); return val; };
716 +            g = (k, v) -> { throw new AssertionError(); };
717 +            assertEquals(55L, (long)map.merge(two, 55L, twoStep(b, f, g)));
718 +            assertEquals(55L, (long)map.get(two));
719 +            assertTrue(map.containsKey(two));
720 +            assertFalse(b.get()); b.set(true);
721 +        }
722 +    }
723 +
724      public enum IntegerEnum {
725  
726          e0, e1, e2, e3, e4, e5, e6, e7, e8, e9,
# Line 838 | Line 1026 | public class Defaults {
1026  
1027          protected ExtendsAbstractMap(M map) { this.map = map; }
1028  
1029 <        public Set<Map.Entry<K,V>> entrySet() {
1029 >        @Override public Set<Map.Entry<K,V>> entrySet() {
1030              return new AbstractSet<Map.Entry<K,V>>() {
1031 <                public int size() {
1031 >                @Override public int size() {
1032                      return map.size();
1033                  }
1034  
1035 <                public Iterator<Map.Entry<K,V>> iterator() {
1035 >                @Override public Iterator<Map.Entry<K,V>> iterator() {
1036                      final Iterator<Map.Entry<K,V>> source = map.entrySet().iterator();
1037                      return new Iterator<Map.Entry<K,V>>() {
1038                         public boolean hasNext() { return source.hasNext(); }
# Line 853 | Line 1041 | public class Defaults {
1041                      };
1042                  }
1043  
1044 <                public boolean add(Map.Entry<K,V> e) {
1044 >                @Override public boolean add(Map.Entry<K,V> e) {
1045                      return map.entrySet().add(e);
1046                  }
1047              };
1048          }
1049  
1050 <        public V put(K key, V value) {
1050 >        @Override public V put(K key, V value) {
1051              return map.put(key, value);
1052          }
1053      }
1054  
1055      /**
1056       * A simple mutable concurrent map implementation that provides only default
1057 <     * implementations of all methods. ie. none of the ConcurrentMap interface
1057 >     * implementations of all methods, i.e. none of the ConcurrentMap interface
1058       * default methods have overridden implementations.
1059       *
1060       * @param <K> Type of keys
# Line 875 | Line 1063 | public class Defaults {
1063      public static class ImplementsConcurrentMap<K,V> extends ExtendsAbstractMap<ConcurrentMap<K,V>, K, V> implements ConcurrentMap<K,V> {
1064          public ImplementsConcurrentMap() { super(new ConcurrentHashMap<K,V>()); }
1065  
1066 <        // ConcurrentMap reabstracts these methods
1066 >        // ConcurrentMap reabstracts these methods.
1067 >        //
1068 >        // Unlike ConcurrentHashMap, we have zero tolerance for null values.
1069  
1070 <        public V replace(K k, V v) { return map.replace(k, v); };
1070 >        @Override public V replace(K k, V v) {
1071 >            return map.replace(requireNonNull(k), requireNonNull(v));
1072 >        }
1073  
1074 <        public boolean replace(K k, V v, V vv) { return map.replace(k, v, vv); };
1074 >        @Override public boolean replace(K k, V v, V vv) {
1075 >            return map.replace(requireNonNull(k),
1076 >                               requireNonNull(v),
1077 >                               requireNonNull(vv));
1078 >        }
1079  
1080 <        public boolean remove(Object k, Object v) { return map.remove(k, v); }
1080 >        @Override public boolean remove(Object k, Object v) {
1081 >            return map.remove(requireNonNull(k), requireNonNull(v));
1082 >        }
1083  
1084 <        public V putIfAbsent(K k, V v) { return map.putIfAbsent(k, v); }
1084 >        @Override public V putIfAbsent(K k, V v) {
1085 >            return map.putIfAbsent(requireNonNull(k), requireNonNull(v));
1086 >        }
1087      }
1088   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines