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 }