8225690: Multiple AttachListener threads can be created
Reviewed-by: sspitsyn, cjplummer
--- a/src/hotspot/os/aix/attachListener_aix.cpp Mon Jul 15 11:23:05 2019 -0400
+++ b/src/hotspot/os/aix/attachListener_aix.cpp Tue Jul 16 07:29:12 2019 +0900
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2018 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -71,17 +71,7 @@
// the file descriptor for the listening socket
static int _listener;
- static void set_path(char* path) {
- if (path == NULL) {
- _has_path = false;
- } else {
- strncpy(_path, path, UNIX_PATH_MAX);
- _path[UNIX_PATH_MAX-1] = '\0';
- _has_path = true;
- }
- }
-
- static void set_listener(int s) { _listener = s; }
+ static bool _atexit_registered;
// reads a request from the given connected socket
static AixAttachOperation* read_request(int s);
@@ -94,6 +84,19 @@
ATTACH_ERROR_BADVERSION = 101 // error codes
};
+ static void set_path(char* path) {
+ if (path == NULL) {
+ _path[0] = '\0';
+ _has_path = false;
+ } else {
+ strncpy(_path, path, UNIX_PATH_MAX);
+ _path[UNIX_PATH_MAX-1] = '\0';
+ _has_path = true;
+ }
+ }
+
+ static void set_listener(int s) { _listener = s; }
+
// initialize the listener, returns 0 if okay
static int init();
@@ -130,6 +133,7 @@
char AixAttachListener::_path[UNIX_PATH_MAX];
bool AixAttachListener::_has_path;
int AixAttachListener::_listener = -1;
+bool AixAttachListener::_atexit_registered = false;
// Shutdown marker to prevent accept blocking during clean-up
bool AixAttachListener::_shutdown = false;
@@ -177,17 +181,15 @@
// should be sufficient for cleanup.
extern "C" {
static void listener_cleanup() {
- static int cleanup_done;
- if (!cleanup_done) {
- cleanup_done = 1;
- AixAttachListener::set_shutdown(true);
- int s = AixAttachListener::listener();
- if (s != -1) {
- ::shutdown(s, 2);
- }
- if (AixAttachListener::has_path()) {
- ::unlink(AixAttachListener::path());
- }
+ AixAttachListener::set_shutdown(true);
+ int s = AixAttachListener::listener();
+ if (s != -1) {
+ AixAttachListener::set_listener(-1);
+ ::shutdown(s, 2);
+ }
+ if (AixAttachListener::has_path()) {
+ ::unlink(AixAttachListener::path());
+ AixAttachListener::set_path(NULL);
}
}
}
@@ -200,7 +202,10 @@
int listener; // listener socket (file descriptor)
// register function to cleanup
- ::atexit(listener_cleanup);
+ if (!_atexit_registered) {
+ _atexit_registered = true;
+ ::atexit(listener_cleanup);
+ }
int n = snprintf(path, UNIX_PATH_MAX, "%s/.java_pid%d",
os::get_temp_directory(), os::current_process_id());
@@ -515,6 +520,26 @@
return ret_code;
}
+bool AttachListener::check_socket_file() {
+ int ret;
+ struct stat64 st;
+ ret = stat64(AixAttachListener::path(), &st);
+ if (ret == -1) { // need to restart attach listener.
+ log_debug(attach)("Socket file %s does not exist - Restart Attach Listener",
+ AixAttachListener::path());
+
+ listener_cleanup();
+
+ // wait to terminate current attach listener instance...
+ while (AttachListener::transit_state(AL_INITIALIZING,
+ AL_NOT_INITIALIZED) != AL_NOT_INITIALIZED) {
+ os::naked_yield();
+ }
+ return is_init_trigger();
+ }
+ return false;
+}
+
// Attach Listener is started lazily except in the case when
// +ReduseSignalUsage is used
bool AttachListener::init_at_startup() {
--- a/src/hotspot/os/bsd/attachListener_bsd.cpp Mon Jul 15 11:23:05 2019 -0400
+++ b/src/hotspot/os/bsd/attachListener_bsd.cpp Tue Jul 16 07:29:12 2019 +0900
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -68,17 +68,7 @@
// the file descriptor for the listening socket
static int _listener;
- static void set_path(char* path) {
- if (path == NULL) {
- _has_path = false;
- } else {
- strncpy(_path, path, UNIX_PATH_MAX);
- _path[UNIX_PATH_MAX-1] = '\0';
- _has_path = true;
- }
- }
-
- static void set_listener(int s) { _listener = s; }
+ static bool _atexit_registered;
// reads a request from the given connected socket
static BsdAttachOperation* read_request(int s);
@@ -91,6 +81,19 @@
ATTACH_ERROR_BADVERSION = 101 // error codes
};
+ static void set_path(char* path) {
+ if (path == NULL) {
+ _path[0] = '\0';
+ _has_path = false;
+ } else {
+ strncpy(_path, path, UNIX_PATH_MAX);
+ _path[UNIX_PATH_MAX-1] = '\0';
+ _has_path = true;
+ }
+ }
+
+ static void set_listener(int s) { _listener = s; }
+
// initialize the listener, returns 0 if okay
static int init();
@@ -124,6 +127,7 @@
char BsdAttachListener::_path[UNIX_PATH_MAX];
bool BsdAttachListener::_has_path;
int BsdAttachListener::_listener = -1;
+bool BsdAttachListener::_atexit_registered = false;
// Supporting class to help split a buffer into individual components
class ArgumentIterator : public StackObj {
@@ -158,16 +162,15 @@
// bound too.
extern "C" {
static void listener_cleanup() {
- static int cleanup_done;
- if (!cleanup_done) {
- cleanup_done = 1;
- int s = BsdAttachListener::listener();
- if (s != -1) {
- ::close(s);
- }
- if (BsdAttachListener::has_path()) {
- ::unlink(BsdAttachListener::path());
- }
+ int s = BsdAttachListener::listener();
+ if (s != -1) {
+ BsdAttachListener::set_listener(-1);
+ ::shutdown(s, SHUT_RDWR);
+ ::close(s);
+ }
+ if (BsdAttachListener::has_path()) {
+ ::unlink(BsdAttachListener::path());
+ BsdAttachListener::set_path(NULL);
}
}
}
@@ -180,7 +183,10 @@
int listener; // listener socket (file descriptor)
// register function to cleanup
- ::atexit(listener_cleanup);
+ if (!_atexit_registered) {
+ _atexit_registered = true;
+ ::atexit(listener_cleanup);
+ }
int n = snprintf(path, UNIX_PATH_MAX, "%s/.java_pid%d",
os::get_temp_directory(), os::current_process_id());
@@ -485,6 +491,28 @@
return ret_code;
}
+bool AttachListener::check_socket_file() {
+ int ret;
+ struct stat st;
+ ret = stat(BsdAttachListener::path(), &st);
+ if (ret == -1) { // need to restart attach listener.
+ log_debug(attach)("Socket file %s does not exist - Restart Attach Listener",
+ BsdAttachListener::path());
+
+ listener_cleanup();
+
+ // wait to terminate current attach listener instance...
+
+ while (AttachListener::transit_state(AL_INITIALIZING,
+
+ AL_NOT_INITIALIZED) != AL_NOT_INITIALIZED) {
+ os::naked_yield();
+ }
+ return is_init_trigger();
+ }
+ return false;
+}
+
// Attach Listener is started lazily except in the case when
// +ReduseSignalUsage is used
bool AttachListener::init_at_startup() {
--- a/src/hotspot/os/linux/attachListener_linux.cpp Mon Jul 15 11:23:05 2019 -0400
+++ b/src/hotspot/os/linux/attachListener_linux.cpp Tue Jul 16 07:29:12 2019 +0900
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -69,17 +69,7 @@
// the file descriptor for the listening socket
static int _listener;
- static void set_path(char* path) {
- if (path == NULL) {
- _has_path = false;
- } else {
- strncpy(_path, path, UNIX_PATH_MAX);
- _path[UNIX_PATH_MAX-1] = '\0';
- _has_path = true;
- }
- }
-
- static void set_listener(int s) { _listener = s; }
+ static bool _atexit_registered;
// reads a request from the given connected socket
static LinuxAttachOperation* read_request(int s);
@@ -92,6 +82,19 @@
ATTACH_ERROR_BADVERSION = 101 // error codes
};
+ static void set_path(char* path) {
+ if (path == NULL) {
+ _path[0] = '\0';
+ _has_path = false;
+ } else {
+ strncpy(_path, path, UNIX_PATH_MAX);
+ _path[UNIX_PATH_MAX-1] = '\0';
+ _has_path = true;
+ }
+ }
+
+ static void set_listener(int s) { _listener = s; }
+
// initialize the listener, returns 0 if okay
static int init();
@@ -125,6 +128,7 @@
char LinuxAttachListener::_path[UNIX_PATH_MAX];
bool LinuxAttachListener::_has_path;
int LinuxAttachListener::_listener = -1;
+bool LinuxAttachListener::_atexit_registered = false;
// Supporting class to help split a buffer into individual components
class ArgumentIterator : public StackObj {
@@ -159,16 +163,15 @@
// bound too.
extern "C" {
static void listener_cleanup() {
- static int cleanup_done;
- if (!cleanup_done) {
- cleanup_done = 1;
- int s = LinuxAttachListener::listener();
- if (s != -1) {
- ::close(s);
- }
- if (LinuxAttachListener::has_path()) {
- ::unlink(LinuxAttachListener::path());
- }
+ int s = LinuxAttachListener::listener();
+ if (s != -1) {
+ LinuxAttachListener::set_listener(-1);
+ ::shutdown(s, SHUT_RDWR);
+ ::close(s);
+ }
+ if (LinuxAttachListener::has_path()) {
+ ::unlink(LinuxAttachListener::path());
+ LinuxAttachListener::set_path(NULL);
}
}
}
@@ -181,7 +184,10 @@
int listener; // listener socket (file descriptor)
// register function to cleanup
- ::atexit(listener_cleanup);
+ if (!_atexit_registered) {
+ _atexit_registered = true;
+ ::atexit(listener_cleanup);
+ }
int n = snprintf(path, UNIX_PATH_MAX, "%s/.java_pid%d",
os::get_temp_directory(), os::current_process_id());
@@ -485,6 +491,26 @@
return ret_code;
}
+bool AttachListener::check_socket_file() {
+ int ret;
+ struct stat64 st;
+ ret = stat64(LinuxAttachListener::path(), &st);
+ if (ret == -1) { // need to restart attach listener.
+ log_debug(attach)("Socket file %s does not exist - Restart Attach Listener",
+ LinuxAttachListener::path());
+
+ listener_cleanup();
+
+ // wait to terminate current attach listener instance...
+ while (AttachListener::transit_state(AL_INITIALIZING,
+ AL_NOT_INITIALIZED) != AL_NOT_INITIALIZED) {
+ os::naked_yield();
+ }
+ return is_init_trigger();
+ }
+ return false;
+}
+
// Attach Listener is started lazily except in the case when
// +ReduseSignalUsage is used
bool AttachListener::init_at_startup() {
--- a/src/hotspot/os/solaris/attachListener_solaris.cpp Mon Jul 15 11:23:05 2019 -0400
+++ b/src/hotspot/os/solaris/attachListener_solaris.cpp Tue Jul 16 07:29:12 2019 +0900
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -75,17 +75,7 @@
// door descriptor returned by door_create
static int _door_descriptor;
- static void set_door_path(char* path) {
- if (path == NULL) {
- _has_door_path = false;
- } else {
- strncpy(_door_path, path, PATH_MAX);
- _door_path[PATH_MAX] = '\0'; // ensure it's nul terminated
- _has_door_path = true;
- }
- }
-
- static void set_door_descriptor(int dd) { _door_descriptor = dd; }
+ static bool _atexit_registered;
// mutex to protect operation list
static mutex_t _mutex;
@@ -121,6 +111,19 @@
ATTACH_ERROR_DENIED = 104
};
+ static void set_door_path(char* path) {
+ if (path == NULL) {
+ _door_path[0] = '\0';
+ _has_door_path = false;
+ } else {
+ strncpy(_door_path, path, PATH_MAX);
+ _door_path[PATH_MAX] = '\0'; // ensure it's nul terminated
+ _has_door_path = true;
+ }
+ }
+
+ static void set_door_descriptor(int dd) { _door_descriptor = dd; }
+
// initialize the listener
static int init();
@@ -169,6 +172,7 @@
char SolarisAttachListener::_door_path[PATH_MAX+1];
volatile bool SolarisAttachListener::_has_door_path;
int SolarisAttachListener::_door_descriptor = -1;
+bool SolarisAttachListener::_atexit_registered = false;
mutex_t SolarisAttachListener::_mutex;
sema_t SolarisAttachListener::_wakeup;
SolarisAttachOperation* SolarisAttachListener::_head = NULL;
@@ -364,18 +368,16 @@
// atexit hook to detach the door and remove the file
extern "C" {
static void listener_cleanup() {
- static int cleanup_done;
- if (!cleanup_done) {
- cleanup_done = 1;
- int dd = SolarisAttachListener::door_descriptor();
- if (dd >= 0) {
- ::close(dd);
- }
- if (SolarisAttachListener::has_door_path()) {
- char* path = SolarisAttachListener::door_path();
- ::fdetach(path);
- ::unlink(path);
- }
+ int dd = SolarisAttachListener::door_descriptor();
+ if (dd >= 0) {
+ SolarisAttachListener::set_door_descriptor(-1);
+ ::close(dd);
+ }
+ if (SolarisAttachListener::has_door_path()) {
+ char* path = SolarisAttachListener::door_path();
+ ::fdetach(path);
+ ::unlink(path);
+ SolarisAttachListener::set_door_path(NULL);
}
}
}
@@ -387,7 +389,10 @@
int fd, res;
// register exit function
- ::atexit(listener_cleanup);
+ if (!_atexit_registered) {
+ _atexit_registered = true;
+ ::atexit(listener_cleanup);
+ }
// create the door descriptor
int dd = ::door_create(enqueue_proc, NULL, 0);
@@ -643,6 +648,26 @@
}
}
+bool AttachListener::check_socket_file() {
+ int ret;
+ struct stat64 st;
+ ret = stat64(SolarisAttachListener::door_path(), &st);
+ if (ret == -1) { // need to restart attach listener.
+ log_debug(attach)("Door file %s does not exist - Restart Attach Listener",
+ SolarisAttachListener::door_path());
+
+ listener_cleanup();
+
+ // wait to terminate current attach listener instance...
+ while (AttachListener::transit_state(AL_INITIALIZING,
+ AL_NOT_INITIALIZED) != AL_NOT_INITIALIZED) {
+ os::naked_yield();
+ }
+ return is_init_trigger();
+ }
+ return false;
+}
+
// If the file .attach_pid<pid> exists in the working directory
// or /tmp then this is the trigger to start the attach mechanism
bool AttachListener::is_init_trigger() {
--- a/src/hotspot/os/windows/attachListener_windows.cpp Mon Jul 15 11:23:05 2019 -0400
+++ b/src/hotspot/os/windows/attachListener_windows.cpp Tue Jul 16 07:29:12 2019 +0900
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -383,6 +383,12 @@
return Win32AttachListener::init();
}
+// This function is used for Un*x OSes only.
+// We need not to implement it for Windows.
+bool AttachListener::check_socket_file() {
+ return false;
+}
+
bool AttachListener::init_at_startup() {
return true;
}
--- a/src/hotspot/share/runtime/os.cpp Mon Jul 15 11:23:05 2019 -0400
+++ b/src/hotspot/share/runtime/os.cpp Tue Jul 16 07:29:12 2019 +0900
@@ -362,8 +362,25 @@
case SIGBREAK: {
// Check if the signal is a trigger to start the Attach Listener - in that
// case don't print stack traces.
- if (!DisableAttachMechanism && AttachListener::is_init_trigger()) {
- continue;
+ if (!DisableAttachMechanism) {
+ // Attempt to transit state to AL_INITIALIZING.
+ AttachListenerState cur_state = AttachListener::transit_state(AL_INITIALIZING, AL_NOT_INITIALIZED);
+ if (cur_state == AL_INITIALIZING) {
+ // Attach Listener has been started to initialize. Ignore this signal.
+ continue;
+ } else if (cur_state == AL_NOT_INITIALIZED) {
+ // Start to initialize.
+ if (!AttachListener::is_init_trigger()) {
+ // Attach Listener could not be started.
+ // So we need to transit the state to AL_NOT_INITIALIZED.
+ AttachListener::set_state(AL_NOT_INITIALIZED);
+ }
+ continue;
+ } else if (AttachListener::check_socket_file()) {
+ // Attach Listener has been started, but unix domain socket file
+ // does not exist. So restart Attach Listener.
+ continue;
+ }
}
// Print stack traces
// Any SIGBREAK operations added here should make sure to flush
--- a/src/hotspot/share/services/attachListener.cpp Mon Jul 15 11:23:05 2019 -0400
+++ b/src/hotspot/share/services/attachListener.cpp Tue Jul 16 07:29:12 2019 +0900
@@ -45,7 +45,7 @@
#include "utilities/debug.hpp"
#include "utilities/formatBuffer.hpp"
-volatile bool AttachListener::_initialized;
+volatile AttachListenerState AttachListener::_state = AL_NOT_INITIALIZED;
// Implementation of "properties" command.
//
@@ -372,6 +372,7 @@
"Should already be setup");
if (AttachListener::pd_init() != 0) {
+ AttachListener::set_state(AL_NOT_INITIALIZED);
return;
}
AttachListener::set_initialized();
@@ -379,6 +380,7 @@
for (;;) {
AttachOperation* op = AttachListener::dequeue();
if (op == NULL) {
+ AttachListener::set_state(AL_NOT_INITIALIZED);
return; // dequeue failed or shutdown
}
@@ -422,6 +424,8 @@
// operation complete - send result and output to client
op->complete(res, &st);
}
+
+ ShouldNotReachHere();
}
bool AttachListener::has_init_error(TRAPS) {
@@ -445,6 +449,7 @@
const char thread_name[] = "Attach Listener";
Handle string = java_lang_String::create_from_str(thread_name, THREAD);
if (has_init_error(THREAD)) {
+ set_state(AL_NOT_INITIALIZED);
return;
}
@@ -456,6 +461,7 @@
string,
THREAD);
if (has_init_error(THREAD)) {
+ set_state(AL_NOT_INITIALIZED);
return;
}
@@ -469,6 +475,7 @@
thread_oop,
THREAD);
if (has_init_error(THREAD)) {
+ set_state(AL_NOT_INITIALIZED);
return;
}
--- a/src/hotspot/share/services/attachListener.hpp Mon Jul 15 11:23:05 2019 -0400
+++ b/src/hotspot/share/services/attachListener.hpp Tue Jul 16 07:29:12 2019 +0900
@@ -26,6 +26,8 @@
#define SHARE_SERVICES_ATTACHLISTENER_HPP
#include "memory/allocation.hpp"
+#include "metaprogramming/isRegisteredEnum.hpp"
+#include "runtime/atomic.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
@@ -49,6 +51,14 @@
AttachOperationFunction func;
};
+enum AttachListenerState {
+ AL_NOT_INITIALIZED,
+ AL_INITIALIZING,
+ AL_INITIALIZED
+};
+
+template<> struct IsRegisteredEnum<AttachListenerState> : public TrueType {};
+
class AttachListener: AllStatic {
public:
static void vm_start() NOT_SERVICES_RETURN;
@@ -58,6 +68,9 @@
// invoke to perform clean-up tasks when all clients detach
static void detachall() NOT_SERVICES_RETURN;
+ // check unix domain socket file on filesystem
+ static bool check_socket_file() NOT_SERVICES_RETURN_(false);
+
// indicates if the Attach Listener needs to be created at startup
static bool init_at_startup() NOT_SERVICES_RETURN_(false);
@@ -67,12 +80,31 @@
#if !INCLUDE_SERVICES
static bool is_attach_supported() { return false; }
#else
+
private:
- static volatile bool _initialized;
+ static volatile AttachListenerState _state;
public:
- static bool is_initialized() { return _initialized; }
- static void set_initialized() { _initialized = true; }
+ static void set_state(AttachListenerState new_state) {
+ Atomic::store(new_state, &_state);
+ }
+
+ static AttachListenerState get_state() {
+ return Atomic::load(&_state);
+ }
+
+ static AttachListenerState transit_state(AttachListenerState new_state,
+ AttachListenerState cmp_state) {
+ return Atomic::cmpxchg(new_state, &_state, cmp_state);
+ }
+
+ static bool is_initialized() {
+ return Atomic::load(&_state) == AL_INITIALIZED;
+ }
+
+ static void set_initialized() {
+ Atomic::store(AL_INITIALIZED, &_state);
+ }
// indicates if this VM supports attach-on-demand
static bool is_attach_supported() { return !DisableAttachMechanism; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/attach/ConcAttachTest.java Tue Jul 16 07:29:12 2019 +0900
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2019, 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 8225690
+ * @requires os.family != "windows"
+ * @library /test/lib
+ * @modules jdk.attach/com.sun.tools.attach
+ * @run main ConcAttachTest
+ */
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import com.sun.tools.attach.VirtualMachine;
+import com.sun.tools.attach.AttachNotSupportedException;
+
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class ConcAttachTest implements Runnable {
+
+ private static final int NUM_CONC_REQUESTS = 100;
+
+ private static final int THREAD_POOL_TIMEOUT_IN_SEC = 30;
+
+ private static CountDownLatch latch;
+
+ private static String strPID;
+
+ // Attach to LingeredApp concurrently.
+ public void run() {
+ VirtualMachine vm = null;
+
+ try {
+ latch.countDown();
+ latch.await();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ try {
+ vm = VirtualMachine.attach(strPID);
+ } catch (AttachNotSupportedException | IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ try {
+ vm.detach();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private static void checkAttachListenerThread() throws InterruptedException, IOException {
+ JDKToolLauncher jcmd = JDKToolLauncher.createUsingTestJDK("jcmd");
+ jcmd.addToolArg(strPID);
+ jcmd.addToolArg("Thread.print");
+
+ ProcessBuilder pb = new ProcessBuilder(jcmd.getCommand());
+ Process jcmdProc = pb.start();
+
+ OutputAnalyzer out = new OutputAnalyzer(jcmdProc);
+
+ jcmdProc.waitFor();
+
+ System.out.println(out.getStdout());
+ System.err.println(out.getStderr());
+
+ long numOfAttachListener = out.asLines()
+ .stream()
+ .filter(l -> l.contains("Attach Listener"))
+ .count();
+
+ Asserts.assertEquals(1L, numOfAttachListener, "AttachListener should exist only 1 thread.");
+ }
+
+ public static void main(String... args) throws Exception {
+ LingeredApp app = null;
+ latch = new CountDownLatch(NUM_CONC_REQUESTS);
+ ExecutorService pool = Executors.newFixedThreadPool(NUM_CONC_REQUESTS);
+
+ try {
+ app = LingeredApp.startApp();
+ strPID = Long.toString(app.getPid());
+
+ for (int i = 0; i < NUM_CONC_REQUESTS; i++) {
+ pool.submit(new ConcAttachTest());
+ }
+
+ pool.shutdown();
+ pool.awaitTermination(THREAD_POOL_TIMEOUT_IN_SEC, TimeUnit.SECONDS);
+
+ checkAttachListenerThread();
+ } finally {
+ LingeredApp.stopApp(app);
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/serviceability/attach/RemovingUnixDomainSocketTest.java Tue Jul 16 07:29:12 2019 +0900
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019, 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 8225193
+ * @requires os.family != "windows"
+ * @library /test/lib
+ * @run main RemovingUnixDomainSocketTest
+ */
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class RemovingUnixDomainSocketTest {
+
+ private static void runJCmd(long pid) throws InterruptedException, IOException {
+ JDKToolLauncher jcmd = JDKToolLauncher.createUsingTestJDK("jcmd");
+ jcmd.addToolArg(Long.toString(pid));
+ jcmd.addToolArg("VM.version");
+
+ ProcessBuilder pb = new ProcessBuilder(jcmd.getCommand());
+ Process jcmdProc = pb.start();
+
+ OutputAnalyzer out = new OutputAnalyzer(jcmdProc);
+
+ jcmdProc.waitFor();
+
+ System.out.println(out.getStdout());
+ System.err.println(out.getStderr());
+
+ out.stderrShouldBeEmpty();
+ }
+
+ public static void main(String... args) throws Exception {
+ LingeredApp app = null;
+ try {
+ app = LingeredApp.startApp();
+
+ // Access to Attach Listener
+ runJCmd(app.getPid());
+
+ // Remove unix domain socket file
+ var sockFile = Path.of(System.getProperty("java.io.tmpdir"),
+ ".java_pid" + app.getPid())
+ .toFile();
+ System.out.println("Remove " + sockFile.toString());
+ sockFile.delete();
+
+ // Access to Attach Listener again
+ runJCmd(app.getPid());
+ } finally {
+ LingeredApp.stopApp(app);
+ }
+ }
+
+}