8234086: VM operation can be simplified

Reviewed-by: kbarrett, dholmes, dcubed
This commit is contained in:
Robbin Ehn 2019-11-29 12:09:25 +01:00
parent 6230791e9e
commit 3d7d54b145
14 changed files with 75 additions and 159 deletions

View file

@ -50,13 +50,15 @@
#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] = new VM_None("QueueHead");
_queue[i] = &_queue_head[i];
_queue[i]->set_next(_queue[i]);
_queue[i]->set_prev(_queue[i]);
}
@ -81,12 +83,7 @@ void VMOperationQueue::insert(VM_Operation* q, VM_Operation* n) {
q->set_next(n);
}
void VMOperationQueue::queue_add_front(int prio, VM_Operation *op) {
_queue_length[prio]++;
insert(_queue[prio]->next(), op);
}
void VMOperationQueue::queue_add_back(int prio, VM_Operation *op) {
void VMOperationQueue::queue_add(int prio, VM_Operation *op) {
_queue_length[prio]++;
insert(_queue[prio]->prev(), op);
}
@ -154,11 +151,11 @@ void VMOperationQueue::add(VM_Operation *op) {
HOTSPOT_VMOPS_REQUEST(
(char *) op->name(), strlen(op->name()),
op->evaluation_mode());
op->evaluate_at_safepoint() ? 0 : 1);
// Encapsulates VM queue policy. Currently, that
// only involves putting them on the right list
queue_add_back(op->evaluate_at_safepoint() ? SafepointPriority : MediumPriority, op);
queue_add(op->evaluate_at_safepoint() ? SafepointPriority : MediumPriority, op);
}
VM_Operation* VMOperationQueue::remove_next() {
@ -380,15 +377,11 @@ static void post_vm_operation_event(EventExecuteVMOperation* event, VM_Operation
assert(event != NULL, "invariant");
assert(event->should_commit(), "invariant");
assert(op != NULL, "invariant");
const bool is_concurrent = op->evaluate_concurrently();
const bool evaluate_at_safepoint = op->evaluate_at_safepoint();
event->set_operation(op->type());
event->set_safepoint(evaluate_at_safepoint);
event->set_blocking(!is_concurrent);
// Only write caller thread information for non-concurrent vm operations.
// For concurrent vm operations, the thread id is set to 0 indicating thread is unknown.
// This is because the caller thread could have exited already.
event->set_caller(is_concurrent ? 0 : JFR_THREAD_ID(op->calling_thread()));
event->set_blocking(true);
event->set_caller(JFR_THREAD_ID(op->calling_thread()));
event->set_safepointId(evaluate_at_safepoint ? SafepointSynchronize::safepoint_id() : 0);
event->commit();
}
@ -400,7 +393,7 @@ void VMThread::evaluate_operation(VM_Operation* op) {
PerfTraceTime vm_op_timer(perf_accumulated_vm_operation_time());
HOTSPOT_VMOPS_BEGIN(
(char *) op->name(), strlen(op->name()),
op->evaluation_mode());
op->evaluate_at_safepoint() ? 0 : 1);
EventExecuteVMOperation event;
op->evaluate();
@ -410,21 +403,11 @@ void VMThread::evaluate_operation(VM_Operation* op) {
HOTSPOT_VMOPS_END(
(char *) op->name(), strlen(op->name()),
op->evaluation_mode());
op->evaluate_at_safepoint() ? 0 : 1);
}
// Last access of info in _cur_vm_operation!
bool c_heap_allocated = op->is_cheap_allocated();
// Mark as completed
if (!op->evaluate_concurrently()) {
op->calling_thread()->increment_vm_operation_completed_count();
}
// It is unsafe to access the _cur_vm_operation after the 'increment_vm_operation_completed_count' call,
// since if it is stack allocated the calling thread might have deallocated
if (c_heap_allocated) {
delete _cur_vm_operation;
}
op->calling_thread()->increment_vm_operation_completed_count();
}
static VM_None safepointALot_op("SafepointALot");
@ -441,6 +424,11 @@ class HandshakeALotTC : public ThreadClosure {
}
};
void VMThread::check_for_forced_cleanup() {
MonitorLocker mq(VMOperationQueue_lock, Mutex::_no_safepoint_check_flag);
mq.notify();
}
VM_Operation* VMThread::no_op_safepoint() {
// Check for handshakes first since we may need to return a VMop.
if (HandshakeALot) {
@ -451,7 +439,8 @@ VM_Operation* VMThread::no_op_safepoint() {
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()) {
if ((max_time_exceeded && SafepointSynchronize::is_cleanup_needed()) ||
SafepointSynchronize::is_forced_cleanup_needed()) {
return &cleanup_op;
}
if (SafepointALot) {
@ -480,8 +469,7 @@ void VMThread::loop() {
_cur_vm_operation = _vm_queue->remove_next();
// Stall time tracking code
if (PrintVMQWaitTime && _cur_vm_operation != NULL &&
!_cur_vm_operation->evaluate_concurrently()) {
if (PrintVMQWaitTime && _cur_vm_operation != NULL) {
long stall = os::javaTimeMillis() - _cur_vm_operation->timestamp();
if (stall > 0)
tty->print_cr("%s stall: %ld", _cur_vm_operation->name(), stall);
@ -489,8 +477,8 @@ void VMThread::loop() {
while (!should_terminate() && _cur_vm_operation == NULL) {
// wait with a timeout to guarantee safepoints at regular intervals
bool timedout =
mu_queue.wait(GuaranteedSafepointInterval);
// (if there is cleanup work to do)
(void)mu_queue.wait(GuaranteedSafepointInterval);
// Support for self destruction
if ((SelfDestructTimer != 0) && !VMError::is_error_reported() &&
@ -499,9 +487,12 @@ void VMThread::loop() {
exit(-1);
}
if (timedout) {
// 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.
// 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
@ -624,15 +615,6 @@ void VMThread::loop() {
{ MonitorLocker mu(VMOperationRequest_lock, Mutex::_no_safepoint_check_flag);
mu.notify_all();
}
// We want to make sure that we get to a safepoint regularly
// even when executing VMops that don't require safepoints.
if ((_cur_vm_operation = VMThread::no_op_safepoint()) != NULL) {
HandleMark hm(VMThread::vm_thread());
SafepointSynchronize::begin();
SafepointSynchronize::end();
_cur_vm_operation = NULL;
}
}
}
@ -667,11 +649,7 @@ void VMThread::execute(VM_Operation* op) {
if (!t->is_VM_thread()) {
SkipGCALot sgcalot(t); // avoid re-entrant attempts to gc-a-lot
// JavaThread or WatcherThread
bool concurrent = op->evaluate_concurrently();
// only blocking VM operations need to verify the caller's safepoint state:
if (!concurrent) {
t->check_for_valid_safepoint_state();
}
t->check_for_valid_safepoint_state();
// New request from Java thread, evaluate prologue
if (!op->doit_prologue()) {
@ -681,16 +659,8 @@ void VMThread::execute(VM_Operation* op) {
// Setup VM_operations for execution
op->set_calling_thread(t);
// It does not make sense to execute the epilogue, if the VM operation object is getting
// deallocated by the VM thread.
bool execute_epilog = !op->is_cheap_allocated();
assert(!concurrent || op->is_cheap_allocated(), "concurrent => cheap_allocated");
// Get ticket number for non-concurrent VM operations
int ticket = 0;
if (!concurrent) {
ticket = t->vm_operation_ticket();
}
// 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
@ -702,9 +672,8 @@ void VMThread::execute(VM_Operation* op) {
op->set_timestamp(os::javaTimeMillis());
ml.notify();
}
if (!concurrent) {
// Wait for completion of request (non-concurrent)
{
// 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);
@ -712,10 +681,7 @@ void VMThread::execute(VM_Operation* op) {
ml.wait();
}
}
if (execute_epilog) {
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");
@ -744,9 +710,6 @@ void VMThread::execute(VM_Operation* op) {
op->evaluate();
}
// Free memory if needed
if (op->is_cheap_allocated()) delete op;
_cur_vm_operation = prev_vm_operation;
}
}