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