1 /* 2 * Copyright (c) 2008, 2018, 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 26 /* 27 * @test 28 * @key stress gc randomness 29 * 30 * @summary converted from VM Testbase gc/hashcode/ExternalHashingTest. 31 * VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, jrockit] 32 * VM Testbase readme: 33 * DESCRIPTION 34 * Test the possible interaction of external hashing and locking on object 35 * headers. 36 * The approach is to nearly simultaneously lock/hash a relatively small group 37 * of objects. We do this repeatedly (munging), recording all hash values 38 * collected therein. 39 * After doing this for a large number of groups, we force a garbage collection, 40 * which would change the hashCode of an object if it hasn't previously been 41 * hashed. In our case, we _know_ what the previous hashcode was, so we can 42 * recalculate all of their hashes and compare with the original value. 43 * If any of the hashCodes hash changed, we know we have a problem. 44 * 45 * COMMENTS 46 * This test was ported from JRockit test suite. 47 * 48 * @library /vmTestbase 49 * /test/lib 50 * @run driver jdk.test.lib.FileInstaller . . 51 * @run main/othervm -XX:-UseGCOverheadLimit gc.hashcode.ExternalHashingTest.ExternalHashingTest 52 */ 53 54 package gc.hashcode.ExternalHashingTest; 55 56 import java.text.SimpleDateFormat; 57 import java.util.Date; 58 import java.util.Random; 59 import java.util.Vector; 60 61 import jdk.test.lib.Utils; 62 63 /** 64 * Test the possible interaction of external hashing and locking on object 65 * headers. 66 * 67 * The approach is to nearly simultaneously lock/hash a relatively small group 68 * of objects. We do this repeatedly (munging), recording all hash values 69 * collected therein. 70 * 71 * After doing this for a large number of groups, we force a garbage collection, 72 * which would change the hashCode of an object if it hasn't previously been 73 * hashed. In our case, we _know_ what the previous hashcode was, so we can 74 * recalculate all of their hashes and compare with the original value. 75 * 76 * If any of the hashCodes hash changed, we know we have a problem. 77 */ 78 79 public final class ExternalHashingTest { 80 81 /** Random number generator. */ 82 static Random rand = Utils.getRandomInstance(); 83 84 /** Goes to true when the threads should start working. */ 85 public static volatile boolean startingGun; 86 87 /** Goes to true when the hashing thread is done. */ 88 public static volatile boolean finishHashing; 89 90 /** Goes to true when the locking thread is done. */ 91 public static volatile boolean finishLocking; 92 93 /** The number of objects in each batch. */ 94 private static final int BATCH_SIZE = 20; 95 96 /** This is the global list of objects that have been hashed. */ 97 static Vector allObjects = new Vector(); 98 99 /** This is the corresponding list of hashCodes collected. */ 100 static Vector allHashes = new Vector(); 101 102 /** The default milliseconds to run the program. */ 103 private static final long DEFAULT_DURATION = 10000; 104 105 /** All static */ 106 private ExternalHashingTest() {} 107 108 /** 109 * This object holds garbage. It is a (probably unnecessary){ embellishment 110 * to increase the amount of garbage created by this benchmark. 111 * <p> 112 * It is global to discourage optimizer from removing it. 113 */ 114 public static Object[] garbageMonger; 115 116 /** 117 * We need a fairly short pause, since we're not using a semaphore to 118 * synchronize threads. 119 */ 120 public static void pause() { 121 try { 122 // Thread.sleep(100); 123 Thread.yield(); 124 } catch (Exception e) { 125 e.printStackTrace(); 126 System.exit(1); 127 } 128 } 129 130 /** 131 * Returns System.currentTimeMillis() in the Date format. 132 * @return String 133 */ 134 private static String getDateString() { 135 SimpleDateFormat df = new SimpleDateFormat("MMM dd, yyyy HH:mm:ss z"); 136 Date date = new Date(); 137 date.setTime(System.currentTimeMillis()); 138 return df.format(date); 139 } 140 141 /** 142 * Main driver. 143 * @param args command line arguments aren't used. 144 */ 145 public static void main(String[] args) { 146 147 long timeToRun = DEFAULT_DURATION;; 148 149 try { 150 for (int i = 0; i < args.length; i++) { 151 if ("-stressTime".equals(args[i])) { 152 if (i + 1 == args.length) { 153 throw new RuntimeException("Test bug: value of -stressTime option absents"); 154 } 155 timeToRun = Long.parseLong(args[i + 1]); 156 if (timeToRun <= 0) { 157 throw new RuntimeException("Test bug: value of -stressTime option is not a positive number"); 158 } 159 break; 160 } 161 } 162 } catch (NumberFormatException e) { 163 throw new RuntimeException("Test bug: Exception occured while parsing -stressTime option's value", e); 164 } 165 166 long startTime = System.currentTimeMillis(); 167 168 System.out.println("[" + getDateString() + "] Test duration is: " + timeToRun + " ms"); 169 System.out.println("[" + getDateString() + "] Do munge objects..."); 170 while ((System.currentTimeMillis() - startTime) < timeToRun) { 171 for (int i = 0; i < 100; i++) { 172 mungeObjects(); 173 } 174 System.out.println("[" + getDateString() + "] The next 100 objects are munged..."); 175 } 176 177 // Force a GC (so that objects move their position) 178 System.out.println("[" + getDateString() + "] Force a GC..."); 179 garbageMonger = null; 180 System.gc(); 181 182 // Now, to check to see if hashes are correct 183 System.out.println("[" + getDateString() + "] Check hash codes..."); 184 for (int i = 0; i < allObjects.size(); i++) { 185 Object o = allObjects.elementAt(i); 186 int hash = ((Integer) allHashes.elementAt(i)).intValue(); 187 188 if (o.hashCode() != hash) { 189 System.out.println("Inconsistent hash code found (Object " 190 + i + " out of " + allObjects.size()); 191 System.out.println("Object = " + o.toString() + "; hashCode = 0x" 192 + Integer.toHexString(o.hashCode()) 193 + "; expected = 0x" + Integer.toHexString(hash)); 194 System.exit(1); 195 } 196 } 197 198 System.exit(95 /* PASSED */); 199 } 200 201 /** 202 * Add a single batch of objects to the mix. 203 * <p> 204 * It prepares a list of candidate objects, and presents them to a 205 * LockerThread and a HasherThread in randomized orders. 206 * <p> 207 * The two threads are launched, and control is returned to the caller after 208 * they have finished their processing. 209 */ 210 private static void mungeObjects() { 211 212 startingGun = false; 213 finishHashing = false; 214 finishLocking = false; 215 216 /* Create the list of victims. */ 217 Object[] candidates = new Object[BATCH_SIZE]; 218 for (int i = 0; i < candidates.length; i++) { 219 candidates[i] = new Object(); 220 } 221 222 Object[] lockedList = randomize(candidates); 223 Object[] hashedList = randomize(candidates); 224 int[] foundHashes = new int[BATCH_SIZE]; 225 226 // Launch the child threads 227 LockerThread locker = new LockerThread(lockedList); 228 Thread lockerThread = new Thread(locker); 229 Thread hasherThread = new Thread(new HasherThread(hashedList, 230 foundHashes)); 231 lockerThread.start(); 232 hasherThread.start(); 233 startingGun = true; 234 235 while (!finishLocking || !finishHashing) { 236 pause(); 237 } 238 239 garbageMonger = new Object[BATCH_SIZE]; 240 for (int i = 0; i < BATCH_SIZE; i++) { 241 /* Add all of the results of this pass to the global list. */ 242 allObjects.add(hashedList[i]); 243 allHashes.add(new Integer(foundHashes[i])); 244 245 /* Create even more garbage for the GC to find */ 246 garbageMonger[i] = new Object(); 247 } 248 249 // just some noise to make sure that do-nothing code is not optimized 250 // away. 251 if (locker.getCount() != BATCH_SIZE) { 252 throw new InternalError("should not get here"); 253 } 254 } 255 256 /** 257 * Return the list of objects in random order 258 */ 259 private static Object[] randomize(Object[] list) { 260 261 Vector v = new Vector(); 262 for (int i = 0; i < list.length; i++) { 263 v.add(list[i]); 264 } 265 266 Object[] result = new Object[list.length]; 267 for (int i = 0; i < list.length; i++) { 268 int pos = rand.nextInt(list.length - i); 269 result[i] = v.remove(pos); 270 } 271 return result; 272 } 273 } 274 275 /** 276 * This helper thread locks all objects in a list in a given order before 277 * returning. 278 */ 279 280 class LockerThread implements Runnable { 281 282 /** The list of objects to be locked. */ 283 Object[] theList; 284 285 /** 286 * This junk counter is an attempt to cause the contents of the synchronized 287 * block not to be completely optimized away. 288 */ 289 int count; 290 291 /** 292 * Construct a LockerThread and provide a list of objects to work with. 293 * @param list the objects to lock. 294 */ 295 LockerThread(Object[] list) { 296 theList = list; 297 count = 0; 298 } 299 300 /** 301 * Proceed to lock the objects... 302 */ 303 public void run() { 304 // Synchronize. Wait for caller to say all is go. 305 while (!ExternalHashingTest.startingGun) { 306 ExternalHashingTest.pause(); 307 } 308 309 // Perform the locking 310 for (int i = 0; i < theList.length; i++) { 311 synchronized (theList[i]) { 312 count++; 313 } 314 } 315 316 // Tell the caller we are done. 317 ExternalHashingTest.finishLocking = true; 318 } 319 320 /** 321 * Discourage compiler from removing do-nothing count field. 322 * @return the number of objects locked. 323 */ 324 public int getCount() { 325 return count; 326 } 327 } 328 329 /** 330 * This helper thread hashes all objects in a list in a given order before 331 * returning. 332 */ 333 334 class HasherThread implements Runnable { 335 336 /** The list of objects to be hashed. */ 337 Object[] theList; 338 339 /** The list of hash codes. */ 340 int[] theHashes; 341 342 /** 343 * Construct a HasherThread and provide a list of objects to work with. 344 * @param list the objects to hash. 345 * @param hashes for storing the hash values. 346 */ 347 HasherThread(Object[] list, int[] hashes) { 348 theList = list; 349 theHashes = hashes; 350 } 351 352 /** 353 * Proceed to hash the objects. 354 */ 355 public void run() { 356 // Synchronize. Wait for caller to say all is go. 357 while (!ExternalHashingTest.startingGun) { 358 ExternalHashingTest.pause(); 359 } 360 361 // Perform the hashing (collect for the caller) 362 for (int i = 0; i < theList.length; i++) { 363 theHashes[i] = theList[i].hashCode(); 364 } 365 // Tell the caller we are done. 366 ExternalHashingTest.finishHashing = true; 367 } 368 }