8212107: VMThread issues and cleanup
Reviewed-by: shade, dcubed, coleenp, dholmes, redestad
This commit is contained in:
parent
6bddeb709d
commit
431338bcb3
@ -454,12 +454,8 @@ static bool prepare_for_emergency_dump(Thread* thread) {
|
||||
Heap_lock->unlock();
|
||||
}
|
||||
|
||||
if (VMOperationQueue_lock->owned_by_self()) {
|
||||
VMOperationQueue_lock->unlock();
|
||||
}
|
||||
|
||||
if (VMOperationRequest_lock->owned_by_self()) {
|
||||
VMOperationRequest_lock->unlock();
|
||||
if (VMOperation_lock->owned_by_self()) {
|
||||
VMOperation_lock->unlock();
|
||||
}
|
||||
|
||||
if (Service_lock->owned_by_self()) {
|
||||
|
@ -65,8 +65,7 @@ Monitor* CodeSweeper_lock = NULL;
|
||||
Mutex* MethodData_lock = NULL;
|
||||
Mutex* TouchedMethodLog_lock = NULL;
|
||||
Mutex* RetData_lock = NULL;
|
||||
Monitor* VMOperationQueue_lock = NULL;
|
||||
Monitor* VMOperationRequest_lock = NULL;
|
||||
Monitor* VMOperation_lock = NULL;
|
||||
Monitor* Threads_lock = NULL;
|
||||
Mutex* NonJavaThreadsList_lock = NULL;
|
||||
Mutex* NonJavaThreadsListSync_lock = NULL;
|
||||
@ -280,8 +279,7 @@ void mutex_init() {
|
||||
def(NonJavaThreadsList_lock , PaddedMutex, barrier, 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(VMOperationRequest_lock , PaddedMonitor, nonleaf, true, _safepoint_check_always);
|
||||
def(VMOperation_lock , PaddedMonitor, nonleaf, true, _safepoint_check_always); // VM_thread allowed to block on these
|
||||
def(RetData_lock , PaddedMutex , nonleaf, false, _safepoint_check_always);
|
||||
def(Terminator_lock , PaddedMonitor, nonleaf, true, _safepoint_check_always);
|
||||
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* TouchedMethodLog_lock; // a lock on allocation of LogExecutedMethods info
|
||||
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* VMOperationRequest_lock; // a lock on Threads waiting for a vm_operation to terminate
|
||||
extern Monitor* VMOperation_lock; // a lock on queue of vm_operations waiting to execute
|
||||
extern Monitor* Threads_lock; // a lock on the Threads table of active Java threads
|
||||
// (also used by Safepoints too to block threads creation/destruction)
|
||||
extern Mutex* NonJavaThreadsList_lock; // a lock on the NonJavaThreads list
|
||||
|
@ -255,8 +255,6 @@ Thread::Thread() {
|
||||
NOT_PRODUCT(_skip_gcalot = false;)
|
||||
_jvmti_env_iteration_count = 0;
|
||||
set_allocated_bytes(0);
|
||||
_vm_operation_started_count = 0;
|
||||
_vm_operation_completed_count = 0;
|
||||
_current_pending_monitor = NULL;
|
||||
_current_pending_monitor_is_from_java = true;
|
||||
_current_waiting_monitor = NULL;
|
||||
|
@ -401,9 +401,6 @@ class Thread: public ThreadShadow {
|
||||
|
||||
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
|
||||
// is waiting to lock
|
||||
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; }
|
||||
|
||||
// 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.
|
||||
ObjectMonitor* current_pending_monitor() {
|
||||
return _current_pending_monitor;
|
||||
|
@ -121,14 +121,12 @@ class VM_Operation : public StackObj {
|
||||
|
||||
private:
|
||||
Thread* _calling_thread;
|
||||
VM_Operation* _next;
|
||||
VM_Operation* _prev;
|
||||
|
||||
// The VM operation name array
|
||||
static const char* _names[];
|
||||
|
||||
public:
|
||||
VM_Operation() : _calling_thread(NULL), _next(NULL), _prev(NULL) {}
|
||||
VM_Operation() : _calling_thread(NULL) {}
|
||||
|
||||
// VM operation support (used by VM thread)
|
||||
Thread* calling_thread() const { return _calling_thread; }
|
||||
@ -148,12 +146,6 @@ class VM_Operation : public StackObj {
|
||||
virtual bool doit_prologue() { return true; };
|
||||
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.
|
||||
virtual VMOp_Type type() const = 0;
|
||||
virtual bool allow_nested_vm_operations() const { return false; }
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "compiler/compileBroker.hpp"
|
||||
#include "gc/shared/collectedHeap.hpp"
|
||||
#include "jfr/jfrEvents.hpp"
|
||||
#include "jfr/support/jfrThreadId.hpp"
|
||||
#include "logging/log.hpp"
|
||||
@ -32,7 +31,6 @@
|
||||
#include "logging/logConfiguration.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "memory/universe.hpp"
|
||||
#include "oops/method.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "oops/verifyOopClosure.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
@ -43,101 +41,13 @@
|
||||
#include "runtime/safepoint.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/timerTrace.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
#include "runtime/vmOperations.hpp"
|
||||
#include "services/runtimeService.hpp"
|
||||
#include "utilities/dtrace.hpp"
|
||||
#include "utilities/events.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
|
||||
@ -169,12 +79,15 @@ void VMOperationTimeoutTask::disarm() {
|
||||
//------------------------------------------------------------------------------------------------------------------
|
||||
// Implementation of VMThread stuff
|
||||
|
||||
static VM_None safepointALot_op("SafepointALot");
|
||||
static VM_Cleanup cleanup_op;
|
||||
|
||||
bool VMThread::_should_terminate = false;
|
||||
bool VMThread::_terminated = false;
|
||||
Monitor* VMThread::_terminate_lock = NULL;
|
||||
VMThread* VMThread::_vm_thread = 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;
|
||||
VMOperationTimeoutTask* VMThread::_timeout_task = NULL;
|
||||
|
||||
@ -198,10 +111,6 @@ void VMThread::create() {
|
||||
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,
|
||||
Monitor::_safepoint_check_never);
|
||||
|
||||
@ -310,9 +219,10 @@ void VMThread::run() {
|
||||
// and wait until operation is performed.
|
||||
void VMThread::wait_for_vm_thread_exit() {
|
||||
assert(JavaThread::current()->is_terminated(), "Should be terminated");
|
||||
{ MonitorLocker mu(VMOperationQueue_lock, Mutex::_no_safepoint_check_flag);
|
||||
{
|
||||
MonitorLocker mu(VMOperation_lock);
|
||||
_should_terminate = true;
|
||||
mu.notify();
|
||||
mu.notify_all();
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
{ 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();
|
||||
}
|
||||
}
|
||||
@ -364,13 +275,8 @@ void VMThread::evaluate_operation(VM_Operation* op) {
|
||||
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 {
|
||||
public:
|
||||
HandshakeALotClosure() : HandshakeClosure("HandshakeALot") {}
|
||||
@ -381,24 +287,180 @@ class HandshakeALotClosure : public HandshakeClosure {
|
||||
}
|
||||
};
|
||||
|
||||
VM_Operation* VMThread::no_op_safepoint() {
|
||||
// Check for handshakes first since we may need to return a VMop.
|
||||
if (HandshakeALot) {
|
||||
HandshakeALotClosure hal_cl;
|
||||
Handshake::execute(&hal_cl);
|
||||
bool VMThread::handshake_alot() {
|
||||
assert(_cur_vm_operation == NULL, "should not have an op yet");
|
||||
assert(_next_vm_operation == NULL, "should not have an op yet");
|
||||
if (!HandshakeALot) {
|
||||
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.
|
||||
long interval_ms = SafepointTracing::time_since_last_safepoint_ms();
|
||||
bool max_time_exceeded = GuaranteedSafepointInterval != 0 &&
|
||||
(interval_ms >= GuaranteedSafepointInterval);
|
||||
if (max_time_exceeded && SafepointSynchronize::is_cleanup_needed()) {
|
||||
return &cleanup_op;
|
||||
if (!max_time_exceeded) {
|
||||
return;
|
||||
}
|
||||
if (SafepointALot) {
|
||||
return &safepointALot_op;
|
||||
if (SafepointSynchronize::is_cleanup_needed()) {
|
||||
_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() {
|
||||
@ -406,94 +468,17 @@ void VMThread::loop() {
|
||||
|
||||
SafepointSynchronize::init(_vm_thread);
|
||||
|
||||
while(true) {
|
||||
//
|
||||
// Wait for VM operation
|
||||
//
|
||||
// use no_safepoint_check to get lock without attempting to "sneak"
|
||||
{ MonitorLocker mu_queue(VMOperationQueue_lock,
|
||||
Mutex::_no_safepoint_check_flag);
|
||||
// Need to set a calling thread for ops not passed
|
||||
// via the normal way.
|
||||
cleanup_op.set_calling_thread(_vm_thread);
|
||||
safepointALot_op.set_calling_thread(_vm_thread);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
if (should_terminate()) break;
|
||||
} // Release mu_queue_lock
|
||||
|
||||
//
|
||||
// Execute 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();
|
||||
}
|
||||
while (true) {
|
||||
if (should_terminate()) break;
|
||||
wait_for_operation();
|
||||
if (should_terminate()) break;
|
||||
assert(_next_vm_operation != NULL, "Must have one");
|
||||
inner_execute(_next_vm_operation);
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,71 +510,28 @@ class SkipGCALot : public StackObj {
|
||||
void VMThread::execute(VM_Operation* op) {
|
||||
Thread* t = Thread::current();
|
||||
|
||||
if (!t->is_VM_thread()) {
|
||||
SkipGCALot sgcalot(t); // avoid re-entrant attempts to gc-a-lot
|
||||
// JavaThread or WatcherThread
|
||||
t->check_for_valid_safepoint_state();
|
||||
|
||||
// New request from Java thread, evaluate prologue
|
||||
if (!op->doit_prologue()) {
|
||||
return; // op was cancelled
|
||||
}
|
||||
|
||||
// Setup VM_operations for execution
|
||||
if (t->is_VM_thread()) {
|
||||
op->set_calling_thread(t);
|
||||
|
||||
// Get ticket number for the VM operation
|
||||
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();
|
||||
} 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;
|
||||
((VMThread*)t)->inner_execute(op);
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid re-entrant attempts to gc-a-lot
|
||||
SkipGCALot sgcalot(t);
|
||||
|
||||
// JavaThread or WatcherThread
|
||||
t->check_for_valid_safepoint_state();
|
||||
|
||||
// New request from Java thread, evaluate prologue
|
||||
if (!op->doit_prologue()) {
|
||||
return; // op was cancelled
|
||||
}
|
||||
|
||||
op->set_calling_thread(t);
|
||||
|
||||
wait_until_executed(op);
|
||||
|
||||
op->doit_epilogue();
|
||||
}
|
||||
|
||||
void VMThread::verify() {
|
||||
|
@ -30,53 +30,6 @@
|
||||
#include "runtime/task.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
|
||||
// too long. Periodic tasks do not participate in safepoint protocol, and therefore
|
||||
// can fire when application threads are stopped.
|
||||
@ -114,9 +67,12 @@ class VMThread: public NamedThread {
|
||||
|
||||
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 inner_execute(VM_Operation* op);
|
||||
void wait_for_operation();
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
@ -143,8 +99,16 @@ class VMThread: public NamedThread {
|
||||
static void execute(VM_Operation* op);
|
||||
|
||||
// Returns the current vm operation if any.
|
||||
static VM_Operation* vm_operation() { return _cur_vm_operation; }
|
||||
static VM_Operation::VMOp_Type vm_op_type() { return _cur_vm_operation->type(); }
|
||||
static VM_Operation* vm_operation() {
|
||||
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.
|
||||
static VMThread* vm_thread() { return _vm_thread; }
|
||||
@ -152,7 +116,9 @@ class VMThread: public NamedThread {
|
||||
void verify();
|
||||
|
||||
// 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
|
||||
virtual void run();
|
||||
@ -161,10 +127,14 @@ class VMThread: public NamedThread {
|
||||
static void create();
|
||||
static void destroy();
|
||||
|
||||
static void wait_until_executed(VM_Operation* op);
|
||||
|
||||
private:
|
||||
// VM_Operation support
|
||||
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
|
||||
static VMThread* _vm_thread;
|
||||
|
Loading…
Reference in New Issue
Block a user