Skip to content

Commit

Permalink
Separate the Windows portion of the signal module.
Browse files Browse the repository at this point in the history
This splits out the Windows-specific part of signal and adds a very
simple signal handler that only works with SIGINT and SIGBREAK.
  • Loading branch information
jdhardy committed May 20, 2012
1 parent 8cfc05a commit f933c70
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 156 deletions.
Expand Up @@ -58,9 +58,10 @@
<Compile Include="bz2\dotnetzip\BZip2\Rand.cs" />
<Compile Include="cmath.cs" />
<Compile Include="msvcrt.cs" />
<Compile Include="NativeSignal.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="mmap.cs" />
<Compile Include="NtSignalState.cs" />
<Compile Include="SimpleSignalState.cs" />
<Compile Include="ResourceMetaPathImporter.cs" />
<Compile Include="signal.cs" />
<Compile Include="winsound.cs" />
Expand Down
34 changes: 0 additions & 34 deletions Languages/IronPython/IronPython.Modules/NativeSignal.cs

This file was deleted.

129 changes: 129 additions & 0 deletions Languages/IronPython/IronPython.Modules/NtSignalState.cs
@@ -0,0 +1,129 @@
/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
*
* ***************************************************************************/

using System;
using System.Runtime.InteropServices;
using IronPython.Runtime;

#if FEATURE_PROCESS

namespace IronPython.Modules {
public static partial class PythonSignal {
internal class NtSignalState : PythonSignalState {
//Windows signals
internal const uint CTRL_C_EVENT = 0;
internal const uint CTRL_BREAK_EVENT = 1;
internal const uint CTRL_CLOSE_EVENT = 2;
internal const uint CTRL_LOGOFF_EVENT = 5;
internal const uint CTRL_SHUTDOWN_EVENT = 6;

//We use a single Windows event handler to process all signals. This handler simply
//delegates the work out to PySignalToPyHandler.
public NativeSignal.WinSignalsHandler WinAllSignalsHandlerDelegate;

public NtSignalState(PythonContext pc) : base(pc) {
WinAllSignalsHandlerDelegate = new NativeSignal.WinSignalsHandler(WindowsEventHandler);
NativeSignal.SetConsoleCtrlHandler(this.WinAllSignalsHandlerDelegate, true);
}

//Our implementation of WinSignalsHandler
private bool WindowsEventHandler(uint winSignal) {
bool retVal;
int pySignal;

switch (winSignal) {
case CTRL_C_EVENT:
pySignal = SIGINT;
break;
case CTRL_BREAK_EVENT:
pySignal = SIGBREAK;
break;
case CTRL_CLOSE_EVENT:
pySignal = SIGBREAK;
break;
case CTRL_LOGOFF_EVENT:
pySignal = SIGBREAK;
break;
case CTRL_SHUTDOWN_EVENT:
pySignal = SIGBREAK;
break;
default:
throw new Exception("unreachable");
}

lock (PySignalToPyHandler) {
if (PySignalToPyHandler[pySignal].GetType() == typeof(int)) {
int tempId = (int)PySignalToPyHandler[pySignal];

if (tempId == SIG_DFL) {
//SIG_DFL - we let Windows do whatever it normally would
retVal = false;
} else if (tempId == SIG_IGN) {
//SIG_IGN - we do nothing, but tell Windows we handled the signal
retVal = true;
} else {
throw new Exception("unreachable");
}
} else if (PySignalToPyHandler[pySignal] == default_int_handler) {
if (pySignal != SIGINT) {
//We're dealing with the default_int_handlerImpl which we
//know doesn't care about the frame parameter
retVal = true;
default_int_handlerImpl(pySignal, null);
} else {
//Let the real interrupt handler throw a KeyboardInterrupt for SIGINT.
//It handles this far more gracefully than we can
retVal = false;
}
} else {
//We're dealing with a callable matching PySignalHandler's signature
retVal = true;
PySignalHandler temp = (PySignalHandler)Converter.ConvertToDelegate(PySignalToPyHandler[pySignal],
typeof(PySignalHandler));

try {
if (SignalPythonContext.PythonOptions.Frames) {
temp.Invoke(pySignal, SysModule._getframeImpl(null,
0,
SignalPythonContext._mainThreadFunctionStack));
} else {
temp.Invoke(pySignal, null);
}
} catch (Exception e) {
System.Console.WriteLine(SignalPythonContext.FormatException(e));
}
}
}

return retVal;
}
}

internal static class NativeSignal {
// Windows API expects to be given a function pointer like this to handle signals
internal delegate bool WinSignalsHandler(uint winSignal);

[DllImport("Kernel32")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SetConsoleCtrlHandler(WinSignalsHandler Handler, [MarshalAs(UnmanagedType.Bool)]bool Add);

[DllImport("Kernel32")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId);
}
}
}

#endif
85 changes: 85 additions & 0 deletions Languages/IronPython/IronPython.Modules/SimpleSignalState.cs
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IronPython.Runtime;

#if FEATURE_PROCESS

namespace IronPython.Modules {
public static partial class PythonSignal {
internal class SimpleSignalState : PythonSignalState {
public SimpleSignalState(PythonContext pc)
: base(pc) {
Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress);
}

void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) {
int pySignal;
switch(e.SpecialKey) {
case ConsoleSpecialKey.ControlC:
pySignal = SIGINT;
break;

case ConsoleSpecialKey.ControlBreak:
pySignal = SIGBREAK;
break;

default:
throw new InvalidOperationException("unreachable");
}

lock (PySignalToPyHandler) {
if (PySignalToPyHandler[pySignal].GetType() == typeof(int)) {
int tempId = (int)PySignalToPyHandler[pySignal];

if (tempId == SIG_DFL) {
//SIG_DFL - do whatever it normally would
return;
} else if (tempId == SIG_IGN) {
//SIG_IGN - we do nothing, but tell the OS we handled the signal
e.Cancel = false;
return;
} else {
throw new Exception("unreachable");
}
} else if (PySignalToPyHandler[pySignal] == default_int_handler) {
if (pySignal != SIGINT) {
//We're dealing with the default_int_handlerImpl which we
//know doesn't care about the frame parameter
e.Cancel = true;
default_int_handlerImpl(pySignal, null);
return;
} else {
//Let the real interrupt handler throw a KeyboardInterrupt for SIGINT.
//It handles this far more gracefully than we can
return;
}
} else {
//We're dealing with a callable matching PySignalHandler's signature
PySignalHandler temp = (PySignalHandler)Converter.ConvertToDelegate(PySignalToPyHandler[pySignal],
typeof(PySignalHandler));

try {
if (SignalPythonContext.PythonOptions.Frames) {
temp.Invoke(pySignal, SysModule._getframeImpl(null,
0,
SignalPythonContext._mainThreadFunctionStack));
} else {
temp.Invoke(pySignal, null);
}
} catch (Exception ex) {
System.Console.WriteLine(SignalPythonContext.FormatException(ex));
}

e.Cancel = true;
return;
}
}
}
}
}
}

#endif

19 changes: 0 additions & 19 deletions Languages/IronPython/IronPython.Modules/nt.cs
Expand Up @@ -1428,25 +1428,6 @@ public class stat_result : IList, IList<object> {
[Documentation(@"Send signal sig to the process pid. Constants for the specific signals available on the host platform
are defined in the signal module.")]
public static void kill(CodeContext/*!*/ context, int pid, int sig) {

//The following calls to GenerateConsoleCtrlEvent will fail under
//most circumstances. We'll try them any ways though as this seems
//to be the only mechanism in Windows to send signals to processes
switch (sig) {
case PythonSignal.SIGINT:
if (NativeSignal.GenerateConsoleCtrlEvent(PythonSignal.CTRL_C_EVENT, (uint)pid)!= false) {
return;
}
break;
case PythonSignal.SIGBREAK:
if (NativeSignal.GenerateConsoleCtrlEvent(PythonSignal.CTRL_BREAK_EVENT, (uint)pid) != false) {
return;
}
break;
default:
throw PythonOps.ValueError("signal number out of range");
}

//If the calls to GenerateConsoleCtrlEvent didn't work, simply
//forcefully kill the process.
Process toKill = Process.GetProcessById(pid);
Expand Down

0 comments on commit f933c70

Please sign in to comment.