/*
* Copyright (c) 2010, 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 static com.sun.max.platform.Platform.*;
import static com.sun.max.vm.VMOptions.*;
import java.util.*;
import java.util.concurrent.atomic.*;
import sun.misc.*;
import com.sun.max.*;
import com.sun.max.annotate.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.*;
import com.sun.max.vm.compiler.*;
/**
* The thread used to post and dispatch signals to user supplied {@link SignalHandler}s.
* <p>
* The special C signal handler mentioned in {@link Signal} is 'userSignalHandler' in trap.c.
* This C signal handler atomically adds a signal to a queue in this class by up-calling
* {@link #tryPostSignal(int)} and then notifies the native semaphore
* on which the signal dispatching thread is {@linkplain #waitForSignal() waiting}.
* <p>
* The native layer of the VM also uses thread signal masks so that only one thread
* (the {@linkplain VmOperationThread VM operation thread}) handles the
* signals that are dispatched by this class.
*/
public final class SignalDispatcher extends Thread {
/**
* A set of counters, one per supported signal, used to post and consume signals.
*/
private static final AtomicIntegerArray PendingSignals = new AtomicIntegerArray(platform().nsig + 1);
/**
* The special signal used to terminate the signal dispatcher thread.
*/
private static final int ExitSignal = PendingSignals.length() - 1;
@HOSTED_ONLY
public SignalDispatcher(ThreadGroup group) {
super(group, "Signal Dispatcher");
setDaemon(true);
}
/**
* Blocks the current thread until a pending signal is available.
*
* @return the next available pending signal (which is removed from the set of pending signals)
*/
private int waitForSignal() {
while (true) {
for (int signal = 0; signal < PendingSignals.length(); signal++) {
int n = PendingSignals.get(signal);
if (n > 0 && PendingSignals.compareAndSet(signal, n, n - 1)) {
if (TraceSignals) {
boolean lockDisabledSafepoints = Log.lock();
Log.print("Handling signal ");
Log.print(signal);
Log.println(" on the SignalDispatcher thread");
Log.unlock(lockDisabledSafepoints);
}
return signal;
}
}
nativeSignalWait();
}
}
private static VMBooleanXXOption TraceSignalsOption = register(new VMBooleanXXOption("-XX:-TraceSignals", "Trace traps.") {
@Override
public boolean parseValue(Pointer optionValue) {
TraceSignals = TraceSignalsOption.getValue();
nativeSetSignalTracing(TraceSignals);
return true;
}
}, MaxineVM.Phase.PRISTINE);
private static boolean TraceSignals = TraceSignalsOption.getValue();
/*
* An ordinary lock (mutex) cannot be used when notifying the signal
* dispatcher of a signal as the platform specific functions for
* notifying condition variables (e.g. pthread_cond_signal(3)) are
* typically not safe to call within an OS-level signal handler.
* Instead, a single OS-level semaphore (e.g. POSIX sem_init(3))
* is used.
*/
private static native void nativeSignalInit(Address tryPostSignalAddress);
private static native void nativeSignalFinalize();
private static native void nativeSignalNotify();
private static native void nativeSignalWait();
@C_FUNCTION // called on the primordial thrad
private static native void nativeSetSignalTracing(boolean flag);
/**
* The handle by which the address of {@link #tryPostSignal} can be communicated to the native substrate.
*/
private static CriticalMethod tryPostSignal = new CriticalMethod(SignalDispatcher.class, "tryPostSignal", null, CallEntryPoint.C_ENTRY_POINT);
static {
new CriticalNativeMethod(SignalDispatcher.class, "nativeSignalInit");
new CriticalNativeMethod(SignalDispatcher.class, "nativeSignalFinalize");
new CriticalNativeMethod(SignalDispatcher.class, "nativeSignalNotify");
new CriticalNativeMethod(SignalDispatcher.class, "nativeSignalWait");
}
/**
* Attempts to atomically increment an element of {@link #PendingSignals}.
* This is provided so that the native substrate does not have to encode
* architecture specific mechanisms for trying to perform an atomic
* update on a given value in memory.
*
* This code is called from within a native signal handler and so
* must not block, cause any exception or assume that the thread
* pointer/safepoint latch (i.e. R14 on x64) is set up correctly.
*
* @param signal the index of the element in {@link #PendingSignals} on which an atomic increment attempt is performed
* @return {@code true} if the update succeeded, {@code false} otherwise
*/
@VM_ENTRY_POINT
@NO_SAFEPOINT_POLLS("executes inside a native signal handler")
private static boolean tryPostSignal(int signal) {
int n = PendingSignals.get(signal);
return PendingSignals.compareAndSet(signal, n, n + 1);
}
/**
* Dispatcher thread state: 0 = not started, 1 = started, 2 = terminated.
*/
private static volatile int state;
/**
* Terminates the signal dispatcher thread.
*/
public static void terminate() {
PendingSignals.incrementAndGet(ExitSignal);
if (state == 1) {
nativeSignalNotify();
}
state = 2;
}
/**
* This flag can be used to work-around the fact that the rapid creation of a lot of
* threads has a performance issue. Of course, it means that there's a risk that a
* signal handler that blocks will cause handling of all subsequent signals to be
* blocked. On the other hand, signal handling throughput is increased as there's
* no need to create a new thread for each signal.
*/
static boolean SerializeSignals = true;
static {
VMOptions.addFieldOption("-XX:", "SerializeSignals",
"Run Java signal handlers on a single thread.");
}
@ALIAS(declaringClass = Signal.class)
static Hashtable<Signal, SignalHandler> handlers;
@ALIAS(declaringClass = Signal.class)
static Hashtable<Integer, Signal> signals;
@Override
public void run() {
if (state == 2) {
// already terminated
return;
}
nativeSignalInit(tryPostSignal.address());
state = 1;
while (true) {
int signal = waitForSignal();
if (signal == ExitSignal) {
nativeSignalFinalize();
return;
}
final Signal sig = signals.get(signal);
final SignalHandler handler = handlers.get(sig);
if (handler != null) {
if (SerializeSignals) {
try {
handler.handle(sig);
} catch (Throwable e) {
Log.println("Exception occurred while dispatching signal " + signal + " to handler - VM may need to be forcibly terminated");
Log.print(Utils.stackTraceAsString(e));
}
} else {
Runnable runnable = new Runnable() {
public void run() {
// Don't bother to reset the priority. Signal handler will
// run at maximum priority inherited from the VM signal
// dispatch thread.
// Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
handler.handle(sig);
}
};
new Thread(runnable, sig + " handler").start();
}
}
}
}
}