diff --git a/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/monitor/MonitoredHost.java b/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/monitor/MonitoredHost.java index b119a004520..fafcce1c47f 100644 --- a/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/monitor/MonitoredHost.java +++ b/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/monitor/MonitoredHost.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2023, 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 @@ -50,7 +50,7 @@ * @see HostListener */ public abstract class MonitoredHost { - private static Map monitoredHosts = + private static final Map monitoredHosts = new HashMap(); /* @@ -133,13 +133,6 @@ public static MonitoredHost getMonitoredHost(VmIdentifier vmid) return getMonitoredHost(hostId); } - - /* - * Load the MonitoredHostServices - */ - private static ServiceLoader monitoredHostServiceLoader = - ServiceLoader.load(MonitoredHostService.class, MonitoredHostService.class.getClassLoader()); - /** * Factory method to construct a MonitoredHost instance to manage the * connection to the host indicated by {@code hostId}. @@ -167,9 +160,12 @@ public static MonitoredHost getMonitoredHost(HostIdentifier hostId) hostId = resolveHostId(hostId); - for (MonitoredHostService mhs : monitoredHostServiceLoader) { + ServiceLoader services = ServiceLoader.load( + MonitoredHostService.class, MonitoredHostService.class.getClassLoader()); + for (MonitoredHostService mhs : services) { if (mhs.getScheme().equals(hostId.getScheme())) { mh = mhs.getMonitoredHost(hostId); + break; } } diff --git a/test/jdk/sun/jvmstat/monitor/MonitoredVm/ConcurrentGetMonitoredHost.java b/test/jdk/sun/jvmstat/monitor/MonitoredVm/ConcurrentGetMonitoredHost.java new file mode 100644 index 00000000000..65294f45e64 --- /dev/null +++ b/test/jdk/sun/jvmstat/monitor/MonitoredVm/ConcurrentGetMonitoredHost.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023, 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.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import sun.jvmstat.monitor.MonitoredHost; +import sun.jvmstat.monitor.VmIdentifier; + +/* + * @test + * @bug 8320687 + * @summary verify that sun.jvmstat.monitor.MonitoredHost.getMonitoredHost() doesn't + * unexpectedly throw an exception when invoked by concurrent threads + * + * @run main/othervm ConcurrentGetMonitoredHost + */ +public class ConcurrentGetMonitoredHost { + + /* + * Launches multiple concurrent threads and invokes MonitoredHost.getMonitoredHost() + * in each of these threads and expects the call to return successfully without any + * exceptions. + */ + public static void main(final String[] args) throws Exception { + final String pidStr = "12345"; + final VmIdentifier vmid = new VmIdentifier(pidStr); + final int numTasks = 100; + final List tasks = new ArrayList<>(); + for (int i = 0; i < numTasks; i++) { + tasks.add(new Task(vmid)); + } + System.out.println("Submitting " + numTasks + " concurrent tasks to" + + " get MonitoredHost for " + vmid); + try { + final ExecutorService executor = Executors.newCachedThreadPool(); + // wait for all tasks to complete + final List> results = executor.invokeAll(tasks); + // verify each one successfully completed and each of + // the returned MonitoredHost is not null + for (final Future result : results) { + final MonitoredHost mh = result.get(); + if (mh == null) { + throw new AssertionError("MonitoredHost.getMonitoredHost() returned" + + " null for vmid " + vmid); + } + } + } catch (Exception e) { + } + System.out.println("All " + numTasks + " completed successfully"); + } + + // a task which just calls MonitoredHost.getMonitoredHost(VmIdentifier) and + // returns the resultant MonitoredHost + private static final class Task implements Callable { + private final VmIdentifier vmid; + + private Task(final VmIdentifier vmid) { + this.vmid = Objects.requireNonNull(vmid); + } + + @Override + public MonitoredHost call() throws Exception { + return MonitoredHost.getMonitoredHost(this.vmid); + } + } +}