1 /*
   2  * Copyright (c) 2018, 2019, 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 /*
  25  * @test
  26  * @bug 4087516
  27  * @summary Incorrect locking leads to deadlock in monitorCacheMaybeExpand.
  28  * @author Anand Palaniswamy
  29  * @build MonitorCacheMaybeExpand_DeadLock
  30  * @run main/othervm MonitorCacheMaybeExpand_DeadLock
  31  */
  32 
  33 /**
  34  * Background on the bug:
  35  *
  36  *     The thread local monitor cache had a locking bug (till
  37  *     1.2beta1) where two threads trying to expand the monitor cache
  38  *     at the same time would cause deadlock. The code paths that the
  39  *     two threads must be executing for this to happen is described
  40  *     in the bug report.
  41  *
  42  * Caveat and red-flag:
  43  *
  44  *     Since deadlocks are very timing dependent, there is a good
  45  *     chance this test case will not catch the bug most of the time
  46  *     -- on your machine and setting, it is _possible_ that the two
  47  *     threads might not try a monitorCacheExpand at the same
  48  *     time. But in practice, on Solaris native threads, this program
  49  *     deadlocks the VM in about 2 seconds pretty consistently,
  50  *     whether MP or not.
  51  *
  52  *     The rationale for running this test despite this rather large
  53  *     caveat is that at worst, it can do no harm.
  54  *
  55  * The idea:
  56  *
  57  *     Is to create two monitor hungry threads.
  58  *
  59  *     Originally Tom Rodriguez and I suspected that this weird state
  60  *     of two threads trying to expand monitor cache can happen only
  61  *     if:
  62  *
  63  *         Thread 1: Is in the middle of a monitorCacheMaybeExpand.
  64  *         Thread 2: Runs GC and tries to freeClasses(). This causes
  65  *                   sysFree() to be invoked, which in turn needs a
  66  *                   mutex_lock -- and oops, we end up deadlocking
  67  *                   with 1 on green_threads.
  68  *
  69  *     Which is why this test tries to cause class GC at regular
  70  *     intervals.
  71  *
  72  *     Turns out that the GC is not required. Two instances of the
  73  *     monitor hungry threads deadlock the VM pretty quick. :-) Infact
  74  *     the static initializer in the forName'd classes running
  75  *     alongside one of the hungry threads is sufficient to
  76  *     deadlock. Still keep the GC stuff just-in-case (and also
  77  *     because I wrote it :-).
  78  *
  79  */
  80 public class MonitorCacheMaybeExpand_DeadLock {
  81 
  82     /**
  83      * A monitor-hungry thread.
  84      */
  85     static class LotsaMonitors extends Thread {
  86 
  87         /** How many recursions? Could cause Java stack overflow. */
  88         static final int MAX_DEPTH = 800;
  89 
  90         /** What is our depth? */
  91         int depth = 0;
  92 
  93         /** Thread ID */
  94         int tid;
  95 
  96         /** So output will have thread number. */
  97         public LotsaMonitors(int tid, int depth) {
  98             super("LotsaMonitors #" + new Integer(tid).toString());
  99             this.tid = tid;
 100             this.depth = depth;
 101         }
 102 
 103         /** Start a recursion that grabs monitors. */
 104         public void run() {
 105             System.out.println(">>>Starting " + this.toString() + " ...");
 106             Thread.currentThread().yield();
 107             this.recurse();
 108             System.out.println("<<<Finished " + this.toString());
 109         }
 110 
 111         /** Every call to this method grabs an extra monitor. */
 112         synchronized void recurse() {
 113             if (this.depth > 0) {
 114                 new LotsaMonitors(tid, depth-1).recurse();
 115             }
 116         }
 117     }
 118 
 119     /**
 120      * The test.
 121      */
 122     public static void main(String[] args) {
 123         /* Start the two of these crazy threads. */
 124         new LotsaMonitors(1, LotsaMonitors.MAX_DEPTH).start();
 125         new LotsaMonitors(2, LotsaMonitors.MAX_DEPTH).start();
 126 
 127         /* And sit there and GC for good measure. */
 128         for (int i = 0; i < MAX_GC_ITERATIONS; i++) {
 129             new LotsaMonitors(i+3, LotsaMonitors.MAX_DEPTH).start();
 130             System.out.println(">>>Loading 10 classes and gc'ing ...");
 131             Class[] classes = new Class[10];
 132             fillClasses(classes);
 133             classes = null;
 134             System.gc();
 135             Thread.currentThread().yield();
 136             System.out.println("<<<Finished loading 10 classes and gc'ing");
 137         }
 138     }
 139 
 140     /** How many times to GC? */
 141     static final int MAX_GC_ITERATIONS = 10;
 142 
 143     /** Load some classes into the array. */
 144     static void fillClasses(Class[] classes) {
 145         for (int i = 0; i < classes.length; i++) {
 146             try {
 147                 classes[i] = Class.forName(classnames[i]);
 148             } catch (ClassNotFoundException cnfe) {
 149                 cnfe.printStackTrace();
 150             }
 151         }
 152     }
 153 
 154     /** Some random classes to load. */
 155     private static String[] classnames = {
 156         "java.text.DecimalFormat",
 157         "java.text.MessageFormat",
 158         "java.util.GregorianCalendar",
 159         "java.util.ResourceBundle",
 160         "java.text.Collator",
 161         "java.util.Date",
 162         "java.io.Reader",
 163         "java.io.Writer",
 164         "java.lang.IllegalAccessException",
 165         "java.lang.InstantiationException",
 166         "java.lang.ClassNotFoundException",
 167         "java.lang.CloneNotSupportedException",
 168         "java.lang.InterruptedException",
 169         "java.lang.NoSuchFieldException",
 170         "java.lang.NoSuchMethodException",
 171         "java.lang.RuntimeException",
 172         "java.lang.ArithmeticException",
 173         "java.lang.ArrayStoreException",
 174         "java.lang.ClassCastException",
 175         "java.lang.StringIndexOutOfBoundsException",
 176         "java.lang.NegativeArraySizeException",
 177         "java.lang.IllegalStateException",
 178         "java.lang.IllegalArgumentException",
 179         "java.lang.NumberFormatException",
 180         "java.lang.IllegalThreadStateException",
 181         "java.lang.IllegalMonitorStateException",
 182         "java.lang.SecurityException",
 183         "java.lang.ExceptionInInitializerError"
 184     };
 185 
 186 }