mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-17 01:24:33 +02:00
8212107: VMThread issues and cleanup
Reviewed-by: shade, dcubed, coleenp, dholmes, redestad
This commit is contained in:
parent
6bddeb709d
commit
431338bcb3
8 changed files with 236 additions and 349 deletions
|
@ -454,12 +454,8 @@ static bool prepare_for_emergency_dump(Thread* thread) {
|
||||||
Heap_lock->unlock();
|
Heap_lock->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (VMOperationQueue_lock->owned_by_self()) {
|
if (VMOperation_lock->owned_by_self()) {
|
||||||
VMOperationQueue_lock->unlock();
|
VMOperation_lock->unlock();
|
||||||
}
|
|
||||||
|
|
||||||
if (VMOperationRequest_lock->owned_by_self()) {
|
|
||||||
VMOperationRequest_lock->unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Service_lock->owned_by_self()) {
|
if (Service_lock->owned_by_self()) {
|
||||||
|
|
|
@ -65,8 +65,7 @@ Monitor* CodeSweeper_lock = NULL;
|
||||||
Mutex* MethodData_lock = NULL;
|
Mutex* MethodData_lock = NULL;
|
||||||
Mutex* TouchedMethodLog_lock = NULL;
|
Mutex* TouchedMethodLog_lock = NULL;
|
||||||
Mutex* RetData_lock = NULL;
|
Mutex* RetData_lock = NULL;
|
||||||
Monitor* VMOperationQueue_lock = NULL;
|
Monitor* VMOperation_lock = NULL;
|
||||||
Monitor* VMOperationRequest_lock = NULL;
|
|
||||||
Monitor* Threads_lock = NULL;
|
Monitor* Threads_lock = NULL;
|
||||||
Mutex* NonJavaThreadsList_lock = NULL;
|
Mutex* NonJavaThreadsList_lock = NULL;
|
||||||
Mutex* NonJavaThreadsListSync_lock = NULL;
|
Mutex* NonJavaThreadsListSync_lock = NULL;
|
||||||
|
@ -280,8 +279,7 @@ void mutex_init() {
|
||||||
def(NonJavaThreadsList_lock , PaddedMutex, barrier, true, _safepoint_check_never);
|
def(NonJavaThreadsList_lock , PaddedMutex, barrier, true, _safepoint_check_never);
|
||||||
def(NonJavaThreadsListSync_lock , PaddedMutex, leaf, true, _safepoint_check_never);
|
def(NonJavaThreadsListSync_lock , PaddedMutex, leaf, true, _safepoint_check_never);
|
||||||
|
|
||||||
def(VMOperationQueue_lock , PaddedMonitor, nonleaf, true, _safepoint_check_never); // VM_thread allowed to block on these
|
def(VMOperation_lock , PaddedMonitor, nonleaf, true, _safepoint_check_always); // VM_thread allowed to block on these
|
||||||
def(VMOperationRequest_lock , PaddedMonitor, nonleaf, true, _safepoint_check_always);
|
|
||||||
def(RetData_lock , PaddedMutex , nonleaf, false, _safepoint_check_always);
|
def(RetData_lock , PaddedMutex , nonleaf, false, _safepoint_check_always);
|
||||||
def(Terminator_lock , PaddedMonitor, nonleaf, true, _safepoint_check_always);
|
def(Terminator_lock , PaddedMonitor, nonleaf, true, _safepoint_check_always);
|
||||||
def(InitCompleted_lock , PaddedMonitor, leaf, true, _safepoint_check_never);
|
def(InitCompleted_lock , PaddedMonitor, leaf, true, _safepoint_check_never);
|
||||||
|
|
|
@ -58,8 +58,7 @@ extern Monitor* CodeSweeper_lock; // a lock used by the sweeper o
|
||||||
extern Mutex* MethodData_lock; // a lock on installation of method data
|
extern Mutex* MethodData_lock; // a lock on installation of method data
|
||||||
extern Mutex* TouchedMethodLog_lock; // a lock on allocation of LogExecutedMethods info
|
extern Mutex* TouchedMethodLog_lock; // a lock on allocation of LogExecutedMethods info
|
||||||
extern Mutex* RetData_lock; // a lock on installation of RetData inside method data
|
extern Mutex* RetData_lock; // a lock on installation of RetData inside method data
|
||||||
extern Monitor* VMOperationQueue_lock; // a lock on queue of vm_operations waiting to execute
|
extern Monitor* VMOperation_lock; // a lock on queue of vm_operations waiting to execute
|
||||||
extern Monitor* VMOperationRequest_lock; // a lock on Threads waiting for a vm_operation to terminate
|
|
||||||
extern Monitor* Threads_lock; // a lock on the Threads table of active Java threads
|
extern Monitor* Threads_lock; // a lock on the Threads table of active Java threads
|
||||||
// (also used by Safepoints too to block threads creation/destruction)
|
// (also used by Safepoints too to block threads creation/destruction)
|
||||||
extern Mutex* NonJavaThreadsList_lock; // a lock on the NonJavaThreads list
|
extern Mutex* NonJavaThreadsList_lock; // a lock on the NonJavaThreads list
|
||||||
|
|
|
@ -255,8 +255,6 @@ Thread::Thread() {
|
||||||
NOT_PRODUCT(_skip_gcalot = false;)
|
NOT_PRODUCT(_skip_gcalot = false;)
|
||||||
_jvmti_env_iteration_count = 0;
|
_jvmti_env_iteration_count = 0;
|
||||||
set_allocated_bytes(0);
|
set_allocated_bytes(0);
|
||||||
_vm_operation_started_count = 0;
|
|
||||||
_vm_operation_completed_count = 0;
|
|
||||||
_current_pending_monitor = NULL;
|
_current_pending_monitor = NULL;
|
||||||
_current_pending_monitor_is_from_java = true;
|
_current_pending_monitor_is_from_java = true;
|
||||||
_current_waiting_monitor = NULL;
|
_current_waiting_monitor = NULL;
|
||||||
|
|
|
@ -401,9 +401,6 @@ class Thread: public ThreadShadow {
|
||||||
|
|
||||||
JFR_ONLY(DEFINE_THREAD_LOCAL_FIELD_JFR;) // Thread-local data for jfr
|
JFR_ONLY(DEFINE_THREAD_LOCAL_FIELD_JFR;) // Thread-local data for jfr
|
||||||
|
|
||||||
int _vm_operation_started_count; // VM_Operation support
|
|
||||||
int _vm_operation_completed_count; // VM_Operation support
|
|
||||||
|
|
||||||
ObjectMonitor* _current_pending_monitor; // ObjectMonitor this thread
|
ObjectMonitor* _current_pending_monitor; // ObjectMonitor this thread
|
||||||
// is waiting to lock
|
// is waiting to lock
|
||||||
bool _current_pending_monitor_is_from_java; // locking is from Java code
|
bool _current_pending_monitor_is_from_java; // locking is from Java code
|
||||||
|
@ -621,11 +618,6 @@ class Thread: public ThreadShadow {
|
||||||
|
|
||||||
bool is_trace_suspend() { return (_suspend_flags & _trace_flag) != 0; }
|
bool is_trace_suspend() { return (_suspend_flags & _trace_flag) != 0; }
|
||||||
|
|
||||||
// VM operation support
|
|
||||||
int vm_operation_ticket() { return ++_vm_operation_started_count; }
|
|
||||||
int vm_operation_completed_count() { return _vm_operation_completed_count; }
|
|
||||||
void increment_vm_operation_completed_count() { _vm_operation_completed_count++; }
|
|
||||||
|
|
||||||
// For tracking the heavyweight monitor the thread is pending on.
|
// For tracking the heavyweight monitor the thread is pending on.
|
||||||
ObjectMonitor* current_pending_monitor() {
|
ObjectMonitor* current_pending_monitor() {
|
||||||
return _current_pending_monitor;
|
return _current_pending_monitor;
|
||||||
|
|
|
@ -121,14 +121,12 @@ class VM_Operation : public StackObj {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Thread* _calling_thread;
|
Thread* _calling_thread;
|
||||||
VM_Operation* _next;
|
|
||||||
VM_Operation* _prev;
|
|
||||||
|
|
||||||
// The VM operation name array
|
// The VM operation name array
|
||||||
static const char* _names[];
|
static const char* _names[];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VM_Operation() : _calling_thread(NULL), _next(NULL), _prev(NULL) {}
|
VM_Operation() : _calling_thread(NULL) {}
|
||||||
|
|
||||||
// VM operation support (used by VM thread)
|
// VM operation support (used by VM thread)
|
||||||
Thread* calling_thread() const { return _calling_thread; }
|
Thread* calling_thread() const { return _calling_thread; }
|
||||||
|
@ -148,12 +146,6 @@ class VM_Operation : public StackObj {
|
||||||
virtual bool doit_prologue() { return true; };
|
virtual bool doit_prologue() { return true; };
|
||||||
virtual void doit_epilogue() {};
|
virtual void doit_epilogue() {};
|
||||||
|
|
||||||
// Linking
|
|
||||||
VM_Operation *next() const { return _next; }
|
|
||||||
VM_Operation *prev() const { return _prev; }
|
|
||||||
void set_next(VM_Operation *next) { _next = next; }
|
|
||||||
void set_prev(VM_Operation *prev) { _prev = prev; }
|
|
||||||
|
|
||||||
// Configuration. Override these appropriately in subclasses.
|
// Configuration. Override these appropriately in subclasses.
|
||||||
virtual VMOp_Type type() const = 0;
|
virtual VMOp_Type type() const = 0;
|
||||||
virtual bool allow_nested_vm_operations() const { return false; }
|
virtual bool allow_nested_vm_operations() const { return false; }
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
#include "compiler/compileBroker.hpp"
|
#include "compiler/compileBroker.hpp"
|
||||||
#include "gc/shared/collectedHeap.hpp"
|
|
||||||
#include "jfr/jfrEvents.hpp"
|
#include "jfr/jfrEvents.hpp"
|
||||||
#include "jfr/support/jfrThreadId.hpp"
|
#include "jfr/support/jfrThreadId.hpp"
|
||||||
#include "logging/log.hpp"
|
#include "logging/log.hpp"
|
||||||
|
@ -32,7 +31,6 @@
|
||||||
#include "logging/logConfiguration.hpp"
|
#include "logging/logConfiguration.hpp"
|
||||||
#include "memory/resourceArea.hpp"
|
#include "memory/resourceArea.hpp"
|
||||||
#include "memory/universe.hpp"
|
#include "memory/universe.hpp"
|
||||||
#include "oops/method.hpp"
|
|
||||||
#include "oops/oop.inline.hpp"
|
#include "oops/oop.inline.hpp"
|
||||||
#include "oops/verifyOopClosure.hpp"
|
#include "oops/verifyOopClosure.hpp"
|
||||||
#include "runtime/atomic.hpp"
|
#include "runtime/atomic.hpp"
|
||||||
|
@ -43,101 +41,13 @@
|
||||||
#include "runtime/safepoint.hpp"
|
#include "runtime/safepoint.hpp"
|
||||||
#include "runtime/synchronizer.hpp"
|
#include "runtime/synchronizer.hpp"
|
||||||
#include "runtime/thread.inline.hpp"
|
#include "runtime/thread.inline.hpp"
|
||||||
|
#include "runtime/timerTrace.hpp"
|
||||||
#include "runtime/vmThread.hpp"
|
#include "runtime/vmThread.hpp"
|
||||||
#include "runtime/vmOperations.hpp"
|
#include "runtime/vmOperations.hpp"
|
||||||
#include "services/runtimeService.hpp"
|
|
||||||
#include "utilities/dtrace.hpp"
|
#include "utilities/dtrace.hpp"
|
||||||
#include "utilities/events.hpp"
|
#include "utilities/events.hpp"
|
||||||
#include "utilities/vmError.hpp"
|
#include "utilities/vmError.hpp"
|
||||||
#include "utilities/xmlstream.hpp"
|
|
||||||
|
|
||||||
VM_QueueHead VMOperationQueue::_queue_head[VMOperationQueue::nof_priorities];
|
|
||||||
|
|
||||||
VMOperationQueue::VMOperationQueue() {
|
|
||||||
// The queue is a circular doubled-linked list, which always contains
|
|
||||||
// one element (i.e., one element means empty).
|
|
||||||
for(int i = 0; i < nof_priorities; i++) {
|
|
||||||
_queue_length[i] = 0;
|
|
||||||
_queue_counter = 0;
|
|
||||||
_queue[i] = &_queue_head[i];
|
|
||||||
_queue[i]->set_next(_queue[i]);
|
|
||||||
_queue[i]->set_prev(_queue[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool VMOperationQueue::queue_empty(int prio) {
|
|
||||||
// It is empty if there is exactly one element
|
|
||||||
bool empty = (_queue[prio] == _queue[prio]->next());
|
|
||||||
assert( (_queue_length[prio] == 0 && empty) ||
|
|
||||||
(_queue_length[prio] > 0 && !empty), "sanity check");
|
|
||||||
return _queue_length[prio] == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inserts an element to the right of the q element
|
|
||||||
void VMOperationQueue::insert(VM_Operation* q, VM_Operation* n) {
|
|
||||||
assert(q->next()->prev() == q && q->prev()->next() == q, "sanity check");
|
|
||||||
n->set_prev(q);
|
|
||||||
n->set_next(q->next());
|
|
||||||
q->next()->set_prev(n);
|
|
||||||
q->set_next(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VMOperationQueue::queue_add(int prio, VM_Operation *op) {
|
|
||||||
_queue_length[prio]++;
|
|
||||||
insert(_queue[prio]->prev(), op);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void VMOperationQueue::unlink(VM_Operation* q) {
|
|
||||||
assert(q->next()->prev() == q && q->prev()->next() == q, "sanity check");
|
|
||||||
q->prev()->set_next(q->next());
|
|
||||||
q->next()->set_prev(q->prev());
|
|
||||||
}
|
|
||||||
|
|
||||||
VM_Operation* VMOperationQueue::queue_remove_front(int prio) {
|
|
||||||
if (queue_empty(prio)) return NULL;
|
|
||||||
assert(_queue_length[prio] >= 0, "sanity check");
|
|
||||||
_queue_length[prio]--;
|
|
||||||
VM_Operation* r = _queue[prio]->next();
|
|
||||||
assert(r != _queue[prio], "cannot remove base element");
|
|
||||||
unlink(r);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------
|
|
||||||
// High-level interface
|
|
||||||
void VMOperationQueue::add(VM_Operation *op) {
|
|
||||||
|
|
||||||
HOTSPOT_VMOPS_REQUEST(
|
|
||||||
(char *) op->name(), strlen(op->name()),
|
|
||||||
op->evaluate_at_safepoint() ? 0 : 1);
|
|
||||||
|
|
||||||
// Encapsulates VM queue policy. Currently, that
|
|
||||||
// only involves putting them on the right list
|
|
||||||
queue_add(op->evaluate_at_safepoint() ? SafepointPriority : MediumPriority, op);
|
|
||||||
}
|
|
||||||
|
|
||||||
VM_Operation* VMOperationQueue::remove_next() {
|
|
||||||
// Assuming VMOperation queue is two-level priority queue. If there are
|
|
||||||
// more than two priorities, we need a different scheduling algorithm.
|
|
||||||
assert(SafepointPriority == 0 && MediumPriority == 1 && nof_priorities == 2,
|
|
||||||
"current algorithm does not work");
|
|
||||||
|
|
||||||
// simple counter based scheduling to prevent starvation of lower priority
|
|
||||||
// queue. -- see 4390175
|
|
||||||
int high_prio, low_prio;
|
|
||||||
if (_queue_counter++ < 10) {
|
|
||||||
high_prio = SafepointPriority;
|
|
||||||
low_prio = MediumPriority;
|
|
||||||
} else {
|
|
||||||
_queue_counter = 0;
|
|
||||||
high_prio = MediumPriority;
|
|
||||||
low_prio = SafepointPriority;
|
|
||||||
}
|
|
||||||
|
|
||||||
return queue_remove_front(queue_empty(high_prio) ? low_prio : high_prio);
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------
|
||||||
// Timeout machinery
|
// Timeout machinery
|
||||||
|
@ -169,12 +79,15 @@ void VMOperationTimeoutTask::disarm() {
|
||||||
//------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------
|
||||||
// Implementation of VMThread stuff
|
// Implementation of VMThread stuff
|
||||||
|
|
||||||
|
static VM_None safepointALot_op("SafepointALot");
|
||||||
|
static VM_Cleanup cleanup_op;
|
||||||
|
|
||||||
bool VMThread::_should_terminate = false;
|
bool VMThread::_should_terminate = false;
|
||||||
bool VMThread::_terminated = false;
|
bool VMThread::_terminated = false;
|
||||||
Monitor* VMThread::_terminate_lock = NULL;
|
Monitor* VMThread::_terminate_lock = NULL;
|
||||||
VMThread* VMThread::_vm_thread = NULL;
|
VMThread* VMThread::_vm_thread = NULL;
|
||||||
VM_Operation* VMThread::_cur_vm_operation = NULL;
|
VM_Operation* VMThread::_cur_vm_operation = NULL;
|
||||||
VMOperationQueue* VMThread::_vm_queue = NULL;
|
VM_Operation* VMThread::_next_vm_operation = &cleanup_op; // Prevent any thread from setting an operation until VM thread is ready.
|
||||||
PerfCounter* VMThread::_perf_accumulated_vm_operation_time = NULL;
|
PerfCounter* VMThread::_perf_accumulated_vm_operation_time = NULL;
|
||||||
VMOperationTimeoutTask* VMThread::_timeout_task = NULL;
|
VMOperationTimeoutTask* VMThread::_timeout_task = NULL;
|
||||||
|
|
||||||
|
@ -198,10 +111,6 @@ void VMThread::create() {
|
||||||
assert(_timeout_task == NULL, "sanity");
|
assert(_timeout_task == NULL, "sanity");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create VM operation queue
|
|
||||||
_vm_queue = new VMOperationQueue();
|
|
||||||
guarantee(_vm_queue != NULL, "just checking");
|
|
||||||
|
|
||||||
_terminate_lock = new Monitor(Mutex::safepoint, "VMThread::_terminate_lock", true,
|
_terminate_lock = new Monitor(Mutex::safepoint, "VMThread::_terminate_lock", true,
|
||||||
Monitor::_safepoint_check_never);
|
Monitor::_safepoint_check_never);
|
||||||
|
|
||||||
|
@ -310,9 +219,10 @@ void VMThread::run() {
|
||||||
// and wait until operation is performed.
|
// and wait until operation is performed.
|
||||||
void VMThread::wait_for_vm_thread_exit() {
|
void VMThread::wait_for_vm_thread_exit() {
|
||||||
assert(JavaThread::current()->is_terminated(), "Should be terminated");
|
assert(JavaThread::current()->is_terminated(), "Should be terminated");
|
||||||
{ MonitorLocker mu(VMOperationQueue_lock, Mutex::_no_safepoint_check_flag);
|
{
|
||||||
|
MonitorLocker mu(VMOperation_lock);
|
||||||
_should_terminate = true;
|
_should_terminate = true;
|
||||||
mu.notify();
|
mu.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: VM thread leaves at Safepoint. We are not stopped by Safepoint
|
// Note: VM thread leaves at Safepoint. We are not stopped by Safepoint
|
||||||
|
@ -324,8 +234,9 @@ void VMThread::wait_for_vm_thread_exit() {
|
||||||
// Note: it should be OK to use Terminator_lock here. But this is called
|
// Note: it should be OK to use Terminator_lock here. But this is called
|
||||||
// at a very delicate time (VM shutdown) and we are operating in non- VM
|
// at a very delicate time (VM shutdown) and we are operating in non- VM
|
||||||
// thread at Safepoint. It's safer to not share lock with other threads.
|
// thread at Safepoint. It's safer to not share lock with other threads.
|
||||||
{ MonitorLocker ml(_terminate_lock, Mutex::_no_safepoint_check_flag);
|
{
|
||||||
while(!VMThread::is_terminated()) {
|
MonitorLocker ml(_terminate_lock, Mutex::_no_safepoint_check_flag);
|
||||||
|
while (!VMThread::is_terminated()) {
|
||||||
ml.wait();
|
ml.wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -364,13 +275,8 @@ void VMThread::evaluate_operation(VM_Operation* op) {
|
||||||
op->evaluate_at_safepoint() ? 0 : 1);
|
op->evaluate_at_safepoint() ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark as completed
|
|
||||||
op->calling_thread()->increment_vm_operation_completed_count();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static VM_None safepointALot_op("SafepointALot");
|
|
||||||
static VM_Cleanup cleanup_op;
|
|
||||||
|
|
||||||
class HandshakeALotClosure : public HandshakeClosure {
|
class HandshakeALotClosure : public HandshakeClosure {
|
||||||
public:
|
public:
|
||||||
HandshakeALotClosure() : HandshakeClosure("HandshakeALot") {}
|
HandshakeALotClosure() : HandshakeClosure("HandshakeALot") {}
|
||||||
|
@ -381,24 +287,180 @@ class HandshakeALotClosure : public HandshakeClosure {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
VM_Operation* VMThread::no_op_safepoint() {
|
bool VMThread::handshake_alot() {
|
||||||
// Check for handshakes first since we may need to return a VMop.
|
assert(_cur_vm_operation == NULL, "should not have an op yet");
|
||||||
if (HandshakeALot) {
|
assert(_next_vm_operation == NULL, "should not have an op yet");
|
||||||
HandshakeALotClosure hal_cl;
|
if (!HandshakeALot) {
|
||||||
Handshake::execute(&hal_cl);
|
return false;
|
||||||
}
|
}
|
||||||
|
static jlong last_halot_ms = 0;
|
||||||
|
jlong now_ms = nanos_to_millis(os::javaTimeNanos());
|
||||||
|
// If only HandshakeALot is set, but GuaranteedSafepointInterval is 0,
|
||||||
|
// we emit a handshake if it's been more than a second since the last one.
|
||||||
|
jlong interval = GuaranteedSafepointInterval != 0 ? GuaranteedSafepointInterval : 1000;
|
||||||
|
jlong deadline_ms = interval + last_halot_ms;
|
||||||
|
if (now_ms > deadline_ms) {
|
||||||
|
last_halot_ms = now_ms;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMThread::setup_periodic_safepoint_if_needed() {
|
||||||
|
assert(_cur_vm_operation == NULL, "Already have an op");
|
||||||
|
assert(_next_vm_operation == NULL, "Already have an op");
|
||||||
// Check for a cleanup before SafepointALot to keep stats correct.
|
// Check for a cleanup before SafepointALot to keep stats correct.
|
||||||
long interval_ms = SafepointTracing::time_since_last_safepoint_ms();
|
long interval_ms = SafepointTracing::time_since_last_safepoint_ms();
|
||||||
bool max_time_exceeded = GuaranteedSafepointInterval != 0 &&
|
bool max_time_exceeded = GuaranteedSafepointInterval != 0 &&
|
||||||
(interval_ms >= GuaranteedSafepointInterval);
|
(interval_ms >= GuaranteedSafepointInterval);
|
||||||
if (max_time_exceeded && SafepointSynchronize::is_cleanup_needed()) {
|
if (!max_time_exceeded) {
|
||||||
return &cleanup_op;
|
return;
|
||||||
}
|
}
|
||||||
if (SafepointALot) {
|
if (SafepointSynchronize::is_cleanup_needed()) {
|
||||||
return &safepointALot_op;
|
_next_vm_operation = &cleanup_op;
|
||||||
|
} else if (SafepointALot) {
|
||||||
|
_next_vm_operation = &safepointALot_op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMThread::set_next_operation(VM_Operation *op) {
|
||||||
|
if (_next_vm_operation != NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
log_debug(vmthread)("Adding VM operation: %s", op->name());
|
||||||
|
|
||||||
|
_next_vm_operation = op;
|
||||||
|
|
||||||
|
HOTSPOT_VMOPS_REQUEST(
|
||||||
|
(char *) op->name(), strlen(op->name()),
|
||||||
|
op->evaluate_at_safepoint() ? 0 : 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMThread::wait_until_executed(VM_Operation* op) {
|
||||||
|
MonitorLocker ml(VMOperation_lock,
|
||||||
|
Thread::current()->is_Java_thread() ?
|
||||||
|
Mutex::_safepoint_check_flag :
|
||||||
|
Mutex::_no_safepoint_check_flag);
|
||||||
|
{
|
||||||
|
TraceTime timer("Installing VM operation", TRACETIME_LOG(Trace, vmthread));
|
||||||
|
while (true) {
|
||||||
|
if (VMThread::vm_thread()->set_next_operation(op)) {
|
||||||
|
ml.notify_all();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Wait to install this operation as the next operation in the VM Thread
|
||||||
|
log_trace(vmthread)("A VM operation already set, waiting");
|
||||||
|
ml.wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Wait until the operation has been processed
|
||||||
|
TraceTime timer("Waiting for VM operation to be completed", TRACETIME_LOG(Trace, vmthread));
|
||||||
|
// _next_vm_operation is cleared holding VMOperation_lock after it has been
|
||||||
|
// executed. We wait until _next_vm_operation is not our op.
|
||||||
|
while (_next_vm_operation == op) {
|
||||||
|
// VM Thread can process it once we unlock the mutex on wait.
|
||||||
|
ml.wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void self_destruct_if_needed() {
|
||||||
|
// Support for self destruction
|
||||||
|
if ((SelfDestructTimer != 0) && !VMError::is_error_reported() &&
|
||||||
|
(os::elapsedTime() > (double)SelfDestructTimer * 60.0)) {
|
||||||
|
tty->print_cr("VM self-destructed");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMThread::inner_execute(VM_Operation* op) {
|
||||||
|
assert(Thread::current()->is_VM_thread(), "Must be the VM thread");
|
||||||
|
|
||||||
|
VM_Operation* prev_vm_operation = NULL;
|
||||||
|
if (_cur_vm_operation != NULL) {
|
||||||
|
// Check that the VM operation allows nested VM operation.
|
||||||
|
// This is normally not the case, e.g., the compiler
|
||||||
|
// does not allow nested scavenges or compiles.
|
||||||
|
if (!_cur_vm_operation->allow_nested_vm_operations()) {
|
||||||
|
fatal("Unexpected nested VM operation %s requested by operation %s",
|
||||||
|
op->name(), _cur_vm_operation->name());
|
||||||
|
}
|
||||||
|
op->set_calling_thread(_cur_vm_operation->calling_thread());
|
||||||
|
prev_vm_operation = _cur_vm_operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cur_vm_operation = op;
|
||||||
|
|
||||||
|
HandleMark hm(VMThread::vm_thread());
|
||||||
|
EventMark em("Executing %s VM operation: %s", prev_vm_operation != NULL ? "nested" : "", op->name());
|
||||||
|
|
||||||
|
log_debug(vmthread)("Evaluating %s %s VM operation: %s",
|
||||||
|
prev_vm_operation != NULL ? "nested" : "",
|
||||||
|
_cur_vm_operation->evaluate_at_safepoint() ? "safepoint" : "non-safepoint",
|
||||||
|
_cur_vm_operation->name());
|
||||||
|
|
||||||
|
bool end_safepoint = false;
|
||||||
|
if (_cur_vm_operation->evaluate_at_safepoint() &&
|
||||||
|
!SafepointSynchronize::is_at_safepoint()) {
|
||||||
|
SafepointSynchronize::begin();
|
||||||
|
if (_timeout_task != NULL) {
|
||||||
|
_timeout_task->arm();
|
||||||
|
}
|
||||||
|
end_safepoint = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluate_operation(_cur_vm_operation);
|
||||||
|
|
||||||
|
if (end_safepoint) {
|
||||||
|
if (_timeout_task != NULL) {
|
||||||
|
_timeout_task->disarm();
|
||||||
|
}
|
||||||
|
SafepointSynchronize::end();
|
||||||
|
}
|
||||||
|
|
||||||
|
_cur_vm_operation = prev_vm_operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMThread::wait_for_operation() {
|
||||||
|
assert(Thread::current()->is_VM_thread(), "Must be the VM thread");
|
||||||
|
MonitorLocker ml_op_lock(VMOperation_lock, Mutex::_no_safepoint_check_flag);
|
||||||
|
|
||||||
|
// Clear previous operation.
|
||||||
|
// On first call this clears a dummy place-holder.
|
||||||
|
_next_vm_operation = NULL;
|
||||||
|
// Notify operation is done and notify a next operation can be installed.
|
||||||
|
ml_op_lock.notify_all();
|
||||||
|
|
||||||
|
while (!should_terminate()) {
|
||||||
|
self_destruct_if_needed();
|
||||||
|
if (_next_vm_operation != NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (handshake_alot()) {
|
||||||
|
{
|
||||||
|
MutexUnlocker mul(VMOperation_lock);
|
||||||
|
HandshakeALotClosure hal_cl;
|
||||||
|
Handshake::execute(&hal_cl);
|
||||||
|
}
|
||||||
|
// When we unlocked above someone might have setup a new op.
|
||||||
|
if (_next_vm_operation != NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(_next_vm_operation == NULL, "Must be");
|
||||||
|
assert(_cur_vm_operation == NULL, "Must be");
|
||||||
|
|
||||||
|
setup_periodic_safepoint_if_needed();
|
||||||
|
if (_next_vm_operation != NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We didn't find anything to execute, notify any waiter so they can install an op.
|
||||||
|
ml_op_lock.notify_all();
|
||||||
|
ml_op_lock.wait(GuaranteedSafepointInterval);
|
||||||
}
|
}
|
||||||
// Nothing to be done.
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMThread::loop() {
|
void VMThread::loop() {
|
||||||
|
@ -406,94 +468,17 @@ void VMThread::loop() {
|
||||||
|
|
||||||
SafepointSynchronize::init(_vm_thread);
|
SafepointSynchronize::init(_vm_thread);
|
||||||
|
|
||||||
while(true) {
|
// Need to set a calling thread for ops not passed
|
||||||
//
|
// via the normal way.
|
||||||
// Wait for VM operation
|
cleanup_op.set_calling_thread(_vm_thread);
|
||||||
//
|
safepointALot_op.set_calling_thread(_vm_thread);
|
||||||
// use no_safepoint_check to get lock without attempting to "sneak"
|
|
||||||
{ MonitorLocker mu_queue(VMOperationQueue_lock,
|
|
||||||
Mutex::_no_safepoint_check_flag);
|
|
||||||
|
|
||||||
// Look for new operation
|
|
||||||
assert(_cur_vm_operation == NULL, "no current one should be executing");
|
|
||||||
_cur_vm_operation = _vm_queue->remove_next();
|
|
||||||
|
|
||||||
while (!should_terminate() && _cur_vm_operation == NULL) {
|
|
||||||
// wait with a timeout to guarantee safepoints at regular intervals
|
|
||||||
// (if there is cleanup work to do)
|
|
||||||
(void)mu_queue.wait(GuaranteedSafepointInterval);
|
|
||||||
|
|
||||||
// Support for self destruction
|
|
||||||
if ((SelfDestructTimer != 0) && !VMError::is_error_reported() &&
|
|
||||||
(os::elapsedTime() > (double)SelfDestructTimer * 60.0)) {
|
|
||||||
tty->print_cr("VM self-destructed");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the queue contains a safepoint VM op,
|
|
||||||
// clean up will be done so we can skip this part.
|
|
||||||
if (!_vm_queue->peek_at_safepoint_priority()) {
|
|
||||||
|
|
||||||
// Have to unlock VMOperationQueue_lock just in case no_op_safepoint()
|
|
||||||
// has to do a handshake when HandshakeALot is enabled.
|
|
||||||
MutexUnlocker mul(VMOperationQueue_lock, Mutex::_no_safepoint_check_flag);
|
|
||||||
if ((_cur_vm_operation = VMThread::no_op_safepoint()) != NULL) {
|
|
||||||
// Force a safepoint since we have not had one for at least
|
|
||||||
// 'GuaranteedSafepointInterval' milliseconds and we need to clean
|
|
||||||
// something. This will run all the clean-up processing that needs
|
|
||||||
// to be done at a safepoint.
|
|
||||||
SafepointSynchronize::begin();
|
|
||||||
SafepointSynchronize::end();
|
|
||||||
_cur_vm_operation = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_cur_vm_operation = _vm_queue->remove_next();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
while (true) {
|
||||||
if (should_terminate()) break;
|
if (should_terminate()) break;
|
||||||
} // Release mu_queue_lock
|
wait_for_operation();
|
||||||
|
if (should_terminate()) break;
|
||||||
//
|
assert(_next_vm_operation != NULL, "Must have one");
|
||||||
// Execute VM operation
|
inner_execute(_next_vm_operation);
|
||||||
//
|
|
||||||
{ HandleMark hm(VMThread::vm_thread());
|
|
||||||
|
|
||||||
EventMark em("Executing VM operation: %s", vm_operation()->name());
|
|
||||||
assert(_cur_vm_operation != NULL, "we should have found an operation to execute");
|
|
||||||
|
|
||||||
// If we are at a safepoint we will evaluate all the operations that
|
|
||||||
// follow that also require a safepoint
|
|
||||||
if (_cur_vm_operation->evaluate_at_safepoint()) {
|
|
||||||
log_debug(vmthread)("Evaluating safepoint VM operation: %s", _cur_vm_operation->name());
|
|
||||||
|
|
||||||
SafepointSynchronize::begin();
|
|
||||||
|
|
||||||
if (_timeout_task != NULL) {
|
|
||||||
_timeout_task->arm();
|
|
||||||
}
|
|
||||||
|
|
||||||
evaluate_operation(_cur_vm_operation);
|
|
||||||
_cur_vm_operation = NULL;
|
|
||||||
|
|
||||||
if (_timeout_task != NULL) {
|
|
||||||
_timeout_task->disarm();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Complete safepoint synchronization
|
|
||||||
SafepointSynchronize::end();
|
|
||||||
|
|
||||||
} else { // not a safepoint operation
|
|
||||||
log_debug(vmthread)("Evaluating non-safepoint VM operation: %s", _cur_vm_operation->name());
|
|
||||||
evaluate_operation(_cur_vm_operation);
|
|
||||||
_cur_vm_operation = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Notify (potential) waiting Java thread(s)
|
|
||||||
{ MonitorLocker mu(VMOperationRequest_lock, Mutex::_no_safepoint_check_flag);
|
|
||||||
mu.notify_all();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,8 +510,15 @@ class SkipGCALot : public StackObj {
|
||||||
void VMThread::execute(VM_Operation* op) {
|
void VMThread::execute(VM_Operation* op) {
|
||||||
Thread* t = Thread::current();
|
Thread* t = Thread::current();
|
||||||
|
|
||||||
if (!t->is_VM_thread()) {
|
if (t->is_VM_thread()) {
|
||||||
SkipGCALot sgcalot(t); // avoid re-entrant attempts to gc-a-lot
|
op->set_calling_thread(t);
|
||||||
|
((VMThread*)t)->inner_execute(op);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid re-entrant attempts to gc-a-lot
|
||||||
|
SkipGCALot sgcalot(t);
|
||||||
|
|
||||||
// JavaThread or WatcherThread
|
// JavaThread or WatcherThread
|
||||||
t->check_for_valid_safepoint_state();
|
t->check_for_valid_safepoint_state();
|
||||||
|
|
||||||
|
@ -535,61 +527,11 @@ void VMThread::execute(VM_Operation* op) {
|
||||||
return; // op was cancelled
|
return; // op was cancelled
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup VM_operations for execution
|
|
||||||
op->set_calling_thread(t);
|
op->set_calling_thread(t);
|
||||||
|
|
||||||
// Get ticket number for the VM operation
|
wait_until_executed(op);
|
||||||
int ticket = t->vm_operation_ticket();
|
|
||||||
|
|
||||||
// Add VM operation to list of waiting threads. We are guaranteed not to block while holding the
|
|
||||||
// VMOperationQueue_lock, so we can block without a safepoint check. This allows vm operation requests
|
|
||||||
// to be queued up during a safepoint synchronization.
|
|
||||||
{
|
|
||||||
MonitorLocker ml(VMOperationQueue_lock, Mutex::_no_safepoint_check_flag);
|
|
||||||
log_debug(vmthread)("Adding VM operation: %s", op->name());
|
|
||||||
_vm_queue->add(op);
|
|
||||||
ml.notify();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// Wait for completion of request
|
|
||||||
// Note: only a JavaThread triggers the safepoint check when locking
|
|
||||||
MonitorLocker ml(VMOperationRequest_lock,
|
|
||||||
t->is_Java_thread() ? Mutex::_safepoint_check_flag : Mutex::_no_safepoint_check_flag);
|
|
||||||
while(t->vm_operation_completed_count() < ticket) {
|
|
||||||
ml.wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
op->doit_epilogue();
|
op->doit_epilogue();
|
||||||
} else {
|
|
||||||
// invoked by VM thread; usually nested VM operation
|
|
||||||
assert(t->is_VM_thread(), "must be a VM thread");
|
|
||||||
VM_Operation* prev_vm_operation = vm_operation();
|
|
||||||
if (prev_vm_operation != NULL) {
|
|
||||||
// Check the VM operation allows nested VM operation. This normally not the case, e.g., the compiler
|
|
||||||
// does not allow nested scavenges or compiles.
|
|
||||||
if (!prev_vm_operation->allow_nested_vm_operations()) {
|
|
||||||
fatal("Nested VM operation %s requested by operation %s",
|
|
||||||
op->name(), vm_operation()->name());
|
|
||||||
}
|
|
||||||
op->set_calling_thread(prev_vm_operation->calling_thread());
|
|
||||||
}
|
|
||||||
|
|
||||||
EventMark em("Executing %s VM operation: %s", prev_vm_operation ? "nested" : "", op->name());
|
|
||||||
|
|
||||||
// Release all internal handles after operation is evaluated
|
|
||||||
HandleMark hm(t);
|
|
||||||
_cur_vm_operation = op;
|
|
||||||
|
|
||||||
if (op->evaluate_at_safepoint() && !SafepointSynchronize::is_at_safepoint()) {
|
|
||||||
SafepointSynchronize::begin();
|
|
||||||
op->evaluate();
|
|
||||||
SafepointSynchronize::end();
|
|
||||||
} else {
|
|
||||||
op->evaluate();
|
|
||||||
}
|
|
||||||
|
|
||||||
_cur_vm_operation = prev_vm_operation;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMThread::verify() {
|
void VMThread::verify() {
|
||||||
|
|
|
@ -30,53 +30,6 @@
|
||||||
#include "runtime/task.hpp"
|
#include "runtime/task.hpp"
|
||||||
#include "runtime/vmOperations.hpp"
|
#include "runtime/vmOperations.hpp"
|
||||||
|
|
||||||
class VM_QueueHead : public VM_None {
|
|
||||||
public:
|
|
||||||
VM_QueueHead() : VM_None("QueueHead") {}
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Prioritized queue of VM operations.
|
|
||||||
//
|
|
||||||
// Encapsulates both queue management and
|
|
||||||
// and priority policy
|
|
||||||
//
|
|
||||||
class VMOperationQueue : public CHeapObj<mtInternal> {
|
|
||||||
private:
|
|
||||||
enum Priorities {
|
|
||||||
SafepointPriority, // Highest priority (operation executed at a safepoint)
|
|
||||||
MediumPriority, // Medium priority
|
|
||||||
nof_priorities
|
|
||||||
};
|
|
||||||
|
|
||||||
// We maintain a doubled linked list, with explicit count.
|
|
||||||
int _queue_length[nof_priorities];
|
|
||||||
int _queue_counter;
|
|
||||||
VM_Operation* _queue [nof_priorities];
|
|
||||||
|
|
||||||
static VM_QueueHead _queue_head[nof_priorities];
|
|
||||||
|
|
||||||
// Double-linked non-empty list insert.
|
|
||||||
void insert(VM_Operation* q,VM_Operation* n);
|
|
||||||
void unlink(VM_Operation* q);
|
|
||||||
|
|
||||||
// Basic queue manipulation
|
|
||||||
bool queue_empty (int prio);
|
|
||||||
void queue_add (int prio, VM_Operation *op);
|
|
||||||
VM_Operation* queue_remove_front(int prio);
|
|
||||||
// lock-free query: may return the wrong answer but must not break
|
|
||||||
bool queue_peek(int prio) { return _queue_length[prio] > 0; }
|
|
||||||
|
|
||||||
public:
|
|
||||||
VMOperationQueue();
|
|
||||||
|
|
||||||
// Highlevel operations. Encapsulates policy
|
|
||||||
void add(VM_Operation *op);
|
|
||||||
VM_Operation* remove_next(); // Returns next or null
|
|
||||||
bool peek_at_safepoint_priority() { return queue_peek(SafepointPriority); }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// VM operation timeout handling: warn or abort the VM when VM operation takes
|
// VM operation timeout handling: warn or abort the VM when VM operation takes
|
||||||
// too long. Periodic tasks do not participate in safepoint protocol, and therefore
|
// too long. Periodic tasks do not participate in safepoint protocol, and therefore
|
||||||
// can fire when application threads are stopped.
|
// can fire when application threads are stopped.
|
||||||
|
@ -114,9 +67,12 @@ class VMThread: public NamedThread {
|
||||||
|
|
||||||
static VMOperationTimeoutTask* _timeout_task;
|
static VMOperationTimeoutTask* _timeout_task;
|
||||||
|
|
||||||
static VM_Operation* no_op_safepoint();
|
static bool handshake_alot();
|
||||||
|
static void setup_periodic_safepoint_if_needed();
|
||||||
|
|
||||||
void evaluate_operation(VM_Operation* op);
|
void evaluate_operation(VM_Operation* op);
|
||||||
|
void inner_execute(VM_Operation* op);
|
||||||
|
void wait_for_operation();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor
|
// Constructor
|
||||||
|
@ -143,8 +99,16 @@ class VMThread: public NamedThread {
|
||||||
static void execute(VM_Operation* op);
|
static void execute(VM_Operation* op);
|
||||||
|
|
||||||
// Returns the current vm operation if any.
|
// Returns the current vm operation if any.
|
||||||
static VM_Operation* vm_operation() { return _cur_vm_operation; }
|
static VM_Operation* vm_operation() {
|
||||||
static VM_Operation::VMOp_Type vm_op_type() { return _cur_vm_operation->type(); }
|
assert(Thread::current()->is_VM_thread(), "Must be");
|
||||||
|
return _cur_vm_operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VM_Operation::VMOp_Type vm_op_type() {
|
||||||
|
VM_Operation* op = vm_operation();
|
||||||
|
assert(op != NULL, "sanity");
|
||||||
|
return op->type();
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the single instance of VMThread.
|
// Returns the single instance of VMThread.
|
||||||
static VMThread* vm_thread() { return _vm_thread; }
|
static VMThread* vm_thread() { return _vm_thread; }
|
||||||
|
@ -152,7 +116,9 @@ class VMThread: public NamedThread {
|
||||||
void verify();
|
void verify();
|
||||||
|
|
||||||
// Performance measurement
|
// Performance measurement
|
||||||
static PerfCounter* perf_accumulated_vm_operation_time() { return _perf_accumulated_vm_operation_time; }
|
static PerfCounter* perf_accumulated_vm_operation_time() {
|
||||||
|
return _perf_accumulated_vm_operation_time;
|
||||||
|
}
|
||||||
|
|
||||||
// Entry for starting vm thread
|
// Entry for starting vm thread
|
||||||
virtual void run();
|
virtual void run();
|
||||||
|
@ -161,10 +127,14 @@ class VMThread: public NamedThread {
|
||||||
static void create();
|
static void create();
|
||||||
static void destroy();
|
static void destroy();
|
||||||
|
|
||||||
|
static void wait_until_executed(VM_Operation* op);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// VM_Operation support
|
// VM_Operation support
|
||||||
static VM_Operation* _cur_vm_operation; // Current VM operation
|
static VM_Operation* _cur_vm_operation; // Current VM operation
|
||||||
static VMOperationQueue* _vm_queue; // Queue (w/ policy) of VM operations
|
static VM_Operation* _next_vm_operation; // Next VM operation
|
||||||
|
|
||||||
|
bool set_next_operation(VM_Operation *op); // Set the _next_vm_operation if possible.
|
||||||
|
|
||||||
// Pointer to single-instance of VM thread
|
// Pointer to single-instance of VM thread
|
||||||
static VMThread* _vm_thread;
|
static VMThread* _vm_thread;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue