Whipped up some native code to allow signals to be caught and handled from
within Java (on Linux anyway). We dig it the most baby! git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2360 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# $Id: Makefile,v 1.4 2003/02/18 23:06:54 ray Exp $
|
||||
# $Id: Makefile,v 1.5 2003/03/31 02:10:04 mdb Exp $
|
||||
|
||||
#
|
||||
# Executable definitions
|
||||
@@ -30,10 +30,12 @@ INSTALL_TARGET=${INSTALL_PATH}/${TARGET}
|
||||
all: ${INSTALL_TARGET}
|
||||
|
||||
${INSTALL_TARGET}: com_threerings_util_keybd_Keyboard.c
|
||||
${CC} ${INCLUDES} -c com_threerings_util_keybd_Keyboard.c \
|
||||
@echo Compiling Keyboard.c
|
||||
@${CC} ${INCLUDES} -c com_threerings_util_keybd_Keyboard.c \
|
||||
-o com_threerings_util_keybd_Keyboard.o
|
||||
${MKDIR} -p ${INSTALL_PATH}
|
||||
${CC} -o ${INSTALL_TARGET} com_threerings_util_keybd_Keyboard.o \
|
||||
@echo Creating libkeybd.so
|
||||
@${MKDIR} -p ${INSTALL_PATH}
|
||||
@${CC} -o ${INSTALL_TARGET} com_threerings_util_keybd_Keyboard.o \
|
||||
${LIBRARIES} ${LIBRARIES_PATH} -shared
|
||||
|
||||
clean:
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
//
|
||||
// $Id: SignalManager.java,v 1.1 2003/03/31 02:10:04 mdb Exp $
|
||||
|
||||
package com.threerings.util.signal;
|
||||
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.ObserverList;
|
||||
import com.threerings.util.Log;
|
||||
|
||||
/**
|
||||
* Uses native code to catch Unix signals and invoke callbacks on a
|
||||
* separate signal handler thread. If the native library cannot be loaded,
|
||||
* signal handlers will be allowed to be registered but will never be
|
||||
* called.
|
||||
*/
|
||||
public class SignalManager
|
||||
{
|
||||
/* Hangup (POSIX). */
|
||||
public static final int SIGHUP = 1;
|
||||
|
||||
/* Interrupt (ANSI). */
|
||||
public static final int SIGINT = 2;
|
||||
|
||||
/* Quit (POSIX). */
|
||||
public static final int SIGQUIT = 3;
|
||||
|
||||
/* Illegal instruction (ANSI). */
|
||||
public static final int SIGILL = 4;
|
||||
|
||||
/* Trace trap (POSIX). */
|
||||
public static final int SIGTRAP = 5;
|
||||
|
||||
/* Abort (ANSI). */
|
||||
public static final int SIGABRT = 6;
|
||||
|
||||
/* IOT trap (4.2 BSD). */
|
||||
public static final int SIGIOT = 6;
|
||||
|
||||
/* BUS error (4.2 BSD). */
|
||||
public static final int SIGBUS = 7;
|
||||
|
||||
/* Floating-point exception (ANSI). */
|
||||
public static final int SIGFPE = 8;
|
||||
|
||||
/* Kill, unblockable (POSIX). */
|
||||
public static final int SIGKILL = 9;
|
||||
|
||||
/* User-defined signal 1 (POSIX). */
|
||||
public static final int SIGUSR1 = 10;
|
||||
|
||||
/* Segmentation violation (ANSI). */
|
||||
public static final int SIGSEGV = 11;
|
||||
|
||||
/* User-defined signal 2 (POSIX). */
|
||||
public static final int SIGUSR2 = 12;
|
||||
|
||||
/* Broken pipe (POSIX). */
|
||||
public static final int SIGPIPE = 13;
|
||||
|
||||
/* Alarm clock (POSIX). */
|
||||
public static final int SIGALRM = 14;
|
||||
|
||||
/* Termination (ANSI). */
|
||||
public static final int SIGTERM = 15;
|
||||
|
||||
/* Stack fault. */
|
||||
public static final int SIGSTKFLT = 16;
|
||||
|
||||
/* Child status has changed (POSIX). */
|
||||
public static final int SIGCHLD = 17;
|
||||
|
||||
/* Continue (POSIX). */
|
||||
public static final int SIGCONT = 18;
|
||||
|
||||
/* Stop, unblockable (POSIX). */
|
||||
public static final int SIGSTOP = 19;
|
||||
|
||||
/* Keyboard stop (POSIX). */
|
||||
public static final int SIGTSTP = 20;
|
||||
|
||||
/* Background read from tty (POSIX). */
|
||||
public static final int SIGTTIN = 21;
|
||||
|
||||
/* Background write to tty (POSIX). */
|
||||
public static final int SIGTTOU = 22;
|
||||
|
||||
/* Urgent condition on socket (4.2 BSD). */
|
||||
public static final int SIGURG = 23;
|
||||
|
||||
/* CPU limit exceeded (4.2 BSD). */
|
||||
public static final int SIGXCPU = 24;
|
||||
|
||||
/* File size limit exceeded (4.2 BSD). */
|
||||
public static final int SIGXFSZ = 25;
|
||||
|
||||
/* Virtual alarm clock (4.2 BSD). */
|
||||
public static final int SIGVTALRM = 26;
|
||||
|
||||
/* Profiling alarm clock (4.2 BSD). */
|
||||
public static final int SIGPROF = 27;
|
||||
|
||||
/* Window size change (4.3 BSD, Sun). */
|
||||
public static final int SIGWINCH = 28;
|
||||
|
||||
/* I/O now possible (4.2 BSD). */
|
||||
public static final int SIGIO = 29;
|
||||
|
||||
/* Pollable event occurred (System V). */
|
||||
public static final int SIGPOLL = SIGIO;
|
||||
|
||||
/* Power failure restart (System V). */
|
||||
public static final int SIGPWR = 30;
|
||||
|
||||
/* Bad system call. */
|
||||
public static final int SIGSYS = 31;
|
||||
|
||||
/** Used to dispatch signal notifications. */
|
||||
public static interface SignalHandler
|
||||
{
|
||||
/**
|
||||
* Called when the specified signal is received.
|
||||
*
|
||||
* @return true if the signal handler should remain registered,
|
||||
* false if it should be removed.
|
||||
*/
|
||||
public boolean signalReceived (int signal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if signal dispatching services are available, false if
|
||||
* we could not load our native library.
|
||||
*/
|
||||
public boolean servicesAvailable ()
|
||||
{
|
||||
return _haveLibrary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a signal handler for the specified signal.
|
||||
*/
|
||||
public synchronized static void registerSignalHandler (
|
||||
int signal, SignalHandler handler)
|
||||
{
|
||||
ObserverList list = (ObserverList)_handlers.get(signal);
|
||||
if (list == null) {
|
||||
_handlers.put(signal, list = new ObserverList(
|
||||
ObserverList.SAFE_IN_ORDER_NOTIFY));
|
||||
if (_haveLibrary) {
|
||||
activateHandler(signal);
|
||||
}
|
||||
}
|
||||
list.add(handler);
|
||||
|
||||
// make sure the signal dispatcher thread is started
|
||||
if (_haveLibrary && _sigdis == null) {
|
||||
_sigdis = new Thread() {
|
||||
public void run () {
|
||||
Log.info("Dispatching signals...");
|
||||
dispatchSignals();
|
||||
}
|
||||
};
|
||||
_sigdis.setDaemon(true);
|
||||
_sigdis.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the registration for the specified signal handler.
|
||||
*/
|
||||
public synchronized static void removeSignalHandler (
|
||||
int signal, SignalHandler handler)
|
||||
{
|
||||
ObserverList list = (ObserverList)_handlers.get(signal);
|
||||
if (list == null || !list.contains(handler)) {
|
||||
Log.warning("Requested to remove non-registered handler " +
|
||||
"[signal=" + signal + ", handler=" + handler + "].");
|
||||
return;
|
||||
}
|
||||
list.remove(handler);
|
||||
checkEmpty(signal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by native code when a signal is received.
|
||||
*/
|
||||
protected synchronized static void signalReceived (final int signal)
|
||||
{
|
||||
// this is hack, but we seem to get a call to our signal handler
|
||||
// for each thread if the user presses ctrl-c in the terminal, so
|
||||
// we "collapse" those into one call back
|
||||
long now = System.currentTimeMillis();
|
||||
if (signal == SIGINT) {
|
||||
if (_lastINTed == now) {
|
||||
return;
|
||||
} else {
|
||||
_lastINTed = now;
|
||||
}
|
||||
}
|
||||
ObserverList list = (ObserverList)_handlers.get(signal);
|
||||
if (list != null) {
|
||||
list.apply(new ObserverList.ObserverOp() {
|
||||
public boolean apply (Object obs) {
|
||||
return ((SignalHandler)obs).signalReceived(signal);
|
||||
}
|
||||
});
|
||||
}
|
||||
checkEmpty(signal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a signal handler is removed.
|
||||
*/
|
||||
protected static void checkEmpty (int signal)
|
||||
{
|
||||
ObserverList list = (ObserverList)_handlers.get(signal);
|
||||
if (list != null && list.size() == 0) {
|
||||
_handlers.remove(signal);
|
||||
if (_haveLibrary) {
|
||||
deactivateHandler(signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the signal handler for the specified signal.
|
||||
*/
|
||||
protected static native void activateHandler (int signal);
|
||||
|
||||
/**
|
||||
* Deactivates the signal handler for the specified signal.
|
||||
*/
|
||||
protected static native void deactivateHandler (int signal);
|
||||
|
||||
/**
|
||||
* Consigns a Java thread to the task of dispatching signal handler
|
||||
* callbacks.
|
||||
*/
|
||||
protected static native void dispatchSignals ();
|
||||
|
||||
/** A mapping from signal number to a list of handlers. */
|
||||
protected static HashIntMap _handlers = new HashIntMap();
|
||||
|
||||
/** Our signal dispatcher thread. */
|
||||
protected static Thread _sigdis;
|
||||
|
||||
/** Set to true if we successfully load our native library. */
|
||||
protected static boolean _haveLibrary;
|
||||
|
||||
/** Used to collapse bogus multiple delivery of SIGINT when the user
|
||||
* pressed ctrl-c in the console. */
|
||||
protected static long _lastINTed = 0L;
|
||||
|
||||
static {
|
||||
try {
|
||||
System.loadLibrary("signal");
|
||||
_haveLibrary = true;
|
||||
} catch (Throwable t) {
|
||||
Log.info("Could not load libsignal.so; signal handling disabled.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* Header for class com_threerings_util_signal_SignalManager */
|
||||
|
||||
#ifndef _Included_com_threerings_util_signal_SignalManager
|
||||
#define _Included_com_threerings_util_signal_SignalManager
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#undef com_threerings_util_signal_SignalManager_SIGHUP
|
||||
#define com_threerings_util_signal_SignalManager_SIGHUP 1L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGINT
|
||||
#define com_threerings_util_signal_SignalManager_SIGINT 2L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGQUIT
|
||||
#define com_threerings_util_signal_SignalManager_SIGQUIT 3L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGILL
|
||||
#define com_threerings_util_signal_SignalManager_SIGILL 4L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGTRAP
|
||||
#define com_threerings_util_signal_SignalManager_SIGTRAP 5L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGABRT
|
||||
#define com_threerings_util_signal_SignalManager_SIGABRT 6L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGIOT
|
||||
#define com_threerings_util_signal_SignalManager_SIGIOT 6L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGBUS
|
||||
#define com_threerings_util_signal_SignalManager_SIGBUS 7L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGFPE
|
||||
#define com_threerings_util_signal_SignalManager_SIGFPE 8L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGKILL
|
||||
#define com_threerings_util_signal_SignalManager_SIGKILL 9L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGUSR1
|
||||
#define com_threerings_util_signal_SignalManager_SIGUSR1 10L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGSEGV
|
||||
#define com_threerings_util_signal_SignalManager_SIGSEGV 11L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGUSR2
|
||||
#define com_threerings_util_signal_SignalManager_SIGUSR2 12L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGPIPE
|
||||
#define com_threerings_util_signal_SignalManager_SIGPIPE 13L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGALRM
|
||||
#define com_threerings_util_signal_SignalManager_SIGALRM 14L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGTERM
|
||||
#define com_threerings_util_signal_SignalManager_SIGTERM 15L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGSTKFLT
|
||||
#define com_threerings_util_signal_SignalManager_SIGSTKFLT 16L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGCHLD
|
||||
#define com_threerings_util_signal_SignalManager_SIGCHLD 17L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGCONT
|
||||
#define com_threerings_util_signal_SignalManager_SIGCONT 18L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGSTOP
|
||||
#define com_threerings_util_signal_SignalManager_SIGSTOP 19L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGTSTP
|
||||
#define com_threerings_util_signal_SignalManager_SIGTSTP 20L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGTTIN
|
||||
#define com_threerings_util_signal_SignalManager_SIGTTIN 21L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGTTOU
|
||||
#define com_threerings_util_signal_SignalManager_SIGTTOU 22L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGURG
|
||||
#define com_threerings_util_signal_SignalManager_SIGURG 23L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGXCPU
|
||||
#define com_threerings_util_signal_SignalManager_SIGXCPU 24L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGXFSZ
|
||||
#define com_threerings_util_signal_SignalManager_SIGXFSZ 25L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGVTALRM
|
||||
#define com_threerings_util_signal_SignalManager_SIGVTALRM 26L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGPROF
|
||||
#define com_threerings_util_signal_SignalManager_SIGPROF 27L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGWINCH
|
||||
#define com_threerings_util_signal_SignalManager_SIGWINCH 28L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGIO
|
||||
#define com_threerings_util_signal_SignalManager_SIGIO 29L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGPOLL
|
||||
#define com_threerings_util_signal_SignalManager_SIGPOLL 29L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGPWR
|
||||
#define com_threerings_util_signal_SignalManager_SIGPWR 30L
|
||||
#undef com_threerings_util_signal_SignalManager_SIGSYS
|
||||
#define com_threerings_util_signal_SignalManager_SIGSYS 31L
|
||||
/* Inaccessible static: _handlers */
|
||||
/*
|
||||
* Class: com_threerings_util_signal_SignalManager
|
||||
* Method: activateHandler
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_threerings_util_signal_SignalManager_activateHandler
|
||||
(JNIEnv *, jclass, jint);
|
||||
|
||||
/*
|
||||
* Class: com_threerings_util_signal_SignalManager
|
||||
* Method: deactivateHandler
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_threerings_util_signal_SignalManager_deactivateHandler
|
||||
(JNIEnv *, jclass, jint);
|
||||
|
||||
/*
|
||||
* Class: com_threerings_util_signal_SignalManager
|
||||
* Method: dispatchSignals
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_threerings_util_signal_SignalManager_dispatchSignals
|
||||
(JNIEnv *, jclass);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#
|
||||
# $Id: Makefile,v 1.1 2003/03/31 02:10:04 mdb Exp $
|
||||
|
||||
#
|
||||
# Executable definitions
|
||||
|
||||
CC=gcc
|
||||
RM=rm
|
||||
CP=cp
|
||||
MKDIR=mkdir
|
||||
|
||||
#
|
||||
# Directory definitions
|
||||
|
||||
ROOT=../../../../../../..
|
||||
LIBRARIES_PATH=
|
||||
INSTALL_PATH=${ROOT}/dist/lib/i686-Linux
|
||||
|
||||
#
|
||||
# Parameter and file definitions
|
||||
|
||||
INCLUDES=-I.. -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux
|
||||
LIBRARIES=
|
||||
TARGET=libsignal.so
|
||||
INSTALL_TARGET=${INSTALL_PATH}/${TARGET}
|
||||
|
||||
#
|
||||
# Target definitions
|
||||
|
||||
all: ${INSTALL_TARGET}
|
||||
|
||||
${INSTALL_TARGET}: com_threerings_util_signal_SignalManager.c
|
||||
@echo Compiling SignalManager.c
|
||||
@${CC} ${INCLUDES} -c com_threerings_util_signal_SignalManager.c \
|
||||
-o com_threerings_util_signal_SignalManager.o
|
||||
@echo Creating libsignal.so
|
||||
@${MKDIR} -p ${INSTALL_PATH}
|
||||
@${CC} -o ${INSTALL_TARGET} com_threerings_util_signal_SignalManager.o \
|
||||
${LIBRARIES} ${LIBRARIES_PATH} -shared
|
||||
|
||||
clean:
|
||||
-${RM} *.o
|
||||
-${RM} ${INSTALL_TARGET}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* $Id: com_threerings_util_signal_SignalManager.c,v 1.1 2003/03/31 02:10:04 mdb Exp $
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <jni.h>
|
||||
#include "com_threerings_util_signal_SignalManager.h"
|
||||
|
||||
static int writefd;
|
||||
|
||||
typedef void (*sighandler_t)(int);
|
||||
static sighandler_t old_handlers[64];
|
||||
static sighandler_t handlers[64];
|
||||
|
||||
static void
|
||||
signal_handler (int signo)
|
||||
{
|
||||
write(writefd, &signo, sizeof(signo));
|
||||
signal(SIGINT, signal_handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class: com_threerings_util_signal_SignalManager
|
||||
* Method: activateHandler
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_threerings_util_signal_SignalManager_activateHandler (
|
||||
JNIEnv* env, jclass clazz, jint signo)
|
||||
{
|
||||
old_handlers[signo] = signal(signo, signal_handler);
|
||||
if (old_handlers[signo] == SIG_ERR) {
|
||||
fprintf(stderr, "Error setting signal handler (%d): %s\n",
|
||||
signo, strerror(errno));
|
||||
old_handlers[signo] = 0;
|
||||
} else {
|
||||
handlers[signo] = signal_handler;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class: com_threerings_util_signal_SignalManager
|
||||
* Method: deactivateHandler
|
||||
* Signature: (I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_threerings_util_signal_SignalManager_deactivateHandler (
|
||||
JNIEnv* env, jclass clazz, jint signo)
|
||||
{
|
||||
if (old_handlers[signo] != 0) {
|
||||
signal(signo, old_handlers[signo]);
|
||||
old_handlers[signo] = 0;
|
||||
handlers[signo] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class: com_threerings_util_signal_SignalManager
|
||||
* Method: dispatchSignals
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_threerings_util_signal_SignalManager_dispatchSignals (
|
||||
JNIEnv* env, jclass clazz)
|
||||
{
|
||||
int filedes[2], readfd;
|
||||
jint signo;
|
||||
jmethodID mid = (*env)->GetStaticMethodID(
|
||||
env, clazz, "signalReceived", "(I)V");
|
||||
|
||||
if (pipe(filedes) < 0) {
|
||||
fprintf(stderr, "Failed to create signal pipe: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
readfd = filedes[0];
|
||||
writefd = filedes[1];
|
||||
|
||||
while (1) {
|
||||
int got = read(readfd, &signo, sizeof(int));
|
||||
if (got < 0) {
|
||||
fprintf(stderr, "Signal pipe read failed: %s\n", strerror(errno));
|
||||
} else if (got == 0) {
|
||||
fprintf(stderr, "Signal pipe read returned zero bytes.\n");
|
||||
} else {
|
||||
if (signo < 0 || signo >= 64 || handlers[signo] == 0) {
|
||||
fprintf(stderr, "Received bogus signal (%d).\n", signo);
|
||||
} else {
|
||||
(*env)->CallStaticVoidMethod(env, clazz, mid, signo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user