Skip to content

Commit

Permalink
Improve signal handling, fixes #59
Browse files Browse the repository at this point in the history
  • Loading branch information
gnodet committed Dec 13, 2016
1 parent 7576a62 commit 65dd1e9
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public void raise(Signal signal) {
}
}

@Deprecated
protected void handleDefaultSignal(Signal signal) {
}

Expand Down
36 changes: 10 additions & 26 deletions src/main/java/org/jline/terminal/impl/AbstractWindowsTerminal.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,10 @@ public AbstractWindowsTerminal(OutputStream output, String name, boolean nativeS
attributes.setControlChar(Attributes.ControlChar.VSUSP, ctrl('Z'));
// Handle signals
if (nativeSignals) {
if (signalHandler == SignalHandler.SIG_DFL) {
for (final Signal signal : Signal.values()) {
Signals.registerDefault(signal.name());
}
} else if (signalHandler == SignalHandler.SIG_IGN) {
for (final Signal signal : Signal.values()) {
Signals.registerIgnore(signal.name());
}
} else {
for (final Signal signal : Signal.values()) {
for (final Signal signal : Signal.values()) {
if (signalHandler == SignalHandler.SIG_DFL) {
nativeHandlers.put(signal, Signals.registerDefault(signal.name()));
} else {
nativeHandlers.put(signal, Signals.register(signal.name(), () -> raise(signal)));
}
}
Expand All @@ -99,26 +93,16 @@ public AbstractWindowsTerminal(OutputStream output, String name, boolean nativeS
@Override
public SignalHandler handle(Signal signal, SignalHandler handler) {
SignalHandler prev = super.handle(signal, handler);
if (handler == SignalHandler.SIG_DFL) {
Signals.registerDefault(signal.name());
nativeHandlers.remove(signal);
} else if (handler == SignalHandler.SIG_IGN) {
Signals.registerIgnore(signal.name());
nativeHandlers.remove(signal);
} else {
nativeHandlers.put(signal, Signals.register(signal.name(), () -> raise(signal)));
if (prev != handler) {
if (handler == SignalHandler.SIG_DFL) {
Signals.registerDefault(signal.name());
} else {
Signals.register(signal.name(), () -> raise(signal));
}
}
return prev;
}

@Override
protected void handleDefaultSignal(Signal signal) {
Object handler = nativeHandlers.get(signal);
if (handler != null) {
Signals.invokeHandler(signal.name(), handler);
}
}

protected String getConsoleEncoding() {
int codepage = getConsoleOutputCP();
//http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html
Expand Down
36 changes: 10 additions & 26 deletions src/main/java/org/jline/terminal/impl/PosixSysTerminal.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,10 @@ public PosixSysTerminal(String name, String type, Pty pty, String encoding,
this.writer = new PrintWriter(new OutputStreamWriter(output, encoding));
parseInfoCmp();
if (nativeSignals) {
if (signalHandler == SignalHandler.SIG_DFL) {
for (final Signal signal : Signal.values()) {
Signals.registerDefault(signal.name());
}
} else if (signalHandler == SignalHandler.SIG_IGN) {
for (final Signal signal : Signal.values()) {
Signals.registerIgnore(signal.name());
}
} else {
for (final Signal signal : Signal.values()) {
for (final Signal signal : Signal.values()) {
if (signalHandler == SignalHandler.SIG_DFL) {
nativeHandlers.put(signal, Signals.registerDefault(signal.name()));
} else {
nativeHandlers.put(signal, Signals.register(signal.name(), () -> raise(signal)));
}
}
Expand All @@ -63,26 +57,16 @@ public PosixSysTerminal(String name, String type, Pty pty, String encoding,
@Override
public SignalHandler handle(Signal signal, SignalHandler handler) {
SignalHandler prev = super.handle(signal, handler);
if (handler == SignalHandler.SIG_DFL) {
Signals.registerDefault(signal.name());
nativeHandlers.remove(signal);
} else if (handler == SignalHandler.SIG_IGN) {
Signals.registerIgnore(signal.name());
nativeHandlers.remove(signal);
} else {
nativeHandlers.put(signal, Signals.register(signal.name(), () -> raise(signal)));
if (prev != handler) {
if (handler == SignalHandler.SIG_DFL) {
Signals.registerDefault(signal.name());
} else {
Signals.register(signal.name(), () -> raise(signal));
}
}
return prev;
}

@Override
protected void handleDefaultSignal(Signal signal) {
Object handler = nativeHandlers.get(signal);
if (handler != null) {
Signals.invokeHandler(signal.name(), handler);
}
}

public NonBlockingReader reader() {
return reader;
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/org/jline/utils/Log.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public static void trace(final Object... messages) {
log(Level.FINEST, messages);
}

public static void trace(Supplier<String> supplier) {
log(Level.FINEST, supplier);
}

public static void debug(Supplier<String> supplier) {
log(Level.FINE, supplier);
}
Expand Down
39 changes: 36 additions & 3 deletions src/main/java/org/jline/utils/Signals.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
*/
package org.jline.utils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Objects;

Expand Down Expand Up @@ -44,12 +42,20 @@ public static Object register(String name, final Runnable handler, ClassLoader l
Object signalHandler = Proxy.newProxyInstance(loader,
new Class<?>[]{signalHandlerClass}, (proxy, method, args) -> {
// only method we are proxying is handle()
handler.run();
if (method.getDeclaringClass() == Object.class) {
if ("toString".equals(method.getName())) {
return handler.toString();
}
} else if (method.getDeclaringClass() == signalHandlerClass) {
Log.trace(() -> "Calling handler " + toString(handler) + " for signal " + name);
handler.run();
}
return null;
});
return doRegister(name, signalHandler);
} catch (Exception e) {
// Ignore this one too, if the above failed, the signal API is incompatible with what we're expecting
Log.debug("Error registering handler for signal ", name, e);
return null;
}
}
Expand All @@ -60,16 +66,19 @@ public static Object registerDefault(String name) {
return doRegister(name, signalHandlerClass.getField("SIG_DFL").get(null));
} catch (Exception e) {
// Ignore this one too, if the above failed, the signal API is incompatible with what we're expecting
Log.debug("Error registering default handler for signal ", name, e);
return null;
}
}

@Deprecated
public static Object registerIgnore(String name) {
try {
Class<?> signalHandlerClass = Class.forName("sun.misc.SignalHandler");
return doRegister(name, signalHandlerClass.getField("SIG_IGN").get(null));
} catch (Exception e) {
// Ignore this one too, if the above failed, the signal API is incompatible with what we're expecting
Log.debug("Error registering ignored handler for signal ", name, e);
return null;
}
}
Expand All @@ -82,9 +91,11 @@ public static void unregister(String name, Object previous) {
}
} catch (Exception e) {
// Ignore
Log.debug("Error unregistering handler for signal ", name, e);
}
}

@Deprecated
public static void invokeHandler(String name, Object handler) {
try {
Class<?> signalClass = Class.forName("sun.misc.Signal");
Expand All @@ -93,15 +104,37 @@ public static void invokeHandler(String name, Object handler) {
signalHandlerClass.getMethod("handle", signalClass).invoke(handler, signal);
} catch (Exception e) {
// Ignore
Log.debug("Error invoking handler for signal ", name, e);
}
}

private static Object doRegister(String name, Object handler) throws Exception {
Log.trace(() -> "Registering signal " + name + " with handler " + toString(handler));
if ("QUIT".equals(name) || "INFO".equals(name) && "9".equals(System.getProperty("java.specification.version"))) {
Log.trace(() -> "Ignoring unsupported signal " + name);
return null;
}
Class<?> signalClass = Class.forName("sun.misc.Signal");
Class<?> signalHandlerClass = Class.forName("sun.misc.SignalHandler");
Object signal = signalClass.getConstructor(String.class).newInstance(name);
return signalClass.getMethod("handle", signalClass, signalHandlerClass)
.invoke(null, signal, handler);
}

@SuppressWarnings("")
private static String toString(Object handler) {
try {
Class<?> signalHandlerClass = Class.forName("sun.misc.SignalHandler");
if (handler == signalHandlerClass.getField("SIG_DFL").get(null)) {
return "SIG_DFL";
}
if (handler == signalHandlerClass.getField("SIG_IGN").get(null)) {
return "SIG_IGN";
}
} catch (Throwable t) {
// ignore
}
return handler != null ? handler.toString() : "null";
}

}
10 changes: 5 additions & 5 deletions src/test/java/org/jline/terminal/impl/PosixSysTerminalTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void testNativeSignalsDefault() throws Exception {
"name", "ansi", pty,
Charset.defaultCharset().name(), true,
SignalHandler.SIG_DFL)) {
assertEquals(0, terminal.nativeHandlers.size());
assertEquals(Signal.values().length, terminal.nativeHandlers.size());
}
}

Expand All @@ -48,7 +48,7 @@ public void testNativeSignalsIgnore() throws Exception {
"name", "ansi", pty,
Charset.defaultCharset().name(), true,
SignalHandler.SIG_IGN)) {
assertEquals(0, terminal.nativeHandlers.size());
assertEquals(Signal.values().length, terminal.nativeHandlers.size());
}
}

Expand All @@ -63,11 +63,11 @@ public void testNativeSignalsRegister() throws Exception {
"name", "ansi", pty,
Charset.defaultCharset().name(), true,
SignalHandler.SIG_DFL)) {
assertEquals(0, terminal.nativeHandlers.size());
assertEquals(Signal.values().length, terminal.nativeHandlers.size());
SignalHandler prev = terminal.handle(Signal.INT, s -> {});
assertEquals(1, terminal.nativeHandlers.size());
assertEquals(Signal.values().length, terminal.nativeHandlers.size());
terminal.handle(Signal.INT, prev);
assertEquals(0, terminal.nativeHandlers.size());
assertEquals(Signal.values().length, terminal.nativeHandlers.size());
}
}
}

0 comments on commit 65dd1e9

Please sign in to comment.