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 }