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:
Michael Bayne
2003-03-31 02:10:04 +00:00
parent 34cd22cc18
commit 5f160cbd3a
5 changed files with 510 additions and 4 deletions
@@ -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}
@@ -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);
}
}
}
}