8225690: Multiple AttachListener threads can be created
authorysuenaga
Tue, 16 Jul 2019 07:29:12 +0900
changeset 55682 70fab3a8ff02
parent 55681 7b671e6b0d5b
child 55683 b528b724b16d
8225690: Multiple AttachListener threads can be created Reviewed-by: sspitsyn, cjplummer
src/hotspot/os/aix/attachListener_aix.cpp
src/hotspot/os/bsd/attachListener_bsd.cpp
src/hotspot/os/linux/attachListener_linux.cpp
src/hotspot/os/solaris/attachListener_solaris.cpp
src/hotspot/os/windows/attachListener_windows.cpp
src/hotspot/share/runtime/os.cpp
src/hotspot/share/services/attachListener.cpp
src/hotspot/share/services/attachListener.hpp
test/hotspot/jtreg/serviceability/attach/ConcAttachTest.java
test/hotspot/jtreg/serviceability/attach/RemovingUnixDomainSocketTest.java
--- 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);
+        }
+    }
+
+}