/*
* Copyright (c) 2017, 2017, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.oracle.svm.core.posix;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.graalvm.nativeimage.ImageInfo;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.c.type.CIntPointer;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.word.WordFactory;
import com.oracle.svm.core.CErrorNumber;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.annotate.TargetElement;
import com.oracle.svm.core.jdk.JDK11OrLater;
import com.oracle.svm.core.jdk.JDK8OrEarlier;
import com.oracle.svm.core.jdk.RuntimeSupport;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.os.IsDefined;
import com.oracle.svm.core.posix.headers.CSunMiscSignal;
import com.oracle.svm.core.posix.headers.Errno;
import com.oracle.svm.core.posix.headers.Signal;
import com.oracle.svm.core.posix.headers.Signal.SignalDispatcher;
import com.oracle.svm.core.posix.headers.Time;
import com.oracle.svm.core.util.VMError;
@Platforms(Platform.HOSTED_ONLY.class)
class Package_jdk_internal_misc implements Function<TargetClass, String> {
@Override
public String apply(TargetClass annotation) {
if (JavaVersionUtil.JAVA_SPEC <= 8) {
return "sun.misc." + annotation.className();
} else {
return "jdk.internal.misc." + annotation.className();
}
}
}
@TargetClass(classNameProvider = Package_jdk_internal_misc.class, className = "Signal")
final class Target_jdk_internal_misc_Signal {
@Substitute
@TargetElement(onlyWith = JDK8OrEarlier.class)
private static /* native */ int findSignal(String signalName) {
return Util_jdk_internal_misc_Signal.numberFromName(signalName);
}
@Substitute
@TargetElement(onlyWith = JDK11OrLater.class)
private static /* native */ int findSignal0(String signalName) {
return Util_jdk_internal_misc_Signal.numberFromName(signalName);
}
@Substitute
private static long handle0(int sig, long nativeH) {
if (ImageInfo.isSharedLibrary()) {
throw new IllegalArgumentException("Installing signal handlers is not allowed for native-image shared libraries.");
}
return Util_jdk_internal_misc_Signal.handle0(sig, nativeH);
}
The Java side of raising a signal calls the C side of raising a signal. /** The Java side of raising a signal calls the C side of raising a signal. */
@Substitute
private static void raise0(int signalNumber) {
Signal.raise(signalNumber);
}
Called by the VM to execute Java signal handlers. Except that in sun.misc.Signal, this method
is private.
/**
* Called by the VM to execute Java signal handlers. Except that in sun.misc.Signal, this method
* is private.
*/
@Alias
static native void dispatch(int number);
}
Support for Target_sun_misc_Signal. /** Support for Target_sun_misc_Signal. */
final class Util_jdk_internal_misc_Signal {
A thread to dispatch signals as they are raised. /** A thread to dispatch signals as they are raised. */
private static Thread dispatchThread = null;
A lock to synchronize runtime initialization. /** A lock to synchronize runtime initialization. */
private static final ReentrantLock initializationLock = new ReentrantLock();
An initialization flag. /** An initialization flag. */
private static volatile boolean initialized = false;
A map from signal numbers to handlers. /** A map from signal numbers to handlers. */
private static SignalState[] signalState = null;
private Util_jdk_internal_misc_Signal() {
/* All-static class. */
}
Constants for the longs from sun.misc.Signal. /** Constants for the longs from sun.misc.Signal. */
private static final long sunMiscSignalDefaultHandler = 0;
private static final long sunMiscSignalIgnoreHandler = 1;
private static final long sunMiscSignalDispatchHandler = 2;
private static final long sunMiscSignalErrorHandler = -1;
Register a Java signal handler with the the C signal handling mechanism.
This implementation does not complain (by returning -1) about registering signal handlers for
signals that the VM itself uses.
/**
* Register a Java signal handler with the the C signal handling mechanism.
*
* This implementation does not complain (by returning -1) about registering signal handlers for
* signals that the VM itself uses.
*/
protected static long handle0(int sig, long nativeH) {
ensureInitialized();
final Signal.SignalDispatcher newDispatcher = nativeHToDispatcher(nativeH);
/* If the dispatcher is the CSunMiscSignal handler, then check if the signal is in range. */
if ((newDispatcher == CSunMiscSignal.countingHandlerFunctionPointer()) && (CSunMiscSignal.signalRangeCheck(sig) != 1)) {
return sunMiscSignalErrorHandler;
}
updateDispatcher(sig, newDispatcher);
final Signal.SignalDispatcher oldDispatcher = Signal.signal(sig, newDispatcher);
CIntPointer sigset = StackValue.get(CIntPointer.class);
sigset.write(1 << (sig - 1));
Signal.sigprocmask(Signal.SIG_UNBLOCK(), (Signal.sigset_tPointer) sigset, WordFactory.nullPointer());
final long result = dispatcherToNativeH(oldDispatcher);
return result;
}
Runtime initialization. /** Runtime initialization. */
private static void ensureInitialized() throws IllegalArgumentException {
/* Ask if initialization is needed. */
if (!initialized) {
/*
* Lock so initialization only happens once, and so initialization finishes before any
* uses, e.g., from other threads.
*/
initializationLock.lock();
try {
/* Ask if initialization is *still* needed now that I have the lock. */
if (!initialized) {
/* Open the C signal handling mechanism. */
final int openResult = CSunMiscSignal.open();
if (openResult != 0) {
final int openErrno = CErrorNumber.getCErrorNumber();
/* Check for the C signal handling mechanism already being open. */
if (openErrno == Errno.EBUSY()) {
throw new IllegalArgumentException("C signal handling mechanism is in use.");
}
/* Report other failure. */
Log.log().string("Util_sun_misc_Signal.ensureInitialized: CSunMiscSignal.create() failed.")
.string(" errno: ").signed(openErrno).string(" ").string(Errno.strerror(openErrno)).newline();
throw VMError.shouldNotReachHere("Util_sun_misc_Signal.ensureInitialized: CSunMiscSignal.open() failed.");
}
/* Initialize the table of signal states. */
signalState = createSignalStateTable();
/* Create and start a daemon thread to dispatch to Java signal handlers. */
dispatchThread = new Thread(new DispatchThread());
dispatchThread.setName("Signal Dispatcher");
dispatchThread.setDaemon(true);
dispatchThread.start();
RuntimeSupport.getRuntimeSupport().addTearDownHook(() -> DispatchThread.interrupt(dispatchThread));
/* Initialization is complete. */
initialized = true;
}
} finally {
initializationLock.unlock();
}
}
}
Create a table of signal states. This would be straightforward, except for the
platform-specific signals. See GR-7858: @Platform @CEnum members.
/**
* Create a table of signal states. This would be straightforward, except for the
* platform-specific signals. See GR-7858: @Platform @CEnum members.
*/
private static SignalState[] createSignalStateTable() {
/* Fill in the table. */
List<SignalState> signalStateList = new ArrayList<>();
for (Signal.SignalEnum value : Signal.SignalEnum.values()) {
signalStateList.add(new SignalState(value.name(), value.getCValue()));
}
if (IsDefined.isLinux()) {
for (Signal.LinuxSignalEnum value : Signal.LinuxSignalEnum.values()) {
signalStateList.add(new SignalState(value.name(), value.getCValue()));
}
}
if (IsDefined.isDarwin()) {
for (Signal.DarwinSignalEnum value : Signal.DarwinSignalEnum.values()) {
signalStateList.add(new SignalState(value.name(), value.getCValue()));
}
}
final SignalState[] result = signalStateList.toArray(new SignalState[0]);
return result;
}
Map from a Java signal name to a signal number. /** Map from a Java signal name to a signal number. */
protected static int numberFromName(String javaSignalName) {
ensureInitialized();
/* Java deals in signal names without the leading "SIG" prefix, but C uses it. */
final String cSignalName = "SIG" + javaSignalName;
for (int index = 0; index < signalState.length; index += 1) {
final SignalState entry = signalState[index];
if (entry.getName().equals(cSignalName)) {
return entry.getNumber();
}
}
/* {@link sun.misc.Signal#findSignal(String)} expects a -1 on failure. */
return -1;
}
Update the dispatcher of an entry in the signal state table. /** Update the dispatcher of an entry in the signal state table. */
private static void updateDispatcher(int sig, Signal.SignalDispatcher dispatcher) {
for (int index = 0; index < signalState.length; index += 1) {
final SignalState entry = signalState[index];
if (entry.getNumber() == sig) {
entry.setDispatcher(dispatcher);
return;
}
}
}
Map from the handler numbers Java uses to the function pointers that C uses. /** Map from the handler numbers Java uses to the function pointers that C uses. */
private static Signal.SignalDispatcher nativeHToDispatcher(long nativeH) {
final Signal.SignalDispatcher result;
if (nativeH == sunMiscSignalDefaultHandler) {
result = Signal.SIG_DFL();
} else if (nativeH == sunMiscSignalIgnoreHandler) {
result = Signal.SIG_IGN();
} else if (nativeH == sunMiscSignalDispatchHandler) {
result = CSunMiscSignal.countingHandlerFunctionPointer();
} else if (nativeH == sunMiscSignalErrorHandler) {
result = Signal.SIG_ERR();
} else {
result = WordFactory.pointer(nativeH);
}
return result;
}
Map from the function pointers that C uses to the numbers that Java uses. /** Map from the function pointers that C uses to the numbers that Java uses. */
private static long dispatcherToNativeH(Signal.SignalDispatcher handler) {
final long result;
if (handler == Signal.SIG_DFL()) {
result = sunMiscSignalDefaultHandler;
} else if (handler == Signal.SIG_IGN()) {
result = sunMiscSignalIgnoreHandler;
} else if (handler == CSunMiscSignal.countingHandlerFunctionPointer()) {
result = sunMiscSignalDispatchHandler;
} else if (handler == Signal.SIG_ERR()) {
result = sunMiscSignalErrorHandler;
} else {
result = handler.rawValue();
}
return result;
}
A runnable to notice when signals have been raised. /** A runnable to notice when signals have been raised. */
protected static final class DispatchThread implements Runnable {
protected DispatchThread() {
/* Nothing to do. */
}
static void interrupt(Thread thread) {
thread.interrupt();
SignalState.wakeUp();
}
Wait to be notified that a signal has been raised in the C signal handler, then find any
that were raised and dispatch to the Java signal handler. The C signal handler increments
the counts and this method decrements them.
/**
* Wait to be notified that a signal has been raised in the C signal handler, then find any
* that were raised and dispatch to the Java signal handler. The C signal handler increments
* the counts and this method decrements them.
*/
@Override
public void run() {
while (!Thread.interrupted()) {
/*
* Block waiting for one or more signals to be raised. Or a wake up for termination.
*/
SignalState.await();
if (Thread.interrupted()) {
/* Thread was interrupted for termination. */
break;
}
/* Find any counters that are non-zero. */
for (final SignalState entry : signalState) {
final SignalDispatcher dispatcher = entry.getDispatcher();
/* If the handler is the Java signal handler ... */
if (dispatcher.equal(CSunMiscSignal.countingHandlerFunctionPointer())) {
/* ... and if there are outstanding signals to be dispatched. */
if (entry.decrementCount() > 0L) {
Target_jdk_internal_misc_Signal.dispatch(entry.getNumber());
}
}
}
}
/* If this thread is exiting, then the C signal handling mechanism can be closed. */
CSunMiscSignal.close();
}
}
An entry in a table of signal numbers and handlers. There is a parallel table of counts of
outstanding signals maintained in C. There are convenience method here for accessing those
counts.
/**
* An entry in a table of signal numbers and handlers. There is a parallel table of counts of
* outstanding signals maintained in C. There are convenience method here for accessing those
* counts.
*/
private static final class SignalState {
The C signal name. /** The C signal name. */
private final String name;
The C signal number. /** The C signal number. */
private final int number;
The C signal handler. /** The C signal handler. */
private Signal.SignalDispatcher dispatcher;
This just allocates an entry. The entry is initialized at runtime. /** This just allocates an entry. The entry is initialized at runtime. */
protected SignalState(String cName, int cValue) {
this.name = cName;
this.number = cValue;
this.dispatcher = Signal.SIG_DFL();
}
protected String getName() {
return name;
}
protected int getNumber() {
return number;
}
protected Signal.SignalDispatcher getDispatcher() {
return dispatcher;
}
protected void setDispatcher(Signal.SignalDispatcher value) {
dispatcher = value;
}
/*
* Convenient access to C functions, with checks for success.
*/
protected static void await() {
final int awaitResult = CSunMiscSignal.await();
PosixUtils.checkStatusIs0(awaitResult, "Util_sun_misc_Signal.SignalState.await(): CSunMiscSignal.await() failed.");
}
protected static void wakeUp() {
final int awaitResult = CSunMiscSignal.post();
PosixUtils.checkStatusIs0(awaitResult, "Util_sun_misc_Signal.SignalState.post(): CSunMiscSignal.post() failed.");
}
/*
* Decrement a counter towards zero. Returns the original value, or -1 if the signal number
* is out of range.
*/
protected long decrementCount() {
/* Not checking the result. */
return CSunMiscSignal.decrementCount(number);
}
}
}
@AutomaticFeature
class IgnoreSIGPIPEFeature implements Feature {
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
RuntimeSupport.getRuntimeSupport().addStartupHook(new Runnable() {
@Override
/**
* Ignore SIGPIPE. Reading from a closed pipe, instead of delivering a process-wide
* signal whose default action is to terminate the process, will instead return an error
* code from the specific write operation.
*
* From pipe(7}: If all file descriptors referring to the read end of a pipe have been
* closed, then a write(2) will cause a SIGPIPE signal to be generated for the calling
* process. If the calling process is ignoring this signal, then write(2) fails with the
* error EPIPE.
*/
public void run() {
final Signal.SignalDispatcher signalResult = Signal.signal(Signal.SignalEnum.SIGPIPE.getCValue(), Signal.SIG_IGN());
VMError.guarantee(signalResult != Signal.SIG_ERR(), "IgnoreSIGPIPEFeature.run: Could not ignore SIGPIPE");
}
});
}
}
@TargetClass(className = "jdk.internal.misc.VM", onlyWith = JDK11OrLater.class)
final class Target_jdk_internal_misc_VM {
/* Implementation from src/hotspot/share/prims/jvm.cpp#L286 translated to Java. */
@Substitute
public static long getNanoTimeAdjustment(long offsetInSeconds) {
final long maxDiffSecs = 0x0100000000L;
final long minDiffSecs = -maxDiffSecs;
Time.timeval tv = StackValue.get(Time.timeval.class);
int status = Time.gettimeofday(tv, WordFactory.nullPointer());
assert status != -1 : "linux error";
long seconds = tv.tv_sec();
long nanos = tv.tv_usec() * 1000;
long diff = seconds - offsetInSeconds;
if (diff >= maxDiffSecs || diff <= minDiffSecs) {
return -1;
}
return diff * 1000000000 + nanos;
}
}
Dummy class to have a class with the file's name. /** Dummy class to have a class with the file's name. */
public final class SunMiscSubstitutions {
}