• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }