8225690: Multiple AttachListener threads can be created
Reviewed-by: sspitsyn, cjplummer
This commit is contained in:
parent
5b05ea5a02
commit
2870c9d55e
@ -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 @@ class AixAttachListener: AllStatic {
|
||||
// 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 @@ class AixAttachListener: AllStatic {
|
||||
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 @@ class AixAttachOperation: public AttachOperation {
|
||||
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 @@ class ArgumentIterator : public StackObj {
|
||||
// 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 AixAttachListener::init() {
|
||||
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 @@ int AttachListener::pd_init() {
|
||||
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() {
|
||||
|
@ -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 @@ class BsdAttachListener: AllStatic {
|
||||
// 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 @@ class BsdAttachListener: AllStatic {
|
||||
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 @@ class BsdAttachOperation: public AttachOperation {
|
||||
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 @@ class ArgumentIterator : public StackObj {
|
||||
// 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 BsdAttachListener::init() {
|
||||
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 @@ int AttachListener::pd_init() {
|
||||
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() {
|
||||
|
@ -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 @@ class LinuxAttachListener: AllStatic {
|
||||
// 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 @@ class LinuxAttachListener: AllStatic {
|
||||
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 @@ class LinuxAttachOperation: public AttachOperation {
|
||||
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 @@ class ArgumentIterator : public StackObj {
|
||||
// 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 LinuxAttachListener::init() {
|
||||
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 @@ int AttachListener::pd_init() {
|
||||
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() {
|
||||
|
@ -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 @@ class SolarisAttachListener: AllStatic {
|
||||
// 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 @@ class SolarisAttachListener: AllStatic {
|
||||
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 @@ class SolarisAttachOperation: public AttachOperation {
|
||||
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 @@ extern "C" {
|
||||
// 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 SolarisAttachListener::create_door() {
|
||||
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::init_at_startup() {
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -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 @@ int AttachListener::pd_init() {
|
||||
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;
|
||||
}
|
||||
|
@ -362,8 +362,25 @@ static void signal_thread_entry(JavaThread* thread, TRAPS) {
|
||||
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
|
||||
|
@ -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 @@ static void attach_listener_thread_entry(JavaThread* thread, TRAPS) {
|
||||
"Should already be setup");
|
||||
|
||||
if (AttachListener::pd_init() != 0) {
|
||||
AttachListener::set_state(AL_NOT_INITIALIZED);
|
||||
return;
|
||||
}
|
||||
AttachListener::set_initialized();
|
||||
@ -379,6 +380,7 @@ static void attach_listener_thread_entry(JavaThread* thread, TRAPS) {
|
||||
for (;;) {
|
||||
AttachOperation* op = AttachListener::dequeue();
|
||||
if (op == NULL) {
|
||||
AttachListener::set_state(AL_NOT_INITIALIZED);
|
||||
return; // dequeue failed or shutdown
|
||||
}
|
||||
|
||||
@ -422,6 +424,8 @@ static void attach_listener_thread_entry(JavaThread* thread, TRAPS) {
|
||||
// operation complete - send result and output to client
|
||||
op->complete(res, &st);
|
||||
}
|
||||
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
bool AttachListener::has_init_error(TRAPS) {
|
||||
@ -445,6 +449,7 @@ void AttachListener::init() {
|
||||
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 @@ void AttachListener::init() {
|
||||
string,
|
||||
THREAD);
|
||||
if (has_init_error(THREAD)) {
|
||||
set_state(AL_NOT_INITIALIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -469,6 +475,7 @@ void AttachListener::init() {
|
||||
thread_oop,
|
||||
THREAD);
|
||||
if (has_init_error(THREAD)) {
|
||||
set_state(AL_NOT_INITIALIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -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 @@ struct AttachOperationFunctionInfo {
|
||||
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 @@ class AttachListener: AllStatic {
|
||||
// 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 @@ class AttachListener: AllStatic {
|
||||
#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; }
|
||||
|
126
test/hotspot/jtreg/serviceability/attach/ConcAttachTest.java
Normal file
126
test/hotspot/jtreg/serviceability/attach/ConcAttachTest.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user