1 /* 2 * Copyright (c) 2011, 2012 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 7045594 27 * @summary ResourceBundle setting race in Logger.getLogger(name, rbName) 28 * @author Daniel D. Daugherty 29 * @build RacingThreadsTest LoggerResourceBundleRace 30 * @run main LoggerResourceBundleRace 31 */ 32 33 import java.util.ListResourceBundle; 34 import java.util.MissingResourceException; 35 import java.util.concurrent.atomic.AtomicInteger; 36 import java.util.logging.Logger; 37 38 39 public class LoggerResourceBundleRace extends RacingThreadsTest { 40 private final static int N_LOOPS = 500000; // # of race loops 41 private final static int N_SECS = 15; // # of secs to run test 42 // # of parallel threads; must match number of MyResources inner classes 43 private final static int N_THREADS = 3; 44 45 private final static String LOGGER_PREFIX = "myLogger-"; 46 private final static String RESOURCE_PREFIX 47 = "LoggerResourceBundleRace$MyResources"; 48 // these counters are AtomicInteger since any worker thread can increment 49 private final static AtomicInteger iaeCnt = new AtomicInteger(); 50 private final static AtomicInteger worksCnt = new AtomicInteger(); 51 52 Logger dummy; // dummy Logger 53 54 LoggerResourceBundleRace(String name, int n_threads, int n_loops, 55 int n_secs) { 56 super(name, n_threads, n_loops, n_secs); 57 } 58 59 60 // Main test driver 61 // 62 public static void main(String[] args) { 63 LoggerResourceBundleRace test 64 = new LoggerResourceBundleRace("LoggerResourceBundleRace", 65 N_THREADS, N_LOOPS, N_SECS); 66 test.setVerbose( 67 Boolean.getBoolean("LoggerResourceBundleRace.verbose")); 68 69 DriverThread driver = new DriverThread(test); 70 MyWorkerThread[] workers = new MyWorkerThread[N_THREADS]; 71 for (int i = 0; i < workers.length; i++) { 72 workers[i] = new MyWorkerThread(i, test); 73 } 74 test.runTest(driver, workers); 75 } 76 77 public void oneTimeDriverInit(DriverThread dt) { 78 super.oneTimeDriverInit(dt); 79 dummy = null; 80 } 81 82 public void perRaceDriverInit(DriverThread dt) { 83 super.perRaceDriverInit(dt); 84 85 // - allocate a new dummy Logger without a ResourceBundle; 86 // this gives the racing threads less to do 87 // - reset the counters 88 dummy = Logger.getLogger(LOGGER_PREFIX + getLoopCnt()); 89 iaeCnt.set(0); 90 worksCnt.set(0); 91 } 92 93 public void executeRace(WorkerThread wt) { 94 super.executeRace(wt); 95 96 Logger myLogger = null; 97 try { 98 MyWorkerThread mwt = (MyWorkerThread) wt; // short hand 99 100 // Here is the race: 101 // - the target Logger object has already been created by 102 // the DriverThread without a ResourceBundle name 103 // - in parallel, each WorkerThread calls Logger.getLogger() 104 // with a different ResourceBundle name 105 // - Logger.getLogger() should only successfully set the 106 // ResourceBundle name for one WorkerThread; all other 107 // WorkerThread calls to Logger.getLogger() should throw 108 // IllegalArgumentException 109 myLogger = Logger.getLogger(LOGGER_PREFIX + getLoopCnt(), 110 mwt.rbName); 111 if (myLogger.getResourceBundleName().equals(mwt.rbName)) { 112 // no exception and the ResourceBundle names match 113 worksCnt.incrementAndGet(); // ignore return 114 } else { 115 System.err.println(wt.getName() 116 + ": ERROR: expected ResourceBundleName '" 117 + mwt.rbName + "' does not match actual '" 118 + myLogger.getResourceBundleName() + "'"); 119 incAndGetFailCnt(); // ignore return 120 } 121 } catch (IllegalArgumentException iae) { 122 iaeCnt.incrementAndGet(); // ignore return 123 } catch (MissingResourceException mre) { 124 // This exception happens when N_THREADS above does not 125 // match the number of MyResources inner classes below. 126 // We exit since this is a coding error. 127 unexpectedException(wt, mre); 128 System.exit(2); 129 } 130 } 131 132 public void checkRaceResults(DriverThread dt) { 133 super.checkRaceResults(dt); 134 135 if (worksCnt.get() != 1) { 136 System.err.println(dt.getName() + ": ERROR: worksCnt should be 1" 137 + ": loopCnt=" + getLoopCnt() + ", worksCnt=" + worksCnt.get()); 138 incAndGetFailCnt(); // ignore return 139 } else if (iaeCnt.get() != N_THREADS - 1) { 140 System.err.println(dt.getName() + ": ERROR: iaeCnt should be " 141 + (N_THREADS - 1) + ": loopCnt=" + getLoopCnt() 142 + ", iaeCnt=" + iaeCnt.get()); 143 incAndGetFailCnt(); // ignore return 144 } 145 } 146 147 public void oneTimeDriverEpilog(DriverThread dt) { 148 super.oneTimeDriverEpilog(dt); 149 150 // Use the dummy Logger after the testing loop to make sure that 151 // dummy doesn't get optimized away in the testing loop. 152 dummy.info("This is a test message."); 153 } 154 155 // N_THREADS above must match number of MyResources inner classes 156 // 157 public static class MyResources0 extends ListResourceBundle { 158 final static Object[][] contents = { 159 {"sample1", "translation #1 for sample1"}, 160 {"sample2", "translation #1 for sample2"}, 161 }; 162 163 public Object[][] getContents() { 164 return contents; 165 } 166 } 167 168 public static class MyResources1 extends ListResourceBundle { 169 final static Object[][] contents = { 170 {"sample1", "translation #2 for sample1"}, 171 {"sample2", "translation #2 for sample2"}, 172 }; 173 174 public Object[][] getContents() { 175 return contents; 176 } 177 } 178 179 public static class MyResources2 extends ListResourceBundle { 180 final static Object[][] contents = { 181 {"sample1", "translation #3 for sample1"}, 182 {"sample2", "translation #3 for sample2"}, 183 }; 184 185 public Object[][] getContents() { 186 return contents; 187 } 188 } 189 190 191 // WorkerThread with a thread specific ResourceBundle name 192 // 193 public static class MyWorkerThread extends WorkerThread { 194 public final String rbName; // ResourceBundle name 195 196 MyWorkerThread(int workerNum, RacingThreadsTest test) { 197 super(workerNum, test); 198 199 rbName = RESOURCE_PREFIX + workerNum; 200 } 201 } 202 }