/*
* Copyright (c) 2007, 2011, 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.
*/
package com.sun.max.vm.runtime;
import java.lang.Thread.UncaughtExceptionHandler;
import com.sun.max.annotate.*;
import com.sun.max.program.*;
import com.sun.max.vm.*;
import com.sun.max.vm.heap.*;
import com.sun.max.vm.monitor.modal.sync.*;
import com.sun.max.vm.thread.*;
/**
* The thread used to {@linkplain #submit(VmOperation) execute} {@linkplain VmOperation VM operations}.
*/
public class VmOperationThread extends Thread implements UncaughtExceptionHandler {
/**
* A special exception thrown when a non-VM operation thread {@linkplain VmOperationThread#submit(VmOperation)
* submits} a VM operation while holding the {@linkplain VmThreadMap#THREAD_LOCK thread lock}. There is a single,
* pre-allocated {@linkplain #INSTANCE instance} of this object so that raising this exception does not require any
* allocation.
*
*/
public static final class HoldsThreadLockError extends OutOfMemoryError {
private HoldsThreadLockError() {
}
public static final HoldsThreadLockError INSTANCE = new HoldsThreadLockError();
}
private final VmOperationQueue queue;
private boolean shouldTerminate;
private boolean terminated;
static boolean TraceVmOperations;
static boolean TraceRequestLock;
public static VmOperationThread instance() {
return (VmOperationThread) VmThread.vmOperationThread.javaThread();
}
static {
VMOptions.addFieldOption("-XX:", "TraceVmOperations", VmOperationThread.class, "Trace VM operations.");
VMOptions.addFieldOption("-XX:", "TraceRequestLock", VmOperationThread.class, "Trace VM_OPERATION_REQUEST_LOCK.");
}
@HOSTED_ONLY
public VmOperationThread(ThreadGroup group) {
super(group, "VmOperationThread");
queue = new VmOperationQueue();
setDaemon(true);
setUncaughtExceptionHandler(this);
}
/**
* Lock used to block a VM operation submitting thread until the operation is completed.
*/
private static final Object REQUEST_LOCK = JavaMonitorManager.newVmLock("VM_OPERATION_REQUEST_LOCK");
/**
* Lock used to synchronize access to the VM operation queue.
*/
private static final Object QUEUE_LOCK = JavaMonitorManager.newVmLock("VM_OPERATION_QUEUE_LOCK");
/**
* Lock used to block the thread trying to terminate the VM operation thread.
*/
private static final Object TERMINATE_LOCK = JavaMonitorManager.newVmLock("VM_OPERATION_THREAD_TERMINATION_LOCK");
@Override
public final void start() {
synchronized (QUEUE_LOCK) {
super.start();
try {
// Block until the VM operation thread is waiting for requests:
QUEUE_LOCK.wait();
} catch (InterruptedException interruptedException) {
throw ProgramError.unexpected(interruptedException);
}
}
}
private VmOperation currentOperation;
public void promoteToGlobalSafepoint() {
if (VmThread.current().isVmOperationThread()) {
if (currentOperation != null && currentOperation.requiresGlobalSafepoint()) {
return;
}
FatalError.unimplemented();
}
FatalError.unexpected("Only the VM thread can promote a VmOperation's mode to global safepoint");
}
public boolean runsGlobalSafepointOperation() {
VmOperation vmOp = currentOperation;
return vmOp != null && vmOp.requiresGlobalSafepoint();
}
@Override
public void run() {
if (TraceVmOperations) {
Log.println("Started VM operation thread");
}
synchronized (QUEUE_LOCK) {
// Let the thread that started the VM operation thread now continue
QUEUE_LOCK.notify();
}
while (true) {
// Wait for VM operation
synchronized (QUEUE_LOCK) {
FatalError.check(currentOperation == null, "Polling operation queue while current operation is pending");
currentOperation = queue.poll();
while (!shouldTerminate && currentOperation == null) {
try {
QUEUE_LOCK.wait();
currentOperation = queue.poll();
} catch (InterruptedException e) {
Log.println("Caught InterruptedException while polling VM operation queue");
}
}
if (shouldTerminate) {
break;
}
}
if (TraceVmOperations) {
boolean lockDisabledSafepoints = Log.lock();
Log.print("VM operation thread about to run operation ");
Log.print(currentOperation.name);
Log.print(" submitted by ");
Log.printThread(currentOperation.callingThread(), true);
Log.unlock(lockDisabledSafepoints);
}
// Execute VM operation
if (currentOperation.disablesHeapAllocation()) {
Heap.disableAllocationForCurrentThread();
}
try {
currentOperation.run();
} finally {
if (currentOperation.disablesHeapAllocation()) {
Heap.enableAllocationForCurrentThread();
}
if (currentOperation.mode.isBlocking()) {
synchronized (REQUEST_LOCK) {
currentOperation.callingThread().decrementPendingOperations();
if (TraceVmOperations || TraceRequestLock) {
boolean lockDisabledSafepoints = Log.lock();
Log.print("VM operation thread finished operation ");
Log.print(currentOperation.name);
Log.print(" submitted by ");
Log.printThread(currentOperation.callingThread(), false);
Log.println(" and is notifying REQUEST_LOCK waiters");
Log.unlock(lockDisabledSafepoints);
}
REQUEST_LOCK.notifyAll();
}
}
currentOperation = null;
}
}
// Signal other threads that VM operation thread is gone
synchronized (TERMINATE_LOCK) {
terminated = true;
TERMINATE_LOCK.notify();
}
if (TraceVmOperations) {
Log.println("VM operation thread stopped");
}
}
/**
* Schedules an operation for execution on the VM operation thread. The caller is
* blocked until the operation is completed or the scheduling is canceled by
* {@link VmOperation#doItPrologue(boolean)}.
*
* @param operation a VM operation to be executed on the VM operation thread
*/
public static void submit(VmOperation operation) {
VmThread vmThread = VmThread.current();
VmOperationThread vmOperationThread = instance();
FatalError.check(Log.lockOwner() != VmThread.current(), "log should not be locked by thread submitting a VM operation");
if (!vmThread.isVmOperationThread()) {
if (!operation.doItPrologue(false)) {
// Operation was canceled
return;
}
if (Thread.holdsLock(VmThreadMap.THREAD_LOCK)) {
// The VM operation thread requires this lock to proceed
throw VmOperationThread.HoldsThreadLockError.INSTANCE;
}
// Any given operation instance can be put on the queue at most once
// so synchronize all threads trying to use the same VM operation
// instance here.
synchronized (operation) {
operation.setCallingThread(vmThread);
if (operation.mode.isBlocking()) {
// Increment before putting the operations on the queue, otherwise,
// a race may occurs with the VmOperationThread.
vmThread.incrementPendingOperations();
}
// Add operation to queue
synchronized (QUEUE_LOCK) {
vmOperationThread.queue.add(operation);
if (TraceVmOperations) {
boolean lockDisabledSafepoints = Log.lock();
Log.print("VM operation ");
Log.print(operation.name);
Log.print(" submitted by ");
Log.printThread(vmThread, false);
Log.println(" - notifying VM operation thread");
Log.unlock(lockDisabledSafepoints);
}
QUEUE_LOCK.notify();
}
if (operation.mode.isBlocking()) {
// Wait until operation completes
synchronized (REQUEST_LOCK) {
int count = 0;
while (vmThread.pendingOperations() > 0) {
if (TraceRequestLock) {
boolean lockDisabledSafepoints = Log.lock();
count++;
Log.printThread(vmThread, false);
Log.print(" waiting #");
Log.print(count);
Log.print(" on REQUEST_LOCK for completion of VM operation ");
Log.println(operation.name);
Log.unlock(lockDisabledSafepoints);
}
try {
REQUEST_LOCK.wait();
} catch (InterruptedException e) {
Log.println("Caught InterruptedException while waiting for VM operation to complete");
}
}
if (TraceRequestLock) {
Log.printThread(vmThread, false);
Log.print(" resuming after #");
Log.print(count);
Log.print(" wait on REQUEST_LOCK, completed VM operation ");
Log.println(operation.name);
}
}
}
operation.doItEpilogue(false);
if (TraceVmOperations) {
boolean lockDisabledSafepoints = Log.lock();
Log.print("VM operation ");
Log.print(operation.name);
Log.print(" submitted by ");
Log.printThread(vmThread, false);
Log.println(" - done");
Log.unlock(lockDisabledSafepoints);
}
}
} else {
// Invoked by VM operation thread
VmOperation enclosingOperation = vmOperationThread.currentOperation;
boolean nested = enclosingOperation != null;
if (nested) {
// Nested operation: check that it's allowed for the enclosing operation
boolean fatal = enclosingOperation.disAllowsNestedOperations;
if (TraceVmOperations || fatal) {
boolean lockDisabledSafepoints = Log.lock();
Log.print("Nested VM operation ");
Log.print(operation.name);
Log.print(" requested by operation ");
Log.println(enclosingOperation.name);
Log.unlock(lockDisabledSafepoints);
}
if (fatal) {
FatalError.unexpected("Nested VM operation requested when current operation doesn't allow it");
}
operation.enclosing = enclosingOperation;
}
if (!operation.doItPrologue(nested)) {
// Operation was canceled
return;
}
vmOperationThread.currentOperation = operation;
try {
operation.run();
} finally {
operation.doItEpilogue(nested);
vmOperationThread.currentOperation = enclosingOperation;
operation.enclosing = null;
}
}
}
/**
* Notifies the VM operation thread that it should stop. The current thread
* is blocked until the VM thread stops.
*/
public static void terminate() {
VmOperationThread vmOperationThread = instance();
vmOperationThread.shouldTerminate = true;
if (TraceVmOperations) {
Log.println("Terminating VM operation thread");
}
synchronized (QUEUE_LOCK) {
QUEUE_LOCK.notify();
}
synchronized (TERMINATE_LOCK) {
while (!vmOperationThread.terminated) {
try {
TERMINATE_LOCK.wait();
} catch (InterruptedException e) {
Log.println("Caught InterruptedException while waiting for VM operation thread to stop");
}
}
}
}
@Override
public void uncaughtException(Thread thread, Throwable e) {
FatalError.unexpected("Uncaught exception on VM operation thread", e);
}
}