/* * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @bug 7045594 8002070 * @summary ResourceBundle setting race in Logger.getLogger(name, rbName) * @author Daniel D. Daugherty * @build RacingThreadsTest LoggerResourceBundleRace * @run main/othervm LoggerResourceBundleRace * * (In samevm mode, the bundle classes don't end up in the classpath.) */ import java.util.ListResourceBundle; import java.util.MissingResourceException; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; public class LoggerResourceBundleRace extends RacingThreadsTest { private final static int N_LOOPS = 500000; // # of race loops private final static int N_SECS = 15; // # of secs to run test // # of parallel threads; must match number of MyResources inner classes private final static int N_THREADS = 3; private final static String LOGGER_PREFIX = "myLogger-"; private final static String RESOURCE_PREFIX = "LoggerResourceBundleRace$MyResources"; // these counters are AtomicInteger since any worker thread can increment private final static AtomicInteger iaeCnt = new AtomicInteger(); private final static AtomicInteger worksCnt = new AtomicInteger(); Logger dummy; // dummy Logger LoggerResourceBundleRace(String name, int n_threads, int n_loops, int n_secs) { super(name, n_threads, n_loops, n_secs); } // Main test driver // public static void main(String[] args) { LoggerResourceBundleRace test = new LoggerResourceBundleRace("LoggerResourceBundleRace", N_THREADS, N_LOOPS, N_SECS); test.setVerbose( Boolean.getBoolean("LoggerResourceBundleRace.verbose")); DriverThread driver = new DriverThread(test); MyWorkerThread[] workers = new MyWorkerThread[N_THREADS]; for (int i = 0; i < workers.length; i++) { workers[i] = new MyWorkerThread(i, test); } test.runTest(driver, workers); } public void oneTimeDriverInit(DriverThread dt) { super.oneTimeDriverInit(dt); dummy = null; } public void perRaceDriverInit(DriverThread dt) { super.perRaceDriverInit(dt); // - allocate a new dummy Logger without a ResourceBundle; // this gives the racing threads less to do // - reset the counters dummy = Logger.getLogger(LOGGER_PREFIX + getLoopCnt()); iaeCnt.set(0); worksCnt.set(0); } public void executeRace(WorkerThread wt) { super.executeRace(wt); Logger myLogger = null; try { MyWorkerThread mwt = (MyWorkerThread) wt; // short hand // Here is the race: // - the target Logger object has already been created by // the DriverThread without a ResourceBundle name // - in parallel, each WorkerThread calls Logger.getLogger() // with a different ResourceBundle name // - Logger.getLogger() should only successfully set the // ResourceBundle name for one WorkerThread; all other // WorkerThread calls to Logger.getLogger() should throw // IllegalArgumentException myLogger = Logger.getLogger(LOGGER_PREFIX + getLoopCnt(), mwt.rbName); if (myLogger.getResourceBundleName().equals(mwt.rbName)) { // no exception and the ResourceBundle names match worksCnt.incrementAndGet(); // ignore return } else { System.err.println(wt.getName() + ": ERROR: expected ResourceBundleName '" + mwt.rbName + "' does not match actual '" + myLogger.getResourceBundleName() + "'"); incAndGetFailCnt(); // ignore return } } catch (IllegalArgumentException iae) { iaeCnt.incrementAndGet(); // ignore return } catch (MissingResourceException mre) { // This exception happens when N_THREADS above does not // match the number of MyResources inner classes below. // We exit since this is a coding error. unexpectedException(wt, mre); System.exit(2); } } public void checkRaceResults(DriverThread dt) { super.checkRaceResults(dt); if (worksCnt.get() != 1) { System.err.println(dt.getName() + ": ERROR: worksCnt should be 1" + ": loopCnt=" + getLoopCnt() + ", worksCnt=" + worksCnt.get()); incAndGetFailCnt(); // ignore return } else if (iaeCnt.get() != N_THREADS - 1) { System.err.println(dt.getName() + ": ERROR: iaeCnt should be " + (N_THREADS - 1) + ": loopCnt=" + getLoopCnt() + ", iaeCnt=" + iaeCnt.get()); incAndGetFailCnt(); // ignore return } } public void oneTimeDriverEpilog(DriverThread dt) { super.oneTimeDriverEpilog(dt); // Use the dummy Logger after the testing loop to make sure that // dummy doesn't get optimized away in the testing loop. dummy.info("This is a test message."); } // N_THREADS above must match number of MyResources inner classes // public static class MyResources0 extends ListResourceBundle { final static Object[][] contents = { {"sample1", "translation #1 for sample1"}, {"sample2", "translation #1 for sample2"}, }; public Object[][] getContents() { return contents; } } public static class MyResources1 extends ListResourceBundle { final static Object[][] contents = { {"sample1", "translation #2 for sample1"}, {"sample2", "translation #2 for sample2"}, }; public Object[][] getContents() { return contents; } } public static class MyResources2 extends ListResourceBundle { final static Object[][] contents = { {"sample1", "translation #3 for sample1"}, {"sample2", "translation #3 for sample2"}, }; public Object[][] getContents() { return contents; } } // WorkerThread with a thread specific ResourceBundle name // public static class MyWorkerThread extends WorkerThread { public final String rbName; // ResourceBundle name MyWorkerThread(int workerNum, RacingThreadsTest test) { super(workerNum, test); rbName = RESOURCE_PREFIX + workerNum; } } }