7012768: InetAddress lookupTable leaks/deadlocks when using unsupported name service spi

Reviewed-by: alanb, michaelm
diff --git a/jdk/src/share/classes/java/net/InetAddress.java b/jdk/src/share/classes/java/net/InetAddress.java
index 889cc6d..926ef86 100644
--- a/jdk/src/share/classes/java/net/InetAddress.java
+++ b/jdk/src/share/classes/java/net/InetAddress.java
@@ -677,8 +677,7 @@
 
     static InetAddressImpl  impl;
 
-    private static HashMap<String, InetAddress[]> lookupTable
-        = new HashMap<String, InetAddress[]>();
+    private static final HashMap<String, Void> lookupTable = new HashMap<>();
 
     /**
      * Represents a cache entry
@@ -737,7 +736,7 @@
 
                 // As we iterate in insertion order we can
                 // terminate when a non-expired entry is found.
-                LinkedList<String> expired = new LinkedList<String>();
+                LinkedList<String> expired = new LinkedList<>();
                 long now = System.currentTimeMillis();
                 for (String key : cache.keySet()) {
                     CacheEntry entry = cache.get(key);
@@ -1227,43 +1226,45 @@
         //         lookupTable and return null so the
         //         following code would do  a lookup itself.
         if ((addresses = checkLookupTable(host)) == null) {
-            // This is the first thread which looks up the addresses
-            // this host or the cache entry for this host has been
-            // expired so this thread should do the lookup.
-            for (NameService nameService : nameServices) {
-                try {
-                    /*
-                     * Do not put the call to lookup() inside the
-                     * constructor.  if you do you will still be
-                     * allocating space when the lookup fails.
-                     */
+            try {
+                // This is the first thread which looks up the addresses
+                // this host or the cache entry for this host has been
+                // expired so this thread should do the lookup.
+                for (NameService nameService : nameServices) {
+                    try {
+                        /*
+                         * Do not put the call to lookup() inside the
+                         * constructor.  if you do you will still be
+                         * allocating space when the lookup fails.
+                         */
 
-                    addresses = nameService.lookupAllHostAddr(host);
-                    success = true;
-                    break;
-                } catch (UnknownHostException uhe) {
-                    if (host.equalsIgnoreCase("localhost")) {
-                        InetAddress[] local = new InetAddress[] { impl.loopbackAddress() };
-                        addresses = local;
+                        addresses = nameService.lookupAllHostAddr(host);
                         success = true;
                         break;
-                    }
-                    else {
-                        addresses = unknown_array;
-                        success = false;
-                        ex = uhe;
+                    } catch (UnknownHostException uhe) {
+                        if (host.equalsIgnoreCase("localhost")) {
+                            InetAddress[] local = new InetAddress[] { impl.loopbackAddress() };
+                            addresses = local;
+                            success = true;
+                            break;
+                        }
+                        else {
+                            addresses = unknown_array;
+                            success = false;
+                            ex = uhe;
+                        }
                     }
                 }
-            }
 
-            // Cache the addresses.
-            cacheAddresses(host, addresses, success);
-            // Delete the host from the lookupTable, and
-            // notify all threads waiting for the monitor
-            // for lookupTable.
-            updateLookupTable(host);
-            if (!success && ex != null)
-                throw ex;
+                // Cache the addresses.
+                cacheAddresses(host, addresses, success);
+                if (!success && ex != null)
+                    throw ex;
+            } finally {
+                // Delete host from the lookupTable and notify
+                // all threads waiting on the lookupTable monitor.
+                updateLookupTable(host);
+            }
         }
 
         return addresses;
@@ -1271,16 +1272,13 @@
 
 
     private static InetAddress[] checkLookupTable(String host) {
-        // make sure addresses is null.
-        InetAddress[] addresses = null;
-
         synchronized (lookupTable) {
             // If the host isn't in the lookupTable, add it in the
             // lookuptable and return null. The caller should do
             // the lookup.
             if (lookupTable.containsKey(host) == false) {
                 lookupTable.put(host, null);
-                return addresses;
+                return null;
             }
 
             // If the host is in the lookupTable, it means that another
@@ -1298,10 +1296,11 @@
         // the host. This thread should retry to get the addresses
         // from the addressCache. If it doesn't get the addresses from
         // the cache, it will try to look up the addresses itself.
-        addresses = getCachedAddresses(host);
+        InetAddress[] addresses = getCachedAddresses(host);
         if (addresses == null) {
             synchronized (lookupTable) {
                 lookupTable.put(host, null);
+                return null;
             }
         }
 
diff --git a/jdk/test/java/net/InetAddress/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor b/jdk/test/java/net/InetAddress/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
deleted file mode 100644
index efcf237..0000000
--- a/jdk/test/java/net/InetAddress/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
+++ /dev/null
@@ -1,2 +0,0 @@
-Simple1NameServiceDescriptor
-Simple2NameServiceDescriptor
diff --git a/jdk/test/sun/net/InetAddress/nameservice/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor b/jdk/test/sun/net/InetAddress/nameservice/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
deleted file mode 100644
index 6fc851d..0000000
--- a/jdk/test/sun/net/InetAddress/nameservice/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
+++ /dev/null
@@ -1,2 +0,0 @@
-# name service provider descriptor
-SimpleNameServiceDescriptor
diff --git a/jdk/test/sun/net/InetAddress/nameservice/chaining/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor b/jdk/test/sun/net/InetAddress/nameservice/chaining/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
new file mode 100644
index 0000000..5ed3b8e
--- /dev/null
+++ b/jdk/test/sun/net/InetAddress/nameservice/chaining/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
@@ -0,0 +1,23 @@
+# Copyright (c) 2011, 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
+
+Simple1NameServiceDescriptor
+Simple2NameServiceDescriptor
diff --git a/jdk/test/java/net/InetAddress/B4762344.java b/jdk/test/sun/net/InetAddress/nameservice/chaining/Providers.java
similarity index 90%
rename from jdk/test/java/net/InetAddress/B4762344.java
rename to jdk/test/sun/net/InetAddress/nameservice/chaining/Providers.java
index 0e28705..590f4e2 100644
--- a/jdk/test/java/net/InetAddress/B4762344.java
+++ b/jdk/test/sun/net/InetAddress/nameservice/chaining/Providers.java
@@ -25,15 +25,17 @@
  * @test
  * @bug 4762344
  * @summary 2nd nameservice provider is non functional
- * @build B4762344 SimpleNameService Simple1NameServiceDescriptor Simple2NameServiceDescriptor
- * @run main/othervm -Dsun.net.spi.nameservice.provider.1=simple1,sun -Dsun.net.spi.nameservice.provider.2=simple2,sun B4762344
+ * @compile -XDignore.symbol.file=true SimpleNameService.java
+ *                                     Simple1NameServiceDescriptor.java
+ *                                     Simple2NameServiceDescriptor.java
+ * @run main/othervm -Dsun.net.spi.nameservice.provider.1=simple1,sun -Dsun.net.spi.nameservice.provider.2=simple2,sun Providers
  */
 
 import java.net.*;
 import java.util.*;
 
 
-public class B4762344 {
+public class Providers {
     private static String[][] hostnames = new String[][] {
             // both providers know this host, but with different address
             new String[] {"blade", "10.0.0.1"},
diff --git a/jdk/test/java/net/InetAddress/Simple1NameServiceDescriptor.java b/jdk/test/sun/net/InetAddress/nameservice/chaining/Simple1NameServiceDescriptor.java
similarity index 100%
rename from jdk/test/java/net/InetAddress/Simple1NameServiceDescriptor.java
rename to jdk/test/sun/net/InetAddress/nameservice/chaining/Simple1NameServiceDescriptor.java
diff --git a/jdk/test/java/net/InetAddress/Simple2NameServiceDescriptor.java b/jdk/test/sun/net/InetAddress/nameservice/chaining/Simple2NameServiceDescriptor.java
similarity index 100%
rename from jdk/test/java/net/InetAddress/Simple2NameServiceDescriptor.java
rename to jdk/test/sun/net/InetAddress/nameservice/chaining/Simple2NameServiceDescriptor.java
diff --git a/jdk/test/java/net/InetAddress/SimpleNameService.java b/jdk/test/sun/net/InetAddress/nameservice/chaining/SimpleNameService.java
similarity index 100%
rename from jdk/test/java/net/InetAddress/SimpleNameService.java
rename to jdk/test/sun/net/InetAddress/nameservice/chaining/SimpleNameService.java
diff --git a/jdk/test/sun/net/InetAddress/nameservice/deadlock/Hang.java b/jdk/test/sun/net/InetAddress/nameservice/deadlock/Hang.java
new file mode 100644
index 0000000..79246ea
--- /dev/null
+++ b/jdk/test/sun/net/InetAddress/nameservice/deadlock/Hang.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011, 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.
+ */
+
+/**
+ * @test
+ * @bug 7012768
+ * @compile -XDignore.symbol.file=true ThrowingNameService.java
+ *          ThrowingNameServiceDescriptor.java
+ * @run main/othervm/timeout=30 -Dsun.net.spi.nameservice.provider.1=throwing,sun Hang
+ * @summary InetAddress lookupTable leaks/deadlocks when using unsupported
+ *          name service spi
+ */
+
+import java.net.InetAddress;
+
+public class Hang {
+    public static void main(String[] args) throws Exception {
+        try {
+            // 1st attempt - IllegalStateException caught below
+            InetAddress.getByName("host.company.com");
+        } catch (IllegalStateException e) { }
+
+        // 2nd attempt - Stuck here forever if bug exists
+        InetAddress.getByName("host.company.com");
+    }
+}
diff --git a/jdk/test/sun/net/InetAddress/nameservice/deadlock/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor b/jdk/test/sun/net/InetAddress/nameservice/deadlock/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
new file mode 100644
index 0000000..3490a90
--- /dev/null
+++ b/jdk/test/sun/net/InetAddress/nameservice/deadlock/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
@@ -0,0 +1,22 @@
+# Copyright (c) 2011, 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.
+
+ThrowingNameServiceDescriptor
diff --git a/jdk/test/sun/net/InetAddress/nameservice/deadlock/ThrowingNameService.java b/jdk/test/sun/net/InetAddress/nameservice/deadlock/ThrowingNameService.java
new file mode 100644
index 0000000..292b31d
--- /dev/null
+++ b/jdk/test/sun/net/InetAddress/nameservice/deadlock/ThrowingNameService.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2011, 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 java.net.InetAddress;
+import java.net.UnknownHostException;
+import sun.net.spi.nameservice.NameService;
+
+public class ThrowingNameService implements NameService {
+    static boolean firstCall = true;
+
+    @Override
+    public InetAddress[] lookupAllHostAddr(String host) throws UnknownHostException {
+        if (firstCall) {
+            firstCall = false;
+            // throw unchecked exception first time round
+            throw new IllegalStateException();
+        }
+
+        // return any valid address
+        return new InetAddress[] { InetAddress.getLoopbackAddress() };
+    }
+
+    @Override
+    public String getHostByAddr(byte[] addr) throws UnknownHostException {
+        throw new IllegalStateException();
+    }
+}
diff --git a/jdk/test/sun/net/InetAddress/nameservice/deadlock/ThrowingNameServiceDescriptor.java b/jdk/test/sun/net/InetAddress/nameservice/deadlock/ThrowingNameServiceDescriptor.java
new file mode 100644
index 0000000..3075412
--- /dev/null
+++ b/jdk/test/sun/net/InetAddress/nameservice/deadlock/ThrowingNameServiceDescriptor.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2011, 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 sun.net.spi.nameservice.*;
+
+public class ThrowingNameServiceDescriptor implements NameServiceDescriptor {
+    public NameService createNameService() {
+        return new ThrowingNameService();
+    }
+
+    @Override
+    public String getProviderName() {
+        return "sun";
+    }
+
+    @Override
+    public String getType() {
+        return "throwing";
+    }
+}
diff --git a/jdk/test/sun/net/InetAddress/nameservice/CacheTest.java b/jdk/test/sun/net/InetAddress/nameservice/simple/CacheTest.java
similarity index 96%
rename from jdk/test/sun/net/InetAddress/nameservice/CacheTest.java
rename to jdk/test/sun/net/InetAddress/nameservice/simple/CacheTest.java
index b6bf0e1..0aadfd3 100644
--- a/jdk/test/sun/net/InetAddress/nameservice/CacheTest.java
+++ b/jdk/test/sun/net/InetAddress/nameservice/simple/CacheTest.java
@@ -26,8 +26,8 @@
  * @summary Check that InetAddress doesn't continue to throw UHE
  *          after the name service has recovered and the negative ttl
  *          on the initial lookup has expired.
- *
- * @build CacheTest SimpleNameService SimpleNameServiceDescriptor
+ * @compile -XDignore.symbol.file=true SimpleNameService.java
+ *                                     SimpleNameServiceDescriptor.java
  * @run main/othervm/timeout=200 -Dsun.net.spi.nameservice.provider.1=simple,sun CacheTest
  */
 import java.net.InetAddress;
diff --git a/jdk/test/sun/net/InetAddress/nameservice/B6442088.java b/jdk/test/sun/net/InetAddress/nameservice/simple/DefaultCaching.java
similarity index 94%
rename from jdk/test/sun/net/InetAddress/nameservice/B6442088.java
rename to jdk/test/sun/net/InetAddress/nameservice/simple/DefaultCaching.java
index a68f798..f1010da 100644
--- a/jdk/test/sun/net/InetAddress/nameservice/B6442088.java
+++ b/jdk/test/sun/net/InetAddress/nameservice/simple/DefaultCaching.java
@@ -25,15 +25,15 @@
  * @bug 6442088
  * @summary Change default DNS caching behavior for code not running under
  *          security manager.
- *
- * @build B6442088 SimpleNameService SimpleNameServiceDescriptor
- * @run main/othervm/timeout=200 -Dsun.net.inetaddr.ttl=20 -Dsun.net.spi.nameservice.provider.1=simple,sun B6442088
+ * @compile -XDignore.symbol.file=true SimpleNameService.java
+ *                                     SimpleNameServiceDescriptor.java
+ * @run main/othervm/timeout=200 -Dsun.net.inetaddr.ttl=20 -Dsun.net.spi.nameservice.provider.1=simple,sun DefaultCaching
  */
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.security.Security;
 
-public class B6442088 {
+public class DefaultCaching {
 
     public static void main(String args[]) throws Exception {
 
diff --git a/jdk/test/sun/net/InetAddress/nameservice/simple/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor b/jdk/test/sun/net/InetAddress/nameservice/simple/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
new file mode 100644
index 0000000..b7527c07
--- /dev/null
+++ b/jdk/test/sun/net/InetAddress/nameservice/simple/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor
@@ -0,0 +1,22 @@
+# Copyright (c) 2011, 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.
+
+SimpleNameServiceDescriptor    # name service provider descriptor
diff --git a/jdk/test/sun/net/InetAddress/nameservice/SimpleNameService.java b/jdk/test/sun/net/InetAddress/nameservice/simple/SimpleNameService.java
similarity index 100%
rename from jdk/test/sun/net/InetAddress/nameservice/SimpleNameService.java
rename to jdk/test/sun/net/InetAddress/nameservice/simple/SimpleNameService.java
diff --git a/jdk/test/sun/net/InetAddress/nameservice/SimpleNameServiceDescriptor.java b/jdk/test/sun/net/InetAddress/nameservice/simple/SimpleNameServiceDescriptor.java
similarity index 100%
rename from jdk/test/sun/net/InetAddress/nameservice/SimpleNameServiceDescriptor.java
rename to jdk/test/sun/net/InetAddress/nameservice/simple/SimpleNameServiceDescriptor.java