1 /* 2 * Copyright (c) 2017, 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 package test.java.util.TimeZone; 24 25 import java.util.Calendar; 26 import java.util.Locale; 27 import java.util.SimpleTimeZone; 28 import java.util.TimeZone; 29 import java.util.function.Supplier; 30 31 import org.testng.Assert; 32 import org.testng.annotations.Test; 33 /* 34 * @test 35 * @bug 8191216 36 * @summary test that provokes race between cloning and lazily initializing 37 * SimpleTimeZone cache fields 38 */ 39 public class SimpleTimeZoneCloneRaceTest { 40 41 @Test testSimpleTimeZone()42 public void testSimpleTimeZone() throws InterruptedException { 43 44 // shared TZ user repeatedly samples sharedTZ and calculates offset 45 // using the shared instance 46 TimeZoneUser sharedTZuser = new TimeZoneUser(() -> sharedTZ); 47 48 // cloned TZ user repeatedly samples sharedTZ then clones it and 49 // calculates offset using the clone... 50 TimeZoneUser clonedTZuser = new TimeZoneUser(() -> { 51 // sample shared TZ 52 TimeZone tz = sharedTZ; 53 // do some computation that takes roughly the same time as it takes 54 // sharedTZUser to start changing cache fields in shared TZ 55 cpuHogTZ.getOffset(time); 56 // now clone the sampled TZ and return it, hoping the clone is done 57 // at about the right time.... 58 return (TimeZone) tz.clone(); 59 }); 60 61 // start threads 62 Thread t1 = new Thread(sharedTZuser); 63 Thread t2 = new Thread(clonedTZuser); 64 t1.start(); 65 t2.start(); 66 67 // plant new SimpleTimeZone instances for 2 seconds 68 long t0 = System.currentTimeMillis(); 69 do { 70 TimeZone tz1 = createSTZ(); 71 TimeZone tz2 = createSTZ(); 72 cpuHogTZ = tz1; 73 sharedTZ = tz2; 74 } while (System.currentTimeMillis() - t0 < 2000L); 75 76 sharedTZuser.stop = true; 77 clonedTZuser.stop = true; 78 t1.join(); 79 t2.join(); 80 81 Assert.assertFalse(clonedTZuser.incorrectCount > 0, clonedTZuser.incorrectCount + 82 " fatal data races detected"); 83 } 84 createSTZ()85 static SimpleTimeZone createSTZ() { 86 return new SimpleTimeZone(-28800000, 87 "America/Los_Angeles", 88 Calendar.APRIL, 1, -Calendar.SUNDAY, 89 7200000, 90 Calendar.OCTOBER, -1, Calendar.SUNDAY, 91 7200000, 92 3600000); 93 } 94 95 static volatile TimeZone cpuHogTZ = createSTZ(); 96 static volatile TimeZone sharedTZ = createSTZ(); 97 static final long time; 98 static final long correctOffset; 99 100 static { 101 TimeZone tz = createSTZ(); 102 Calendar cal = Calendar.getInstance(tz, Locale.ROOT); 103 cal.set(2000, Calendar.MAY, 1, 0, 0, 0); 104 time = cal.getTimeInMillis(); 105 correctOffset = tz.getOffset(time); 106 } 107 108 static class TimeZoneUser implements Runnable { 109 private final Supplier<? extends TimeZone> tzSupplier; 110 TimeZoneUser(Supplier<? extends TimeZone> tzSupplier)111 TimeZoneUser(Supplier<? extends TimeZone> tzSupplier) { 112 this.tzSupplier = tzSupplier; 113 } 114 115 volatile boolean stop; 116 int correctCount, incorrectCount; 117 118 @Override run()119 public void run() { 120 while (!stop) { 121 TimeZone tz = tzSupplier.get(); 122 int offset = tz.getOffset(time); 123 if (offset == correctOffset) { 124 correctCount++; 125 } else { 126 incorrectCount++; 127 } 128 } 129 } 130 } 131 }