1 /*
   2  * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /* @test
  25  * @run main/othervm -Xmx16m EphemeronTests
  26  * @summary Basic functional tests for Ephemerons
  27  * @author Peter Levart
  28  */
  29 
  30 import java.lang.ref.Ephemeron;
  31 import java.lang.ref.Reference;
  32 import java.lang.ref.SoftReference;
  33 import java.lang.ref.WeakReference;
  34 import java.util.ArrayList;
  35 import java.util.List;
  36 import java.util.function.Function;
  37 
  38 public class EphemeronTests {
  39 
  40     public static void main(String[] args) throws Exception {
  41 
  42         testValueToKeyChain(100, true);
  43         testValueToKeyChain(100, false);
  44 
  45         testReachability(SoftReference::new);
  46         testReachability(WeakReference::new);
  47     }
  48 
  49     static class Key {
  50         final int i;
  51 
  52         Key(int i) {
  53             this.i = i;
  54         }
  55 
  56         @Override
  57         public String toString() {
  58             return "k" + i;
  59         }
  60     }
  61 
  62     static class Value {
  63         final Key key;
  64 
  65         Value(Key key) {
  66             this.key = key;
  67         }
  68 
  69         @Override
  70         public String toString() {
  71             return "v(" + key + ")";
  72         }
  73     }
  74 
  75     static class Eph extends Ephemeron<Key, Value> {
  76         public Eph(Key key, Value value) {
  77             super(key, value);
  78         }
  79 
  80         @Override
  81         public String toString() {
  82             return getKey() + "=" + getValue();
  83         }
  84     }
  85 
  86     static void testValueToKeyChain(int size, boolean forwardChaining) throws Exception {
  87         System.out.println();
  88         System.out.println((forwardChaining ? "Forward" : "Backward") +
  89                            " chaining of value->key ...");
  90         System.out.println();
  91         List<Eph> ephs = new ArrayList<>(size);
  92 
  93         Key k1 = new Key(1);
  94         {
  95             Key kp = k1;
  96             for (int i = 2; i <= size; i++) {
  97                 Key ki = new Key(i);
  98                 ephs.add(
  99                     forwardChaining
 100                     ? new Eph(kp, new Value(ki))
 101                     : new Eph(ki, new Value(kp))
 102                 );
 103                 kp = ki;
 104             }
 105             ephs.add(
 106                 forwardChaining
 107                 ? new Eph(kp, new Value(k1))
 108                 : new Eph(k1, new Value(kp))
 109             );
 110             kp = null;
 111         }
 112 
 113         fillHeap();
 114         System.out.println("1: " + toString(ephs));
 115         for (Eph eph : ephs) assertAlive(eph);
 116         k1.getClass(); // reachabilityFence
 117 
 118         k1 = null;
 119         fillHeap();
 120         System.out.println("2: " + toString(ephs));
 121         for (Eph eph : ephs) assertCleared(eph);
 122     }
 123 
 124     static void testReachability(Function<Object, Reference<Object>> referenceConstructor) {
 125         Reference<Object> kRef, vRef;
 126         Ephemeron<Object, Object> eph;
 127         Object k;
 128         {
 129             k = new Object();
 130             Object v = new Object();
 131             kRef = referenceConstructor.apply(k);
 132             vRef = referenceConstructor.apply(v);
 133             eph = new Ephemeron<>(k, v);
 134             v = null;
 135         }
 136 
 137         System.out.println();
 138         System.out.println(kRef.getClass().getSimpleName() + " reachability test");
 139         System.out.println();
 140 
 141         while (true) {
 142             fillHeap();
 143             if ((k = kRef.get()) == null) {
 144                 System.out.println("key cleared");
 145                 assertCleared(kRef);
 146                 assertCleared(eph);
 147                 assertCleared(vRef);
 148                 break;
 149             } else {
 150                 System.out.println("key alive");
 151                 assertAlive(kRef);
 152                 assertAlive(eph);
 153                 assertAlive(vRef);
 154                 k.getClass(); // reachabilityFence
 155             }
 156             k = null;
 157         }
 158     }
 159 
 160     // utils
 161 
 162     static void fillHeap() {
 163         long t0 = System.nanoTime();
 164         Object junk = null;
 165         try {
 166             while (true) {
 167                 Object[] nj = new Object[16384];
 168                 nj[0] = junk;
 169                 junk = nj;
 170                 nj = null;
 171             }
 172         } catch (OutOfMemoryError e) {
 173             junk = null;
 174             System.gc();
 175         }
 176         long t = System.nanoTime() - t0;
 177         System.out.println("Fill Heap (" + (t / 1000000L) + " ms)");
 178         try {
 179             Thread.sleep(100L);
 180         } catch (InterruptedException e) {}
 181     }
 182 
 183     static void assertAlive(Reference<?> reference) {
 184         if (reference.get() == null) {
 185             throw new AssertionError(reference + " is cleared");
 186         }
 187     }
 188 
 189     static void assertAlive(Ephemeron<?, ?> ephemeron) {
 190         if (ephemeron.getKey() == null || ephemeron.getValue() == null) {
 191             throw new AssertionError(ephemeron + " is cleared");
 192         }
 193     }
 194 
 195     static void assertCleared(Reference<?> reference) {
 196         if (reference.get() != null) {
 197             throw new AssertionError(reference + " is not cleared");
 198         }
 199     }
 200 
 201     static void assertCleared(Ephemeron<?, ?> ephemeron) {
 202         if (ephemeron.getKey() != null || ephemeron.getValue() != null) {
 203             throw new AssertionError(ephemeron + " is not cleared");
 204         }
 205     }
 206 
 207     static String toString(List<?> list) {
 208         if (list == null) return "null";
 209         if (list.size() <= 10) return list.toString();
 210         String prefix = list.subList(0, 5).toString();
 211         String suffix = list.subList(list.size() - 5, list.size()).toString();
 212 
 213         return prefix.substring(0, prefix.length() - 1) + ", ..., " + suffix.substring(1);
 214     }
 215 }