Path: blob/master/test/jdk/sun/util/calendar/CalendarSystemDeadLockTest.java
66644 views
/*1* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.util.ArrayList;24import java.util.Collections;25import java.util.List;26import java.util.concurrent.Callable;27import java.util.concurrent.CountDownLatch;28import java.util.concurrent.ExecutorService;29import java.util.concurrent.Executors;30import java.util.concurrent.Future;3132/**33* @test34* @bug 827379035* @summary Verify that concurrent classloading of sun.util.calendar.Gregorian and36* sun.util.calendar.CalendarSystem doesn't lead to a deadlock37* @modules java.base/sun.util.calendar:open38* @run main/othervm CalendarSystemDeadLockTest39* @run main/othervm CalendarSystemDeadLockTest40* @run main/othervm CalendarSystemDeadLockTest41* @run main/othervm CalendarSystemDeadLockTest42* @run main/othervm CalendarSystemDeadLockTest43*/44public class CalendarSystemDeadLockTest {4546public static void main(final String[] args) throws Exception {47testConcurrentClassLoad();48}4950/**51* Loads {@code sun.util.calendar.Gregorian} and {@code sun.util.calendar.CalendarSystem}52* and invokes {@code sun.util.calendar.CalendarSystem#getGregorianCalendar()} concurrently53* in a thread of their own and expects the classloading of both those classes54* to succeed. Additionally, after these tasks are done, calls the55* sun.util.calendar.CalendarSystem#getGregorianCalendar() and expects it to return a singleton56* instance57*/58private static void testConcurrentClassLoad() throws Exception {59final int numTasks = 7;60final CountDownLatch taskTriggerLatch = new CountDownLatch(numTasks);61final List<Callable<?>> tasks = new ArrayList<>();62// add the sun.util.calendar.Gregorian and sun.util.calendar.CalendarSystem for classloading.63// there are main 2 classes which had a cyclic call in their static init64tasks.add(new ClassLoadTask("sun.util.calendar.Gregorian", taskTriggerLatch));65tasks.add(new ClassLoadTask("sun.util.calendar.CalendarSystem", taskTriggerLatch));66// add a few other classes for classloading, those which call CalendarSystem#getGregorianCalendar()67// or CalendarSystem#forName() during their static init68tasks.add(new ClassLoadTask("java.util.GregorianCalendar", taskTriggerLatch));69tasks.add(new ClassLoadTask("java.util.Date", taskTriggerLatch));70tasks.add(new ClassLoadTask("java.util.JapaneseImperialCalendar", taskTriggerLatch));71// add a couple of tasks which directly invoke sun.util.calendar.CalendarSystem#getGregorianCalendar()72tasks.add(new GetGregorianCalTask(taskTriggerLatch));73tasks.add(new GetGregorianCalTask(taskTriggerLatch));74// before triggering the tests make sure we have created the correct number of tasks75// the countdown latch uses/expects76if (numTasks != tasks.size()) {77throw new RuntimeException("Test setup failure - unexpected number of tasks " + tasks.size()78+ ", expected " + numTasks);79}80final ExecutorService executor = Executors.newFixedThreadPool(tasks.size());81try {82final Future<?>[] results = new Future[tasks.size()];83// submit84int i = 0;85for (final Callable<?> task : tasks) {86results[i++] = executor.submit(task);87}88// wait for completion89for (i = 0; i < tasks.size(); i++) {90results[i].get();91}92} finally {93executor.shutdownNow();94}95// check that the sun.util.calendar.CalendarSystem#getGregorianCalendar() does indeed return96// a proper instance97final Object gCal = callCalSystemGetGregorianCal();98if (gCal == null) {99throw new RuntimeException("sun.util.calendar.CalendarSystem#getGregorianCalendar()" +100" unexpectedly returned null");101}102// now verify that each call to getGregorianCalendar(), either in the tasks or here, returned the exact103// same instance104if (GetGregorianCalTask.instances.size() != 2) {105throw new RuntimeException("Unexpected number of results from call " +106"to sun.util.calendar.CalendarSystem#getGregorianCalendar()");107}108// intentional identity check since sun.util.calendar.CalendarSystem#getGregorianCalendar() is109// expected to return a singleton instance110if ((gCal != GetGregorianCalTask.instances.get(0)) || (gCal != GetGregorianCalTask.instances.get(1))) {111throw new RuntimeException("sun.util.calendar.CalendarSystem#getGregorianCalendar()" +112" returned different instances");113}114}115116/**117* Reflectively calls sun.util.calendar.CalendarSystem#getGregorianCalendar() and returns118* the result119*/120private static Object callCalSystemGetGregorianCal() throws Exception {121final Class<?> k = Class.forName("sun.util.calendar.CalendarSystem");122return k.getDeclaredMethod("getGregorianCalendar").invoke(null);123}124125private static class ClassLoadTask implements Callable<Class<?>> {126private final String className;127private final CountDownLatch latch;128129private ClassLoadTask(final String className, final CountDownLatch latch) {130this.className = className;131this.latch = latch;132}133134@Override135public Class<?> call() {136System.out.println(Thread.currentThread().getName() + " loading " + this.className);137try {138// let the other tasks know we are ready to trigger our work139latch.countDown();140// wait for the other task to let us know they are ready to trigger their work too141latch.await();142return Class.forName(this.className);143} catch (Exception e) {144throw new RuntimeException(e);145}146}147}148149private static class GetGregorianCalTask implements Callable<Object> {150// keeps track of the instances returned by calls to sun.util.calendar.CalendarSystem#getGregorianCalendar()151// by this task152private static final List<Object> instances = Collections.synchronizedList(new ArrayList<>());153private final CountDownLatch latch;154155private GetGregorianCalTask(final CountDownLatch latch) {156this.latch = latch;157}158159@Override160public Object call() {161System.out.println(Thread.currentThread().getName()162+ " calling sun.util.calendar.CalendarSystem#getGregorianCalendar()");163try {164// let the other tasks know we are ready to trigger our work165latch.countDown();166// wait for the other task to let us know they are ready to trigger their work too167latch.await();168final Object inst = callCalSystemGetGregorianCal();169instances.add(inst);170return inst;171} catch (Exception e) {172throw new RuntimeException(e);173}174}175}176}177178179