On Thu, 18 Feb 2021 05:01:41 GMT, Jaikiran Pai <j...@openjdk.org> wrote:

>>> > Changes looks OK to me, any specific reason for removing "final" 
>>> > specifier from "getInstance" & "register" methods ?.
>>> 
>>> Hello Vyom, thank you for the review. Since those two methods are `static`, 
>>> the `final` was redundant on them and since this patch was already changing 
>>> those 2 methods, I decided to remove it while I was at it. However, if you 
>>> and others feel that this patch shouldn't change it, I will introduce it 
>>> back.
>> 
>> I think it's OK for me. What about improving the test little bit,  your test 
>> wants to  load both classes at the same time. Please have a look on modified 
>> test.
>> 
>> 
>> /*
>>  * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
>>  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
>>  *
>>  * This code is free software; you can redistribute it and/or modify it
>>  * under the terms of the GNU General Public License version 2 only, as
>>  * published by the Free Software Foundation.
>>  *
>>  * This code is distributed in the hope that it will be useful, but WITHOUT
>>  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
>>  * version 2 for more details (a copy is included in the LICENSE file that
>>  * accompanied this code).
>>  *
>>  * You should have received a copy of the GNU General Public License version
>>  * 2 along with this work; if not, write to the Free Software Foundation,
>>  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
>>  *
>>  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
>>  * or visit www.oracle.com if you need additional information or have any
>>  * questions.
>>  */
>> import org.testng.Assert;
>> import org.testng.annotations.Test;
>> 
>> import java.util.concurrent.Callable;
>> import java.util.concurrent.CountDownLatch;
>> import java.util.concurrent.ExecutorService;
>> import java.util.concurrent.Executors;
>> import java.util.concurrent.Future;
>> 
>> /**
>>  * @test 
>>  * @bug 8260366
>>  * @summary Verify that concurrent classloading of
>>  * sun.net.ext.ExtendedSocketOptions and jdk.net.ExtendedSocketOptions 
>> doesn't
>>  * lead to a deadlock
>>  * @modules java.base/sun.net.ext:open
>>  * @run testng/othervm ExtendedSocketOptionsTest
>>  * @run testng/othervm ExtendedSocketOptionsTest
>>  * @run testng/othervm ExtendedSocketOptionsTest
>>  * @run testng/othervm ExtendedSocketOptionsTest
>>  * @run testng/othervm ExtendedSocketOptionsTest
>>  */
>> public class ExtendedSocketOptionsTest {
>> 
>>     /**
>>      * Loads {@code jdk.net.ExtendedSocketOptions} and
>>      * {@code sun.net.ext.ExtendedSocketOptions} concurrently in a thread of
>>      * their own and expects the classloading of both those classes to
>>      * succeed.Additionally, after the classloading is successfully done, 
>> calls
>>      * the sun.net.ext.ExtendedSocketOptions#getInstance() and expects it to
>>      * return a registered ExtendedSocketOptions instance.
>>      *
>>      * @throws java.lang.Exception
>>      */
>>     @Test
>>     public void testConcurrentClassLoad() throws Exception {
>>         CountDownLatch latch = new CountDownLatch(1);
>>         final Callable<Class<?>> task1 = new 
>> Task("jdk.net.ExtendedSocketOptions", latch);
>>         final Callable<Class<?>> task2 = new 
>> Task("sun.net.ext.ExtendedSocketOptions", latch);
>>         final ExecutorService executor = Executors.newFixedThreadPool(2);
>>         try {
>>             final Future<Class<?>>[] results = new Future[2];
>> 
>>             // submit
>>             results[0] = executor.submit(task1);
>>             results[1] = executor.submit(task2);
>>             latch.countDown();
>> 
>>             // wait for completion
>>             for (Future<Class<?>> result: results) {
>>                 final Class<?> k = result.get();
>>                 System.out.println("Completed loading " + k.getName());
>>             }
>>         } finally {
>>             executor.shutdownNow();
>>         }
>>         // check that the sun.net.ext.ExtendedSocketOptions#getInstance() 
>> does indeed return
>>         // the registered instance
>>         final Class<?> k = 
>> Class.forName("sun.net.ext.ExtendedSocketOptions");
>>         final Object extSocketOptions = 
>> k.getDeclaredMethod("getInstance").invoke(null);
>>         Assert.assertNotNull(extSocketOptions, 
>> "sun.net.ext.ExtendedSocketOptions#getInstance()"
>>                 + " unexpectedly returned null");
>>     }
>> 
>>     private static class Task implements Callable<Class<?>> {
>> 
>>         private final String className;
>>         private final CountDownLatch latch;
>> 
>>         private Task(final String className, CountDownLatch latch) {
>>             this.className = className;
>>             this.latch = latch;
>>         }
>> 
>>         @Override
>>         public Class<?> call() {
>>             System.out.println(Thread.currentThread().getName() + " loading 
>> " + this.className);
>>             try {
>>                 latch.await();
>>                 return Class.forName(this.className);
>>             } catch (ClassNotFoundException | InterruptedException e) {
>>                 System.err.println("Failed to load " + this.className);
>>                 throw new RuntimeException(e);
>>             }
>>         }
>>     }
>> }
>
>> What about improving the test little bit, your test wants to load both 
>> classes at the same time. Please have a look on modified test.
> 
> Hello Vyom, I think that's a good suggestion to use a latch for deciding when 
> to trigger the classloading. I've taken your input and have made some 
> relatively minor change to the way that latch gets used and updated my PR 
> with that change. The latch now waits for both the tasks to reach the point 
> where they are going to do a `Class.forName` on their respectively class 
> names. This should make the test trigger that classloading in separate 
> threads as simultaneously as possible.

I think below change will address Andrey's concern 
public static ExtendedSocketOptions getInstance() {
        ExtendedSocketOptions temp = instance;
        if (temp == null) {
            synchronized (ExtendedSocketOptions.class) {
                try {
                    // If the class is present, it will be initialized which
                    // triggers registration of the extended socket options.
                    Class<?> c = Class.forName("jdk.net.ExtendedSocketOptions");
                } catch (ClassNotFoundException e) {
                    // the jdk.net module is not present => no extended socket 
options
                    instance = new NoExtendedSocketOptions();
                }
                temp = instance;
            }
        }
        return temp;
    }

-------------

PR: https://git.openjdk.java.net/jdk/pull/2601

Reply via email to